Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ModlistHashChecker v0.2.1
ModListHashChecker.dll
Decompiled 8 months agousing 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) { } } }