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)
{
}
}
}