Decompiled source of ValheimEnforcer v0.7.1

plugins/ValheimEnforcer.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
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.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using ValheimEnforcer.common;
using ValheimEnforcer.modules;
using ValheimEnforcer.modules.compat;
using ValheimEnforcer.modules.compat.ExtraSlots;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ValheimEnforcer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ValheimEnforcer")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("0.7.1")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.7.1.0")]
namespace ValheimEnforcer
{
	internal class ValConfig
	{
		[CompilerGenerated]
		private sealed class <OnClientReceiveCharacter>d__40 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public ZPackage package;

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

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

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

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

			private bool MoveNext()
			{
				if (<>1__state != 0)
				{
					return false;
				}
				<>1__state = -1;
				DataObjects.Character playerCharacter = DataObjects.yamldeserializer.Deserialize<DataObjects.Character>(package.ReadString());
				Logger.LogDebug("Recieved Player character data from server.");
				CharacterManager.SetPlayerCharacter(playerCharacter);
				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();
			}
		}

		[CompilerGenerated]
		private sealed class <OnClientReceiveCheatReport>d__43 : IEnumerator<object>, IDisposable, IEnumerator
		{
			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 <OnClientReceiveCheatReport>d__43(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

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

			private bool MoveNext()
			{
				if (<>1__state != 0)
				{
					return false;
				}
				<>1__state = -1;
				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();
			}
		}

		[CompilerGenerated]
		private sealed class <OnClientReceiveConfiscatedItems>d__44 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public ZPackage package;

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

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

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

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

			private bool MoveNext()
			{
				if (<>1__state != 0)
				{
					return false;
				}
				<>1__state = -1;
				List<DataObjects.PackedItem> list = DataObjects.yamldeserializer.Deserialize<List<DataObjects.PackedItem>>(package.ReadString());
				Logger.LogInfo($"Received {list.Count} confiscated item(s) returned from server.");
				foreach (DataObjects.PackedItem item in list)
				{
					Logger.LogInfo($"Adding returned confiscated item: {item.prefabName} x{item.m_stack}");
					item.AddToInventory(Player.m_localPlayer, use_position: false);
				}
				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();
			}
		}

		[CompilerGenerated]
		private sealed class <OnServerReceiveCheatReport>d__42 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public ZPackage package;

			public long sender;

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

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

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

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

