Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ValheimEnforcer v0.7.1
plugins/ValheimEnforcer.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
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