Decompiled source of PurpleTurtle AdminToolNotifier v0.1.0

BepInEx/plugins/PurpleTurtleAdminToolNotifier/MagiCorp.PurpleTurtleAdminToolNotifier.Client.dll

Decompiled 4 hours ago
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<FieldInfo, bool> flags = new Dictionary<FieldInfo, bool>();

	private readonly Queue<string> pending = new Queue<string>();

	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<string>("MagiCorp_QolAck_v1", (Action<ZRpc, string>)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<SkillType>(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();
				};
			}
		}
	}
}