			private bool MoveNext()
			{
				if (<>1__state != 0)
				{
					return false;
				}
				<>1__state = -1;
				string text = package.ReadString();
				DataObjects.CheatSummaryReport cheatSummaryReport;
				try
				{
					cheatSummaryReport = DataObjects.yamldeserializer.Deserialize<DataObjects.CheatSummaryReport>(text);
				}
				catch (Exception ex)
				{
					Logger.LogWarning($"Failed to deserialize cheat report from {sender}: {ex.Message}");
					return false;
				}
				ZNetPeer peer = ZNet.instance.GetPeer(sender);
				string playerName = cheatSummaryReport.PlayerName;
				string endPointString = peer.m_socket.GetEndPointString();
				Logger.LogWarning($"Cheat detection from {playerName} ({endPointString}): valheim-tooler: {cheatSummaryReport.ValheimToolerStatus} cheatengine: {cheatSummaryReport.CheatEngineStatus.IsCheatEngineDetected()}");
				string text2 = CheatDetectionAction.Value ?? "Log";
				if (peer == null)
				{
					Logger.LogWarning("Received cheat report for " + playerName + " but could not find corresponding peer. No action will be taken.");
					return false;
				}
				switch (text2)
				{
				case "Kick":
					Logger.LogWarning("Kicking " + playerName + " for cheat usage.");
					ZNet.instance.Kick(playerName);
					break;
				case "Ban":
					Logger.LogWarning("Banning " + playerName + " for cheat usage.");
					ZNet.instance.Ban(playerName);
					break;
				}
				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();
			}
		}

		[CompilerGenerated]
		private sealed class <OnServerRecieveCharacter>d__39 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public ZPackage package;

			public long sender;

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

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

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

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

			private bool MoveNext()
			{
				if (<>1__state != 0)
				{
					return false;
				}
				<>1__state = -1;
				DataObjects.Character character = DataObjects.yamldeserializer.Deserialize<DataObjects.Character>(package.ReadString());
				Logger.LogInfo($"Recieved Player data update for {sender} - {character.Name}|{character.HostID}");
				WritePlayerCharacterToSave(character.HostID, character);
				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();
			}
		}

		[CompilerGenerated]
		private sealed class <OnServerReturnConfiscatedReceive>d__41 : IEnumerator<object>, IDisposable, IEnumerator
		{
			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 <OnServerReturnConfiscatedReceive>d__41(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

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

			private bool MoveNext()
			{
				if (<>1__state != 0)
				{
					return false;
				}
				<>1__state = -1;
				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 ConfigFile cfg;

		public static ConfigEntry<bool> EnableDebugMode;

		public static ConfigEntry<bool> UpdateLoadedModsOnStartup;

		public static ConfigEntry<bool> AutoAddModsToRequired;

		public static ConfigEntry<bool> RemoveNontrackedItemsFromJoiningPlayers;

		public static ConfigEntry<bool> AddMissingItemsFromPlayerServerSave;

		public static ConfigEntry<bool> PreventExternalSkillRaises;

		public static ConfigEntry<bool> NewCharactersRemoveExtraItems;

		public static ConfigEntry<bool> NewCharacterSetSkillsToZero;

		public static ConfigEntry<bool> newCharacterClearCustomData;

		public static ConfigEntry<bool> PreventExternalCustomDataChanges;

		public static ConfigEntry<bool> ValidateItemCustomData;

		public static ConfigEntry<bool> ValidateItemDurability;

		public static ConfigEntry<float> ItemValidationDurabilityAllowedVariance;

		public static ConfigEntry<bool> InternalStorageMode;

		public static ConfigEntry<bool> EnableCheatDetection;

		public static ConfigEntry<bool> DetectCheatEngine;

		public static ConfigEntry<bool> DetectValheimTooler;

		public static ConfigEntry<bool> DetectSpeedhack;

		public static ConfigEntry<string> CheatDetectionAction;

		public static ConfigEntry<int> CheatScanIntervalSeconds;

		internal const string ModsFileName = "Mods.yaml";

		internal const string ValheimEnforcer = "ValheimEnforcer";

		internal const string CharacterFolder = "Characters";

		internal static string ModsConfigFilePath = Path.Combine(Paths.ConfigPath, "ValheimEnforcer", "Mods.yaml");

		internal static string CharacterFilePath = Path.Combine(Paths.ConfigPath, "ValheimEnforcer", "Characters");

		internal static CustomRPC CharacterSaveRPC;

		internal static CustomRPC ReturnConfiscatedItemsRPC;

		internal static CustomRPC CheatDetectionRPC;

		public ValConfig(ConfigFile cf)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_0059: Expected O, but got Unknown
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			//IL_0085: Expected O, but got Unknown
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			//IL_00b1: Expected O, but got Unknown
			cfg = cf;
			cfg.SaveOnConfigSet = true;
			CreateConfigValues(cf);
			Logger.SetDebugLogging(EnableDebugMode.Value);
			SetupMainFileWatcher();
			CharacterSaveRPC = NetworkManager.Instance.AddRPC("VENFORCE_CHAR", new CoroutineHandler(OnServerRecieveCharacter), new CoroutineHandler(OnClientReceiveCharacter));
			ReturnConfiscatedItemsRPC = NetworkManager.Instance.AddRPC("VENFORCE_RETURN_CONFISCATED", new CoroutineHandler(OnServerReturnConfiscatedReceive), new CoroutineHandler(OnClientReceiveConfiscatedItems));
			CheatDetectionRPC = NetworkManager.Instance.AddRPC("VENFORCE_CHEAT", new CoroutineHandler(OnServerReceiveCheatReport), new CoroutineHandler(OnClientReceiveCheatReport));
			SynchronizationManager.Instance.AddInitialSynchronization(CharacterSaveRPC, (Func<ZNetPeer, ZPackage>)SendSavedCharacter);
			LoadYamlConfigs(new Dictionary<string, Action<string>> { { ModsConfigFilePath, CreateModsFile } });
		}

		private void CreateConfigValues(ConfigFile Config)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			EnableDebugMode = Config.Bind<bool>("Client config", "EnableDebugMode", false, new ConfigDescription("Enables Debug logging.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdvanced = true
			} }));
			EnableDebugMode.SettingChanged += Logger.EnableDebugLogging;
			Logger.CheckEnableDebugLogging();
			UpdateLoadedModsOnStartup = BindServerConfig("Mods", "UpdateLoadedModsOnStartup", value: true, "Whether or not the mod configuration file will update its loaded mods once they are detected.");
			AutoAddModsToRequired = BindServerConfig("Mods", "AutoAddModsToRequired", value: true, "If true, automatically adds mods not found in the optional, admin, or server-only mod lists.");
			RemoveNontrackedItemsFromJoiningPlayers = BindServerConfig("Player Sync", "RemoveNontrackedItemsFromJoiningPlayers", value: true, "If enabled, any items that are not tracked by the server will be removed from joining player's inventories.");
			AddMissingItemsFromPlayerServerSave = BindServerConfig("Player Sync", "AddMissingItemsFromPlayerServerSave", value: true, "If enabled, any items the player does not have that are listed on the server will be given to the player when joining");
			PreventExternalSkillRaises = BindServerConfig("Player Sync", "PreventExternalSkillRaises", value: true, "If enabled, player skill gains outside of the server are removed when connecting.");
			NewCharactersRemoveExtraItems = BindServerConfig("Player Sync", "NewCharactersRemoveExtraItems", value: false, "If enabled, new characters that have no existing character file will have all items removed except for starting items.");
			NewCharacterSetSkillsToZero = BindServerConfig("Player Sync", "NewCharacterSetSkillsToZero", value: false, "If enabled, new characters will have their skills set to zero. Prevents players from raising skills before connecting.");
			PreventExternalCustomDataChanges = BindServerConfig("Player Sync", "PreventExternalCustomDataChanges", value: true, "If enabled, tracks player custom data. Warning: custom data can be large and can impact how other mods function.");
			newCharacterClearCustomData = BindServerConfig("Player Sync", "newCharacterClearCustomData", value: true, "If enabled, new characters will have their custom data cleared.");
			ValidateItemCustomData = BindServerConfig("Player Sync", "ValidateItemCustomData", value: true, "If enabled, custom data on items will be validated.");
			ValidateItemDurability = BindServerConfig("Player Sync", "ValidateItemDurability", value: true, "If enabled, item durability will be validated");
			ItemValidationDurabilityAllowedVariance = BindServerConfig("Player Sync", "ItemValidationDurabilityAllowedVariance", 10f, "Allowed variance for item durability validation.", advanced: true, 0f, 100f);
			InternalStorageMode = BindServerConfig("Advanced", "InternalStorageMode", value: false, "If enabled, player character data will be stored within your world. Enables full portability of the world without having to synchronize configurations.", null, advanced: true);
			EnableCheatDetection = BindServerConfig("Anti-Cheat", "EnableCheatDetection", value: false, "Enable client-side scanning for known cheat tools (Cheat Engine, ValheimTooler). Detections are reported to the server.");
			DetectValheimTooler = BindServerConfig("Anti-Cheat", "DetectValheimTooler", value: true, "Scan loaded assemblies for ValheimTooler. High confidence, very low cost.");
			DetectCheatEngine = BindServerConfig("Anti-Cheat", "DetectCheatEngine", value: true, "Scan for Cheat Engine (processes, windows, injected speedhack/DBK modules, debugger). Note: Cheat Engine has legitimate uses — prefer Log action over Kick/Ban.");
			CheatDetectionAction = BindServerConfig("Anti-Cheat", "ActionOnDetection", "Kick", "Server-side action taken when a client reports a cheat detection.", new AcceptableValueList<string>(new string[3] { "Log", "Kick", "Ban" }));
			CheatScanIntervalSeconds = BindServerConfig("Anti-Cheat", "ScanIntervalSeconds", 5, "Seconds between scans on the client.", advanced: false, 1, 60);
		}

		internal static void WritePlayerCharacterToSave(string id, DataObjects.Character character)
		{
			if (InternalStorageMode.Value)
			{
				Logger.LogInfo("Saving character with internal storage mode.");
				InternalDataStore.SaveAccountCharacter(character);
			}
			Directory.CreateDirectory(Path.Combine(Paths.ConfigPath, "ValheimEnforcer", "Characters"));
			string text = Path.Combine(Directory.CreateDirectory(Path.Combine(Paths.ConfigPath, "ValheimEnforcer", "Characters", id)).FullName, character.Name + ".yaml");
			Logger.LogInfo("Writing to " + text);
			File.WriteAllText(text, DataObjects.yamlserializer.Serialize((object)character));
		}

		internal static DataObjects.Character LoadCharacterFromSave(string id, string name)
		{
			if (InternalStorageMode.Value)
			{
				Logger.LogInfo("Loading character from internal storage system.");
				DataObjects.Character accountCharacter = InternalDataStore.GetAccountCharacter(id, name);
				if (accountCharacter == null)
				{
					Logger.LogDebug("No character file found for player with " + id + "-" + name + " is this character new?");
				}
				return accountCharacter;
			}
			string path = Path.Combine(Paths.ConfigPath, "ValheimEnforcer", "Characters", id, name + ".yaml");
			if (!File.Exists(path))
			{
				Logger.LogDebug("No character file found for player with " + id + "-" + name + " is this character new?");
				return null;
			}
			string text = File.ReadAllText(path);
			return DataObjects.yamldeserializer.Deserialize<DataObjects.Character>(text);
		}

		public static string GetSecondaryConfigDirectoryPath()
		{
			return Directory.CreateDirectory(Path.Combine(Paths.ConfigPath, "ValheimEnforcer")).FullName;
		}

		internal void LoadYamlConfigs(Dictionary<string, Action<string>> configFilesToFind)
		{
			string[] files = Directory.GetFiles(GetSecondaryConfigDirectoryPath());
			List<string> list = new List<string>();
			List<string> list2 = configFilesToFind.Keys.ToList();
			string[] array = files;
			foreach (string text in array)
			{
				if (list2.Contains(text))
				{
					list.Add(text);
					Logger.LogDebug("Found config: " + text);
				}
			}
			foreach (KeyValuePair<string, Action<string>> item in configFilesToFind)
			{
				if (!list.Contains(item.Key))
				{
					configFilesToFind[item.Key](item.Key);
					list.Add(item.Key);
				}
			}
			foreach (string item2 in list)
			{
				string fileName = Path.GetFileName(item2);
				Logger.LogDebug("Setting filewatcher for " + fileName);
				SetupFileWatcher(fileName);
			}
		}

		private void SetupFileWatcher(string filtername)
		{
			FileSystemWatcher fileSystemWatcher = new FileSystemWatcher();
			fileSystemWatcher.Path = GetSecondaryConfigDirectoryPath();
			fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
			fileSystemWatcher.Filter = filtername;
			fileSystemWatcher.Changed += UpdateConfigFileOnChange;
			fileSystemWatcher.Created += UpdateConfigFileOnChange;
			fileSystemWatcher.Renamed += UpdateConfigFileOnChange;
			fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			fileSystemWatcher.EnableRaisingEvents = true;
		}

		private static void UpdateConfigFileOnChange(object sender, FileSystemEventArgs e)
		{
			if (!SynchronizationManager.Instance.PlayerIsAdmin)
			{
				Logger.LogInfo("Player is not an admin, and not allowed to change local configuration. Ignoring.");
			}
			else if (File.Exists(e.FullPath))
			{
				string yamlstring = File.ReadAllText(e.FullPath);
				FileInfo fileInfo = new FileInfo(e.FullPath);
				Logger.LogDebug("Filewatch changes from: (" + fileInfo.Name + ") " + fileInfo.FullName);
				if (fileInfo.Name == "Mods.yaml")
				{
					Logger.LogDebug("Triggering Mod Settings update.");
					ModManager.UpdateModSettingConfigs(yamlstring);
				}
			}
		}

		private static void CreateModsFile(string filepath)
		{
			Logger.LogDebug("Loot config missing, recreating.");
			using StreamWriter streamWriter = new StreamWriter(filepath);
			string value = "#################################################\n# Valheim Enforcer - Required, Admin and Optional Mods\n#################################################\n";
			streamWriter.WriteLine(value);
			streamWriter.WriteLine(ModManager.GetDefaultConfig());
		}

		internal static ZPackage SendSavedCharacter(ZNetPeer peer)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Expected O, but got Unknown
			string endPointString = peer.m_socket.GetEndPointString();
			Logger.LogInfo("Sending saved character data to player " + peer.m_playerName + " with ID: " + endPointString);
			ZPackage val = new ZPackage();
			if (InternalStorageMode.Value)
			{
				Logger.LogInfo("Using internal storage mode to send character data.");
				DataObjects.Character accountCharacter = InternalDataStore.GetAccountCharacter(endPointString, peer.m_playerName);
				if (accountCharacter == null)
				{
					Logger.LogInfo("No character data found for player " + peer.m_playerName + " with ID: " + endPointString + ", no character data will be sent.");
					return new ZPackage();
				}
				string text = DataObjects.yamlserializer.Serialize((object)accountCharacter);
				val.Write(text);
				return val;
			}
			string text2 = Path.Combine(Path.Combine(Paths.ConfigPath, "ValheimEnforcer", "Characters", endPointString ?? ""), peer.m_playerName + ".yaml");
			if (!File.Exists(text2))
			{
				Logger.LogInfo("path: " + text2 + " does not exist, no character data will be sent.");
				return new ZPackage();
			}
			string text3 = File.ReadAllText(text2);
			val.Write(text3);
			return val;
		}

		[IteratorStateMachine(typeof(<OnServerRecieveCharacter>d__39))]
		public static IEnumerator OnServerRecieveCharacter(long sender, ZPackage package)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <OnServerRecieveCharacter>d__39(0)
			{
				sender = sender,
				package = package
			};
		}

		[IteratorStateMachine(typeof(<OnClientReceiveCharacter>d__40))]
		public static IEnumerator OnClientReceiveCharacter(long sender, ZPackage package)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <OnClientReceiveCharacter>d__40(0)
			{
				package = package
			};
		}

		[IteratorStateMachine(typeof(<OnServerReturnConfiscatedReceive>d__41))]
		public static IEnumerator OnServerReturnConfiscatedReceive(long sender, ZPackage package)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <OnServerReturnConfiscatedReceive>d__41(0);
		}

		[IteratorStateMachine(typeof(<OnServerReceiveCheatReport>d__42))]
		public static IEnumerator OnServerReceiveCheatReport(long sender, ZPackage package)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <OnServerReceiveCheatReport>d__42(0)
			{
				sender = sender,
				package = package
			};
		}

		[IteratorStateMachine(typeof(<OnClientReceiveCheatReport>d__43))]
		public static IEnumerator OnClientReceiveCheatReport(long sender, ZPackage package)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <OnClientReceiveCheatReport>d__43(0);
		}

		[IteratorStateMachine(typeof(<OnClientReceiveConfiscatedItems>d__44))]
		public static IEnumerator OnClientReceiveConfiscatedItems(long sender, ZPackage package)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <OnClientReceiveConfiscatedItems>d__44(0)
			{
				package = package
			};
		}

		internal static ZPackage SendCharacterAsZpackage(DataObjects.Character chara)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			string text = DataObjects.yamlserializer.Serialize((object)chara);
			ZPackage val = new ZPackage();
			val.Write(text);
			return val;
		}

		internal static void SetupMainFileWatcher()
		{
			FileSystemWatcher fileSystemWatcher = new FileSystemWatcher();
			fileSystemWatcher.NotifyFilter = NotifyFilters.LastWrite;
			fileSystemWatcher.Path = Path.GetDirectoryName(cfg.ConfigFilePath);
			fileSystemWatcher.Filter = "MidnightsFX.ImpactfulSkills.cfg";
			fileSystemWatcher.Changed += OnConfigFileChanged;
			fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			fileSystemWatcher.EnableRaisingEvents = true;
		}

		private static void OnConfigFileChanged(object sender, FileSystemEventArgs e)
		{
			if (ZNet.instance.IsServer())
			{
				Logger.LogInfo("Configuration file has been changed, reloading settings.");
				cfg.Reload();
			}
		}

		public static ConfigEntry<List<string>> BindServerConfig(string catagory, string key, List<string> value, string description, bool advanced = false)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			return cfg.Bind<List<string>>(catagory, key, value, new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				IsAdvanced = advanced
			} }));
		}

		public static ConfigEntry<float[]> BindServerConfig(string catagory, string key, float[] value, string description, bool advanced = false, float valmin = 0f, float valmax = 150f)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			return cfg.Bind<float[]>(catagory, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(valmin, valmax), new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				IsAdvanced = advanced
			} }));
		}

		public static ConfigEntry<bool> BindServerConfig(string catagory, string key, bool value, string description, AcceptableValueBase acceptableValues = null, bool advanced = false)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			return cfg.Bind<bool>(catagory, key, value, new ConfigDescription(description, acceptableValues, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				IsAdvanced = advanced
			} }));
		}

		public static ConfigEntry<int> BindServerConfig(string catagory, string key, int value, string description, bool advanced = false, int valmin = 0, int valmax = 150)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			return cfg.Bind<int>(catagory, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<int>(valmin, valmax), new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				IsAdvanced = advanced
			} }));
		}

		public static ConfigEntry<float> BindServerConfig(string catagory, string key, float value, string description, bool advanced = false, float valmin = 0f, float valmax = 150f)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			return cfg.Bind<float>(catagory, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(valmin, valmax), new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				IsAdvanced = advanced
			} }));
		}

		public static ConfigEntry<string> BindServerConfig(string catagory, string key, string value, string description, AcceptableValueList<string> acceptableValues = null, bool advanced = false)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			return cfg.Bind<string>(catagory, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)acceptableValues, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true,
				IsAdvanced = advanced
			} }));
		}
	}
	internal class Logger
	{
		public static LogLevel Level = (LogLevel)16;

		public static void EnableDebugLogging(object sender, EventArgs e)
		{
			CheckEnableDebugLogging();
		}

		public static void CheckEnableDebugLogging()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (ValConfig.EnableDebugMode.Value)
			{
				Level = (LogLevel)32;
			}
			else
			{
				Level = (LogLevel)16;
			}
		}

		public static void SetDebugLogging(bool state)
		{
			//IL_000d: 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)
			if (state)
			{
				Level = (LogLevel)32;
			}
			else
			{
				Level = (LogLevel)16;
			}
		}

		public static void LogDebug(string message)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			if ((int)Level >= 32)
			{
				ValheimEnforcer.Log.LogInfo((object)message);
			}
		}

		public static void LogInfo(string message)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			if ((int)Level >= 16)
			{
				ValheimEnforcer.Log.LogInfo((object)message);
			}
		}

		public static void LogWarning(string message)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			if ((int)Level >= 4)
			{
				ValheimEnforcer.Log.LogWarning((object)message);
			}
		}

		public static void LogError(string message)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			if ((int)Level >= 2)
			{
				ValheimEnforcer.Log.LogError((object)message);
			}
		}
	}
	[BepInPlugin("MidnightsFX.ValheimEnforcer", "ValheimEnforcer", "0.7.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	internal class ValheimEnforcer : BaseUnityPlugin
	{
		public const string PluginGUID = "MidnightsFX.ValheimEnforcer";

		public const string PluginName = "ValheimEnforcer";

		public const string PluginVersion = "0.7.1";

		internal static ManualLogSource Log;

		internal ValConfig cfg;

		public static CustomLocalization Localization = LocalizationManager.Instance.GetLocalization();

		public static AssetBundle EmbeddedResourceBundle;

		public void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			cfg = new ValConfig(((BaseUnityPlugin)this).Config);
			EmbeddedResourceBundle = AssetUtils.LoadAssetBundleFromResources("ValheimEnforcer.assets.vebundle", typeof(ValheimEnforcer).Assembly);
			PrefabManager.OnPrefabsRegistered += ModManager.SetModsActive;
			PrefabManager.OnPrefabsRegistered += InternalDataStore.InstanciateOrLinkMetadataRegistry;
			PrefabManager.OnVanillaPrefabsAvailable += ModManager.SetModsActive;
			GUIManager.OnCustomGUIAvailable += ModManager.AddErrorMessageDetailsForMenu;
			InternalDataStore.RegisterMetadataHolder();
			TerminalCommands.AddCommands();
			MinimapManager.OnVanillaMapDataLoaded += CheatDetector.Initialize;
			Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null);
		}
	}
}
namespace ValheimEnforcer.modules
{
	internal static class CharacterManager
	{
		[HarmonyPatch(typeof(Player), "Save")]
		public static class SaveSync
		{
			[HarmonyPrefix]
			[HarmonyPriority(600)]
			private static void PlayerSave(Player __instance)
			{
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)__instance == (Object)null)
				{
					return;
				}
				Scene activeScene = SceneManager.GetActiveScene();
				if (!((Scene)(ref activeScene)).name.Equals("main"))
				{
					return;
				}
				string text = "";
				string text2 = "";
				DataObjects.Character character = null;
				if (PlayerCharacter != null)
				{
					character = PlayerCharacter;
					text = PlayerCharacter.HostID;
					text2 = PlayerCharacter.Name;
				}
				else
				{
					text = GetPlayerID(__instance);
					text2 = __instance.GetPlayerName();
				}
				Logger.LogDebug("Saving character for player " + text2 + " with id " + text);
				if (PlayerCharacter == null)
				{
					character = ValConfig.LoadCharacterFromSave(text, text2);
				}
				if (character == null)
				{
					Logger.LogWarning("Attempted to save character for player " + text2 + " with ID " + text + " but no existing character data was found. Creating new character data.");
					character = new DataObjects.Character
					{
						Name = text2,
						HostID = text,
						SkillLevels = ((Character)__instance).GetSkills().GetSkillList().ToDictionary((Skill skill) => skill.m_info.m_skill, (Skill skill) => skill.m_level),
						ConfiscatedItems = null
					};
					foreach (ItemData item in ((Humanoid)__instance).GetInventory().GetAllItems().ToList())
					{
						character.AddItemToPlayerItems(item);
					}
					if (ValConfig.PreventExternalCustomDataChanges.Value)
					{
						character.PlayerCustomData = __instance.m_customData;
					}
				}
				else
				{
					Logger.LogDebug("Existing character data found for player " + text2 + " with ID " + text + ". Updating character data with current player information.");
					character.SkillLevels = ((Character)__instance).GetSkills().GetSkillList().ToDictionary((Skill skill) => skill.m_info.m_skill, (Skill skill) => skill.m_level);
					Logger.LogDebug("Updated player skills for " + text2 + " with ID " + text + ".");
					if (ValConfig.PreventExternalCustomDataChanges.Value)
					{
						character.PlayerCustomData = __instance.m_customData;
						Logger.LogDebug("Updated player custom data.");
					}
					character.PlayerItems.Clear();
					foreach (ItemData item2 in ((Humanoid)__instance).GetInventory().GetAllItems().ToList())
					{
						character.AddItemToPlayerItems(item2);
					}
					Logger.LogDebug("Updated player Items for " + text2 + " with ID " + text + ".");
				}
				if (character == null)
				{
					Logger.LogWarning("Savable character was null, not sending network updates.");
					return;
				}
				ValConfig.WritePlayerCharacterToSave(text, character);
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.GetServerPeer() != null)
				{
					Logger.LogDebug("Sending updated character data to server.");
					ValConfig.CharacterSaveRPC.SendPackage(ZNet.instance.GetServerPeer().m_uid, ValConfig.SendCharacterAsZpackage(character));
				}
			}
		}

		[HarmonyPatch(typeof(Game), "SpawnPlayer")]
		public static class LoadAndValidatePlayerPatch
		{
			[HarmonyPostfix]
			[HarmonyPriority(800)]
			private static void PlayerSpawn(Game __instance)
			{
				LoadAndValidatePlayer(Player.m_localPlayer);
			}
		}

		[HarmonyPatch(typeof(Game), "Logout")]
		public static class ClearPlayerCharacterOnLogout
		{
			[HarmonyPostfix]
			[HarmonyPriority(0)]
			private static void Postfix()
			{
				if (PlayerCharacter != null)
				{
					Logger.LogDebug("Clearing selected save profile for " + PlayerCharacter.Name + " on logout.");
					PlayerCharacter = null;
				}
			}
		}

		[HarmonyPatch(typeof(Player))]
		public static class LoadPlayerCustomData
		{
			[HarmonyPostfix]
			[HarmonyPriority(800)]
			[HarmonyPatch("Load")]
			private static void Postfix(Player __instance)
			{
				DataObjects.Character character = null;
				string id;
				string name;
				if (PlayerCharacter != null)
				{
					character = PlayerCharacter;
					id = PlayerCharacter.HostID;
					name = PlayerCharacter.Name;
				}
				else
				{
					id = GetPlayerID(__instance);
					name = __instance.GetPlayerName();
				}
				if (PlayerCharacter == null)
				{
					character = ValConfig.LoadCharacterFromSave(id, name);
				}
				if (character == null)
				{
					if (ValConfig.PreventExternalCustomDataChanges.Value && ValConfig.newCharacterClearCustomData.Value)
					{
						__instance.m_customData.Clear();
					}
				}
				else if (ValConfig.PreventExternalCustomDataChanges.Value)
				{
					__instance.m_customData = character.PlayerCustomData;
					Logger.LogDebug("Set player custom data.");
				}
			}
		}

		internal static DataObjects.Character PlayerCharacter = null;

		internal static List<string> staringAllowedPrefabs = new List<string> { "ArmorRagsChest", "ArmorRagsLegs", "Torch" };

		internal static void SetPlayerCharacter(DataObjects.Character character)
		{
			if (character != null)
			{
				Logger.LogDebug("Set character from Saved server data");
				PlayerCharacter = character;
			}
		}

		internal static string GetPlayerID(Player player)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			List<PlayerInfo> playerList = ZNet.instance.GetPlayerList();
			string text = "";
			foreach (PlayerInfo item in playerList)
			{
				Logger.LogDebug($"Checking player {item.m_characterID} with ID {item.m_userInfo.m_id.m_userID} against local player {((Character)player).m_nview.GetZDO().m_uid}");
				if (item.m_characterID == ((Character)player).m_nview.GetZDO().m_uid)
				{
					text = item.m_userInfo.m_id.m_userID;
					break;
				}
			}
			if (text.Length < 1)
			{
				string playerName = player.GetPlayerName();
				foreach (PlayerInfo item2 in playerList)
				{
					if (item2.m_name == playerName)
					{
						text = item2.m_userInfo.m_id.m_userID;
						Logger.LogDebug("Matched player " + playerName + " by name to ID " + text);
						break;
					}
				}
			}
			if (text.Length < 1)
			{
				Logger.LogWarning("Failed to find matching player ID for local player " + player.GetPlayerName() + ". Defaulting to ZDO UID as player ID.");
				text = ((object)(ZDOID)(ref ((Character)player).m_nview.GetZDO().m_uid)).ToString();
			}
			if (text.Contains(":"))
			{
				Logger.LogDebug("Player ID contained invalid character : removing.");
				text = text.Split(new char[1] { ':' })[0];
			}
			return text;
		}

		private static void LoadAndValidatePlayer(Player player)
		{
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			string text;
			string text2;
			if (PlayerCharacter != null)
			{
				text = PlayerCharacter.HostID;
				text2 = PlayerCharacter.Name;
			}
			else
			{
				text = GetPlayerID(player);
				text2 = player.GetPlayerName();
			}
			Logger.LogInfo("Player " + text2 + " with ID " + text + " validating character data.");
			DataObjects.Character savableChar = PlayerCharacter;
			if (savableChar == null)
			{
				Logger.LogInfo("No existing character data found for player " + text2 + " with ID " + text + ". Attempting to load from local save.");
				savableChar = ValConfig.LoadCharacterFromSave(text, text2);
				if (savableChar == null)
				{
					savableChar = new DataObjects.Character
					{
						Name = player.GetPlayerName(),
						HostID = text,
						SkillLevels = ((Character)player).GetSkills().GetSkillList().ToDictionary((Skill skill) => skill.m_info.m_skill, (Skill skill) => skill.m_level)
					};
					if (ValConfig.NewCharacterSetSkillsToZero.Value)
					{
						Logger.LogInfo("New character save for player " + text2 + " with ID " + text + ", skills set to zero.");
						foreach (SkillType item3 in savableChar.SkillLevels.Keys.ToList())
						{
							savableChar.SkillLevels[item3] = 0f;
						}
					}
					if (ValConfig.NewCharactersRemoveExtraItems.Value)
					{
						Logger.LogInfo("New character save for player " + text2 + " with ID " + text + ", checking to removing non-starter items.");
						List<ItemData> removeItems = new List<ItemData>();
						((Humanoid)player).m_inventory.GetAllItems().ForEach(delegate(ItemData item)
						{
							if (!staringAllowedPrefabs.Contains(((Object)item.m_dropPrefab).name))
							{
								Logger.LogInfo($"Removing non-starter item {((Object)item.m_dropPrefab).name}x{item.m_stack} from new player {savableChar.Name}");
								savableChar.AddConfiscatedItem(item, "New character, non-starter item");
								removeItems.Add(item);
							}
							if (item.m_quality > 1)
							{
								Logger.LogInfo($"Removing high quality item {((Object)item.m_dropPrefab).name}x{item.m_stack} with quality {item.m_quality} from new player {savableChar.Name}");
								savableChar.AddConfiscatedItem(item, $"Item quality did not match saved item {item.m_quality}");
								removeItems.Add(item);
							}
						});
						foreach (ItemData item4 in removeItems)
						{
							((Humanoid)player).UnequipItem(item4, true);
							((Humanoid)player).GetInventory().RemoveItem(item4);
						}
					}
					foreach (ItemData item5 in ((Humanoid)player).GetInventory().GetAllItems().ToList())
					{
						savableChar.AddItemToPlayerItems(item5);
						if (ValConfig.ValidateItemCustomData.Value)
						{
							item5.m_customData.Clear();
						}
					}
				}
			}
			if (ValConfig.RemoveNontrackedItemsFromJoiningPlayers.Value)
			{
				new List<ItemData>();
				foreach (KeyValuePair<ItemData, DataObjects.ItemValidatorResult> item6 in ValidateItems(((Humanoid)player).m_inventory.GetAllItems(), savableChar))
				{
					if (!item6.Value.Validated)
					{
						Logger.LogInfo($"Removing item {((Object)item6.Key.m_dropPrefab).name}x{item6.Key.m_stack} from player {savableChar.Name}. Validation message: {item6.Value.ValidationMessage}");
						savableChar.AddConfiscatedItem(item6.Key, item6.Value.ValidationMessage);
						((Humanoid)player).UnequipItem(item6.Key, true);
						((Humanoid)player).GetInventory().RemoveItem(item6.Key);
					}
				}
			}
			if (ValConfig.AddMissingItemsFromPlayerServerSave.Value)
			{
				Logger.LogDebug("Checking to restore player items.");
				List<Tuple<string, int>> list = new List<Tuple<string, int>>();
				foreach (ItemData allItem in ((Humanoid)player).m_inventory.GetAllItems())
				{
					list.Add(new Tuple<string, int>(((Object)allItem.m_dropPrefab).name, allItem.m_stack));
				}
				foreach (DataObjects.PackedItem playerItem in savableChar.PlayerItems)
				{
					Tuple<string, int> item2 = new Tuple<string, int>(playerItem.prefabName, playerItem.m_stack);
					if (!list.Contains(item2))
					{
						Logger.LogInfo($"Adding missing item to players inventory: {playerItem.prefabName}x{playerItem.m_stack}");
						playerItem.AddToInventory(player, use_position: false);
					}
				}
			}
			Logger.LogDebug("Validated player items.");
			if (ValConfig.PreventExternalSkillRaises.Value)
			{
				((Character)player).GetSkills().GetSkillList().ForEach(delegate(Skill skill)
				{
					//IL_0011: Unknown result type (might be due to invalid IL or missing references)
					//IL_003b: Unknown result type (might be due to invalid IL or missing references)
					if (savableChar.SkillLevels.TryGetValue(skill.m_info.m_skill, out var value) && skill.m_level > value)
					{
						Logger.LogInfo($"Removing external skill gains for {skill.m_info.m_skill} from {value} to {skill.m_level} from player {savableChar.Name}");
						skill.m_level = value;
					}
				});
			}
			Logger.LogDebug("Validated player skills.");
			PlayerCharacter = savableChar;
			ValConfig.WritePlayerCharacterToSave(text, savableChar);
			if (ZNet.instance.GetServerPeer() != null)
			{
				ValConfig.CharacterSaveRPC.SendPackage(ZNet.instance.GetServerPeer().m_uid, ValConfig.SendCharacterAsZpackage(savableChar));
			}
		}

		internal static Dictionary<ItemData, DataObjects.ItemValidatorResult> ValidateItems(List<ItemData> playerItems, DataObjects.Character savedChar)
		{
			Dictionary<ItemData, DataObjects.ItemValidatorResult> dictionary = new Dictionary<ItemData, DataObjects.ItemValidatorResult>();
			Logger.LogInfo($"Player Items: {playerItems.Count} | SavedCharacter Items: {savedChar.PlayerItems.Count}");
			foreach (ItemData playerItem in playerItems)
			{
				Logger.LogDebug("Checking player item: " + ((Object)playerItem.m_dropPrefab).name);
				DataObjects.ValidationSummary validationSummary = new DataObjects.ValidationSummary();
				dictionary.Add(playerItem, new DataObjects.ItemValidatorResult
				{
					CharacterItemRef = playerItem
				});
				string text = "";
				foreach (DataObjects.PackedItem playerItem2 in savedChar.PlayerItems)
				{
					if (!(playerItem2.prefabName == ((Object)playerItem.m_dropPrefab).name) || playerItem2.m_stack != playerItem.m_stack)
					{
						continue;
					}
					validationSummary.NameAndStackMatch = true;
					int num = playerItem2.m_quality;
					if (num == 0)
					{
						num = 1;
					}
					if (num == playerItem.m_quality)
					{
						validationSummary.QualityMatch = true;
						text += $"{num} != {playerItem.m_quality} ";
					}
					if (ValConfig.ValidateItemDurability.Value && playerItem.m_durability <= playerItem2.m_durability - ValConfig.ItemValidationDurabilityAllowedVariance.Value && playerItem.m_durability >= playerItem2.m_durability + ValConfig.ItemValidationDurabilityAllowedVariance.Value)
					{
						validationSummary.DurabilityMatch = false;
						text += $"Durability mismatch. Expected {playerItem2.m_durability} got {playerItem.m_durability} ";
						Logger.LogDebug($"Item {((Object)playerItem.m_dropPrefab).name} durability mismatch. Expected {playerItem2.m_durability} got {playerItem.m_durability} | {playerItem.m_durability} >= {playerItem2.m_durability - ValConfig.ItemValidationDurabilityAllowedVariance.Value} && {playerItem.m_durability} <= {playerItem2.m_durability + ValConfig.ItemValidationDurabilityAllowedVariance.Value}");
					}
					else
					{
						validationSummary.DurabilityMatch = true;
					}
					validationSummary.CustomDataMatch = true;
					if (ValConfig.ValidateItemCustomData.Value)
					{
						foreach (KeyValuePair<string, string> customDatum in playerItem.m_customData)
						{
							if (playerItem2.m_customdata.ContainsKey(customDatum.Key) && playerItem2.m_customdata[customDatum.Key] != customDatum.Value)
							{
								validationSummary.CustomDataMatch = false;
								text = text + "Custom data mismatch on key " + customDatum.Key + ". Expected " + playerItem2.m_customdata[customDatum.Key] + " got " + customDatum.Value + " ";
								Logger.LogDebug("Item " + ((Object)playerItem.m_dropPrefab).name + " custom data mismatch on key " + customDatum.Key + ". Expected " + playerItem2.m_customdata[customDatum.Key] + " got " + customDatum.Value);
							}
						}
					}
					if (validationSummary.IsValid())
					{
						Logger.LogDebug("Item " + ((Object)playerItem.m_dropPrefab).name + " passed validation checks against saved character data.");
						dictionary[playerItem].SavedItemRef = playerItem2;
						dictionary[playerItem].Validated = true;
						break;
					}
				}
				dictionary[playerItem].ValidationResult = validationSummary;
				if (!validationSummary.IsValid())
				{
					dictionary[playerItem].ValidationMessage = "Item " + ((Object)playerItem.m_dropPrefab).name + " failed validation checks against saved character data. " + $"Stack Match: {validationSummary.NameAndStackMatch}, " + $"Quality Match: {validationSummary.QualityMatch}, " + $"Custom Data Match: {validationSummary.CustomDataMatch}, " + $"Durability Match: {validationSummary.DurabilityMatch} | " + text;
				}
			}
			return dictionary;
		}
	}
	internal static class CheatDetector
	{
		private static class NativeWin32
		{
			public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

			[DllImport("user32.dll")]
			public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);

			[DllImport("user32.dll", CharSet = CharSet.Unicode)]
			public static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);

			[DllImport("user32.dll", CharSet = CharSet.Unicode)]
			public static extern int GetWindowTextW(IntPtr hWnd, StringBuilder lpString, int nMaxCount);

			[DllImport("kernel32.dll")]
			public static extern bool IsDebuggerPresent();

			[DllImport("kernel32.dll", ExactSpelling = true, SetLastError = true)]
			public static extern bool CheckRemoteDebuggerPresent(IntPtr hProcess, ref bool isDebuggerPresent);
		}

		internal class CheatDetectorBehaviour : MonoBehaviour
		{
			private float nextScan;

			private void Start()
			{
			}

			private void Update()
			{
				if (ValConfig.EnableCheatDetection.Value && !(Time.unscaledTime < nextScan))
				{
					nextScan = Time.unscaledTime + (float)Mathf.Max(1, ValConfig.CheatScanIntervalSeconds.Value);
					RunScan();
				}
			}

			private static void RunScan()
			{
				if (CharacterManager.PlayerCharacter != null)
				{
					DataObjects.CheatSummaryReport cheatSummaryReport = new DataObjects.CheatSummaryReport
					{
						PlayerName = CharacterManager.PlayerCharacter.Name,
						PlatformID = CharacterManager.PlayerCharacter.HostID,
						CheatEngineStatus = new DataObjects.CheatEngineDetector()
					};
					if (ValConfig.DetectValheimTooler.Value && ValheimToolerLoaded())
					{
						cheatSummaryReport.ValheimToolerStatus = true;
					}
					if (ValConfig.DetectCheatEngine.Value && CheatEngineProcessRunning())
					{
						cheatSummaryReport.CheatEngineStatus.CheatEngineProcessDetected = true;
					}
					if (cheatSummaryReport.cheatsDetected())
					{
						ReportCheatScanSummary(cheatSummaryReport);
					}
				}
			}
		}

		private static readonly HashSet<string> ReportedSignals = new HashSet<string>();

		private static readonly string[] ToolerAssemblyNames = new string[3] { "ValheimTooler", "ValheimToolerMod", "RapidGUI" };

		private static readonly string[] CheatEngineProcessNames = new string[4] { "cheatengine-x86_64", "cheatengine-i386", "cheatengine-x86_64-sse4-avx2", "cheatengine" };

		internal static void Initialize()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			if ((!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsDedicated()) && ValConfig.EnableCheatDetection.Value)
			{
				GameObject val = new GameObject("VE_CheatDetector");
				Object.DontDestroyOnLoad((Object)val);
				((Object)val).hideFlags = (HideFlags)61;
				val.AddComponent<CheatDetectorBehaviour>();
				Logger.LogDebug("CheatDetector initialized.");
			}
		}

		internal static bool ValheimToolerLoaded()
		{
			try
			{
				Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
				foreach (Assembly assembly in assemblies)
				{
					string i = assembly.GetName().Name ?? "";
					if (ToolerAssemblyNames.Any((string t) => t.Equals(i, StringComparison.OrdinalIgnoreCase)))
					{
						return true;
					}
				}
			}
			catch (Exception ex)
			{
				Logger.LogDebug("CheatDetector.ValheimToolerLoaded failed: " + ex.Message);
			}
			return false;
		}

		internal static bool CheatEngineProcessRunning()
		{
			try
			{
				Process[] processes = Process.GetProcesses();
				foreach (Process process in processes)
				{
					string pn = process.ProcessName ?? "";
					if (CheatEngineProcessNames.Any((string n) => pn.IndexOf(n, StringComparison.OrdinalIgnoreCase) >= 0))
					{
						return true;
					}
				}
			}
			catch (Exception ex)
			{
				Logger.LogDebug("CheatDetector.CheatEngineProcessRunning failed: " + ex.Message);
			}
			return false;
		}

		internal static void ReportCheatScanSummary(DataObjects.CheatSummaryReport report)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Expected O, but got Unknown
			try
			{
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.GetServerPeer() != null && ValConfig.CheatDetectionRPC != null)
				{
					string text = DataObjects.yamlserializer.Serialize((object)report);
					ZPackage val = new ZPackage();
					val.Write(text);
					ValConfig.CheatDetectionRPC.SendPackage(ZNet.instance.GetServerPeer().m_uid, val);
				}
			}
			catch (Exception)
			{
			}
		}
	}
	internal static class TerminalCommands
	{
		internal class ListPlayers : ConsoleCommand
		{
			public override string Name => "Enforcer-List-Players";

			public override bool IsCheat => true;

			public override string Help => "Enforcer-List-Players - Provides a full list of all accounts and Player names stored.";

			public override void Run(string[] args)
			{
				if (ValConfig.InternalStorageMode.Value)
				{
					foreach (KeyValuePair<string, List<string>> item in InternalDataStore.GetAccountRegistry())
					{
						Logger.LogInfo("Account:" + item.Key);
						foreach (string item2 in item.Value)
						{
							Logger.LogInfo("   " + item2);
						}
					}
					return;
				}
				foreach (string item3 in Directory.GetFiles(Path.Combine(Paths.ConfigPath, "ValheimEnforcer", "Characters")).ToList())
				{
					List<string> list = Directory.GetFiles(item3).ToList();
					Logger.LogInfo("Account:" + item3.Split(new char[1] { '/' }).Last());
					foreach (string item4 in list)
					{
						Logger.LogInfo("   " + item4.Split(new char[1] { '/' }).Last());
					}
				}
			}
		}

		internal class ListPlayerConfiscatedItems : ConsoleCommand
		{
			public override string Name => "Enforcer-List-Confiscated";

			public override bool IsCheat => true;

			public override string Help => "Gets a list of confiscated items, specific to a player/character. Format: enforcer-list-confiscated 99999999 TerryTheTerrible";

			public override void Run(string[] args)
			{
				if (args.Length != 2)
				{
					Logger.LogInfo("Account ID and playername are required. Ensure your command follows the format: enforcer-list-confiscated 99999999 TerryTheTerrible");
					return;
				}
				string id = args[0];
				string name = args[1];
				DataObjects.Character character = ValConfig.LoadCharacterFromSave(id, name);
				if (character.ConfiscatedItems.Count == 0)
				{
					Logger.LogInfo("Player does not have any confiscated items.");
					return;
				}
				Logger.LogInfo($"Found {character.ConfiscatedItems.Count} confiscated items.");
				foreach (DataObjects.PackedItem confiscatedItem in character.ConfiscatedItems)
				{
					Logger.LogInfo($"  {confiscatedItem.prefabName} x {confiscatedItem.m_stack}");
				}
			}
		}

		internal class ClearPlayerConfiscatedItems : ConsoleCommand
		{
			public override string Name => "Enforcer-Clear-Confiscated";

			public override bool IsCheat => true;

			public override string Help => "Clears any confiscated items listed for the specified player Format: enforcer-retrieve-confiscated 99999999 TerryTheTerrible all";

			public override void Run(string[] args)
			{
				if (args.Length != 3)
				{
					Logger.LogInfo("Account ID and playername are required. Ensure your command follows the format: enforcer-retrieve-confiscated 99999999 TerryTheTerrible all");
					return;
				}
				string id = args[0];
				string name = args[1];
				string text = args[2];
				DataObjects.Character character = ValConfig.LoadCharacterFromSave(id, name);
				if (character == null)
				{
					Logger.LogInfo("Character was not found for the specified account.");
					return;
				}
				if (character.ConfiscatedItems.Count == 0)
				{
					Logger.LogInfo("Player does not have any confiscated items.");
					return;
				}
				character.ConfiscatedItems.Clear();
				ValConfig.WritePlayerCharacterToSave(id, character);
				Logger.LogInfo($"Found {character.ConfiscatedItems.Count} confiscated items.");
				if (string.Compare(text, "all", ignoreCase: true) == 0)
				{
					Logger.LogInfo("Providing all confiscated items.");
					foreach (DataObjects.PackedItem confiscatedItem in character.ConfiscatedItems)
					{
						confiscatedItem.AddToInventory(Player.m_localPlayer, use_position: false);
					}
					character.ConfiscatedItems.Clear();
					return;
				}
				foreach (DataObjects.PackedItem confiscatedItem2 in character.ConfiscatedItems)
				{
					_ = confiscatedItem2;
					List<string> list = text.Split(new char[1] { ',' }).ToList();
					foreach (DataObjects.PackedItem confiscatedItem3 in character.ConfiscatedItems)
					{
						if (list.Contains(confiscatedItem3.prefabName))
						{
							Logger.LogInfo("Providing " + confiscatedItem3.prefabName);
							confiscatedItem3.AddToInventory(Player.m_localPlayer, use_position: false);
						}
					}
				}
			}
		}

		internal class RestorePlayerConfiscatedItems : ConsoleCommand
		{
			public override string Name => "Enforcer-Admin-Take-Confiscated";

			public override bool IsCheat => true;

			public override string Help => "Gives you player confiscated items, use either item prefab or 'all'. Format: enforcer-admin-take-confiscated 99999999 TerryTheTerrible all";

			public override void Run(string[] args)
			{
				if (args.Length != 3)
				{
					Logger.LogInfo("Account ID and playername are required. Ensure your command follows the format: enforcer-admin-take-confiscated 99999999 TerryTheTerrible all");
					return;
				}
				string id = args[0];
				string name = args[1];
				string text = args[2];
				DataObjects.Character character = ValConfig.LoadCharacterFromSave(id, name);
				if (character == null)
				{
					Logger.LogInfo("Character was not found for the specified account.");
					return;
				}
				if (character.ConfiscatedItems.Count == 0)
				{
					Logger.LogInfo("Player does not have any confiscated items.");
					return;
				}
				Logger.LogInfo($"Found {character.ConfiscatedItems.Count} confiscated items.");
				if (string.Compare(text, "all", ignoreCase: true) == 0)
				{
					Logger.LogInfo("Providing all confiscated items.");
					foreach (DataObjects.PackedItem confiscatedItem in character.ConfiscatedItems)
					{
						confiscatedItem.AddToInventory(Player.m_localPlayer, use_position: false);
					}
					character.ConfiscatedItems.Clear();
					return;
				}
				foreach (DataObjects.PackedItem confiscatedItem2 in character.ConfiscatedItems)
				{
					_ = confiscatedItem2;
					List<string> list = text.Split(new char[1] { ',' }).ToList();
					foreach (DataObjects.PackedItem confiscatedItem3 in character.ConfiscatedItems)
					{
						if (list.Contains(confiscatedItem3.prefabName))
						{
							Logger.LogInfo("Providing " + confiscatedItem3.prefabName);
							confiscatedItem3.AddToInventory(Player.m_localPlayer, use_position: false);
						}
					}
				}
				ValConfig.WritePlayerCharacterToSave(character.HostID, character);
			}
		}

		internal class ReturnPlayerConfiscatedItems : ConsoleCommand
		{
			public override string Name => "Enforcer-Return-Confiscated";

			public override bool IsCheat => true;

			public override string Help => "Sends confiscated items to a connected player via RPC. Use 'all' or comma-separated prefab names. Format: Enforcer-Return-Confiscated 99999999 TerryTheTerrible all";

			public override void Run(string[] args)
			{
				//IL_02f0: Unknown result type (might be due to invalid IL or missing references)
				//IL_02f7: Expected O, but got Unknown
				if (args.Length != 3)
				{
					Logger.LogInfo("Account ID, player name, and item filter are required. Ensure your command follows the format: Enforcer-Return-Confiscated 99999999 TerryTheTerrible all");
					return;
				}
				string text = args[0];
				string text2 = args[1];
				string text3 = args[2];
				DataObjects.Character character = ValConfig.LoadCharacterFromSave(text, text2);
				if (character == null)
				{
					Logger.LogInfo("Character was not found for the specified account.");
					return;
				}
				if (character.ConfiscatedItems.Count == 0)
				{
					Logger.LogInfo("Player does not have any confiscated items.");
					return;
				}
				List<DataObjects.PackedItem> list;
				if (string.Compare(text3, "all", ignoreCase: true) == 0)
				{
					list = new List<DataObjects.PackedItem>(character.ConfiscatedItems);
					character.ConfiscatedItems.Clear();
				}
				else
				{
					List<string> targetPrefabs = (from s in text3.Split(new char[1] { ',' })
						select s.Trim()).ToList();
					list = character.ConfiscatedItems.Where((DataObjects.PackedItem i) => targetPrefabs.Contains(i.prefabName)).ToList();
					character.ConfiscatedItems.RemoveAll((DataObjects.PackedItem i) => targetPrefabs.Contains(i.prefabName));
				}
				if (list.Count == 0)
				{
					Logger.LogInfo("No matching confiscated items found for the specified filter.");
					return;
				}
				if ((Object)(object)Player.m_localPlayer != (Object)null && Player.m_localPlayer.GetPlayerName() == text2)
				{
					Logger.LogInfo("Local player is the target, returning player items.");
					foreach (DataObjects.PackedItem item in list)
					{
						Logger.LogInfo("Providing " + item.prefabName);
						item.AddToInventory(Player.m_localPlayer, use_position: false);
					}
					ValConfig.WritePlayerCharacterToSave(text, character);
					return;
				}
				ZNetPeer val = null;
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					foreach (ZNetPeer peer in ZNet.instance.GetPeers())
					{
						if (!(peer.m_playerName != text2))
						{
							string text4 = peer.m_socket.GetEndPointString();
							if (text4.Contains(":"))
							{
								text4 = text4.Split(new char[1] { ':' })[0];
							}
							if (text4 == text)
							{
								val = peer;
								break;
							}
						}
					}
					if (val == null)
					{
						foreach (ZNetPeer peer2 in ZNet.instance.GetPeers())
						{
							if (peer2.m_playerName == text2)
							{
								val = peer2;
								break;
							}
						}
					}
				}
				if (val == null)
				{
					Logger.LogInfo("Player " + text2 + " is not currently connected. Moving items to player inventory save so they are restored on next login.");
					foreach (DataObjects.PackedItem item2 in list)
					{
						character.PlayerItems.Add(item2);
					}
					ValConfig.WritePlayerCharacterToSave(text, character);
				}
				else
				{
					ValConfig.WritePlayerCharacterToSave(text, character);
					Logger.LogInfo($"Sending {list.Count} confiscated item(s) to player {text2}.");
					ZPackage val2 = new ZPackage();
					val2.Write(DataObjects.yamlserializer.Serialize((object)list));
					ValConfig.ReturnConfiscatedItemsRPC.SendPackage(val.m_uid, val2);
				}
			}
		}

		internal static void AddCommands()
		{
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new ListPlayers());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new ListPlayerConfiscatedItems());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new RestorePlayerConfiscatedItems());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new ReturnPlayerConfiscatedItems());
		}
	}
	internal static class InternalDataStore
	{
		private static ZDO MetadataRegistry;

		internal static void SaveAccountCharacter(DataObjects.Character character)
		{
			InstanciateOrLinkMetadataRegistry();
			UpdateAccountRegistry(character.HostID, character.Name);
			string @string = MetadataRegistry.GetString(character.HostID, (string)null);
			if (@string != null)
			{
				DataObjects.CharacterSaveData characterSaveData = DataObjects.yamldeserializer.Deserialize<DataObjects.CharacterSaveData>(@string);
				if (characterSaveData.SavedCharacters.ContainsKey(character.Name))
				{
					characterSaveData.SavedCharacters[character.Name] = character;
				}
				else
				{
					characterSaveData.SavedCharacters.Add(character.Name, character);
				}
				string text = DataObjects.yamlserializer.Serialize((object)characterSaveData);
				MetadataRegistry.Set(character.HostID, text);
			}
			else
			{
				DataObjects.CharacterSaveData characterSaveData2 = new DataObjects.CharacterSaveData
				{
					SavedCharacters = new Dictionary<string, DataObjects.Character> { { character.Name, character } }
				};
				string text2 = DataObjects.yamlserializer.Serialize((object)characterSaveData2);
				MetadataRegistry.Set(character.HostID, text2);
			}
		}

		internal static DataObjects.Character GetAccountCharacter(string accountID, string characterName)
		{
			InstanciateOrLinkMetadataRegistry();
			string @string = MetadataRegistry.GetString(accountID, (string)null);
			if (@string != null)
			{
				Logger.LogDebug("Character data found " + accountID + "-" + characterName + ".");
				DataObjects.CharacterSaveData characterSaveData = DataObjects.yamldeserializer.Deserialize<DataObjects.CharacterSaveData>(@string);
				if (characterSaveData.SavedCharacters.ContainsKey(characterName))
				{
					return characterSaveData.SavedCharacters[characterName];
				}
			}
			return null;
		}

		internal static DataObjects.CharacterSaveData GetAccountData(string accountID)
		{
			InstanciateOrLinkMetadataRegistry();
			string @string = MetadataRegistry.GetString(accountID, (string)null);
			if (@string != null)
			{
				return DataObjects.yamldeserializer.Deserialize<DataObjects.CharacterSaveData>(@string);
			}
			return null;
		}

		internal static void RegisterMetadataHolder()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Expected O, but got Unknown
			CustomPrefab val = new CustomPrefab(ValheimEnforcer.EmbeddedResourceBundle.LoadAsset<GameObject>("VE_METADATA"), false);
			PrefabManager.Instance.AddPrefab(val);
		}

		internal static void InstanciateOrLinkMetadataRegistry()
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			if (MetadataRegistry != null)
			{
				return;
			}
			string text = default(string);
			if (ZoneSystem.instance.GetGlobalKey(DataObjects.CustomDataKey ?? "", ref text))
			{
				string[] array = text.Split(new char[1] { ' ' });
				if (array.Length == 2 && long.TryParse(array[0], out var result) && uint.TryParse(array[1], out var result2))
				{
					ZDOID val = default(ZDOID);
					((ZDOID)(ref val))..ctor(result, result2);
					MetadataRegistry = ZDOMan.instance.GetZDO(val);
				}
			}
			long sessionID = ZDOMan.GetSessionID();
			ZDO val2 = ZDOMan.instance.CreateNewZDO(Vector3.zero, 0);
			val2.Persistent = true;
			val2.SetOwner(sessionID);
			MetadataRegistry = val2;
			ZoneSystem.instance.SetGlobalKey($"{DataObjects.CustomDataKey} {((ZDOID)(ref MetadataRegistry.m_uid)).UserID} {((ZDOID)(ref MetadataRegistry.m_uid)).ID}");
			Logger.LogInfo($"Hooking up Metadata Registry. SessionID:{sessionID} ZDO:{val2.m_uid}");
			Logger.LogInfo($"Setting globalkey: {DataObjects.CustomDataKey} {((ZDOID)(ref MetadataRegistry.m_uid)).UserID} {((ZDOID)(ref MetadataRegistry.m_uid)).ID}");
		}

		internal static void UpdateAccountRegistry(string accountID, string chara = null)
		{
			InstanciateOrLinkMetadataRegistry();
			string @string = MetadataRegistry.GetString("VE_ACCOUNTS", (string)null);
			if (@string != null)
			{
				Dictionary<string, List<string>> dictionary = DataObjects.yamldeserializer.Deserialize<Dictionary<string, List<string>>>(@string);
				if (!dictionary.ContainsKey(accountID))
				{
					if (chara != null)
					{
						dictionary[accountID] = new List<string> { chara };
					}
					else
					{
						dictionary[accountID] = new List<string>();
					}
					string text = DataObjects.yamlserializer.Serialize((object)dictionary);
					MetadataRegistry.Set("VE_ACCOUNTS", text);
				}
			}
			else
			{
				List<string> list = new List<string>();
				if (chara != null)
				{
					list.Add(chara);
				}
				Dictionary<string, List<string>> dictionary2 = new Dictionary<string, List<string>> { { accountID, list } };
				string text2 = DataObjects.yamlserializer.Serialize((object)dictionary2);
				MetadataRegistry.Set("VE_ACCOUNTS", text2);
			}
		}

		internal static Dictionary<string, List<string>> GetAccountRegistry()
		{
			InstanciateOrLinkMetadataRegistry();
			string @string = MetadataRegistry.GetString("VE_ACCOUNTS", (string)null);
			if (@string != null)
			{
				return DataObjects.yamldeserializer.Deserialize<Dictionary<string, List<string>>>(@string);
			}
			return new Dictionary<string, List<string>>();
		}
	}
	internal static class ModManager
	{
		internal static class ValidateMods
		{
			[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
			public static class ZNet_OnNewConnection_Patch
			{
				[HarmonyPrefix]
				[HarmonyPriority(800)]
				private static void Prefix(ZNet __instance, ZNetPeer peer)
				{
					Logger.LogDebug("New Connection, register VE Mod Sync RPC.");
					peer.m_rpc.Register<ZPackage>("RPC_ReceiveModVersionData", (Action<ZRpc, ZPackage>)RPC_ReceiveModVersionData);
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "RPC_ClientHandshake")]
		public static class ZNet_RPC_ClientHandshake_Patch
		{
			[HarmonyPrefix]
			[HarmonyPriority(800)]
			private static void Prefix(ZNet __instance, ZRpc rpc)
			{
				if (ZNetExtension.IsClientInstance(__instance))
				{
					Logger.LogDebug("Client sending mod version data to server");
					rpc.Invoke("RPC_ReceiveModVersionData", new object[1] { ModSettings.ToZPackage() });
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "RPC_ServerHandshake")]
		public static class ZNet_RPC_ServerHandshake_Patch
		{
			[HarmonyPrefix]
			[HarmonyPriority(800)]
			private static void Prefix(ZNet __instance, ZRpc rpc)
			{
				if (__instance.IsServer())
				{
					Logger.LogDebug("Server sending mod version data to client");
					rpc.Invoke("RPC_ReceiveModVersionData", new object[1] { ModSettings.ToZPackage() });
				}
			}
		}

		public class JotunnDetailDisconnectExpansion : MonoBehaviour
		{
			private GameObject ContentView;

			private Text HeaderText;

			private Text FooterText;

			private static string HeaderMessage = "";

			private static string FooterMessage = "";

			private bool textset;

			public void UpdateErrorText(string header, string footer)
			{
				Logger.LogDebug("Set Error results " + header + " " + footer);
				HeaderMessage = header;
				FooterMessage = footer;
				textset = false;
			}

			public void Update()
			{
				if ((Object)(object)GUIManager.CustomGUIFront == (Object)null)
				{
					return;
				}
				Transform val = GUIManager.CustomGUIFront.transform.Find("CompatibilityWindow(Clone)/Scroll View/Viewport/Content");
				if ((Object)(object)val == (Object)null)
				{
					textset = false;
				}
				else if (!textset)
				{
					((Component)GUIManager.CustomGUIFront.transform.Find("CompatibilityWindow(Clone)/Scroll View")).GetComponent<ScrollRect>().scrollSensitivity = 1000f;
					ContentView = ((Component)val).gameObject;
					Transform val2 = ContentView.transform.Find("Failed Connection Text");
					if ((Object)(object)val2 != (Object)null)
					{
						HeaderText = ((Component)val2).GetComponent<Text>();
					}
					else
					{
						Logger.LogDebug("Could not find HeaderText");
					}
					Transform val3 = ContentView.transform.Find("Error Messages Text");
					if ((Object)(object)val3 != (Object)null)
					{
						FooterText = ((Component)val3).GetComponent<Text>();
					}
					else
					{
						Logger.LogDebug("Could not find FooterText");
					}
					if ((Object)(object)HeaderText != (Object)null && !string.IsNullOrEmpty(HeaderMessage))
					{
						HeaderText.text = "<color=#FFA13C>Failed Connection:</color>\n" + HeaderMessage;
					}
					if ((Object)(object)FooterText != (Object)null && !string.IsNullOrEmpty(FooterMessage))
					{
						FooterText.text = "<color=#FFA13C>Further Steps:</color>\n" + FooterMessage;
					}
					Logger.LogDebug("Set error results. H:" + HeaderMessage + " F:" + FooterMessage);
					textset = true;
				}
			}
		}

		internal static Dictionary<string, BaseUnityPlugin> ActiveMods = new Dictionary<string, BaseUnityPlugin>();

		internal static DataObjects.Mods ModSettings { get; set; }

		internal static JotunnDetailDisconnectExpansion DetailsUpdater { get; set; }

		internal static void SetModsActive()
		{
			ActiveMods.Clear();
			ActiveMods = BepInExUtils.GetPlugins(true);
			ModSettings = new DataObjects.Mods();
			Logger.LogDebug($"Detected {ActiveMods.Keys.Count} mods.");
			LoadConfig(File.ReadAllText(ValConfig.ModsConfigFilePath));
			ModSettings.ActiveMods.Clear();
			foreach (KeyValuePair<string, BaseUnityPlugin> activeMod in ActiveMods)
			{
				if (!ModSettings.ActiveMods.ContainsKey(activeMod.Key))
				{
					Logger.LogDebug("Adding Mod " + activeMod.Key + " not found in modlist");
					ModSettings.ActiveMods.Add(activeMod.Key, new DataObjects.Mod
					{
						EnforceVersion = true,
						Version = activeMod.Value.Info.Metadata.Version.ToString(),
						PluginID = activeMod.Value.Info.Metadata.GUID,
						Name = activeMod.Value.Info.Metadata.Name
					});
				}
				Logger.LogDebug($"Found active mod: {activeMod.Key} v{activeMod.Value.Info.Metadata.Version}");
				string currentVersion = activeMod.Value.Info.Metadata.Version.ToString();
				if (ModSettings.RequiredMods.ContainsKey(activeMod.Key))
				{
					UpdateModVersionIfChanged(ModSettings.RequiredMods, activeMod.Key, currentVersion);
				}
				else if (ModSettings.AdminOnlyMods.ContainsKey(activeMod.Key))
				{
					UpdateModVersionIfChanged(ModSettings.AdminOnlyMods, activeMod.Key, currentVersion);
				}
				else if (ModSettings.OptionalMods.ContainsKey(activeMod.Key))
				{
					UpdateModVersionIfChanged(ModSettings.OptionalMods, activeMod.Key, currentVersion);
				}
				else if (ModSettings.ServerOnlyMods.ContainsKey(activeMod.Key))
				{
					UpdateModVersionIfChanged(ModSettings.ServerOnlyMods, activeMod.Key, currentVersion);
				}
				else if (ValConfig.AutoAddModsToRequired.Value)
				{
					Logger.LogDebug("Automatically adding " + activeMod.Key + " as a required mod.");
					ModSettings.RequiredMods.Add(activeMod.Key, new DataObjects.Mod
					{
						EnforceVersion = false,
						Version = activeMod.Value.Info.Metadata.Version.ToString(),
						PluginID = activeMod.Value.Info.Metadata.GUID,
						Name = activeMod.Value.Info.Metadata.Name
					});
				}
			}
			if (ValConfig.UpdateLoadedModsOnStartup.Value)
			{
				Logger.LogDebug("Updated Mods.yaml.");
				File.WriteAllText(ValConfig.ModsConfigFilePath, DataObjects.yamlserializer.Serialize((object)ModSettings));
			}
		}

		private static void UpdateModVersionIfChanged(Dictionary<string, DataObjects.Mod> modList, string key, string currentVersion)
		{
			if (modList[key].Version != currentVersion)
			{
				Logger.LogInfo("Updating version for " + key + ": " + modList[key].Version + " -> " + currentVersion);
				modList[key].Version = currentVersion;
			}
		}

		internal static void UpdateModSettingConfigs(string yamlstring)
		{
			try
			{
				ModSettings = DataObjects.yamldeserializer.Deserialize<DataObjects.Mods>(yamlstring);
			}
			catch
			{
				Logger.LogWarning("Failed to deserialize mod configurations.");
			}
		}

		internal static bool ValidateModlist(DataObjects.Mods CheckingMods, DataObjects.Mods AuthoratativeMods, bool isAdmin, out string summay, out string details)
		{
			summay = "";
			details = "";
			List<string> list = new List<string>();
			List<string> list2 = new List<string>();
			List<string> list3 = AuthoratativeMods.RequiredMods.Keys.Distinct().ToList();
			Logger.LogDebug($"Validating modlist of {CheckingMods.ActiveMods.Count} mods isAdmin? {isAdmin}");
			foreach (KeyValuePair<string, DataObjects.Mod> activeMod in CheckingMods.ActiveMods)
			{
				list3.Remove(activeMod.Key);
				if (AuthoratativeMods.RequiredMods.ContainsKey(activeMod.Key))
				{
					if (!AuthoratativeMods.RequiredMods[activeMod.Key].EnforceVersion || AuthoratativeMods.RequiredMods[activeMod.Key].Version == activeMod.Value.Version)
					{
						continue;
					}
					list2.Add(activeMod.Key);
				}
				if (AuthoratativeMods.AdminOnlyMods.ContainsKey(activeMod.Key))
				{
					if (!isAdmin || !AuthoratativeMods.AdminOnlyMods[activeMod.Key].EnforceVersion || AuthoratativeMods.AdminOnlyMods[activeMod.Key].Version == activeMod.Value.Version)
					{
						continue;
					}
					list2.Add(activeMod.Key);
				}
				if (AuthoratativeMods.OptionalMods.ContainsKey(activeMod.Key))
				{
					if (!AuthoratativeMods.OptionalMods[activeMod.Key].EnforceVersion || AuthoratativeMods.OptionalMods[activeMod.Key].Version == activeMod.Value.Version)
					{
						continue;
					}
					list2.Add(activeMod.Key);
				}
				list.Add(activeMod.Key);
			}
			if (list2.Count > 0)
			{
				Logger.LogWarning("Mods version mismatch with the server found:");
				summay = "A Mod mismatch was detected. Ensure you have the correct versions and are only using allowed mods.";
			}
			if (list3.Count > 0)
			{
				string text = "\nMissing required mods: " + string.Join(", ", list3);
				summay += text;
				Logger.LogWarning(text);
			}
			if (list.Count > 0)
			{
				string text2 = "\nNon-allowed mods found: " + string.Join(", ", list);
				summay += text2;
				Logger.LogWarning(text2);
			}
			if (list2.Count > 0 || list3.Count > 0 || list.Count > 0)
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("\n<b>ValheimEnforcer - Mod Validation Failed</b>");
				if (list2.Count > 0)
				{
					stringBuilder.AppendLine("\n<b>Version Mismatches:</b>");
					foreach (string item in list2)
					{
						stringBuilder.AppendLine("  • " + item);
					}
				}
				if (list3.Count > 0)
				{
					stringBuilder.AppendLine("\n<b>Missing Required Mods:</b>");
					foreach (string item2 in list3)
					{
						stringBuilder.AppendLine("  • " + item2);
					}
				}
				if (list.Count > 0)
				{
					stringBuilder.AppendLine("\n<b>Non-Allowed Mods:</b>");
					foreach (string item3 in list)
					{
						stringBuilder.AppendLine("  • " + item3);
					}
				}
				string text3 = stringBuilder.ToString();
				details = text3;
				return false;
			}
			Logger.LogInfo("Client mod list validated successfully.");
			return true;
		}

		internal static void LoadConfig(string yaml)
		{
			ModSettings = DataObjects.yamldeserializer.Deserialize<DataObjects.Mods>(yaml);
		}

		internal static string GetDefaultConfig()
		{
			if (ModSettings != null)
			{
				return DataObjects.yamlserializer.Serialize((object)ModSettings);
			}
			return DataObjects.yamlserializer.Serialize((object)new DataObjects.Mods());
		}

		private static void RPC_ReceiveModVersionData(ZRpc sender, ZPackage data)
		{
			Logger.LogDebug("Received mod version data from " + sender.m_socket.GetEndPointString());
			string endPointString = sender.m_socket.GetEndPointString();
			if (!ZNet.instance.IsServer())
			{
				DataObjects.Mods mods = new DataObjects.Mods().FromZPackage(data);
				Logger.LogDebug($"Client received server mod data: Required: {mods.RequiredMods.Count}, Optional: {mods.OptionalMods.Count}, AdminOnly: {mods.AdminOnlyMods.Count} mods");
				if (!ValidateModlist(ModSettings, mods, SynchronizationManager.Instance.PlayerIsAdmin, out var summay, out var details))
				{
					DetailsUpdater.UpdateErrorText(summay, details);
					Logger.LogWarning("Mod compatibility check failed for client.");
				}
				return;
			}
			DataObjects.Mods mods2 = new DataObjects.Mods().FromZPackage(data);
			bool flag = ZNet.instance.IsAdmin(sender.m_socket.GetHostName());
			Logger.LogDebug($"Server received server mod data from {endPointString} Admin?{flag}: Required: {mods2.RequiredMods.Count}, Optional: {mods2.OptionalMods.Count}, AdminOnly: {mods2.AdminOnlyMods.Count} mods");
			if (!ValidateModlist(mods2, ModSettings, flag, out var summay2, out var _))
			{
				Logger.LogWarning("Mod compatibility check failed for client at " + endPointString + "\n" + summay2);
				sender.Invoke("Error", new object[1] { 3 });
			}
		}

		internal static void AddErrorMessageDetailsForMenu()
		{
			//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)
			Scene activeScene = SceneManager.GetActiveScene();
			if (((Scene)(ref activeScene)).name.Equals("start"))
			{
				DetailsUpdater = GUIManager.CustomGUIFront.AddComponent<JotunnDetailDisconnectExpansion>();
			}
		}
	}
}
namespace ValheimEnforcer.modules.compat
{
	internal class ModCompatability
	{
		public static bool IsExtraSlotsEnabled;

		internal static void CheckModCompat()
		{
			try
			{
				Dictionary<string, BaseUnityPlugin> plugins = BepInExUtils.GetPlugins(false);
				if (plugins != null && plugins.Keys.Contains("shudnal.ExtraSlots"))
				{
					Logger.LogInfo("Extra Slots mod detected. Enabling compatibility.");
					IsExtraSlotsEnabled = API.IsReady();
				}
			}
			catch
			{
				Logger.LogWarning("Unable to check mod compatibility. Ensure that Bepinex can load.");
			}
		}
	}
}
namespace ValheimEnforcer.modules.compat.ExtraSlots
{
	internal static class Extensions
	{
		internal static ExtraSlot ToExtraSlot(this object slot)
		{
			return new ExtraSlot
			{
				_id = () => (string)AccessTools.Property(API._typeSlot, "ID").GetValue(slot),
				_name = () => (string)AccessTools.Property(API._typeSlot, "Name").GetValue(slot),
				_gridPosition = () => (Vector2i)AccessTools.Property(API._typeSlot, "GridPosition").GetValue(slot),
				_item = () => (ItemData)AccessTools.Property(API._typeSlot, "Item").GetValue(slot),
				_itemFits = (ItemData item) => (bool)AccessTools.Method(API._typeSlot, "ItemFits", (Type[])null, (Type[])null).Invoke(slot, new object[1] { item }),
				_isActive = () => (bool)AccessTools.Property(API._typeSlot, "IsActive").GetValue(slot),
				_isFree = () => (bool)AccessTools.Property(API._typeSlot, "IsFree").GetValue(slot),
				_isHotkeySlot = () => (bool)AccessTools.Property(API._typeSlot, "IsHotkeySlot").GetValue(slot),
				_isEquipmentSlot = () => (bool)AccessTools.Property(API._typeSlot, "IsEquipmentSlot").GetValue(slot),
				_isQuickSlot = () => (bool)AccessTools.Property(API._typeSlot, "IsQuickSlot").GetValue(slot),
				_isMiscSlot = () => (bool)AccessTools.Property(API._typeSlot, "IsMiscSlot").GetValue(slot),
				_isAmmoSlot = () => (bool)AccessTools.Property(API._typeSlot, "IsAmmoSlot").GetValue(slot),
				_isFoodSlot = () => (bool)AccessTools.Property(API._typeSlot, "IsFoodSlot").GetValue(slot),
				_isCustomSlot = () => (bool)AccessTools.Property(API._typeSlot, "IsCustomSlot").GetValue(slot),
				_isEmptySlot = () => (bool)AccessTools.Property(API._typeSlot, "IsEmptySlot").GetValue(slot)
			};
		}
	}
	public class ExtraSlot
	{
		internal Func<string> _id;

		internal Func<string> _name;

		internal Func<Vector2i> _gridPosition;

		internal Func<ItemData> _item;

		internal Func<ItemData, bool> _itemFits;

		internal Func<bool> _isActive;

		internal Func<bool> _isFree;

		internal Func<bool> _isHotkeySlot;

		internal Func<bool> _isEquipmentSlot;

		internal Func<bool> _isQuickSlot;

		internal Func<bool> _isMiscSlot;

		internal Func<bool> _isAmmoSlot;

		internal Func<bool> _isFoodSlot;

		internal Func<bool> _isCustomSlot;

		internal Func<bool> _isEmptySlot;

		public static readonly Vector2i emptyPosition = new Vector2i(-1, -1);

		public string ID
		{
			get
			{
				if (_id == null)
				{
					return "";
				}
				return _id();
			}
		}

		public string Name
		{
			get
			{
				if (_name == null)
				{
					return "";
				}
				return _name();
			}
		}

		public Vector2i GridPosition
		{
			get
			{
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				//IL_0008: Unknown result type (might be due to invalid IL or missing references)
				if (_gridPosition == null)
				{
					return emptyPosition;
				}
				return _gridPosition();
			}
		}

		public ItemData Item
		{
			get
			{
				if (_item == null)
				{
					return null;
				}
				return _item();
			}
		}

		public bool IsActive
		{
			get
			{
				if (_isActive != null)
				{
					return _isActive();
				}
				return false;
			}
		}

		public bool IsFree
		{
			get
			{
				if (_isFree != null)
				{
					return _isFree();
				}
				return false;
			}
		}

		public bool IsHotkeySlot
		{
			get
			{
				if (_isHotkeySlot != null)
				{
					return _isHotkeySlot();
				}
				return false;
			}
		}

		public bool IsEquipmentSlot
		{
			get
			{
				if (_isEquipmentSlot != null)
				{
					return _isEquipmentSlot();
				}
				return false;
			}
		}

		public bool IsQuickSlot
		{
			get
			{
				if (_isQuickSlot != null)
				{
					return _isQuickSlot();
				}
				return false;
			}
		}

		public bool IsMiscSlot
		{
			get
			{
				if (_isMiscSlot != null)
				{
					return _isMiscSlot();
				}
				return false;
			}
		}

		public bool IsAmmoSlot
		{
			get
			{
				if (_isAmmoSlot != null)
				{
					return _isAmmoSlot();
				}
				return false;
			}
		}

		public bool IsFoodSlot
		{
			get
			{
				if (_isFoodSlot != null)
				{
					return _isFoodSlot();
				}
				return false;
			}
		}

		public bool IsCustomSlot
		{
			get
			{
				if (_isCustomSlot != null)
				{
					return _isCustomSlot();
				}
				return false;
			}
		}

		public bool IsEmptySlot
		{
			get
			{
				if (_isEmptySlot != null)
				{
					return _isEmptySlot();
				}
				return false;
			}
		}

		public bool ItemFits(ItemData item)
		{
			if (_itemFits != null)
			{
				return _itemFits(item);
			}
			return false;
		}
	}
	public static class API
	{
		private static bool _isNotReady;

		private static readonly List<ItemData> _emptyItemList = new List<ItemData>();

		private static readonly List<ExtraSlot> _emptySlotList = new List<ExtraSlot>();

		internal static Type _typeAPI;

		internal static Type _typeSlot;

		public static bool IsReady()
		{
			if (_isNotReady)
			{
				return false;
			}
			if (_typeAPI != null && _typeSlot != null)
			{
				return true;
			}
			_isNotReady = !Chainloader.PluginInfos.ContainsKey("shudnal.ExtraSlots");
			if (_isNotReady)
			{
				return false;
			}
			if (_typeAPI == null || _typeSlot == null)
			{
				Assembly assembly = Assembly.GetAssembly(((object)Chainloader.PluginInfos["shudnal.ExtraSlots"].Instance).GetType());
				if (assembly == null)
				{
					_isNotReady = true;
					return false;
				}
				_typeAPI = assembly.GetType("ExtraSlots.API");
				_typeSlot = assembly.GetType("ExtraSlots.Slots+Slot");
			}
			if (_typeAPI != null)
			{
				return _typeSlot != null;
			}
			return false;
		}

		public static List<ExtraSlot> GetExtraSlots()
		{
			if (IsReady())
			{
				return ((IEnumerable<object>)AccessTools.Method(_typeAPI, "GetExtraSlots", (Type[])null, (Type[])null).Invoke(_typeAPI, null)).Select((object slot) => slot.ToExtraSlot()).ToList();
			}
			return _emptySlotList;
		}

		public static List<ExtraSlot> GetEquipmentSlots()
		{
			if (IsReady())
			{
				return ((IEnumerable<object>)AccessTools.Method(_typeAPI, "GetEquipmentSlots", (Type[])null, (Type[])null).Invoke(_typeAPI, null)).Select((object slot) => slot.ToExtraSlot()).ToList();
			}
			return _emptySlotList;
		}

		public static List<ExtraSlot> GetQuickSlots()
		{
			if (IsReady())
			{
				return ((IEnumerable<object>)AccessTools.Method(_typeAPI, "GetQuickSlots", (Type[])null, (Type[])null).Invoke(_typeAPI, null)).Select((object slot) => slot.ToExtraSlot()).ToList();
			}
			return _emptySlotList;
		}

		public static List<ExtraSlot> GetFoodSlots()
		{
			if (IsReady())
			{
				return ((IEnumerable<object>)AccessTools.Method(_typeAPI, "GetFoodSlots", (Type[])null, (Type[])null).Invoke(_typeAPI, null)).Select((object slot) => slot.ToExtraSlot()).ToList();
			}
			return _emptySlotList;
		}

		public static List<ExtraSlot> GetAmmoSlots()
		{
			if (IsReady())
			{
				return ((IEnumerable<object>)AccessTools.Method(_typeAPI, "GetAmmoSlots", (Type[])null, (Type[])null).Invoke(_typeAPI, null)).Select((object slot) => slot.ToExtraSlot()).ToList();
			}
			return _emptySlotList;
		}

		public static List<ExtraSlot> GetMiscSlots()
		{
			if (IsReady())
			{
				return ((IEnumerable<object>)AccessTools.Method(_typeAPI, "GetMiscSlots", (Type[])null, (Type[])null).Invoke(_typeAPI, null)).Select((object slot) => slot.ToExtraSlot()).ToList();
			}
			return _emptySlotList;
		}

		public static ExtraSlot FindSlot(string slotID)
		{
			if (!IsReady())
			{
				return null;
			}
			return AccessTools.Method(_typeAPI, "FindSlot", (Type[])null, (Type[])null).Invoke(_typeAPI, new object[1] { slotID }).ToExtraSlot();
		}

		public static List<ItemData> GetAllExtraSlotsItems()
		{
			if (IsReady())
			{
				return (List<ItemData>)AccessTools.Method(_typeAPI, "GetAllExtraSlotsItems", (Type[])null, (Type[])null).Invoke(_typeAPI, null);
			}
			return _emptyItemList;
		}

		public static List<ItemData> GetEquipmentSlotsItems()
		{
			if (IsReady())
			{
				return (List<ItemData>)AccessTools.Method(_typeAPI, "GetEquipmentSlotsItems", (Type[])null, (Type[])null).Invoke(_typeAPI, null);
			}
			return _emptyItemList;
		}

		public static List<ItemData> GetQuickSlotsItems()
		{
			if (IsReady())
			{
				return (List<ItemData>)AccessTools.Method(_typeAPI, "GetQuickSlotsItems", (Type[])null, (Type[])null).Invoke(_typeAPI, null);
			}
			return _emptyItemList;
		}

		public static List<ItemData> GetFoodSlotsItems()
		{
			if (IsReady())
			{
				return (List<ItemData>)AccessTools.Method(_typeAPI, "GetFoodSlotsItems", (Type[])null, (Type[])null).Invoke(_typeAPI, null);
			}
			return _emptyItemList;
		}

		public static List<ItemData> GetAmmoSlotsItems()
		{
			if (IsReady())
			{
				return (List<ItemData>)AccessTools.Method(_typeAPI, "GetAmmoSlotsItems", (Type[])null, (Type[])null).Invoke(_typeAPI, null);
			}
			return _emptyItemList;
		}

		public static List<ItemData> GetMiscSlotsItems()
		{
			if (IsReady())
			{
				return (List<ItemData>)AccessTools.Method(_typeAPI, "GetMiscSlotsItems", (Type[])null, (Type[])null).Invoke(_typeAPI, null);
			}
			return _emptyItemList;
		}

		public static int GetExtraRows()
		{
			if (IsReady())
			{
				return (int)AccessTools.Method(_typeAPI, "GetExtraRows", (Type[])null, (Type[])null).Invoke(_typeAPI, null);
			}
			return -1;
		}

		public static int GetInventoryHeightFull()
		{
			if (IsReady())
			{
				return (int)AccessTools.Method(_typeAPI, "GetInventoryHeightFull", (Type[])null, (Type[])null).Invoke(_typeAPI, null);
			}
			return -1;
		}

		public static int GetInventoryHeightPlayer()
		{
			if (IsReady())
			{
				return (int)AccessTools.Method(_typeAPI, "GetInventoryHeightPlayer", (Type[])null, (Type[])null).Invoke(_typeAPI, null);
			}
			return -1;
		}

		public static bool IsGridPositionASlot(Vector2i gridPos)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (!IsReady())
			{
				return false;
			}
			return (bool)AccessTools.Method(_typeAPI, "IsGridPositionASlot", (Type[])null, (Type[])null).Invoke(_typeAPI, new object[1] { gridPos });
		}

		public static bool IsItemInSlot(ItemData item)
		{
			if (!IsReady())
			{
				return false;
			}
			return (bool)AccessTools.Method(_typeAPI, "IsItemInSlot", (Type[])null, (Type[])null).Invoke(_typeAPI, new object[1] { item });
		}

		public static bool IsItemInEquipmentSlot(ItemData item)
		{
			if (!IsReady())
			{
				return false;
			}
			return (bool)AccessTools.Method(_typeAPI, "IsItemInEquipmentSlot", (Type[])null, (Type[])null).Invoke(_typeAPI, new object[1] { item });
		}

		public static bool IsAnyGlobalKeyActive(string requiredKeys)
		{
			if (!IsReady())
			{
				return false;
			}
			return (bool)AccessTools.Method(_typeAPI, "requiredKeys", (Type[])null, (Type[])null).Invoke(_typeAPI, new object[1] { requiredKeys });
		}

		public static bool IsItemTypeKnown(ItemType itemType)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			if (!IsReady())
			{
				return false;
			}
			return (bool)AccessTools.Method(_typeAPI, "IsItemTypeKnown", (Type[])null, (Type[])null).Invoke(_typeAPI, new object[1] { itemType });
		}

		public static bool IsAnyMaterialDiscovered(string itemNames)
		{
			if (!IsReady())
			{
				return false;
			}
			return (bool)AccessTools.Method(_typeAPI, "IsAnyMaterialDiscovered", (Type[])null, (Type[])null).Invoke(_typeAPI, new object[1] { itemNames });
		}

		public static bool AddSlot(string slotID, Func<string> getName, Func<ItemData, bool> itemIsValid, Func<bool> isActive)
		{
			if (!IsReady())
			{
				return false;
			}
			return (bool)AccessTools.Method(_typeAPI, "AddSlot", (Type[])null, (Type[])null).Invoke(_typeAPI, new object[5] { slotID, -1, getName, itemIsValid, isActive });
		}

		public static bool AddSlotWithIndex(string slotID, int slotIndex, Func<string> getName, Func<ItemData, bool> itemIsValid, Func<bool> isActive)
		{
			if (!IsReady())
			{
				return false;
			}
			return (bool)AccessTools.Method(_typeAPI, "AddSlotWithIndex", (Type[])null, (Type[])null).Invoke(_typeAPI, new object[5] { slotID, slotIndex, getName, itemIsValid, isActive });
		}

		public static bool AddSlotBefore(string slotID, Func<string> getName, Func<ItemData, bool> itemIsValid, Func<bool> isActive, params string[] slotIDs)
		{
			if (!IsReady())
			{
				return false;
			}
			return (bool)AccessTools.Method(_typeAPI, "AddSlotBefore", (Type[])null, (Type[])null).Invoke(_typeAPI, new object[5] { slotID, getName, itemIsValid, isActive, slotIDs });
		}

		public static bool AddSlotAfter(string slotID, Func<string> getName, Func<ItemData, bool> itemIsValid, Func<bool> isActive, params string[] slotIDs)
		{
			if (!IsReady())
			{
				return false;
			}
			return (bool)AccessTools.Method(_typeAPI, "AddSlotAfter", (Type[])null, (Type[])null).Invoke(_typeAPI, new object[5] { slotID, getName, itemIsValid, isActive, slotIDs });
		}

		public static bool RemoveSlot(string slotID)
		{
			if (!IsReady())
			{
				return false;
			}
			return (bool)AccessTools.Method(_typeAPI, "RemoveSlot", (Type[])null, (Type[])null).Invoke(_typeAPI, new object[1] { slotID });
		}

		public static void UpdateSlots()
		{
			if (IsReady())
			{
				AccessTools.Method(_typeAPI, "UpdateSlots", (Type[])null, (Type[])null).Invoke(_typeAPI, null);
			}
		}
	}
}
namespace ValheimEnforcer.common
{
	internal static class DataObjects
	{
		public class Mod
		{
			public string PluginID { get; set; }

