Decompiled source of ModlistHashChecker v0.2.1

ModListHashChecker.dll

Decompiled 3 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Steamworks;
using Steamworks.Data;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("darmuh, mrov, Electric")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+fdd84e6606292ded357b2ad62a541778986b0241")]
[assembly: AssemblyProduct("ModListHashChecker")]
[assembly: AssemblyTitle("ModListHashChecker")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ModListHashChecker
{
	public class ConfigManager
	{
		public static ConfigEntry<string> ExpectedModListHash { get; private set; }

		public static ConfigEntry<bool> NoExpectedHashMessage { get; private set; }

		public static ConfigEntry<bool> MenuWarning { get; private set; }

		public static ConfigEntry<bool> JoinWarning { get; private set; }

		public static ConfigEntry<string> WarningMessageText { get; private set; }

		public static ConfigEntry<string> JoinWarningText { get; private set; }

		public static ConfigEntry<int> JoinWarningDelay { get; private set; }

		public static ConfigEntry<string> WarningButtonIgnoreText { get; private set; }

		public static ConfigEntry<string> WarningButtonResetText { get; private set; }

		public static ConfigEntry<string> NoHashMessageText { get; private set; }

		public static ConfigEntry<string> NoHashRightButtonText { get; private set; }

		public static ConfigEntry<string> NoHashLeftButtonText { get; private set; }

		public static ConfigEntry<bool> DisplayHashOnLevelLoad { get; private set; }

		public static ConfigEntry<bool> ChatHashMessageToAll { get; private set; }

		internal static void Init(ConfigFile config)
		{
			ExpectedModListHash = config.Bind<string>("General", "ExpectedModListHash", "", "The expected modlist hash for this modpack. Do not change this unless you know what you're doing.");
			NoExpectedHashMessage = config.Bind<bool>("General", "NoExpectedHashMessage", true, "Enable or Disable displaying a warning message in the menus when the expected hash is empty. Do not change this unless you know what you're doing.");
			NoHashMessageText = config.Bind<string>("Menu Warning", "NoHashMessageText", "ExpectedModListHash configuration item is blank.\n\nWould you like to set it to the currently loaded list of mods?", "Menu Message to display when the expected hash is empty");
			NoHashRightButtonText = config.Bind<string>("Menu Warning", "NoHashRightButtonText", "No", "Button text for leaving the ExpectedModListHash blank");
			NoHashLeftButtonText = config.Bind<string>("Menu Warning", "NoHashLeftButtonText", "Yes", "Button text for setting the ExpectedModListHashto the detected hash");
			MenuWarning = config.Bind<bool>("General", "MenuWarning", true, "Enable or Disable displaying a warning message in the menus when the hash does not match the expected hash.");
			JoinWarning = config.Bind<bool>("General", "JoinWarning", true, "Enable or Disable displaying a warning message when a client joins and the hash does not match the host hash.");
			JoinWarningText = config.Bind<string>("Join Warning", "JoinWarningText", "Your modlist does not match the expected modlist hash.\n\n You may experience issues.", "Message to display in Hash Mismatch Menu Warning Message");
			JoinWarningDelay = config.Bind<int>("Join Warning", "JoinWarningDelay", 5, "Seconds delay until hud warning is displayed for mismatched hash on client join.");
			WarningMessageText = config.Bind<string>("Menu Warning", "WarningMessageText", "Your modlist does not match the expected modlist hash.\n\n You may experience issues.", "Message to display in Hash Mismatch Menu Warning Message");
			WarningButtonIgnoreText = config.Bind<string>("Menu Warning", "WarningButtonIgnoreText", "Okay", "Button text for ignoring the Hash Mismatch Menu Warning Message");
			WarningButtonResetText = config.Bind<string>("Menu Warning", "WarningButtonResetText", "Reset", "Button text for reseting ExpectedModListHash to the detected hash in Hash Mismatch Menu Warning Message");
			DisplayHashOnLevelLoad = config.Bind<bool>("General", "DisplayHashOnLevelLoad", true, "When enabled, will display the modlist hash in the chat on level load.");
			ChatHashMessageToAll = config.Bind<bool>("General", "ChatHashMessageToAll", false, "If DisplayHashOnLevelLoad is enabled, this will determine if your chat message is sent to all players (true) or just displayed for the local client (false)");
		}
	}
	internal class GamePatching
	{
		[HarmonyPatch(typeof(GameNetworkManager), "SteamMatchmaking_OnLobbyCreated")]
		public class LobbyCreatedPatch
		{
			private static void Postfix(Result result, ref Lobby lobby)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Invalid comparison between Unknown and I4
				if ((int)result == 1)
				{
					((Lobby)(ref lobby)).SetData("ModListHash", DictionaryHashGenerator.GenerateHash(Chainloader.PluginInfos));
					ModListHashChecker.Log.LogInfo((object)("Setting lobby ModHashList to " + HashGeneration.GeneratedHash));
				}
			}
		}

		[HarmonyPatch(typeof(RoundManager), "FinishGeneratingLevel")]
		internal class DisplayHashPerRound
		{
			private static void Postfix()
			{
				if (!((Object)(object)HUDManager.Instance == (Object)null) && ConfigManager.DisplayHashOnLevelLoad.Value)
				{
					if (ConfigManager.ChatHashMessageToAll.Value)
					{
						HUDManager.Instance.AddTextToChatOnServer(StartOfRound.Instance.localPlayerController.playerUsername + " ModListHash: " + HashGeneration.GeneratedHash, -1);
					}
					else
					{
						HUDManager.Instance.AddChatMessage("Local ModListHash: " + HashGeneration.GeneratedHash, "", -1, true);
					}
				}
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "Start")]
		public class StartRoundPatch
		{
			[CompilerGenerated]
			private sealed class <WarningMessage>d__1 : IEnumerator<object>, IEnumerator, IDisposable
			{
				private int <>1__state;

				private object <>2__current;

				object IEnumerator<object>.Current
				{
					[DebuggerHidden]
					get
					{
						return <>2__current;
					}
				}

				object IEnumerator.Current
				{
					[DebuggerHidden]
					get
					{
						return <>2__current;
					}
				}

				[DebuggerHidden]
				public <WarningMessage>d__1(int <>1__state)
				{
					this.<>1__state = <>1__state;
				}

				[DebuggerHidden]
				void IDisposable.Dispose()
				{
					<>1__state = -2;
				}

				private bool MoveNext()
				{
					//IL_0023: Unknown result type (might be due to invalid IL or missing references)
					//IL_002d: Expected O, but got Unknown
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
						<>1__state = -1;
						<>2__current = (object)new WaitForSeconds((float)ConfigManager.JoinWarningDelay.Value);
						<>1__state = 1;
						return true;
					case 1:
						<>1__state = -1;
						HUDManager.Instance.DisplayTip("Modlist Hash Mismatch", ConfigManager.JoinWarningText.Value ?? "", false, false, "clientHashMismatch");
						return false;
					}
				}

				bool IEnumerator.MoveNext()
				{
					//ILSpy generated this explicit interface implementation from .override directive in MoveNext
					return this.MoveNext();
				}

				[DebuggerHidden]
				void IEnumerator.Reset()
				{
					throw new NotSupportedException();
				}
			}

			public static void Postfix()
			{
				if (ModListHashChecker.instance.ClientMismatch)
				{
					ModListHashChecker.Log.LogInfo((object)"hash mismatch detected");
					((MonoBehaviour)ModListHashChecker.instance).StartCoroutine(WarningMessage());
				}
			}

			[IteratorStateMachine(typeof(<WarningMessage>d__1))]
			private static IEnumerator WarningMessage()
			{
				//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
				return new <WarningMessage>d__1(0);
			}
		}

		[HarmonyPatch(typeof(GameNetworkManager), "StartClient")]
		public class LobbyJoinPatch
		{
			private static void Postfix()
			{
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				ModListHashChecker.Log.LogInfo((object)"Comparing your modlist with the host's modlist.");
				Lobby? currentLobby = GameNetworkManager.Instance.currentLobby;
				object obj;
				if (!currentLobby.HasValue)
				{
					obj = null;
				}
				else
				{
					Lobby valueOrDefault = currentLobby.GetValueOrDefault();
					obj = ((Lobby)(ref valueOrDefault)).GetData("ModListHash");
				}
				string text = (string)obj;
				if (text == null)
				{
					ModListHashChecker.Log.LogWarning((object)"Host does not have a modlist hash.");
					return;
				}
				ModListHashChecker.Log.LogInfo((object)("Host's modlist hash: " + text));
				ModListHashChecker.Log.LogInfo((object)("Your modlist hash: " + HashGeneration.GeneratedHash));
				if (text == HashGeneration.GeneratedHash)
				{
					ModListHashChecker.Log.LogInfo((object)"Your modlist matches the host's modlist!");
					return;
				}
				ModListHashChecker.Log.LogWarning((object)"Your modlist does not match the host's modlist.");
				ModListHashChecker.Log.LogWarning((object)"You may experience issues.");
				ModListHashChecker.instance.ClientMismatch = true;
			}
		}

		[HarmonyPatch(typeof(PreInitSceneScript), "Awake")]
		public class HashGeneration : MonoBehaviour
		{
			public static string GeneratedHash { get; internal set; } = "";


			private static void Postfix()
			{
				ModListHashChecker.Log.LogInfo((object)"Creating Modlist Hash.");
				Dictionary<string, PluginInfo> pluginInfos = Chainloader.PluginInfos;
				GeneratedHash = DictionaryHashGenerator.GenerateHash(pluginInfos);
				ModListHashChecker.Log.LogInfo((object)"==========================");
				ModListHashChecker.Log.LogInfo((object)("Modlist Hash: " + GeneratedHash));
				if (!string.IsNullOrEmpty(ConfigManager.ExpectedModListHash.Value))
				{
					ModListHashChecker.Log.LogInfo((object)("Expected Hash (from modpack): " + ConfigManager.ExpectedModListHash.Value));
					if (GeneratedHash == ConfigManager.ExpectedModListHash.Value)
					{
						ModListHashChecker.Log.LogMessage((object)"Your modlist matches the expected modlist hash.");
					}
					else
					{
						ModListHashChecker.Log.LogWarning((object)"Your modlist does not match the expected modlist hash.\nYou may experience issues.");
						ModListHashChecker.instance.HashMismatch = true;
					}
				}
				else
				{
					ModListHashChecker.Log.LogMessage((object)"No expected hash found");
					if (ConfigManager.NoExpectedHashMessage.Value)
					{
						ModListHashChecker.instance.NoHashFound = true;
					}
				}
				ModListHashChecker.Log.LogInfo((object)"==========================");
				ModListHashChecker.Log.LogInfo((object)"[Modlist Contents]");
				ModListHashChecker.Log.LogInfo((object)"Mod GUID: Mod Version");
				foreach (KeyValuePair<string, PluginInfo> item in pluginInfos)
				{
					ModListHashChecker.Log.LogInfo((object)$"{item.Key}: {item.Value}");
				}
				ModListHashChecker.Log.LogInfo((object)"==========================");
			}
		}

		[HarmonyPatch(typeof(MenuManager), "OnEnable")]
		public class EnablePatch : MonoBehaviour
		{
			[CompilerGenerated]
			private static class <>O
			{
				public static UnityAction <0>__ResetConfigHash;
			}

			internal static GameObject NewNotification;

			internal static Button FirstButton;

			internal static Button SecondButton;

			internal static TextMeshProUGUI MenuText;

			public static void Postfix(ref MenuManager __instance)
			{
				if (ModListHashChecker.instance.HashMismatch && ConfigManager.MenuWarning.Value)
				{
					MenuMessage(__instance, ConfigManager.WarningButtonResetText.Value, ConfigManager.WarningButtonIgnoreText.Value, ConfigManager.WarningMessageText.Value);
				}
				else if (ModListHashChecker.instance.NoHashFound && ConfigManager.NoExpectedHashMessage.Value)
				{
					MenuMessage(__instance, ConfigManager.NoHashLeftButtonText.Value, ConfigManager.NoHashRightButtonText.Value, ConfigManager.NoHashMessageText.Value);
				}
				else
				{
					ModListHashChecker.Log.LogInfo((object)"Not sending any messages");
				}
			}

			private static void ResetConfigHash()
			{
				ModListHashChecker.Log.LogInfo((object)"Setting expected hash to current hash.");
				ConfigManager.ExpectedModListHash.Value = HashGeneration.GeneratedHash;
				ModListHashChecker.instance.HashMismatch = false;
				ModListHashChecker.instance.NoHashFound = false;
			}

			private static void MenuSetup(MenuManager menuInstance)
			{
				if (!((Object)(object)NewNotification != (Object)null) || !((Object)(object)FirstButton != (Object)null) || !((Object)(object)SecondButton != (Object)null) || !((Object)(object)MenuText != (Object)null))
				{
					NewNotification = Object.Instantiate<GameObject>(menuInstance.menuNotification, menuInstance.menuNotification.transform.parent);
					FirstButton = NewNotification.GetComponentInChildren<Button>();
					SecondButton = Object.Instantiate<Button>(FirstButton, NewNotification.transform);
					MenuText = ((Component)NewNotification.transform.Find("Panel").Find("NotificationText")).gameObject.GetComponent<TextMeshProUGUI>();
				}
			}

			private static void MenuMessage(MenuManager menuInstance, string leftButton, string rightButton, string messageText)
			{
				//IL_0075: Unknown result type (might be due to invalid IL or missing references)
				//IL_007a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0080: Expected O, but got Unknown
				//IL_015a: Unknown result type (might be due to invalid IL or missing references)
				//IL_013c: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)menuInstance == (Object)null || (Object)(object)menuInstance.menuNotification == (Object)null)
				{
					return;
				}
				MenuSetup(menuInstance);
				if ((Object)(object)NewNotification == (Object)null || (Object)(object)SecondButton == (Object)null)
				{
					return;
				}
				TextMeshProUGUI componentInChildren = ((Component)SecondButton).GetComponentInChildren<TextMeshProUGUI>();
				((TMP_Text)componentInChildren).text = "[ " + leftButton + " ]";
				ButtonClickedEvent onClick = SecondButton.onClick;
				object obj = <>O.<0>__ResetConfigHash;
				if (obj == null)
				{
					UnityAction val = ResetConfigHash;
					<>O.<0>__ResetConfigHash = val;
					obj = (object)val;
				}
				((UnityEvent)onClick).AddListener((UnityAction)obj);
				if (menuInstance.isInitScene)
				{
					return;
				}
				ModListHashChecker.Log.LogDebug((object)("Displaying menu notification: " + messageText));
				((TMP_Text)MenuText).text = messageText;
				((TMP_Text)((Component)FirstButton).GetComponentInChildren<TextMeshProUGUI>()).text = "[ " + rightButton + " ]";
				NewNotification.SetActive(true);
				Vector3 localPosition = default(Vector3);
				((Vector3)(ref localPosition))..ctor(62f, -45f, 0f);
				Vector3 localPosition2 = default(Vector3);
				((Vector3)(ref localPosition2))..ctor(-78f, -45f, 0f);
				for (int i = 0; i < NewNotification.GetComponentsInChildren<Button>().Length; i++)
				{
					if (i == 0)
					{
						EventSystem.current.SetSelectedGameObject(((Component)NewNotification.GetComponentsInChildren<Button>()[i]).gameObject);
						((Component)NewNotification.GetComponentsInChildren<Button>()[i]).gameObject.transform.localPosition = localPosition;
					}
					else
					{
						((Component)NewNotification.GetComponentsInChildren<Button>()[i]).gameObject.transform.localPosition = localPosition2;
					}
				}
			}
		}
	}
	public class DictionaryHashGenerator
	{
		public static string GenerateHash(Dictionary<string, PluginInfo> inputDictionary)
		{
			IOrderedEnumerable<KeyValuePair<string, PluginInfo>> source = inputDictionary.OrderBy<KeyValuePair<string, PluginInfo>, string>((KeyValuePair<string, PluginInfo> entry) => entry.Key);
			string s = string.Join(",", source.Select((KeyValuePair<string, PluginInfo> entry) => $"{entry.Key}:{entry.Value}"));
			byte[] bytes = Encoding.UTF8.GetBytes(s);
			using SHA256 sHA = SHA256.Create();
			byte[] array = sHA.ComputeHash(bytes);
			StringBuilder stringBuilder = new StringBuilder();
			byte[] array2 = array;
			foreach (byte b in array2)
			{
				stringBuilder.Append(b.ToString("x2"));
			}
			return stringBuilder.ToString();
		}
	}
	[BepInPlugin("TeamMLC.ModlistHashChecker", "ModlistHashChecker", "0.2.1")]
	public class ModListHashChecker : BaseUnityPlugin
	{
		public static class PluginInfo
		{
			public const string PLUGIN_GUID = "TeamMLC.ModlistHashChecker";

			public const string PLUGIN_NAME = "ModlistHashChecker";

			public const string PLUGIN_VERSION = "0.2.1";
		}

		public static ModListHashChecker instance;

		internal static ManualLogSource Log;

		public bool NoHashFound { get; internal set; }

		public bool HashMismatch { get; internal set; }

		public bool ClientMismatch { get; internal set; }

		private void Awake()
		{
			instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"ModlistHashChecker loaded with version 0.2.1!");
			ConfigManager.Init(((BaseUnityPlugin)this).Config);
			Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null);
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "ModListHashChecker";

		public const string PLUGIN_NAME = "ModListHashChecker";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}