using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using HarmonyLib; using UnityEngine; using UnityEngine.Events; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("MagiCorp.PurpleTurtleAdminToolNotifier.Client")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+dc87b9315a533b989877c4e4b7185e445a0c55e7")] [assembly: AssemblyProduct("MagiCorp.PurpleTurtleAdminToolNotifier.Client")] [assembly: AssemblyTitle("MagiCorp.PurpleTurtleAdminToolNotifier.Client")] [assembly: AssemblyVersion("1.0.0.0")] namespace MagiCorp.PurpleTurtleAdminToolNotifier; [BepInPlugin("magicorp.purpleturtle.admintoolnotifier.client", "Purple Turtle Admin Tool Notifier", "0.1.0")] public sealed class ClientNotifier : BaseUnityPlugin { private const string PluginVersion = "0.1.0"; private const string EventRpc = "MagiCorp_QolAudit_v1"; private const string HelloRpc = "MagiCorp_QolHello_v1"; private const string AckRpc = "MagiCorp_QolAck_v1"; private static ClientNotifier I; private Harmony harmony; private bool hooked; private float nextScan; private float nextHello; private ZRpc serverRpc; private bool ack; private readonly Dictionary flags = new Dictionary(); private readonly Queue pending = new Queue(); private static readonly BindingFlags All = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private void Awake() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown I = this; harmony = new Harmony("magicorp.purpleturtle.admintoolnotifier.client"); TryHook(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Purple Turtle Admin Tool Notifier 0.1.0 loaded. Client-side telemetry for the Purple Turtle server only."); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } I = null; } private void Update() { if (Time.unscaledTime < nextScan) { return; } nextScan = Time.unscaledTime + 0.25f; try { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || instance.IsServer()) { ResetConnection(); return; } TryHook(); ZNetPeer serverPeer = instance.GetServerPeer(); if (serverPeer == null || !serverPeer.IsReady()) { ResetConnection(); return; } if (serverRpc != serverPeer.m_rpc) { ResetConnection(); serverRpc = serverPeer.m_rpc; serverRpc.Register("MagiCorp_QolAck_v1", (Action)delegate(ZRpc rpc, string v) { if (rpc == serverRpc) { if (!ack) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"Purple Turtle audit receiver acknowledged this client."); } ack = true; } }); } if (Time.unscaledTime >= nextHello) { nextHello = Time.unscaledTime + 10f; serverRpc.Invoke("MagiCorp_QolHello_v1", new object[1] { "0.1.0;Purple Turtle client;" + (hooked ? "QoL hooks active" : "target mod absent or hooks incomplete") }); } if ((Object)(object)Player.m_localPlayer != (Object)null) { ReadFlags(); } if (!ack) { return; } for (int num = 0; num < 16; num++) { if (pending.Count <= 0) { break; } serverRpc.Invoke("MagiCorp_QolAudit_v1", new object[1] { pending.Dequeue() }); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogDebug((object)ex.Message); } } private void ResetConnection() { serverRpc = null; ack = false; nextHello = 0f; pending.Clear(); FieldInfo[] array = flags.Keys.ToArray(); foreach (FieldInfo key in array) { flags[key] = false; } } private void Report(string feature, string detail) { if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && !((Object)(object)Player.m_localPlayer == (Object)null)) { if (pending.Count >= 1024) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Purple Turtle audit queue full; report dropped"); return; } pending.Enqueue(Clean(feature, 80) + "\t" + Clean(detail, 700)); ((BaseUnityPlugin)this).Logger.LogWarning((object)("[PurpleTurtleAudit] " + Clean(feature, 80) + ": " + Clean(detail, 300))); } } private void TryHook() { //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Expected O, but got Unknown if (hooked || AccessTools.TypeByName("ValheimAdminTool.Menus.CheatsMenu") == null) { return; } string[,] array = new string[5, 3] { { "ValheimAdminTool.Menus.ItemsMenu", "SpawnItem", "ActionPrefix" }, { "ValheimAdminTool.Menus.EntitiesMenu", "SpawnEntity", "ActionPrefix" }, { "ValheimAdminTool.Menus.SkillsMenu", "SetSkill", "ActionPrefix" }, { "ValheimAdminTool.Controls.ModButton", "CreateButton", "ButtonPrefix" }, { "ValheimAdminTool.Controls.ModSwitch", "CreateSwitch", "SwitchPrefix" } }; int num = 0; for (int i = 0; i < array.GetLength(0); i++) { MethodInfo methodInfo = AccessTools.Method(AccessTools.TypeByName(array[i, 0]), array[i, 1], (Type[])null, (Type[])null); if (methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Missing audit hook: " + array[i, 0] + "." + array[i, 1])); continue; } Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo); if (patchInfo == null || !patchInfo.Owners.Contains(harmony.Id)) { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ClientNotifier), array[i, 2], (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } num++; } string[] array2 = new string[3] { "CheatsMenu", "ToolsMenu", "ESPMenu" }; foreach (string text in array2) { Type type = AccessTools.TypeByName("ValheimAdminTool.Menus." + text); if (!(type != null)) { continue; } foreach (FieldInfo item in from f in type.GetFields(All) where f.FieldType == typeof(bool) select f) { if (!flags.ContainsKey(item)) { flags.Add(item, value: false); } } } hooked = num == 5; ((BaseUnityPlugin)this).Logger.LogInfo((object)("Purple Turtle Admin Tool hooks: " + num + "/5; monitored switches: " + flags.Count)); } private void ReadFlags() { FieldInfo[] array = flags.Keys.ToArray(); foreach (FieldInfo fieldInfo in array) { bool flag = (bool)fieldInfo.GetValue(null); if (flag != flags[fieldInfo]) { flags[fieldInfo] = flag; Report(fieldInfo.DeclaringType.Name + "." + fieldInfo.Name, flag ? "enabled" : "disabled"); } } } private static string Clean(string s, int max) { s = new string((s ?? "").Select((char c) => (!char.IsControl(c)) ? c : ' ').ToArray()); if (s.Length <= max) { return s; } return s.Substring(0, max); } private static void ActionPrefix(MethodBase __originalMethod, object[] __args) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)I == (Object)null)) { string name = __originalMethod.Name; if (!(name == "SetSkill") || !((Object)(object)Player.m_localPlayer != (Object)null) || !Enum.TryParse(Convert.ToString(__args[0]), ignoreCase: true, out SkillType result) || !(Math.Abs(((Character)Player.m_localPlayer).GetSkills().GetSkillLevel(result) - Mathf.Clamp(Convert.ToSingle(__args[1]), 0f, 100f)) < 0.001f)) { string text = ((name == "SetSkill") ? ("skill=" + __args[0]?.ToString() + " value=" + Convert.ToString(__args[1], CultureInfo.InvariantCulture)) : ("prefab=" + __args[0]?.ToString() + " requested_amount=" + __args[2]?.ToString() + ((name == "SpawnEntity") ? (" tamed=" + __args[3]?.ToString() + " level=" + __args[4]) : ""))); I.Report(name, "attempt: " + text); } } } private static void ButtonPrefix(object[] __args) { //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown if ((Object)(object)I == (Object)null || __args.Length < 10) { return; } object obj = __args[9]; UnityAction original = (UnityAction)((obj is UnityAction) ? obj : null); if (original == null) { return; } string text = ((Delegate)(object)original).Method.DeclaringType?.FullName ?? ""; if (text.Contains("CheatsMenu") || text.Contains("ToolsMenu")) { string label = Convert.ToString(__args[2]); __args[9] = (object)(UnityAction)delegate { I?.Report("button", "attempt: " + label); original.Invoke(); }; } } private static void SwitchPrefix(object[] __args) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown for (int i = 6; i <= 7; i++) { object obj = __args[i]; UnityAction original = (UnityAction)((obj is UnityAction) ? obj : null); if (original != null) { __args[i] = (object)(UnityAction)delegate { original.Invoke(); I?.ReadFlags(); }; } } } }