			public string Version { get; set; }

			public string Name { get; set; }

			[DefaultValue(false)]
			public bool EnforceVersion { get; set; }

			[DefaultValue("Minor")]
			public string VersionStrictness { get; set; } = "Minor";

		}

		public class Mods
		{
			public Dictionary<string, Mod> ActiveMods { get; set; } = new Dictionary<string, Mod>();


			public Dictionary<string, Mod> RequiredMods { get; set; } = new Dictionary<string, Mod>();


			public Dictionary<string, Mod> OptionalMods { get; set; } = new Dictionary<string, Mod>();


			public Dictionary<string, Mod> AdminOnlyMods { get; set; } = new Dictionary<string, Mod>();


			public Dictionary<string, Mod> ServerOnlyMods { get; set; } = new Dictionary<string, Mod>();


			public ZPackage ToZPackage()
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: Expected O, but got Unknown
				string text = yamlserializer.Serialize((object)this);
				ZPackage val = new ZPackage();
				val.Write(text);
				return val;
			}

			public Mods FromZPackage(ZPackage incoming)
			{
				Mods mods = yamldeserializer.Deserialize<Mods>(incoming.ReadString());
				ActiveMods = mods.ActiveMods;
				RequiredMods = mods.RequiredMods;
				OptionalMods = mods.OptionalMods;
				AdminOnlyMods = mods.AdminOnlyMods;
				ServerOnlyMods = mods.ServerOnlyMods;
				return mods;
			}
		}

		public class CheatEngineDetector
		{
			public bool CheatEngineModuleLoaded { get; set; }

			public bool CheatEngineProcessDetected { get; set; }

			public bool IsCheatEngineDetected()
			{
				if (!Che