Decompiled source of vhvac v1.8.5

vhvac.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
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 ServerSync;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("vhvac")]
[assembly: AssemblyDescription("Valheim Anti-Cheat - Überprüft Mod-Versionen und erlaubte Mods zwischen Server und Client")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("vh")]
[assembly: AssemblyProduct("vhvac")]
[assembly: AssemblyCopyright("Copyright © vh 2024-2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("a1b2c3d4-e5f6-7890-abcd-ef1234567890")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace vhvac
{
	public static class AntiCheatMonitor
	{
		public class PlayerStats
		{
			public long PlayerId { get; set; }

			public Vector3 LastPosition { get; set; }

			public float LastCheckTime { get; set; }

			public int ViolationCount { get; set; }
		}

		private static readonly Dictionary<long, PlayerStats> PlayerTracking = new Dictionary<long, PlayerStats>();

		public static void RegisterPlayer(long playerId)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (!PlayerTracking.ContainsKey(playerId))
			{
				PlayerTracking[playerId] = new PlayerStats
				{
					PlayerId = playerId,
					LastPosition = Vector3.zero,
					LastCheckTime = Time.time,
					ViolationCount = 0
				};
			}
		}

		public static void UnregisterPlayer(long playerId)
		{
			PlayerTracking.Remove(playerId);
		}
	}
	[HarmonyPatch(typeof(ZNet), "RPC_CharacterID")]
	public static class PlayerConnectionMonitor
	{
		private static void Postfix(ZNet __instance, ZRpc rpc, ZDOID characterID)
		{
			if (__instance.IsServer())
			{
				ZNetPeer peer = __instance.GetPeer(rpc);
				if (peer != null)
				{
					VhVacPlugin.VhLogger.LogDebug((object)$"[VHVAC] Spieler verbunden: {peer.m_playerName} (UID: {peer.m_uid})");
					AntiCheatMonitor.RegisterPlayer(peer.m_uid);
				}
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "RPC_Disconnect")]
	public static class PlayerDisconnectMonitor
	{
		private static void Prefix(ZNet __instance, ZRpc rpc)
		{
			if (__instance.IsServer())
			{
				ZNetPeer peer = __instance.GetPeer(rpc);
				if (peer != null)
				{
					VhVacPlugin.VhLogger.LogDebug((object)("[VHVAC] Spieler getrennt: " + peer.m_playerName));
					AntiCheatMonitor.UnregisterPlayer(peer.m_uid);
				}
			}
		}
	}
	public static class JotunnPatcher
	{
		private const string JotunnHarmonyId = "com.jotunn.jotunn";

		public static void RemoveJotunnCompatibilityPatches()
		{
			try
			{
				VhVacPlugin.VhLogger.LogInfo((object)"[VHVAC] Suche nach Jotunn ModCompatibility Patches...");
				List<MethodBase> list = Harmony.GetAllPatchedMethods().ToList();
				int num = 0;
				foreach (MethodBase item in list)
				{
					Patches patchInfo = Harmony.GetPatchInfo(item);
					if (patchInfo == null)
					{
						continue;
					}
					List<Patch> list2 = patchInfo.Prefixes.Where((Patch p) => IsJotunnModCompatibilityPatch(p)).ToList();
					List<Patch> list3 = patchInfo.Postfixes.Where((Patch p) => IsJotunnModCompatibilityPatch(p)).ToList();
					if (list2.Count == 0 && list3.Count == 0)
					{
						continue;
					}
					string text = item.DeclaringType?.Name + "." + item.Name;
					foreach (Patch item2 in list2)
					{
						VhVacPlugin.VhLogger.LogInfo((object)("[VHVAC] Entferne Jotunn Prefix von " + text + ": " + item2.PatchMethod.Name));
						RemovePatch(item, item2);
						num++;
					}
					foreach (Patch item3 in list3)
					{
						VhVacPlugin.VhLogger.LogInfo((object)("[VHVAC] Entferne Jotunn Postfix von " + text + ": " + item3.PatchMethod.Name));
						RemovePatch(item, item3);
						num++;
					}
				}
				if (num > 0)
				{
					VhVacPlugin.VhLogger.LogInfo((object)$"[VHVAC] {num} Jotunn ModCompatibility Patches erfolgreich entfernt!");
				}
				else
				{
					VhVacPlugin.VhLogger.LogDebug((object)"[VHVAC] Keine Jotunn ModCompatibility Patches gefunden (Jotunn nicht installiert oder bereits entfernt)");
				}
			}
			catch (Exception ex)
			{
				VhVacPlugin.VhLogger.LogWarning((object)("[VHVAC] Fehler beim Entfernen von Jotunn Patches: " + ex.Message));
			}
		}

		private static bool IsJotunnModCompatibilityPatch(Patch patch)
		{
			if (patch.owner == "com.jotunn.jotunn")
			{
				Type declaringType = patch.PatchMethod.DeclaringType;
				if (declaringType != null && (declaringType.FullName ?? declaringType.Name).Contains("ModCompatibility"))
				{
					return true;
				}
			}
			return false;
		}

		private static void RemovePatch(MethodBase original, Patch patch)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				new Harmony("vhvac.patcher").Unpatch(original, patch.PatchMethod);
			}
			catch (Exception ex)
			{
				VhVacPlugin.VhLogger.LogWarning((object)("[VHVAC] Konnte Patch nicht entfernen: " + ex.Message));
			}
		}
	}
	[BepInPlugin("vhvac.core", "vhvac", "1.8.5")]
	public class VhVacPlugin : BaseUnityPlugin
	{
		internal const string ModName = "vhvac";

		internal const string ModVersion = "1.8.5";

		private const string ModGUID = "vhvac.core";

		private static string ConfigFileName = "vhvac.core.cfg";

		private static string ConfigFileFullPath;

		internal static string ConnectionError;

		internal static int ErrorCount;

		private readonly Harmony _harmony = new Harmony("vhvac.core");

		public static readonly ManualLogSource VhLogger;

		public static readonly ConfigSync ConfigSync;

		internal static VhVacPlugin Instance;

		public static string PluginsHash;

		public static Dictionary<string, ModInfo> InstalledMods;

		private static bool _modsCollected;

		private static ConfigFile _effectiveConfig;

		private static bool _usingTempClientConfig;

		public static ConfigEntry<bool> ModEnabled;

		public static ConfigEntry<bool> ConfigLocked;

		public static ConfigEntry<bool> ForceExactMods;

		public static ConfigEntry<bool> AdminBypass;

		public static ConfigEntry<bool> EnableHashCheck;

		public static ConfigEntry<bool> EnableVersionCheck;

		public static ConfigEntry<bool> EnableAllowedModsCheck;

		public static ConfigEntry<string> AllowedMods;

		public static ConfigEntry<bool> DebugMode;

		public static ConfigEntry<string> KickMessageVersionMismatch;

		public static ConfigEntry<string> KickMessageHashMismatch;

		public static ConfigEntry<string> KickMessageUnallowedMod;

		public static ConfigEntry<string> KickMessageMissingMod;

		public static bool IsServer => (int)SystemInfo.graphicsDeviceType == 4;

		private void Awake()
		{
			Instance = this;
			InitializeEffectiveConfig();
			InitConfig();
			if (ModEnabled.Value)
			{
				_harmony.PatchAll(Assembly.GetExecutingAssembly());
				JotunnPatcher.RemoveJotunnCompatibilityPatches();
				VhLogger.LogInfo((object)"[VHVAC] Valheim Anti-Cheat v1.8.5 geladen!");
			}
		}

		private void Start()
		{
			if (!ModEnabled.Value)
			{
				return;
			}
			CollectModInformation();
			PluginsHash = HashHelper.CreateMd5ForPlugins(InstalledMods.Values.ToList());
			VhLogger.LogInfo((object)("[VHVAC] Plugin Hash: " + PluginsHash));
			VhLogger.LogInfo((object)$"[VHVAC] {InstalledMods.Count} Mods gefunden");
			if (DebugMode.Value)
			{
				foreach (KeyValuePair<string, ModInfo> installedMod in InstalledMods)
				{
					VhLogger.LogDebug((object)("[VHVAC] Mod: " + installedMod.Key + " v" + installedMod.Value.Version + " - Hash: " + installedMod.Value.Hash));
				}
			}
			if (IsServer)
			{
				VhLogger.LogInfo((object)"[VHVAC] Server-Modus aktiv - Warte auf Client-Verbindungen");
			}
			else
			{
				VhLogger.LogInfo((object)"[VHVAC] Client-Modus aktiv");
			}
		}

		public static void EnsureModsCollected()
		{
			if (!_modsCollected || InstalledMods.Count == 0)
			{
				Instance?.CollectModInformation();
			}
		}

		private void InitConfig()
		{
			ModEnabled = _effectiveConfig.Bind<bool>("1. Allgemein", "Aktiviert", true, "Aktiviert oder deaktiviert den VHVAC Mod");
			ConfigLocked = _effectiveConfig.Bind<bool>("1. Allgemein", "Config gesperrt", true, "Wenn aktiviert, können nur Admins die Konfiguration ändern");
			ConfigSync.AddLockingConfigEntry<bool>(ConfigLocked);
			DebugMode = _effectiveConfig.Bind<bool>("1. Allgemein", "Debug Modus", false, "Aktiviert erweiterte Debug-Ausgaben");
			ForceExactMods = _effectiveConfig.Bind<bool>("2. Anti-Cheat", "Exakte Mods erzwingen", true, "Client muss exakt die gleichen Mods wie der Server haben");
			ConfigSync.AddConfigEntry<bool>(ForceExactMods);
			AdminBypass = _effectiveConfig.Bind<bool>("2. Anti-Cheat", "Admin Bypass", true, "Admins umgehen alle Prüfungen");
			ConfigSync.AddConfigEntry<bool>(AdminBypass);
			EnableHashCheck = _effectiveConfig.Bind<bool>("2. Anti-Cheat", "Hash Prüfung", true, "Überprüft ob der MD5-Hash der Mods übereinstimmt");
			ConfigSync.AddConfigEntry<bool>(EnableHashCheck);
			EnableVersionCheck = _effectiveConfig.Bind<bool>("2. Anti-Cheat", "Versions Prüfung", true, "Überprüft ob die Mod-Versionen übereinstimmen");
			ConfigSync.AddConfigEntry<bool>(EnableVersionCheck);
			EnableAllowedModsCheck = _effectiveConfig.Bind<bool>("2. Anti-Cheat", "Erlaubte Mods Prüfung", true, "Nur Mods aus der erlaubten Liste werden akzeptiert");
			ConfigSync.AddConfigEntry<bool>(EnableAllowedModsCheck);
			AllowedMods = _effectiveConfig.Bind<string>("2. Anti-Cheat", "Erlaubte Mods", "", "Komma-getrennte Liste von erlaubten Mod-GUIDs. Leer = alle Server-Mods sind erlaubt. Beispiel: author.modname,otherauthor.othermod");
			ConfigSync.AddConfigEntry<string>(AllowedMods);
			KickMessageVersionMismatch = _effectiveConfig.Bind<string>("3. Nachrichten", "Kick Versions Mismatch", "Mod-Version stimmt nicht überein: {0} (Deine: {1}, Server: {2})", "Nachricht bei Versions-Mismatch. {0}=ModName, {1}=ClientVersion, {2}=ServerVersion");
			KickMessageHashMismatch = _effectiveConfig.Bind<string>("3. Nachrichten", "Kick Hash Mismatch", "Mod-Dateien stimmen nicht überein. Bitte installiere die gleichen Mods wie der Server.", "Nachricht bei Hash-Mismatch");
			KickMessageUnallowedMod = _effectiveConfig.Bind<string>("3. Nachrichten", "Kick Unerlaubter Mod", "Unerlaubter Mod gefunden: {0}", "Nachricht bei unerlaubtem Mod. {0}=ModName");
			KickMessageMissingMod = _effectiveConfig.Bind<string>("3. Nachrichten", "Kick Fehlender Mod", "Fehlender Mod: {0}", "Nachricht bei fehlendem Mod. {0}=ModName");
		}

		private void InitializeEffectiveConfig()
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			if (_effectiveConfig != null)
			{
				return;
			}
			string path = Path.Combine(Paths.ConfigPath, "vhvac.core.cfg");
			bool flag = Application.isBatchMode;
			try
			{
				flag |= Environment.GetCommandLineArgs().Any((string a) => string.Equals(a, "-server", StringComparison.OrdinalIgnoreCase));
			}
			catch
			{
			}
			if (flag || File.Exists(path))
			{
				_effectiveConfig = ((BaseUnityPlugin)this).Config;
				_usingTempClientConfig = false;
				return;
			}
			string cachePath = Paths.CachePath;
			string text = Path.Combine(cachePath, "vhvac.core.client.tmp.cfg");
			try
			{
				Directory.CreateDirectory(cachePath);
			}
			catch
			{
			}
			_effectiveConfig = new ConfigFile(text, false);
			try
			{
				_effectiveConfig.SaveOnConfigSet = false;
			}
			catch
			{
			}
			_usingTempClientConfig = true;
			VhLogger.LogInfo((object)"[VHVAC] Client-Modus: Keine Config-Datei erstellt (kein vhvac.core.cfg)");
		}

		private void CollectModInformation()
		{
			InstalledMods.Clear();
			if (Chainloader.PluginInfos != null)
			{
				foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
				{
					PluginInfo value = pluginInfo.Value;
					if (((value != null) ? value.Metadata : null) != null)
					{
						ModInfo value2 = new ModInfo
						{
							GUID = value.Metadata.GUID,
							Name = value.Metadata.Name,
							Version = value.Metadata.Version.ToString(),
							Hash = HashHelper.ComputeFileHash(value.Location)
						};
						if (!InstalledMods.ContainsKey(value.Metadata.GUID))
						{
							InstalledMods[value.Metadata.GUID] = value2;
						}
					}
				}
			}
			try
			{
				ScanPluginDlls();
			}
			catch (Exception ex)
			{
				VhLogger.LogWarning((object)("[VHVAC] Fehler beim Scannen der Plugin-DLLs: " + ex.Message));
			}
			_modsCollected = true;
			if (DebugMode.Value)
			{
				VhLogger.LogDebug((object)$"[VHVAC] Insgesamt {InstalledMods.Count} Mods nach vollständigem Scan");
			}
		}

		private void ScanPluginDlls()
		{
			string[] files = Directory.GetFiles(Paths.PluginPath, "*.dll", SearchOption.AllDirectories);
			foreach (string text in files)
			{
				try
				{
					Type[] types = Assembly.LoadFrom(text).GetTypes();
					for (int j = 0; j < types.Length; j++)
					{
						BepInPlugin customAttribute = ((MemberInfo)types[j]).GetCustomAttribute<BepInPlugin>();
						if (customAttribute != null && !InstalledMods.ContainsKey(customAttribute.GUID))
						{
							ModInfo modInfo = new ModInfo
							{
								GUID = customAttribute.GUID,
								Name = customAttribute.Name,
								Version = customAttribute.Version.ToString(),
								Hash = HashHelper.ComputeFileHash(text)
							};
							InstalledMods[customAttribute.GUID] = modInfo;
							if (DebugMode.Value)
							{
								VhLogger.LogDebug((object)("[VHVAC] DLL-Scan gefunden: " + modInfo.GUID + " v" + modInfo.Version));
							}
						}
					}
				}
				catch (ReflectionTypeLoadException)
				{
				}
				catch (BadImageFormatException)
				{
				}
				catch (Exception ex3)
				{
					if (DebugMode.Value)
					{
						VhLogger.LogDebug((object)("[VHVAC] Konnte DLL nicht scannen: " + Path.GetFileName(text) + " - " + ex3.Message));
					}
				}
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		static VhVacPlugin()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Expected O, but got Unknown
			string configPath = Paths.ConfigPath;
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName;
			ConnectionError = "";
			ErrorCount = 0;
			VhLogger = Logger.CreateLogSource("vhvac");
			ConfigSync = new ConfigSync("vhvac.core")
			{
				DisplayName = "vhvac",
				CurrentVersion = "1.8.5",
				MinimumRequiredVersion = "1.8.5"
			};
			PluginsHash = "";
			InstalledMods = new Dictionary<string, ModInfo>();
			_modsCollected = false;
			_usingTempClientConfig = false;
		}
	}
	public class ModInfo
	{
		public string GUID { get; set; }

		public string Name { get; set; }

		public string Version { get; set; }

		public string Hash { get; set; }

		public ZPackage ToZPackage()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(GUID ?? "");
			val.Write(Name ?? "");
			val.Write(Version ?? "");
			val.Write(Hash ?? "");
			return val;
		}

		public static ModInfo FromZPackage(ZPackage pkg)
		{
			return new ModInfo
			{
				GUID = pkg.ReadString(),
				Name = pkg.ReadString(),
				Version = pkg.ReadString(),
				Hash = pkg.ReadString()
			};
		}
	}
	public static class HashHelper
	{
		public static string CreateMd5ForPlugins(List<ModInfo> mods)
		{
			List<ModInfo> source = mods.OrderBy((ModInfo m) => m.GUID).ToList();
			using MD5 mD = MD5.Create();
			string s = string.Join("|", source.Select((ModInfo m) => m.GUID + ":" + m.Version));
			byte[] bytes = Encoding.UTF8.GetBytes(s);
			return BitConverter.ToString(mD.ComputeHash(bytes)).Replace("-", "").ToLower();
		}

		public static string CreateMd5ForFolder(string path)
		{
			List<string> list = (from p in Directory.GetFiles(path, "*.dll", SearchOption.AllDirectories)
				orderby p
				select p).ToList();
			using MD5 mD = MD5.Create();
			for (int i = 0; i < list.Count; i++)
			{
				byte[] array = File.ReadAllBytes(list[i]);
				if (i == list.Count - 1)
				{
					mD.TransformFinalBlock(array, 0, array.Length);
				}
				else
				{
					mD.TransformBlock(array, 0, array.Length, array, 0);
				}
			}
			return BitConverter.ToString(mD.Hash).Replace("-", "").ToLower();
		}

		public static string ComputeFileHash(string filePath)
		{
			if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
			{
				return "";
			}
			using MD5 mD = MD5.Create();
			using FileStream inputStream = File.OpenRead(filePath);
			return BitConverter.ToString(mD.ComputeHash(inputStream)).Replace("-", "").ToLower();
		}
	}
	[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
	public static class RegisterAndCheckVersion
	{
		private const string RpcName = "VHVAC_ModCheck";

		private const string RpcResponseName = "VHVAC_ModCheckResponse";

		private static void Prefix(ZNetPeer peer, ref ZNet __instance)
		{
			VhVacPlugin.EnsureModsCollected();
			VhVacPlugin.VhLogger.LogDebug((object)"[VHVAC] Registriere Mod-Check RPC Handler");
			peer.m_rpc.Register<ZPackage>("VHVAC_ModCheck", (Action<ZRpc, ZPackage>)RPC_ModCheck);
			peer.m_rpc.Register<ZPackage>("VHVAC_ModCheckResponse", (Action<ZRpc, ZPackage>)RPC_ModCheckResponse);
			VhVacPlugin.VhLogger.LogInfo((object)$"[VHVAC] Sende Mod-Informationen zur Prüfung ({VhVacPlugin.InstalledMods.Count} Mods)");
			ZPackage val = CreateModInfoPackage();
			peer.m_rpc.Invoke("VHVAC_ModCheck", new object[1] { val });
		}

		private static ZPackage CreateModInfoPackage()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(VhVacPlugin.PluginsHash);
			val.Write(VhVacPlugin.InstalledMods.Count);
			foreach (ModInfo value in VhVacPlugin.InstalledMods.Values)
			{
				val.Write(value.GUID);
				val.Write(value.Name);
				val.Write(value.Version);
				val.Write(value.Hash);
			}
			return val;
		}

		private static void RPC_ModCheck(ZRpc rpc, ZPackage pkg)
		{
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Expected O, but got Unknown
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			string text = pkg.ReadString();
			int num = pkg.ReadInt();
			Dictionary<string, ModInfo> dictionary = new Dictionary<string, ModInfo>();
			for (int i = 0; i < num; i++)
			{
				ModInfo modInfo = new ModInfo
				{
					GUID = pkg.ReadString(),
					Name = pkg.ReadString(),
					Version = pkg.ReadString(),
					Hash = pkg.ReadString()
				};
				dictionary[modInfo.GUID] = modInfo;
			}
			VhVacPlugin.VhLogger.LogInfo((object)$"[VHVAC] Empfangen: Hash={text}, {num} Mods");
			if (!ZNet.instance.IsServer())
			{
				return;
			}
			bool flag = false;
			string text2 = "";
			if (VhVacPlugin.AdminBypass.Value)
			{
				string hostName = rpc.GetSocket().GetHostName();
				if (ZNet.instance.m_adminList != null && ZNet.instance.m_adminList.Contains(hostName))
				{
					VhVacPlugin.VhLogger.LogInfo((object)("[VHVAC] Admin-Bypass für " + hostName));
					flag = true;
				}
			}
			if (!flag)
			{
				text2 = PerformModChecks(dictionary, text, rpc);
				flag = string.IsNullOrEmpty(text2);
			}
			if (flag)
			{
				VhVacPlugin.VhLogger.LogInfo((object)("[VHVAC] Client validiert: " + rpc.GetSocket().GetHostName()));
				RpcHandlers.ValidatedPeers.Add(rpc);
				ZPackage val = new ZPackage();
				val.Write(true);
				val.Write("");
				rpc.Invoke("VHVAC_ModCheckResponse", new object[1] { val });
			}
			else
			{
				VhVacPlugin.VhLogger.LogWarning((object)("[VHVAC] Kick: " + rpc.GetSocket().GetHostName() + " - " + text2));
				ZPackage val2 = new ZPackage();
				val2.Write(false);
				val2.Write(text2);
				rpc.Invoke("VHVAC_ModCheckResponse", new object[1] { val2 });
				rpc.Invoke("Error", new object[1] { 3 });
			}
		}

		private static void RPC_ModCheckResponse(ZRpc rpc, ZPackage pkg)
		{
			bool num = pkg.ReadBool();
			string text = pkg.ReadString();
			if (num)
			{
				VhVacPlugin.VhLogger.LogInfo((object)"[VHVAC] Server hat Mod-Prüfung bestätigt!");
				VhVacPlugin.ConnectionError = "";
			}
			else
			{
				VhVacPlugin.VhLogger.LogWarning((object)("[VHVAC] Server hat Verbindung abgelehnt: " + text));
				VhVacPlugin.ConnectionError = "[VHVAC] " + text;
			}
		}

		private static string PerformModChecks(Dictionary<string, ModInfo> remoteMods, string remoteHash, ZRpc rpc)
		{
			List<(string, string)> list = new List<(string, string)>();
			List<(string, string)> list2 = new List<(string, string)>();
			List<(string, string, string)> list3 = new List<(string, string, string)>();
			if (VhVacPlugin.ForceExactMods.Value)
			{
				foreach (KeyValuePair<string, ModInfo> remoteMod in remoteMods)
				{
					if (!VhVacPlugin.InstalledMods.ContainsKey(remoteMod.Key))
					{
						VhVacPlugin.VhLogger.LogWarning((object)("[VHVAC] Client hat unerlaubten Mod: " + remoteMod.Value.Name + " (" + remoteMod.Key + ")"));
						list.Add((remoteMod.Value.Name, remoteMod.Value.Version));
					}
				}
			}
			if (VhVacPlugin.EnableAllowedModsCheck.Value)
			{
				List<string> allowedModsList = GetAllowedModsList();
				if (allowedModsList.Count > 0)
				{
					foreach (KeyValuePair<string, ModInfo> clientMod in remoteMods)
					{
						if (!allowedModsList.Contains<string>(clientMod.Key, StringComparer.OrdinalIgnoreCase) && !list.Any<(string, string)>(((string Name, string Version) m) => m.Name == clientMod.Value.Name))
						{
							VhVacPlugin.VhLogger.LogWarning((object)("[VHVAC] Client hat Mod der nicht auf der Whitelist ist: " + clientMod.Value.Name));
							list.Add((clientMod.Value.Name, clientMod.Value.Version));
						}
					}
				}
			}
			if (VhVacPlugin.ForceExactMods.Value)
			{
				foreach (KeyValuePair<string, ModInfo> installedMod in VhVacPlugin.InstalledMods)
				{
					if (!remoteMods.ContainsKey(installedMod.Key))
					{
						VhVacPlugin.VhLogger.LogWarning((object)("[VHVAC] Client fehlt Mod: " + installedMod.Value.Name + " (" + installedMod.Key + ")"));
						list2.Add((installedMod.Value.Name, installedMod.Value.Version));
					}
				}
			}
			if (VhVacPlugin.EnableVersionCheck.Value)
			{
				foreach (KeyValuePair<string, ModInfo> installedMod2 in VhVacPlugin.InstalledMods)
				{
					if (remoteMods.TryGetValue(installedMod2.Key, out var value) && value.Version != installedMod2.Value.Version)
					{
						VhVacPlugin.VhLogger.LogWarning((object)("[VHVAC] Version Mismatch: " + installedMod2.Value.Name + " - Client: " + value.Version + ", Server: " + installedMod2.Value.Version));
						list3.Add((installedMod2.Value.Name, value.Version, installedMod2.Value.Version));
					}
				}
			}
			if (list.Count > 0 || list2.Count > 0 || list3.Count > 0)
			{
				return FormatCombinedTable(list, list2, list3);
			}
			if (VhVacPlugin.EnableHashCheck.Value && remoteHash != VhVacPlugin.PluginsHash)
			{
				if (VhVacPlugin.DebugMode.Value)
				{
					VhVacPlugin.VhLogger.LogDebug((object)("[VHVAC] Hash-Mismatch: Server=" + VhVacPlugin.PluginsHash + ", Client=" + remoteHash));
				}
				VhVacPlugin.VhLogger.LogWarning((object)"[VHVAC] Hash-Mismatch obwohl alle anderen Prüfungen bestanden");
				return VhVacPlugin.KickMessageHashMismatch.Value;
			}
			return null;
		}

		private static string FormatCombinedTable(List<(string Name, string Version)> unallowed, List<(string Name, string ServerVersion)> missing, List<(string Name, string ClientVersion, string ServerVersion)> versionMismatch)
		{
			StringBuilder stringBuilder = new StringBuilder();
			int num = (VhVacPlugin.ErrorCount = unallowed.Count + missing.Count + versionMismatch.Count);
			stringBuilder.AppendLine("<color=#AAAAAA>  Typ     Mod                       Client     Server</color>");
			stringBuilder.AppendLine();
			int num2 = 0;
			foreach (var item in unallowed)
			{
				if (num2 >= 10)
				{
					break;
				}
				string text;
				if (item.Name.Length <= 22)
				{
					(text, _) = item;
				}
				else
				{
					text = item.Name.Substring(0, 19) + "...";
				}
				string arg = text;
				stringBuilder.AppendLine($"<color=#FF6666>   ✗     {arg,-24} {item.Version,-10}    -</color>");
				num2++;
			}
			foreach (var item2 in missing)
			{
				if (num2 >= 10)
				{
					break;
				}
				string text2;
				if (item2.Name.Length <= 22)
				{
					(text2, _) = item2;
				}
				else
				{
					text2 = item2.Name.Substring(0, 19) + "...";
				}
				string arg2 = text2;
				stringBuilder.AppendLine($"<color=#FFCC66>   ⚠     {arg2,-24}    -        {item2.ServerVersion}</color>");
				num2++;
			}
			foreach (var item3 in versionMismatch)
			{
				if (num2 >= 10)
				{
					break;
				}
				string text3;
				if (item3.Name.Length <= 22)
				{
					(text3, _, _) = item3;
				}
				else
				{
					text3 = item3.Name.Substring(0, 19) + "...";
				}
				string arg3 = text3;
				stringBuilder.AppendLine($"<color=#66CCFF>   ↔     {arg3,-24}</color> <color=#FF8888>{item3.ClientVersion,-10}</color> <color=#88FF88>{item3.ServerVersion}</color>");
				num2++;
			}
			int num3 = num - num2;
			if (num3 > 0)
			{
				stringBuilder.AppendLine($"<color=#AAAAAA>   ...   +{num3} weitere Probleme</color>");
			}
			stringBuilder.AppendLine();
			stringBuilder.Append("<color=#FF6666>✗ Unerlaubt</color>  ");
			stringBuilder.Append("<color=#FFCC66>⚠ Fehlt</color>  ");
			stringBuilder.Append("<color=#66CCFF>↔ Falsche Version</color>");
			return stringBuilder.ToString();
		}

		private static List<string> GetAllowedModsList()
		{
			if (string.IsNullOrWhiteSpace(VhVacPlugin.AllowedMods.Value))
			{
				return new List<string>();
			}
			return (from s in VhVacPlugin.AllowedMods.Value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries)
				select s.Trim()).ToList();
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "ShowConnectError")]
	public class ShowConnectionError
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static UnityAction <>9__2_0;

			internal void <CreateCustomPanel>b__2_0()
			{
				Object.Destroy((Object)(object)_customPanel);
				_customPanel = null;
			}
		}

		private static GameObject _customPanel;

		private static void Postfix(FejdStartup __instance)
		{
			if (__instance.m_connectionFailedPanel.activeSelf && !string.IsNullOrEmpty(VhVacPlugin.ConnectionError))
			{
				__instance.m_connectionFailedPanel.SetActive(false);
				CreateCustomPanel(__instance);
				VhVacPlugin.VhLogger.LogDebug((object)"[VHVAC] Custom Panel erstellt");
				VhVacPlugin.ConnectionError = "";
			}
		}

		private static void CreateCustomPanel(FejdStartup fejdStartup)
		{
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Expected O, but got Unknown
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0310: Unknown result type (might be due to invalid IL or missing references)
			//IL_0324: Unknown result type (might be due to invalid IL or missing references)
			//IL_0376: Unknown result type (might be due to invalid IL or missing references)
			//IL_0385: Unknown result type (might be due to invalid IL or missing references)
			//IL_038c: Expected O, but got Unknown
			//IL_03b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_03df: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0408: Unknown result type (might be due to invalid IL or missing references)
			//IL_0431: Unknown result type (might be due to invalid IL or missing references)
			//IL_0478: Unknown result type (might be due to invalid IL or missing references)
			//IL_047d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0490: Unknown result type (might be due to invalid IL or missing references)
			//IL_0497: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0463: Unknown result type (might be due to invalid IL or missing references)
			//IL_0468: Unknown result type (might be due to invalid IL or missing references)
			//IL_046e: Expected O, but got Unknown
			if ((Object)(object)_customPanel != (Object)null)
			{
				Object.Destroy((Object)(object)_customPanel);
			}
			Transform parent = fejdStartup.m_connectionFailedPanel.transform.parent;
			if ((Object)(object)parent == (Object)null)
			{
				VhVacPlugin.VhLogger.LogWarning((object)"[VHVAC] Kein Parent Transform gefunden!");
				return;
			}
			TMP_FontAsset font = fejdStartup.m_connectionFailedError.font;
			Material fontMaterial = fejdStartup.m_connectionFailedError.fontMaterial;
			int num = Math.Min(VhVacPlugin.ErrorCount, 10);
			int num2 = Math.Min(num, 10);
			float num3 = 700f;
			float val = 120f + (float)num2 * 30f + 60f + 30f + 50f + 20f;
			val = Math.Max(val, 370f);
			val = Math.Min(val, 670f);
			VhVacPlugin.VhLogger.LogInfo((object)$"[VHVAC] Panel size: {num3}x{val} for {num} errors");
			_customPanel = new GameObject("VHVAC_ErrorPanel");
			_customPanel.transform.SetParent(parent, false);
			RectTransform obj = _customPanel.AddComponent<RectTransform>();
			obj.anchorMin = new Vector2(0.5f, 0.5f);
			obj.anchorMax = new Vector2(0.5f, 0.5f);
			obj.pivot = new Vector2(0.5f, 0.5f);
			obj.sizeDelta = new Vector2(num3, val);
			obj.anchoredPosition = Vector2.zero;
			((Graphic)_customPanel.AddComponent<Image>()).color = new Color(0.15f, 0.1f, 0.05f, 0.98f);
			Outline obj2 = _customPanel.AddComponent<Outline>();
			((Shadow)obj2).effectColor = new Color(0.85f, 0.65f, 0.2f, 1f);
			((Shadow)obj2).effectDistance = new Vector2(2f, -2f);
			GameObject val2 = new GameObject("ErrorText");
			val2.transform.SetParent(_customPanel.transform, false);
			RectTransform obj3 = val2.AddComponent<RectTransform>();
			obj3.anchorMin = new Vector2(0f, 0f);
			obj3.anchorMax = new Vector2(1f, 1f);
			obj3.offsetMin = new Vector2(20f, 85f);
			obj3.offsetMax = new Vector2(-20f, -10f);
			TextMeshProUGUI obj4 = val2.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj4).font = font;
			((TMP_Text)obj4).fontMaterial = fontMaterial;
			((TMP_Text)obj4).fontSize = 24f;
			((TMP_Text)obj4).alignment = (TextAlignmentOptions)258;
			((Graphic)obj4).color = Color.white;
			((TMP_Text)obj4).text = "<size=32><b>Verbindung fehlgeschlagen</b></size>\n\n<size=22>" + VhVacPlugin.ConnectionError.Replace("[VHVAC] ", "") + "</size>";
			GameObject val3 = new GameObject("HelpText");
			val3.transform.SetParent(_customPanel.transform, false);
			RectTransform obj5 = val3.AddComponent<RectTransform>();
			obj5.anchorMin = new Vector2(0f, 0f);
			obj5.anchorMax = new Vector2(1f, 0f);
			obj5.pivot = new Vector2(0.5f, 0f);
			obj5.sizeDelta = new Vector2(0f, 25f);
			obj5.anchoredPosition = new Vector2(0f, 55f);
			TextMeshProUGUI obj6 = val3.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj6).font = font;
			((TMP_Text)obj6).fontMaterial = fontMaterial;
			((TMP_Text)obj6).text = "Hilfe: https://valheim-server.de/forum";
			((TMP_Text)obj6).fontSize = 18f;
			((TMP_Text)obj6).alignment = (TextAlignmentOptions)514;
			((Graphic)obj6).color = new Color(0.7f, 0.7f, 0.7f, 1f);
			GameObject val4 = new GameObject("OKButton");
			val4.transform.SetParent(_customPanel.transform, false);
			RectTransform obj7 = val4.AddComponent<RectTransform>();
			obj7.anchorMin = new Vector2(0.5f, 0f);
			obj7.anchorMax = new Vector2(0.5f, 0f);
			obj7.pivot = new Vector2(0.5f, 0f);
			obj7.sizeDelta = new Vector2(180f, 40f);
			obj7.anchoredPosition = new Vector2(0f, 10f);
			Image val5 = val4.AddComponent<Image>();
			((Graphic)val5).color = new Color(0.6f, 0.4f, 0.2f, 1f);
			Button obj8 = val4.AddComponent<Button>();
			((Selectable)obj8).targetGraphic = (Graphic)(object)val5;
			ButtonClickedEvent onClick = obj8.onClick;
			object obj9 = <>c.<>9__2_0;
			if (obj9 == null)
			{
				UnityAction val6 = delegate
				{
					Object.Destroy((Object)(object)_customPanel);
					_customPanel = null;
				};
				<>c.<>9__2_0 = val6;
				obj9 = (object)val6;
			}
			((UnityEvent)onClick).AddListener((UnityAction)obj9);
			GameObject val7 = new GameObject("ButtonText");
			val7.transform.SetParent(val4.transform, false);
			RectTransform obj10 = val7.AddComponent<RectTransform>();
			obj10.anchorMin = Vector2.zero;
			obj10.anchorMax = Vector2.one;
			obj10.sizeDelta = Vector2.zero;
			TextMeshProUGUI obj11 = val7.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj11).font = font;
			((TMP_Text)obj11).fontMaterial = fontMaterial;
			((TMP_Text)obj11).text = "OK";
			((TMP_Text)obj11).fontSize = 28f;
			((TMP_Text)obj11).alignment = (TextAlignmentOptions)514;
			((Graphic)obj11).color = Color.white;
		}
	}
	[HarmonyPatch(typeof(ZNet), "Disconnect")]
	public static class RemoveDisconnectedPeerFromVerified
	{
		private static void Prefix(ZNetPeer peer, ref ZNet __instance)
		{
			if (__instance.IsServer())
			{
				VhVacPlugin.VhLogger.LogInfo((object)("[VHVAC] Peer (" + peer.m_rpc.m_socket.GetHostName() + ") getrennt - entferne aus validierter Liste"));
				RpcHandlers.ValidatedPeers.Remove(peer.m_rpc);
			}
		}
	}
	public static class RpcHandlers
	{
		public static readonly List<ZRpc> ValidatedPeers = new List<ZRpc>();
	}
}