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.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 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.6.1")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.6.1.0")] namespace ValheimEnforcer { internal class ValConfig { [CompilerGenerated] private sealed class d__33 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public ZPackage package; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__33(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(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 d__35 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public ZPackage package; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__35(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 list = DataObjects.yamldeserializer.Deserialize>(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(((Humanoid)Player.m_localPlayer).m_inventory, 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 d__32 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public ZPackage package; public long sender; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__32(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(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 d__34 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__34(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 EnableDebugMode; public static ConfigEntry UpdateLoadedModsOnStartup; public static ConfigEntry AutoAddModsToRequired; public static ConfigEntry RemoveNontrackedItemsFromJoiningPlayers; public static ConfigEntry AddMissingItemsFromPlayerServerSave; public static ConfigEntry PreventExternalSkillRaises; public static ConfigEntry NewCharactersSkillsCleared; public static ConfigEntry NewCharactersRemoveExtraItems; public static ConfigEntry NewCharacterSetSkillsToZero; public static ConfigEntry newCharacterClearCustomData; public static ConfigEntry PreventExternalCustomDataChanges; public static ConfigEntry ValidateItemCustomData; public static ConfigEntry ValidateItemDurability; public static ConfigEntry InternalStorageMode; 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; 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 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)); SynchronizationManager.Instance.AddInitialSynchronization(CharacterSaveRPC, (Func)SendSavedCharacter); LoadYamlConfigs(new Dictionary> { { 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("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."); NewCharactersSkillsCleared = BindServerConfig("Player Sync", "NewCharactersSkillsCleared", value: false, "If enabled, new characters that have no existing character file will have all skills set to 0."); 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: true, "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"); 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); } 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(text); } public static string GetSecondaryConfigDirectoryPath() { return Directory.CreateDirectory(Path.Combine(Paths.ConfigPath, "ValheimEnforcer")).FullName; } internal void LoadYamlConfigs(Dictionary> configFilesToFind) { string[] files = Directory.GetFiles(GetSecondaryConfigDirectoryPath()); List list = new List(); List 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> 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(d__32))] public static IEnumerator OnServerRecieveCharacter(long sender, ZPackage package) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__32(0) { sender = sender, package = package }; } [IteratorStateMachine(typeof(d__33))] public static IEnumerator OnClientReceiveCharacter(long sender, ZPackage package) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__33(0) { package = package }; } [IteratorStateMachine(typeof(d__34))] public static IEnumerator OnServerReturnConfiscatedReceive(long sender, ZPackage package) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__34(0); } [IteratorStateMachine(typeof(d__35))] public static IEnumerator OnClientReceiveConfiscatedItems(long sender, ZPackage package) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__35(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> BindServerConfig(string catagory, string key, List 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>(catagory, key, value, new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, IsAdvanced = advanced } })); } public static ConfigEntry 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(catagory, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange(valmin, valmax), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, IsAdvanced = advanced } })); } public static ConfigEntry 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(catagory, key, value, new ConfigDescription(description, acceptableValues, new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, IsAdvanced = advanced } })); } public static ConfigEntry 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(catagory, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange(valmin, valmax), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, IsAdvanced = advanced } })); } public static ConfigEntry 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(catagory, key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange(valmin, valmax), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, IsAdvanced = advanced } })); } public static ConfigEntry BindServerConfig(string catagory, string key, string value, string description, AcceptableValueList 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(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.6.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*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.6.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(); 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(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 staringAllowedPrefabs = new List { "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 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_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: 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) { if (ValConfig.NewCharactersSkillsCleared.Value) { Logger.LogInfo("New character save for player " + text2 + " with ID " + text + ", skills set to zero."); ((Character)player).GetSkills().CheatResetSkill("all"); } 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("Setting new character skills 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 removeItems = new List(); ((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); 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); 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(); foreach (KeyValuePair 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); ((Humanoid)player).UnequipItem(item6.Key, true); ((Humanoid)player).GetInventory().RemoveItem(item6.Key); } } } if (ValConfig.AddMissingItemsFromPlayerServerSave.Value) { Logger.LogDebug("Checking to restore player items."); List> list = new List>(); foreach (ItemData allItem in ((Humanoid)player).m_inventory.GetAllItems()) { list.Add(new Tuple(((Object)allItem.m_dropPrefab).name, allItem.m_stack)); } foreach (DataObjects.PackedItem playerItem in savableChar.PlayerItems) { Tuple item2 = new Tuple(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(((Humanoid)player).GetInventory(), 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 ValidateItems(List playerItems, DataObjects.Character savedChar) { Dictionary dictionary = new Dictionary(); 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 }); 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; } if (ValConfig.ValidateItemDurability.Value && playerItem2.m_durability != playerItem.m_durability) { validationSummary.DurabilityMatch = false; Logger.LogDebug($"Item {((Object)playerItem.m_dropPrefab).name} durability mismatch. Expected {playerItem2.m_durability} got {playerItem.m_durability}"); } else { validationSummary.DurabilityMatch = true; } validationSummary.CustomDataMatch = true; if (ValConfig.ValidateItemCustomData.Value) { foreach (KeyValuePair customDatum in playerItem.m_customData) { if (playerItem2.m_customdata.ContainsKey(customDatum.Key) && playerItem2.m_customdata[customDatum.Key] != customDatum.Value) { validationSummary.CustomDataMatch = false; 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}"; } } return dictionary; } } 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> 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 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(((Humanoid)Player.m_localPlayer).m_inventory, use_position: false); } character.ConfiscatedItems.Clear(); return; } foreach (DataObjects.PackedItem confiscatedItem2 in character.ConfiscatedItems) { _ = confiscatedItem2; List 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(((Humanoid)Player.m_localPlayer).m_inventory, 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(((Humanoid)Player.m_localPlayer).m_inventory, use_position: false); } character.ConfiscatedItems.Clear(); return; } foreach (DataObjects.PackedItem confiscatedItem2 in character.ConfiscatedItems) { _ = confiscatedItem2; List 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(((Humanoid)Player.m_localPlayer).m_inventory, 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_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: 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 list; if (string.Compare(text3, "all", ignoreCase: true) == 0) { list = new List(character.ConfiscatedItems); character.ConfiscatedItems.Clear(); } else { List 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(((Humanoid)Player.m_localPlayer).m_inventory, 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(@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 { { 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(@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(@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("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> dictionary = DataObjects.yamldeserializer.Deserialize>>(@string); if (!dictionary.ContainsKey(accountID)) { if (chara != null) { dictionary[accountID] = new List { chara }; } else { dictionary[accountID] = new List(); } string text = DataObjects.yamlserializer.Serialize((object)dictionary); MetadataRegistry.Set("VE_ACCOUNTS", text); } } else { List list = new List(); if (chara != null) { list.Add(chara); } Dictionary> dictionary2 = new Dictionary> { { accountID, list } }; string text2 = DataObjects.yamlserializer.Serialize((object)dictionary2); MetadataRegistry.Set("VE_ACCOUNTS", text2); } } internal static Dictionary> GetAccountRegistry() { InstanciateOrLinkMetadataRegistry(); string @string = MetadataRegistry.GetString("VE_ACCOUNTS", (string)null); if (@string != null) { return DataObjects.yamldeserializer.Deserialize>>(@string); } return new Dictionary>(); } } 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("RPC_ReceiveModVersionData", (Action)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().scrollSensitivity = 1000f; ContentView = ((Component)val).gameObject; Transform val2 = ContentView.transform.Find("Failed Connection Text"); if ((Object)(object)val2 != (Object)null) { HeaderText = ((Component)val2).GetComponent(); } 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(); } else { Logger.LogDebug("Could not find FooterText"); } HeaderText.text = "Failed Connection:\n" + HeaderMessage; FooterText.text = "Further Steps:\n" + FooterMessage; Logger.LogDebug("Set error results. H:" + HeaderMessage + " F:" + FooterMessage); textset = true; } } } internal static Dictionary ActiveMods = new Dictionary(); 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 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 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(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 list = new List(); List list2 = new List(); List list3 = AuthoratativeMods.RequiredMods.Keys.Distinct().ToList(); Logger.LogDebug($"Validating modlist of {CheckingMods.ActiveMods.Count} mods isAdmin? {isAdmin}"); foreach (KeyValuePair 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("\nValheimEnforcer - Mod Validation Failed"); if (list2.Count > 0) { stringBuilder.AppendLine("\nVersion Mismatches:"); foreach (string item in list2) { stringBuilder.AppendLine(" • " + item); } } if (list3.Count > 0) { stringBuilder.AppendLine("\nMissing Required Mods:"); foreach (string item2 in list3) { stringBuilder.AppendLine(" • " + item2); } } if (list.Count > 0) { stringBuilder.AppendLine("\nNon-Allowed Mods:"); 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(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(); } } } } 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 ActiveMods { get; set; } = new Dictionary(); public Dictionary RequiredMods { get; set; } = new Dictionary(); public Dictionary OptionalMods { get; set; } = new Dictionary(); public Dictionary AdminOnlyMods { get; set; } = new Dictionary(); public Dictionary ServerOnlyMods { get; set; } = new Dictionary(); 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(incoming.ReadString()); ActiveMods = mods.ActiveMods; RequiredMods = mods.RequiredMods; OptionalMods = mods.OptionalMods; AdminOnlyMods = mods.AdminOnlyMods; ServerOnlyMods = mods.ServerOnlyMods; return mods; } } public class ItemValidatorResult { public PackedItem SavedItemRef { get; set; } public ItemData CharacterItemRef { get; set; } [DefaultValue(false)] public bool Validated { get; set; } public string ValidationMessage { get; set; } public ValidationSummary ValidationResult { get; set; } } public class ValidationSummary { [DefaultValue(false)] public bool NameAndStackMatch { get; set; } [DefaultValue(false)] public bool QualityMatch { get; set; } [DefaultValue(false)] public bool CustomDataMatch { get; set; } [DefaultValue(false)] public bool DurabilityMatch { get; set; } public bool IsValid() { if (NameAndStackMatch && QualityMatch && CustomDataMatch) { return DurabilityMatch; } return false; } } [Serializable] public class PackedItem { public string prefabName { get; set; } public int m_stack { get; set; } public float m_durability { get; set; } public int m_quality { get; set; } [DefaultValue(0)] public int m_variant { get; set; } [DefaultValue(0)] public int m_worldlevel { get; set; } [DefaultValue(0L)] public long m_crafterID { get; set; } [DefaultValue("")] public string m_crafterName { get; set; } public Dictionary m_customdata { get; set; } [DefaultValue(false)] public bool m_equipped { get; set; } public Vector2i m_gridpos { get; set; } public void AddToInventory(Inventory inv, bool use_position) { //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) ZNetView.m_forceDisableInit = true; GameObject obj = Object.Instantiate(Cache.GetPrefab(prefabName)); ZNetView.m_forceDisableInit = false; ItemDrop component = obj.GetComponent(); component.m_itemData.m_stack = m_stack; component.m_itemData.m_durability = m_durability; if (m_quality == 0) { component.m_itemData.m_quality = 1; } else { component.m_itemData.m_quality = m_quality; } component.m_itemData.m_variant = m_variant; component.m_itemData.m_worldLevel = m_worldlevel; component.m_itemData.m_crafterID = m_crafterID; if (m_crafterName == null) { component.m_itemData.m_crafterName = ""; } else { component.m_itemData.m_crafterName = m_crafterName; } component.m_itemData.m_customData = m_customdata; component.m_itemData.m_pickedUp = true; if (use_position) { component.m_itemData.m_gridPos = m_gridpos; inv.AddItem(component.m_itemData, component.m_itemData.m_stack, m_gridpos.x, m_gridpos.y); } else { inv.AddItem(component.m_itemData); } Object.Destroy((Object)(object)obj); } } public class CharacterSaveData { public Dictionary SavedCharacters = new Dictionary(); } public class AccountEntries { public Dictionary> AccountCharacterEntries = new Dictionary>(); } public class Character { public string Name { get; set; } public string HostID { get; set; } public Dictionary SkillLevels { get; set; } = new Dictionary(); public Dictionary PlayerCustomData { get; set; } = new Dictionary(); public List PlayerItems { get; set; } = new List(); public List ConfiscatedItems { get; set; } = new List(); public void AddItemToPlayerItems(ItemData item) { //IL_00d7: Unknown result type (might be due to invalid IL or missing references) if (PlayerItems == null) { PlayerItems = new List(); } Logger.LogDebug($"Adding saved item {((Object)item.m_dropPrefab).name} with quality - {item.m_quality}"); PlayerItems.Add(new PackedItem { prefabName = ((Object)item.m_dropPrefab).name, m_stack = item.m_stack, m_durability = Mathf.Clamp(item.m_durability, 0f, item.m_shared.m_maxDurability), m_quality = item.m_quality, m_variant = item.m_variant, m_worldlevel = item.m_worldLevel, m_crafterID = item.m_crafterID, m_crafterName = item.m_crafterName, m_customdata = item.m_customData, m_equipped = item.m_equipped, m_gridpos = item.m_gridPos }); } public void AddConfiscatedItem(ItemData item) { //IL_009d: Unknown result type (might be due to invalid IL or missing references) if (ConfiscatedItems == null) { ConfiscatedItems = new List(); } ConfiscatedItems.Add(new PackedItem { prefabName = ((Object)item.m_dropPrefab).name, m_stack = item.m_stack, m_durability = item.m_durability, m_quality = item.m_quality, m_variant = item.m_variant, m_worldlevel = item.m_worldLevel, m_crafterID = item.m_crafterID, m_crafterName = item.m_crafterName, m_customdata = item.m_customData, m_equipped = item.m_equipped, m_gridpos = item.m_gridPos }); } } public static IDeserializer yamldeserializer = ((BuilderSkeleton)new DeserializerBuilder()).WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); public static ISerializer yamlserializer = ((BuilderSkeleton)new SerializerBuilder()).WithNamingConvention(CamelCaseNamingConvention.Instance).ConfigureDefaultValuesHandling((DefaultValuesHandling)2).Build(); public static readonly string CustomDataKey = "VE_CUSTOM_DATA"; } }