Please disclose if your mod was created primarily 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 ModConfigEnforcer v4.0.4
ModConfigEnforcer.dll
Decompiled 4 months agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Steamworks; using UnityEngine; using ZstdNet; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ModConfigEnforcer")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ModConfigEnforcer")] [assembly: AssemblyCopyright("Copyright © 2023")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("7e7b5c74-ddfd-4667-980a-04c189e07c67")] [assembly: AssemblyFileVersion("4.0.4")] [assembly: TargetFramework(".NETFramework,Version=v4.6.1", FrameworkDisplayName = ".NET Framework 4.6.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("4.0.4.0")] [module: UnverifiableCode] namespace ModConfigEnforcer; public interface IConfigVariable { bool ValueChanged { get; set; } string GetName(); bool LocalOnly(); object GetValue(); void SetValue(object o); Type GetValueType(); void Serialize(ZPackage zpg); bool Deserialize(ZPackage zpg); void Cleanup(); } public class AutomatedConfigWrapper<T> : IConfigVariable { private ConfigEntry<T> _ConfigFileEntry; private FieldInfo _TypedValueFI; private T _LastValidValue; public bool ValueChanged { get; set; } public AutomatedConfigWrapper(ConfigEntry<T> configEntry) { _ConfigFileEntry = configEntry; _ConfigFileEntry.SettingChanged += SettingChanged; _TypedValueFI = ((object)configEntry).GetType().GetField("_typedValue", BindingFlags.Instance | BindingFlags.NonPublic); } private void SettingChanged(object sender, EventArgs e) { ref T reference = ref _LastValidValue; T val = default(T); if (val == null) { val = reference; reference = ref val; } if (!reference.Equals(_ConfigFileEntry.Value)) { if (!ConfigManager.ShouldUseLocalConfig) { SetValue(_LastValidValue); } else { _LastValidValue = (T)GetValue(); } } } public string GetName() { return ((ConfigEntryBase)_ConfigFileEntry).Definition.Key; } public bool LocalOnly() { return false; } public Type GetValueType() { return typeof(T); } public object GetValue() { return ConfigManager.ShouldUseLocalConfig ? _ConfigFileEntry.Value : _LastValidValue; } public void SetValue(object o) { _LastValidValue = (T)o; if (ConfigManager.ShouldUseLocalConfig) { ValueChanged = !_ConfigFileEntry.Value.Equals(_LastValidValue); _ConfigFileEntry.Value = _LastValidValue; } else { _TypedValueFI.SetValue(_ConfigFileEntry, o); } } public void Serialize(ZPackage zpg) { object value = GetValue(); zpg.FillZPackage(GetValueType().IsEnum ? ((object)(int)value) : value); ValueChanged = false; } public bool Deserialize(ZPackage zpg) { return false; } public void Cleanup() { } } public class ConfigVariable<T> : IConfigVariable { private ConfigEntry<T> _ConfigFileEntry; private T _LocalValue; private bool _LocalOnly; public string Key => ((ConfigEntryBase)_ConfigFileEntry).Definition.Key; public T Value { get { if (!_LocalOnly) { if (!ConfigManager.ShouldUseLocalConfig) { return _LocalValue; } return _ConfigFileEntry.Value; } return _ConfigFileEntry.Value; } } public bool ValueChanged { get; set; } public ConfigVariable(ConfigFile config, string section, string key, T defaultValue, string description, bool localOnly) { _ConfigFileEntry = config.Bind<T>(section, key, defaultValue, description); _LocalValue = _ConfigFileEntry.Value; _LocalOnly = localOnly; } public ConfigVariable(ConfigFile config, string section, string key, T defaultValue, ConfigDescription description, bool localOnly) { _ConfigFileEntry = config.Bind<T>(section, key, defaultValue, description); _LocalValue = _ConfigFileEntry.Value; _LocalOnly = localOnly; } public ConfigVariable(ConfigEntry<T> configFileEntry) { _ConfigFileEntry = configFileEntry; _LocalValue = _ConfigFileEntry.Value; _LocalOnly = false; } public string GetName() { return Key; } public bool LocalOnly() { return _LocalOnly; } public Type GetValueType() { return typeof(T); } public object GetValue() { return Value; } public void SetValue(object o) { T val = (T)o; if (ConfigManager.ShouldUseLocalConfig) { ValueChanged = !_ConfigFileEntry.Value.Equals(val); _ConfigFileEntry.Value = val; } else { _LocalValue = val; } } public void Serialize(ZPackage zpg) { object value = GetValue(); zpg.FillZPackage(GetValueType().IsEnum ? ((object)(int)value) : value); ValueChanged = false; } public bool Deserialize(ZPackage zpg) { return false; } public void Cleanup() { } } public class ClientVariable<T> : IConfigVariable { private T _Value; private string _Name; public T Value => _Value; public bool ValueChanged { get { return false; } set { } } public ClientVariable(string name, T value) { _Name = name; _Value = value; } public string GetName() { return _Name; } public object GetValue() { return Value; } public Type GetValueType() { return typeof(T); } public void SetValue(object o) { _Value = (T)o; } public bool LocalOnly() { return true; } public void Serialize(ZPackage zpg) { } public bool Deserialize(ZPackage zpg) { return false; } public void Cleanup() { } } public class FileWatcherVariable : IConfigVariable { private ConfigManager.ModConfig _Mod; private string _Name; private bool _LocalOnly; private ZPackage _FileContents; private string WatchedFilePath; private string RelativePath; private FileSystemWatcher FSW; private Action<string, ZPackage> FileContentsChanged; public bool ValueChanged { get; set; } private int GetLengthOfPathCommonality(string path1, string path2) { path1 = Path.GetFullPath(path1).ToLower(); path2 = Path.GetFullPath(path2).ToLower(); int result = -1; int num = Mathf.Min(path1.Length, path2.Length); for (int i = 0; i < num; i++) { if (path1[i] != path2[i]) { if (path1[i] == Path.DirectorySeparatorChar || path1[i] == Path.AltDirectorySeparatorChar) { if (path2[i] != Path.DirectorySeparatorChar && path2[i] != Path.AltDirectorySeparatorChar) { break; } result = i + 1; } else if (path2[i] == Path.DirectorySeparatorChar || path2[i] == Path.AltDirectorySeparatorChar) { break; } } else if (path1[i] == Path.DirectorySeparatorChar || path1[i] == Path.AltDirectorySeparatorChar) { result = i + 1; } } return result; } public FileWatcherVariable(ConfigManager.ModConfig mod, string filepath, bool localOnly, Action<string, ZPackage> handler) { //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Expected O, but got Unknown //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Expected O, but got Unknown _Mod = mod; WatchedFilePath = filepath; int lengthOfPathCommonality = GetLengthOfPathCommonality(WatchedFilePath, Assembly.GetCallingAssembly().Location); if (lengthOfPathCommonality == -1) { RelativePath = WatchedFilePath; } else { RelativePath = WatchedFilePath.Substring(lengthOfPathCommonality); } Plugin.Log.LogInfo((object)("Watching " + WatchedFilePath + " with RelativePath " + RelativePath)); FSW = new FileSystemWatcher(Path.GetDirectoryName(WatchedFilePath)); FSW.Filter = Path.GetFileName(filepath); FSW.NotifyFilter = NotifyFilters.Size | NotifyFilters.LastWrite; FSW.IncludeSubdirectories = false; _Name = "FileWatcher_" + FSW.Filter; _LocalOnly = localOnly; if (File.Exists(WatchedFilePath)) { _FileContents = new ZPackage(File.ReadAllBytes(WatchedFilePath)); } else { _FileContents = new ZPackage(); } FileContentsChanged = handler; FSW.Changed += FSW_Changed; FSW.EnableRaisingEvents = true; } private void FSW_Changed(object sender, FileSystemEventArgs e) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown if (_LocalOnly || ConfigManager.ShouldUseLocalConfig) { _FileContents = new ZPackage(File.ReadAllBytes(WatchedFilePath)); FileContentsChanged?.Invoke(RelativePath, _FileContents); if (!_LocalOnly && Object.op_Implicit((Object)(object)ZNet.instance) && Plugin.IsAdmin(Player.m_localPlayer)) { ConfigManager.SendConfigToClient(_Mod.Name, changesOnly: true, 0L); } } } public string GetName() { return _Name; } public bool LocalOnly() { return _LocalOnly; } public object GetValue() { return _FileContents; } public Type GetValueType() { return typeof(ZPackage); } public void SetValue(object o) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown ValueChanged = true; ZPackage val = (ZPackage)((o is ZPackage) ? o : null); _FileContents = new ZPackage(val.GetArray()); FileContentsChanged?.Invoke(RelativePath, _FileContents); } public void Serialize(ZPackage zpg) { zpg.Write(_FileContents); } public bool Deserialize(ZPackage zpg) { return false; } public void Cleanup() { FSW.Dispose(); } } public static class ConfigManager { private class PackageTrackerInfo { private ZPackage[] Packages; public int Received { get; private set; } public int Total { get { ZPackage[] packages = Packages; if (packages == null) { return 0; } return packages.Length; } } public bool Add(ZPackage zpg) { int num = zpg.ReadInt(); int num2 = zpg.ReadInt(); Plugin.Log.LogDebug((object)("Client received package " + num + " of " + num2)); if (Packages == null) { Packages = (ZPackage[])(object)new ZPackage[num2]; } if (Packages[num - 1] == null) { Packages[num - 1] = zpg; return ++Received == num2; } return false; } public ZPackage GetPackage() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown if (Received != Total) { return null; } using MemoryStream memoryStream = new MemoryStream(); for (int i = 0; i < Packages.Length; i++) { byte[] array = Packages[i].ReadByteArray(); memoryStream.Write(array, 0, array.Length); Packages[i] = null; } Packages = null; memoryStream.Flush(); return new ZPackage(memoryStream.ToArray()); } } public class ModConfig { public string Name; public ConfigFile Config; public List<IConfigVariable> Variables = new List<IConfigVariable>(); public Action ServerConfigReceived; public Action ConfigReloaded; public string GetRegistrationType() { bool flag = false; bool flag2 = false; foreach (IConfigVariable variable in Variables) { if (variable.GetType() == typeof(ConfigVariable<>)) { flag = true; } else if (variable.GetType() == typeof(AutomatedConfigWrapper<>)) { flag2 = true; } } if (flag && flag2) { return "manual and automated discovery"; } if (flag) { return "manual"; } return "automated discovery"; } public void SortVariables() { Variables.Sort((IConfigVariable a, IConfigVariable b) => string.Compare(a.GetName(), b.GetName())); } public ZPackage Serialize(bool changesOnly = false) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown ZPackage val = null; bool flag = false; for (int i = 0; i < Variables.Count; i++) { if (!Variables[i].LocalOnly() && (!changesOnly || Variables[i].ValueChanged)) { if (!flag) { val = new ZPackage(); flag = true; val.Write(Name); } val.Write(i); Variables[i].Serialize(val); } } return val; } public void Deserialize(ZPackage zpg) { while (zpg.m_reader.PeekChar() > -1) { int num = zpg.ReadInt(); if (num < 0 || num >= Variables.Count) { Plugin.Log.LogError((object)("Invalid variable index " + num + " read from package, aborting deserialization for mod " + Name)); break; } try { zpg.ReadVariable(Variables[num]); } catch (Exception ex) { Plugin.Log.LogError((object)("Exception deserializing variable " + Variables[num].GetName() + " @ " + num + " for mod " + Name + ": " + ex.Message)); break; } } } public void Cleanup() { foreach (IConfigVariable variable in Variables) { variable.Cleanup(); } } } private static Dictionary<ulong, PackageTrackerInfo> PackageTracking = new Dictionary<ulong, PackageTrackerInfo>(); private const string ConfigRPCName = "ClientReceiveConfigData"; private static ulong LastPackageID; public static bool ShouldUseLocalConfig = true; private static readonly List<string> Mods = new List<string>(); private static readonly Dictionary<string, ModConfig> ModConfigs = new Dictionary<string, ModConfig>(); private static readonly Dictionary<string, IConfigVariable> AutomatedConfigsLocked = new Dictionary<string, IConfigVariable>(); public static event Action<string> UnknownModConfigReceived; public static bool IsConfigLocked(string name) { return AutomatedConfigsLocked.ContainsKey(name); } public static void RegisterRPC(ZRpc zrpc) { zrpc.Register<ZPackage>("ClientReceiveConfigData", (Action<ZRpc, ZPackage>)ClientReceiveConfigData); } internal static List<ModConfig> GetRegisteredModConfigs() { return ModConfigs.Values.ToList(); } internal static ModConfig GetRegisteredModConfig(string name, bool ignoreCase) { if (!ignoreCase) { if (ModConfigs.TryGetValue(name, out var value)) { return value; } return null; } return ModConfigs.Values.FirstOrDefault((ModConfig mc) => string.Compare(mc.Name, name, ignoreCase: true) == 0); } public static void RegisterMod(string modName, ConfigFile configFile, Action scrd = null, Action cr = null) { if (ModConfigs.TryGetValue(modName, out var value)) { value.Variables.Clear(); return; } Mods.Add(modName); ModConfigs[modName] = new ModConfig { Name = modName, Config = configFile, ServerConfigReceived = scrd, ConfigReloaded = cr }; configFile.ConfigReloaded += ConfigFile_ConfigReloaded; } private static void ConfigFile_ConfigReloaded(object sender, EventArgs e) { if (Object.op_Implicit((Object)(object)ZNet.instance) && !ZNet.instance.IsDedicated() && !ZNet.instance.IsServer()) { return; } List<ModConfig> list = new List<ModConfig>(); foreach (ModConfig item in ModConfigs.Values.Where((ModConfig mc) => mc.Config == sender)) { list.Add(item); item.ConfigReloaded?.Invoke(); } if (list.Count > 0) { SendConfigsToClients(list, changesOnly: true); } } public static ConfigVariable<T> RegisterModConfigVariable<T>(string modName, string varName, T defaultValue, string configSection, string configDescription, bool localOnly) { if (!ModConfigs.TryGetValue(modName, out var value)) { return null; } ConfigVariable<T> configVariable = new ConfigVariable<T>(value.Config, configSection, varName, defaultValue, configDescription, localOnly); value.Variables.Add(configVariable); return configVariable; } public static ConfigVariable<T> RegisterModConfigVariable<T>(string modName, string varName, T defaultValue, string configSection, ConfigDescription configDescription, bool localOnly) { if (!ModConfigs.TryGetValue(modName, out var value)) { return null; } ConfigVariable<T> configVariable = new ConfigVariable<T>(value.Config, configSection, varName, defaultValue, configDescription, localOnly); value.Variables.Add(configVariable); return configVariable; } public static void RegisterAutomatedModConfigVariable<T>(string modName, ConfigEntry<T> entry) { if (entry != null && ModConfigs.TryGetValue(modName, out var value)) { AutomatedConfigWrapper<T> automatedConfigWrapper = new AutomatedConfigWrapper<T>(entry); AutomatedConfigsLocked.Add(automatedConfigWrapper.GetName(), automatedConfigWrapper); value.Variables.Add(automatedConfigWrapper); } } public static ClientVariable<T> RegisterClientVariable<T>(string modName, string varName, T value) { ClientVariable<T> clientVariable = new ClientVariable<T>(varName, value); if (RegisterModConfigVariable(modName, clientVariable)) { return clientVariable; } return null; } public static bool RegisterModConfigVariable(string modName, IConfigVariable cv) { if (!ModConfigs.TryGetValue(modName, out var value)) { return false; } value.Variables.Add(cv); return true; } public static FileWatcherVariable RegisterModFileWatcher(string modName, string filePath, bool localOnly, Action<string, ZPackage> handler) { if (!ModConfigs.TryGetValue(modName, out var value)) { return null; } FileWatcherVariable fileWatcherVariable = new FileWatcherVariable(value, filePath, localOnly, handler); value.Variables.Add(fileWatcherVariable); return fileWatcherVariable; } public static void SortModVariables(string modName) { if (ModConfigs.TryGetValue(modName, out var value)) { value.SortVariables(); } } private static void ReadVariable(this ZPackage zp, IConfigVariable cv) { //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Expected O, but got Unknown Type valueType = cv.GetValueType(); if (valueType == typeof(int)) { cv.SetValue(zp.ReadInt()); } else if (valueType == typeof(uint)) { cv.SetValue(zp.ReadUInt()); } else if (valueType == typeof(bool)) { cv.SetValue(zp.ReadBool()); } else if (valueType == typeof(byte)) { cv.SetValue(zp.ReadByte()); } else if (valueType == typeof(byte[])) { cv.SetValue(zp.ReadByteArray()); } else if (valueType == typeof(char)) { cv.SetValue(zp.ReadChar()); } else if (valueType == typeof(sbyte)) { cv.SetValue(zp.ReadSByte()); } else if (valueType == typeof(long)) { cv.SetValue(zp.ReadLong()); } else if (valueType == typeof(ulong)) { cv.SetValue(zp.ReadULong()); } else if (valueType == typeof(float)) { cv.SetValue(zp.ReadSingle()); } else if (valueType == typeof(double)) { cv.SetValue(zp.ReadDouble()); } else if (valueType == typeof(string)) { cv.SetValue(zp.ReadString()); } else if (valueType == typeof(ZPackage)) { cv.SetValue(zp.ReadPackage()); } else if (valueType == typeof(List<string>)) { int num = zp.ReadInt(); List<string> list = new List<string>(num); for (int i = 0; i < num; i++) { list.Add(zp.ReadString()); } cv.SetValue(list); } else if (valueType == typeof(Vector3)) { cv.SetValue((object)new Vector3(zp.ReadSingle(), zp.ReadSingle(), zp.ReadSingle())); } else if (valueType == typeof(Quaternion)) { cv.SetValue((object)new Quaternion(zp.ReadSingle(), zp.ReadSingle(), zp.ReadSingle(), zp.ReadSingle())); } else if (valueType == typeof(ZDOID)) { cv.SetValue(zp.ReadZDOID()); } else if (valueType == typeof(HitData)) { HitData val = new HitData(); val.Deserialize(ref zp); cv.SetValue(val); } else if (valueType.IsEnum) { cv.SetValue(zp.ReadInt()); } else if (!cv.Deserialize(zp)) { Plugin.Log.LogError((object)("Unable to deserialize data for " + cv.ToString())); } } public static void FillZPackage(this ZPackage zp, params object[] ps) { ZRpc.Serialize(ps, ref zp); } private static ZPackage SerializeMod(string modname, bool changesOnly) { if (!ModConfigs.TryGetValue(modname, out var value)) { return null; } return value.Serialize(changesOnly); } public static ZPackage[] CompressPackage(ZPackage data) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Expected O, but got Unknown byte[] array = new Compressor().Wrap(data.GetArray()); int num = array.Length / 450000 + 1; ZPackage[] array2 = (ZPackage[])(object)new ZPackage[num]; int num2 = array.Length / num; LastPackageID++; byte[] array3; ZPackage val; for (int i = 0; i < num - 1; i++) { array3 = new byte[num2]; Array.Copy(array, i * num2, array3, 0, array3.Length); val = new ZPackage(); val.Write(LastPackageID); val.Write(i + 1); val.Write(num); val.Write(array3); array2[i] = val; } int num3 = (num - 1) * num2; array3 = new byte[array.Length - num3]; Array.Copy(array, num3, array3, 0, array3.Length); val = new ZPackage(); val.Write(LastPackageID); val.Write(num); val.Write(num); val.Write(array3); array2[^1] = val; return array2; } public static void SendConfigToClient(string modname, bool changesOnly, long peerID = 0L) { if (!ZNet.instance.IsDedicated() && !ZNet.instance.IsServer()) { return; } ZPackage val = SerializeMod(modname, changesOnly); if (val != null) { ZPackage[] array = CompressPackage(val); for (int i = 0; i < array.Length; i++) { ZNet.instance.m_routedRpc.InvokeRoutedRPC(peerID, "ClientReceiveConfigData", new object[1] { array[i] }); } } } private static void SendConfigsToClients(List<ModConfig> list, bool changesOnly) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)ZNet.instance) || (!ZNet.instance.IsDedicated() && !ZNet.instance.IsServer())) { return; } ZPackage val = new ZPackage(); foreach (ModConfig item in list) { ZPackage val2 = item.Serialize(changesOnly); if (val2 != null) { val.Write(val2); } } if (val.Size() > 0) { ZPackage[] array = CompressPackage(val); for (int i = 0; i < array.Length; i++) { ZNet.instance.m_routedRpc.InvokeRoutedRPC(ZNetView.Everybody, "ClientReceiveConfigData", new object[1] { array[i] }); } } } public static void SendConfigsToClient(ZRpc rpc) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown if (!Object.op_Implicit((Object)(object)ZNet.instance) || (!ZNet.instance.IsDedicated() && !ZNet.instance.IsServer())) { return; } ZPackage val = new ZPackage(); foreach (string mod in Mods) { ZPackage val2 = SerializeMod(mod, changesOnly: false); if (val2 != null) { val.Write(val2); } } if (val.Size() > 0) { ZPackage[] data = CompressPackage(val); Plugin.instance.SendDataToZRpc(rpc, "ClientReceiveConfigData", data); } } private static void ClientReceiveConfigData(ZRpc rpc, ZPackage data) { if (ZNet.instance.IsDedicated() || ZNet.instance.IsServer()) { Plugin.Log.LogWarning((object)"Server should not be sent config values!"); return; } ulong key = data.ReadULong(); if (!PackageTracking.TryGetValue(key, out var value)) { Plugin.Log.LogDebug((object)("Client received new packageID " + key)); value = new PackageTrackerInfo(); PackageTracking[key] = value; } if (value.Add(data)) { SetConfigValues(value.GetPackage()); } } private static void SetConfigValues(ZPackage data) { //IL_000c: 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_002c: Expected O, but got Unknown ShouldUseLocalConfig = false; Dictionary<string, ModConfig> dictionary = new Dictionary<string, ModConfig>(); ZPackage val = new ZPackage(new Decompressor().Unwrap(data.GetArray(), int.MaxValue).ToArray()); try { ZPackage val2 = val.ReadPackage(); while (val2 != null) { string text = val2.ReadString(); if (!string.IsNullOrWhiteSpace(text)) { if (!ModConfigs.TryGetValue(text, out var value)) { ConfigManager.UnknownModConfigReceived?.Invoke(text); if (!ModConfigs.TryGetValue(text, out value)) { Plugin.Log.LogError((object)("Could not find registered mod " + text)); continue; } Plugin.Log.LogInfo((object)("Client received data for previously unregistered mod config " + text)); } value.Deserialize(val2); dictionary[text] = value; Plugin.Log.LogDebug((object)("Client updated with settings for mod " + text)); } val2 = ((val.m_reader.PeekChar() == -1) ? null : val.ReadPackage()); } } catch (Exception ex) { Plugin.Log.LogError((object)("Exception on client SetConfigValues: " + ex.Message)); } foreach (ModConfig value2 in dictionary.Values) { value2.ServerConfigReceived?.Invoke(); } } public static void ClearModConfigs() { Mods.Clear(); foreach (ModConfig value in ModConfigs.Values) { value.Cleanup(); } ModConfigs.Clear(); AutomatedConfigsLocked.Clear(); } } internal sealed class Patches { [HarmonyPatch(typeof(ZNet))] public static class ZNetPatches { [HarmonyPrefix] [HarmonyPatch("Awake")] public static void AwakePrefix(ZNet __instance) { ConfigManager.ShouldUseLocalConfig = true; } [HarmonyPrefix] [HarmonyPriority(int.MaxValue)] [HarmonyPatch("OnNewConnection")] public static void OnNewConnectionPrefix(ZNet __instance, ZNetPeer peer) { ConfigManager.ShouldUseLocalConfig = true; if (!__instance.IsDedicated() && !__instance.IsServer()) { ConfigManager.RegisterRPC(peer.m_rpc); } } [HarmonyPrefix] [HarmonyPriority(int.MaxValue)] [HarmonyPatch("RPC_ServerHandshake")] public static void RPC_ServerHandshakePrefix(ZNet __instance, ZRpc rpc) { if (__instance.IsDedicated() || __instance.IsServer()) { ConfigManager.SendConfigsToClient(rpc); } } } [HarmonyPatch(typeof(ConfigEntryBase))] public static class BepinexConfigEntryBasePatch { [HarmonyPrefix] [HarmonyPatch("OnSettingChanged")] public static bool OnSettingChangedPrefix(object __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (!ConfigManager.IsConfigLocked(((ConfigEntryBase)__instance).Definition.Key)) { return true; } return ConfigManager.ShouldUseLocalConfig; } } [HarmonyPatch(typeof(Terminal))] public static class TerminalPatches { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__0_0; internal void <InitTerminalPostfix>b__0_0(ConsoleEventArgs args) { bool flag = !Object.op_Implicit((Object)(object)ZNet.instance) || ZNet.instance.IsDedicated() || ZNet.instance.IsServer(); if (!flag) { flag = Object.op_Implicit((Object)(object)Player.m_localPlayer) && ZNet.instance.ListContainsId(ZNet.instance.m_adminList, ZNet.instance.GetPeer(((ZDOID)(ref ((Character)Player.m_localPlayer).m_nview.GetZDO().m_uid)).UserID).m_rpc.GetSocket().GetHostName()); } for (int i = 0; i < args.Args.Length; i++) { args.Args[i] = args[i].ToLower(); } if (args.Length == 2) { if (args[1] == "list") { foreach (ConfigManager.ModConfig registeredModConfig2 in ConfigManager.GetRegisteredModConfigs()) { args.Context.AddString(".. " + registeredModConfig2.Name + " (" + registeredModConfig2.GetRegistrationType() + ")"); } return; } if (args[1] == "reload") { if (flag) { args.Context.AddString(".. missing mod registration name"); } else { args.Context.AddString("<color=orange>mce reload</color> is not available on clients in multiplayer."); } } else { args.Context.AddString(".. unknown command option '" + args[1] + "'"); } } else if (args.Length > 2) { if (args[1] == "reload" && !flag) { args.Context.AddString("<color=orange>mce reload</color> is not available on clients in multiplayer."); } else if (args[1] == "list" || args[1] == "reload") { string text = ""; for (int j = 2; j < args.Length; j++) { text += ((j > 2) ? (" " + args[j]) : args[j]); } ConfigManager.ModConfig registeredModConfig = ConfigManager.GetRegisteredModConfig(text, ignoreCase: true); if (registeredModConfig == null) { args.Context.AddString(".. mod named '" + text + "' not found!"); return; } if (args[1] == "list") { foreach (IConfigVariable variable in registeredModConfig.Variables) { string name = variable.GetType().Name; name = name.Remove(name.IndexOf('`')); args.Context.AddString(".. " + variable.GetName() + " :" + (variable.LocalOnly() ? " localOnly " : " ") + name + " : " + variable.GetValue()); } return; } args.Context.AddString(".. reloading config for " + text); registeredModConfig.Config.Reload(); } else { args.Context.AddString(".. unknown command option '" + args[1] + "'"); } } else { args.Context.AddString("<color=orange>mce</color> command supports the following options :"); args.Context.AddString("<color=yellow>list</color> - displays a list of each mod registered with MCE and their registration method"); args.Context.AddString("<color=yellow>list <mod registration name></color> - displays a list of all config options registered for the mod"); if (flag) { args.Context.AddString("<color=yellow>reload <mod registration name></color> - reloads the config options from file for the mod (on servers, this will also send config updates to all clients)"); } } } } [HarmonyPostfix] [HarmonyPatch("InitTerminal")] public static void InitTerminalPostfix(Terminal __instance) { //IL_0032: 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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown object obj = <>c.<>9__0_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { bool flag = !Object.op_Implicit((Object)(object)ZNet.instance) || ZNet.instance.IsDedicated() || ZNet.instance.IsServer(); if (!flag) { flag = Object.op_Implicit((Object)(object)Player.m_localPlayer) && ZNet.instance.ListContainsId(ZNet.instance.m_adminList, ZNet.instance.GetPeer(((ZDOID)(ref ((Character)Player.m_localPlayer).m_nview.GetZDO().m_uid)).UserID).m_rpc.GetSocket().GetHostName()); } for (int i = 0; i < args.Args.Length; i++) { args.Args[i] = args[i].ToLower(); } if (args.Length == 2) { if (args[1] == "list") { foreach (ConfigManager.ModConfig registeredModConfig2 in ConfigManager.GetRegisteredModConfigs()) { args.Context.AddString(".. " + registeredModConfig2.Name + " (" + registeredModConfig2.GetRegistrationType() + ")"); } return; } if (args[1] == "reload") { if (flag) { args.Context.AddString(".. missing mod registration name"); } else { args.Context.AddString("<color=orange>mce reload</color> is not available on clients in multiplayer."); } } else { args.Context.AddString(".. unknown command option '" + args[1] + "'"); } } else if (args.Length > 2) { if (args[1] == "reload" && !flag) { args.Context.AddString("<color=orange>mce reload</color> is not available on clients in multiplayer."); } else if (args[1] == "list" || args[1] == "reload") { string text = ""; for (int j = 2; j < args.Length; j++) { text += ((j > 2) ? (" " + args[j]) : args[j]); } ConfigManager.ModConfig registeredModConfig = ConfigManager.GetRegisteredModConfig(text, ignoreCase: true); if (registeredModConfig == null) { args.Context.AddString(".. mod named '" + text + "' not found!"); } else { if (args[1] == "list") { foreach (IConfigVariable variable in registeredModConfig.Variables) { string name = variable.GetType().Name; name = name.Remove(name.IndexOf('`')); args.Context.AddString(".. " + variable.GetName() + " :" + (variable.LocalOnly() ? " localOnly " : " ") + name + " : " + variable.GetValue()); } return; } args.Context.AddString(".. reloading config for " + text); registeredModConfig.Config.Reload(); } } else { args.Context.AddString(".. unknown command option '" + args[1] + "'"); } } else { args.Context.AddString("<color=orange>mce</color> command supports the following options :"); args.Context.AddString("<color=yellow>list</color> - displays a list of each mod registered with MCE and their registration method"); args.Context.AddString("<color=yellow>list <mod registration name></color> - displays a list of all config options registered for the mod"); if (flag) { args.Context.AddString("<color=yellow>reload <mod registration name></color> - reloads the config options from file for the mod (on servers, this will also send config updates to all clients)"); } } }; <>c.<>9__0_0 = val; obj = (object)val; } new ConsoleCommand("mce", "shows info for Mod Config Enforcer", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } } public static class ZSteamSocketPatches { public static bool RegisterGlobalCallbacksPrefix() { if (ZSteamSocket.m_statusChanged == null) { GCHandle gCHandle = GCHandle.Alloc(30000f, GCHandleType.Pinned); GCHandle gCHandle2 = GCHandle.Alloc(1, GCHandleType.Pinned); GCHandle gCHandle3 = GCHandle.Alloc(0, GCHandleType.Pinned); try { ZSteamSocket.m_statusChanged = Callback<SteamNetConnectionStatusChangedCallback_t>.Create((DispatchDelegate<SteamNetConnectionStatusChangedCallback_t>)ZSteamSocket.OnStatusChanged); SteamNetworkingUtils.SetConfigValue((ESteamNetworkingConfigValue)25, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)3, gCHandle.AddrOfPinnedObject()); SteamNetworkingUtils.SetConfigValue((ESteamNetworkingConfigValue)23, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)1, gCHandle2.AddrOfPinnedObject()); SteamNetworkingUtils.SetConfigValue((ESteamNetworkingConfigValue)12, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)1, gCHandle3.AddrOfPinnedObject()); } catch { } try { ZSteamSocket.m_statusChanged = Callback<SteamNetConnectionStatusChangedCallback_t>.CreateGameServer((DispatchDelegate<SteamNetConnectionStatusChangedCallback_t>)ZSteamSocket.OnStatusChanged); SteamGameServerNetworkingUtils.SetConfigValue((ESteamNetworkingConfigValue)25, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)3, gCHandle.AddrOfPinnedObject()); SteamGameServerNetworkingUtils.SetConfigValue((ESteamNetworkingConfigValue)23, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)1, gCHandle2.AddrOfPinnedObject()); SteamGameServerNetworkingUtils.SetConfigValue((ESteamNetworkingConfigValue)12, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)1, gCHandle3.AddrOfPinnedObject()); } catch { } gCHandle.Free(); gCHandle2.Free(); gCHandle3.Free(); } return false; } } } [BepInPlugin("pfhoenix.modconfigenforcer", "Mod Config Enforcer", "4.0.4")] public class Plugin : BaseUnityPlugin { [CompilerGenerated] private sealed class <SendDataViaZRpc>d__7 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public ZRpc peer; public string rpc; public ZPackage[] zpgs; private int <i>5__2; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <SendDataViaZRpc>d__7(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <i>5__2 = 0; break; case 1: <>1__state = -1; <i>5__2++; break; } if (<i>5__2 < zpgs.Length) { peer.GetSocket().Flush(); peer.Invoke(rpc, new object[1] { zpgs[<i>5__2] }); <>2__current = (object)new WaitForSecondsRealtime(1f); <>1__state = 1; return true; } 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 const string Version = "4.0.4"; private Harmony _Harmony; public static ManualLogSource Log; public static Plugin instance; public static ConfigVariable<bool> OptimizeNetworking; private void Awake() { instance = this; Log = ((BaseUnityPlugin)this).Logger; Assembly assembly = ((object)this).GetType().Assembly; string[] array = new string[2] { "libzstd", "ZstdNet" }; foreach (string text in array) { Stream manifestResourceStream = assembly.GetManifestResourceStream(assembly.GetName().Name + "." + text + ".dll"); if (manifestResourceStream == null) { Log.LogError((object)("Failed to load " + text + ".dll from resource stream!")); continue; } string pluginPath = Paths.PluginPath; char directorySeparatorChar = Path.DirectorySeparatorChar; string path = pluginPath + directorySeparatorChar + text + ".dll"; if (!File.Exists(path)) { using FileStream destination = new FileStream(path, FileMode.OpenOrCreate); manifestResourceStream.CopyTo(destination); } } ConfigManager.RegisterMod("_MCE_", ((BaseUnityPlugin)this).Config); OptimizeNetworking = ConfigManager.RegisterModConfigVariable("_MCE_", "Optimize Networking", defaultValue: true, "Networking", "Optimize Valheim's networking subsystem", localOnly: true); _Harmony = Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), (string)null); if (!OptimizeNetworking.Value) { return; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly2 in assemblies) { if (assembly2.FullName.StartsWith("assembly_valheim")) { Type type = assembly2.GetType("ZSteamSocket"); if (type != null) { MethodInfo method = type.GetMethod("RegisterGlobalCallbacks", BindingFlags.Static | BindingFlags.NonPublic); MethodInfo method2 = typeof(Patches.ZSteamSocketPatches).GetMethod("RegisterGlobalCallbacksPrefix", BindingFlags.Static | BindingFlags.Public); _Harmony.CreateProcessor((MethodBase)method).AddPrefix(method2).Patch(); } break; } } } private void Start() { MethodInfo method = typeof(ConfigManager).GetMethod("RegisterAutomatedModConfigVariable", BindingFlags.Static | BindingFlags.Public); foreach (PluginInfo value in Chainloader.PluginInfos.Values) { if ((Object)(object)value.Instance == (Object)(object)this || !Object.op_Implicit((Object)(object)value.Instance) || !((Behaviour)value.Instance).isActiveAndEnabled) { continue; } Type type = ((object)value.Instance).GetType(); FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); object obj = null; FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { if (string.Compare(fieldInfo.Name, "AutomatedConfigDiscovery", ignoreCase: true) == 0) { obj = fieldInfo.GetValue(value.Instance); break; } } if (obj == null) { continue; } Log.LogDebug((object)("... searching for configuration for " + value.Metadata.Name + " version " + value.Metadata.Version?.ToString() + " ...")); type = obj.GetType(); Action scrd = null; MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo[] array2 = methods; foreach (MethodInfo methodInfo in array2) { if (string.Compare(methodInfo.Name, "ServerConfigReceived", ignoreCase: true) == 0) { scrd = (Action)Delegate.CreateDelegate(typeof(Action), obj, methodInfo); break; } if (string.Compare(methodInfo.Name, "ConfigReloaded", ignoreCase: true) == 0) { _ = (Action)Delegate.CreateDelegate(typeof(Action), obj, methodInfo); break; } } string text = null; FieldInfo[] fields2 = type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); int num = 0; array = fields2; foreach (FieldInfo fieldInfo2 in array) { if (fieldInfo2.FieldType.IsSubclassOf(typeof(ConfigEntryBase)) && !fieldInfo2.IsNotSerialized) { if (text == null) { text = value.Metadata.Name; ConfigManager.RegisterMod(text, value.Instance.Config, scrd); } method.MakeGenericMethod(fieldInfo2.FieldType.GetGenericArguments()[0]).Invoke(null, new object[2] { text, fieldInfo2.GetValue(obj) }); Log.LogDebug((object)("... bound " + fieldInfo2.Name)); num++; } else { if (!(fieldInfo2.FieldType == typeof(string)) || !fieldInfo2.Name.StartsWith("File_")) { continue; } fieldInfo2.Name.Substring(5); MethodInfo methodInfo2 = methods.FirstOrDefault((MethodInfo m) => string.Compare(m.Name, "FileChanged", ignoreCase: true) == 0); if (methodInfo2 != null) { if (text == null) { text = value.Metadata.Name; ConfigManager.RegisterMod(text, value.Instance.Config, scrd); } ConfigManager.RegisterModFileWatcher(text, (string)fieldInfo2.GetValue(obj), fieldInfo2.IsNotSerialized, (Action<string, ZPackage>)Delegate.CreateDelegate(typeof(Action<string, ZPackage>), obj, methodInfo2)); Log.LogDebug((object)("... bound " + fieldInfo2.Name)); num++; } } } if (num == 0) { Log.LogWarning((object)"... no configuration found to enforce!"); } else { ConfigManager.SortModVariables(text); } } } [IteratorStateMachine(typeof(<SendDataViaZRpc>d__7))] private IEnumerator SendDataViaZRpc(ZRpc peer, string rpc, ZPackage[] zpgs) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <SendDataViaZRpc>d__7(0) { peer = peer, rpc = rpc, zpgs = zpgs }; } public void SendDataToZRpc(ZRpc peer, string rpc, ZPackage[] data) { ((MonoBehaviour)this).StartCoroutine(SendDataViaZRpc(peer, rpc, data)); } public static bool IsAdmin(Player player) { if (!Object.op_Implicit((Object)(object)ZNet.instance)) { return true; } if (!Object.op_Implicit((Object)(object)player)) { return false; } if (ZNet.instance.m_adminList == null) { return false; } return ZNet.instance.ListContainsId(ZNet.instance.m_adminList, ZNet.instance.GetPeer(((ZDOID)(ref ((Character)player).m_nview.GetZDO().m_uid)).UserID).m_rpc.GetSocket().GetHostName()); } private void OnDestroy() { ConfigManager.ClearModConfigs(); if (_Harmony != null) { _Harmony.UnpatchSelf(); } } }