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 BronzeAgeChest v1.0.8
plugins\BronzeAgeChest.dll
Decompiled a year agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Common; using HarmonyLib; using Jotunn; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using SimpleJson; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("BronzeAgeChest")] [assembly: AssemblyDescription("https://thunderstore.io/c/valheim/p/probablykory/BronzeAgeChest/")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyProduct("BronzeAgeChest")] [assembly: AssemblyCopyright("Copyright probablykory © 2024")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")] [assembly: AssemblyFileVersion("1.0.8")] [assembly: AssemblyCompany("probablykory")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.8.0")] [module: <d3f99811-048d-4877-8cce-f85228c1c50b>RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [<140a1b61-9b0f-45d5-865a-7e7a0d70c359>Embedded] internal sealed class <140a1b61-9b0f-45d5-865a-7e7a0d70c359>EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [<140a1b61-9b0f-45d5-865a-7e7a0d70c359>Embedded] [CompilerGenerated] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class <d3f99811-048d-4877-8cce-f85228c1c50b>RefSafetyRulesAttribute : Attribute { public readonly int Version; public <d3f99811-048d-4877-8cce-f85228c1c50b>RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BronzeAgeChest { public static class RequirementsEntry { public static RequirementConfig[] Deserialize(string reqs) { return ((IEnumerable<string>)reqs.Split(new char[1] { ',' })).Select((Func<string, RequirementConfig>)delegate(string r) { //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_0020: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown string[] array = r.Split(new char[1] { ':' }); int result; int result2; return new RequirementConfig { Item = array[0], Amount = ((array.Length <= 1 || !int.TryParse(array[1], out result)) ? 1 : result), AmountPerLevel = ((array.Length > 2 && int.TryParse(array[2], out result2)) ? result2 : 0), Recover = true }; }).ToArray(); } public static string Serialize(RequirementConfig[] reqs) { return string.Join(",", reqs.Select((RequirementConfig r) => (r.AmountPerLevel <= 0) ? $"{r.Item}:{r.Amount}" : $"{r.Item}:{r.Amount}:{r.AmountPerLevel}")); } } internal class Entries { private Action<object, EventArgs> _action; public string Name { get; set; } = string.Empty; public ConfigEntry<string> Table { get; set; } public ConfigEntry<int> MinTableLevel { get; set; } public ConfigEntry<int> Amount { get; set; } public ConfigEntry<string> Requirements { get; set; } public static Entries GetFromProps(IPlugin instance, string name, string table, string requirements) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown Entries entries = new Entries(); entries.Name = name; entries.Table = instance.Config(entries.Name, "Table", table, new ConfigDescription("Crafting station needed to construct " + entries.Name + ".", (AcceptableValueBase)(object)CraftingStations.GetAcceptableValueList(), new object[1] { ConfigHelper.GetAdminOnlyFlag() })); entries.Requirements = instance.Config(entries.Name, "Requirements", requirements, new ConfigDescription("The required items to construct " + entries.Name + ".", (AcceptableValueBase)(object)new AcceptableValueConfigNote("You must use valid spawn item codes."), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true, CustomDrawer = SharedDrawers.DrawReqConfigTable() } })); return entries; } private void OnSettingChanged(object sender, EventArgs e) { if (_action != null) { _action(sender, e); } } public void AddSettingsChangedHandler(Action<object, EventArgs> action) { _action = action; Table.SettingChanged += OnSettingChanged; MinTableLevel.SettingChanged += OnSettingChanged; Amount.SettingChanged += OnSettingChanged; Requirements.SettingChanged += OnSettingChanged; } public void RemoveSettingsChangedHandler() { Table.SettingChanged -= OnSettingChanged; MinTableLevel.SettingChanged -= OnSettingChanged; Amount.SettingChanged -= OnSettingChanged; Requirements.SettingChanged -= OnSettingChanged; _action = null; } } public static class Extensions { private static Dictionary<string, CustomPiece> dictPieces; public static Piece GetPiece(Piece piece) { Piece val = null; foreach (KeyValuePair<string, PieceTable> item in ReflectionHelper.GetPrivateField<Dictionary<string, PieceTable>>((object)PieceManager.Instance, "PieceTableMap")) { foreach (GameObject piece2 in item.Value.m_pieces) { Piece component = piece2.GetComponent<Piece>(); if (!string.IsNullOrEmpty(((Object)component).name) && ((Object)component).name.Equals(((Object)piece).name)) { val = component; break; } } if ((Object)(object)val != (Object)null) { break; } } return val; } public static bool Update(this CustomPiece piece, PieceConfig newPiece) { //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Expected O, but got Unknown //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Expected O, but got Unknown if ((Object)(object)ZNetScene.instance != (Object)null) { Piece piece2 = GetPiece(piece.Piece); if ((Object)(object)piece2 == (Object)null) { ManualLogSource logger = Get.Plugin.Logger; object obj; if (piece == null) { obj = null; } else { Piece piece3 = piece.Piece; obj = ((piece3 != null) ? ((Object)piece3).name : null); } logger.LogError((object)("Error updating piece " + (string?)obj + ", did not find existing piece in available piece tables.")); return false; } piece2.m_craftingStation = ZNetScene.instance.GetPrefab(newPiece.CraftingStation).GetComponent<CraftingStation>(); piece2.m_resources = newPiece.GetRequirements(); Requirement[] resources = piece2.m_resources; foreach (Requirement val in resources) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(((Object)val.m_resItem).name.Replace("JVLmock_", "")); if ((Object)(object)itemPrefab != (Object)null) { val.m_resItem = itemPrefab.GetComponent<ItemDrop>(); } } if (dictPieces == null) { Dictionary<string, CustomPiece> privateField = ReflectionHelper.GetPrivateField<Dictionary<string, CustomPiece>>((object)PieceManager.Instance, "Pieces"); if (privateField != null) { dictPieces = privateField; } } if (dictPieces != null) { dictPieces.Remove(((Object)((Component)piece2).gameObject).name); dictPieces.Add(((Object)((Component)piece2).gameObject).name, new CustomPiece(((Component)piece2).gameObject, piece.PieceTable, false)); } return true; } PieceManager.Instance.RemovePiece(((Object)piece.PiecePrefab).name); PieceManager.Instance.AddPiece(new CustomPiece(piece.PiecePrefab, false, newPiece)); return false; } } [BepInPlugin("probablykory.BronzeAgeChest", "BronzeAgeChest", "1.0.8")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] internal class BronzeAgeChest : BaseUnityPlugin, IPlugin { public const string PluginAuthor = "probablykory"; public const string PluginName = "BronzeAgeChest"; public const string PluginVersion = "1.0.8"; public const string PluginGUID = "probablykory.BronzeAgeChest"; internal static BronzeAgeChest Instance; internal AssetBundle assetBundle; private static ConfigEntry<bool> isDebugEnabled = null; private CustomPiece bronzeChest; private const string Name = "$piece_chest_bronze"; private const string PieceTable = "Hammer"; private const string Category = "Furniture"; private static Dictionary<string, string> DefaultEnglishLanguageStrings = new Dictionary<string, string> { { "$piece_chest_bronze", "Rugged Chest" } }; public ManualLogSource Logger { get; private set; } = Logger.CreateLogSource("BronzeAgeChest"); public bool Debug { get { if (isDebugEnabled == null) { return true; } return isDebugEnabled.Value; } } public Entries Entry { get; protected set; } private void Awake() { Instance = this; assetBundle = AssetUtils.LoadAssetBundleFromResources("bronzechest"); isDebugEnabled = this.Config("1 - General", "Debugging Enabled", value: false, "If on, mod will output alot more information in the debug log level."); InitializeFeatures(); AddDefaultLocalizations(); PrefabManager.OnVanillaPrefabsAvailable += OnVanillaPrefabsAvailable; LocalizationManager.OnLocalizationAdded += OnLocalizationAdded; SynchronizationManager.OnConfigurationSynchronized += OnConfigurationSynchronized; ((BaseUnityPlugin)this).Config.ConfigReloaded += OnConfigReloaded; new ConfigWatcher(this); } private void InitializeFeatures() { Entry = Entries.GetFromProps(Instance, "BronzeChest", "Workbench", "Wood:15,Bronze:1,BronzeNails:10"); } private void UpdateFeatures() { bronzeChest.Update(getPieceFromEntry(Entry)); } private void OnVanillaPrefabsAvailable() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown this.LogDebugOnly("Vanilla Prefabs Available received."); bronzeChest = new CustomPiece(assetBundle, "piece_chest_bronze", true, getPieceFromEntry(Entry)); PieceManager.Instance.AddPiece(bronzeChest); PrefabManager.OnVanillaPrefabsAvailable -= OnVanillaPrefabsAvailable; } private void OnConfigReloaded(object sender, EventArgs e) { UpdateFeatures(); } private void OnConfigurationSynchronized(object sender, ConfigurationSynchronizationEventArgs e) { UpdateFeatures(); } private PieceConfig getPieceFromEntry(Entries entry) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown return new PieceConfig { Name = "$piece_chest_bronze", CraftingStation = CraftingStations.GetInternalName(entry.Table.Value), PieceTable = "Hammer", Category = "Furniture", Requirements = RequirementsEntry.Deserialize(entry.Requirements.Value) }; } private void AddDefaultLocalizations() { this.LogDebugOnly("AddLocalizations called."); CustomLocalization localization = LocalizationManager.Instance.GetLocalization(); string text = "English"; localization.AddTranslation(ref text, DefaultEnglishLanguageStrings); } private void OnLocalizationAdded() { this.LogDebugOnly("Localization Added received."); string location = ((object)Instance).GetType().Assembly.Location; char directorySeparatorChar = Path.DirectorySeparatorChar; string text = location.Replace(directorySeparatorChar + "BronzeAgeChest.dll", ""); if (Paths.PluginPath.Equals(text)) { text = Utility.CombinePaths(new string[2] { Paths.PluginPath, "BronzeAgeChest" }); } string text2 = Utility.CombinePaths(new string[4] { text, "Translations", "English", "english.json" }); string directoryName = Path.GetDirectoryName(text2); if (!Directory.Exists(directoryName) || !File.Exists(text2)) { Directory.CreateDirectory(directoryName); string contents = SimpleJson.SerializeObject((object)DefaultEnglishLanguageStrings); File.WriteAllText(text2, contents); this.LogInfo("Default localizations written to " + text2); } LocalizationManager.OnLocalizationAdded -= OnLocalizationAdded; } ConfigFile IPlugin.get_Config() { return ((BaseUnityPlugin)this).Config; } } } namespace Microsoft.CodeAnalysis { [Microsoft.CodeAnalysis.Embedded] [CompilerGenerated] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] [CompilerGenerated] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] [Microsoft.CodeAnalysis.Embedded] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Common { internal class AcceptableValueConfigNote : AcceptableValueBase { public virtual string Note { get; } public AcceptableValueConfigNote(string note) : base(typeof(string)) { if (string.IsNullOrEmpty(note)) { throw new ArgumentException("A string with atleast 1 character is needed", "Note"); } Note = note; } public override object Clamp(object value) { return value; } public override bool IsValid(object value) { return !string.IsNullOrEmpty(value as string); } public override string ToDescriptionString() { return "# Note: " + Note; } } internal static class ConfigHelper { public static ConfigurationManagerAttributes GetAdminOnlyFlag() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown return new ConfigurationManagerAttributes { IsAdminOnly = true }; } public static ConfigurationManagerAttributes GetTags(Action<ConfigEntryBase> action) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown return new ConfigurationManagerAttributes { CustomDrawer = action }; } public static ConfigurationManagerAttributes GetTags() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown return new ConfigurationManagerAttributes(); } public static ConfigEntry<T> Config<T>(this IPlugin instance, string group, string name, T value, ConfigDescription description) { return instance.Config.Bind<T>(group, name, value, description); } public static ConfigEntry<T> Config<T>(this IPlugin instance, string group, string name, T value, string description) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown return instance.Config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { GetAdminOnlyFlag() })); } } internal static class RequirementsEntry { public static RequirementConfig[] Deserialize(string reqs) { return ((IEnumerable<string>)reqs.Split(new char[1] { ',' })).Select((Func<string, RequirementConfig>)delegate(string r) { //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_0020: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown string[] array = r.Split(new char[1] { ':' }); int result; int result2; return new RequirementConfig { Item = array[0], Amount = ((array.Length <= 1 || !int.TryParse(array[1], out result)) ? 1 : result), AmountPerLevel = ((array.Length > 2 && int.TryParse(array[2], out result2)) ? result2 : 0), Recover = true }; }).ToArray(); } public static string Serialize(RequirementConfig[] reqs) { return string.Join(",", reqs.Select((RequirementConfig r) => (r.AmountPerLevel <= 0) ? $"{r.Item}:{r.Amount}" : $"{r.Item}:{r.Amount}:{r.AmountPerLevel}")); } } internal static class SharedDrawers { private static BaseUnityPlugin configManager; private static BaseUnityPlugin GetConfigManager() { if ((Object)(object)configManager == (Object)null && Chainloader.PluginInfos.TryGetValue("com.bepis.bepinex.configurationmanager", out var value) && Object.op_Implicit((Object)(object)value.Instance)) { configManager = value.Instance; } return configManager; } public static int GetRightColumnWidth() { int result = 130; BaseUnityPlugin val = GetConfigManager(); if ((Object)(object)val != (Object)null) { PropertyInfo propertyInfo = ((object)val)?.GetType().GetProperty("RightColumnWidth", BindingFlags.Instance | BindingFlags.NonPublic); if (propertyInfo != null) { result = (int)propertyInfo.GetValue(val); } } return result; } public static void ReloadConfigDisplay() { BaseUnityPlugin val = GetConfigManager(); if ((Object)(object)val != (Object)null) { object obj = ((object)val).GetType()?.GetProperty("DisplayingWindow")?.GetValue(val); if (obj is bool && (bool)obj) { ((object)val).GetType().GetMethod("BuildSettingList").Invoke(val, Array.Empty<object>()); } } } public static Action<ConfigEntryBase> DrawReqConfigTable(bool hasUpgrades = false) { return delegate(ConfigEntryBase cfg) { //IL_005c: 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_008d: Expected O, but got Unknown //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Expected O, but got Unknown //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Expected O, but got Unknown //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Expected O, but got Unknown //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Expected O, but got Unknown //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Expected O, but got Unknown //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Expected O, but got Unknown List<RequirementConfig> list = new List<RequirementConfig>(); bool flag = false; int rightColumnWidth = GetRightColumnWidth(); GUILayout.BeginVertical(Array.Empty<GUILayoutOption>()); foreach (RequirementConfig item in RequirementsEntry.Deserialize((string)cfg.BoxedValue).ToList()) { GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); string text = GUILayout.TextField(item.Item, new GUIStyle(GUI.skin.textField) { fixedWidth = rightColumnWidth - 33 - (hasUpgrades ? 37 : 0) - 21 - 21 - 12 }, Array.Empty<GUILayoutOption>()); string text2 = (string.IsNullOrEmpty(text) ? item.Item : text); flag = flag || text2 != item.Item; int num = item.Amount; if (int.TryParse(GUILayout.TextField(num.ToString(), new GUIStyle(GUI.skin.textField) { fixedWidth = 33f }, Array.Empty<GUILayoutOption>()), out var result) && result != num) { num = result; flag = true; } int num2 = item.AmountPerLevel; if (hasUpgrades && int.TryParse(GUILayout.TextField(num2.ToString(), new GUIStyle(GUI.skin.textField) { fixedWidth = 33f }, Array.Empty<GUILayoutOption>()), out var result2) && result2 != num2) { num2 = result2; flag = true; } if (GUILayout.Button("x", new GUIStyle(GUI.skin.button) { fixedWidth = 21f }, Array.Empty<GUILayoutOption>())) { flag = true; } else { list.Add(new RequirementConfig { Item = text2, Amount = num, AmountPerLevel = num2 }); } if (GUILayout.Button("+", new GUIStyle(GUI.skin.button) { fixedWidth = 21f }, Array.Empty<GUILayoutOption>())) { flag = true; list.Add(new RequirementConfig { Item = "<Prefab Name>", Amount = 1 }); } GUILayout.EndHorizontal(); } GUILayout.EndVertical(); if (flag) { cfg.BoxedValue = RequirementsEntry.Serialize(list.ToArray()); } }; } } internal class ConfigWatcher { private BaseUnityPlugin configurationManager; private IPlugin plugin; public ConfigWatcher(IPlugin plugin) { if (plugin == null) { throw new ArgumentNullException("plugin"); } this.plugin = plugin; CheckForConfigManager(); } private void InitializeWatcher() { string fileName = Path.GetFileName(plugin.Config.ConfigFilePath); new Watcher(Path.GetDirectoryName(plugin.Config.ConfigFilePath), fileName).FileChanged += OnFileChanged; Get.Plugin.LogDebugOnly("File system watcher initialized."); } private void CheckForConfigManager() { PluginInfo value; if (GUIManager.IsHeadless()) { InitializeWatcher(); } else if (Chainloader.PluginInfos.TryGetValue("com.bepis.bepinex.configurationmanager", out value) && Object.op_Implicit((Object)(object)value.Instance)) { configurationManager = value.Instance; Get.Plugin.LogDebugOnly("Configuration manager found, hooking DisplayingWindowChanged"); EventInfo @event = ((object)configurationManager).GetType().GetEvent("DisplayingWindowChanged"); if (@event != null) { Action<object, object> action = OnConfigManagerDisplayingWindowChanged; Delegate handler = Delegate.CreateDelegate(@event.EventHandlerType, action.Target, action.Method); @event.AddEventHandler(configurationManager, handler); } } else { InitializeWatcher(); } } private void OnFileChanged(object sender, FileSystemEventArgs e) { string configFilePath = plugin.Config.ConfigFilePath; if (!File.Exists(configFilePath)) { return; } try { plugin.Config.SaveOnConfigSet = false; plugin.Config.Reload(); plugin.Config.SaveOnConfigSet = true; } catch { Get.Plugin.LogError("There was an issue with your " + Path.GetFileName(configFilePath) + " file."); Get.Plugin.LogError("Please check the format and spelling."); } } private void OnConfigManagerDisplayingWindowChanged(object sender, object e) { if (!(bool)((object)configurationManager).GetType().GetProperty("DisplayingWindow").GetValue(configurationManager, null)) { plugin.Config.SaveOnConfigSet = false; plugin.Config.Reload(); plugin.Config.SaveOnConfigSet = true; } } } internal class CustomSyncedValueBase { private class Patches { [HarmonyPatch(typeof(ZNet), "Shutdown")] [HarmonyPostfix] private static void ZNet_Shutdown() { if (ZNetExtension.IsClientInstance(ZNet.instance)) { foreach (CustomSyncedValueBase syncedValue in SyncedValues) { syncedValue.OnResetFromServer(); } return; } foreach (CustomSyncedValueBase syncedValue2 in SyncedValues) { syncedValue2.OnServerShutdown(); } } } public readonly string Name; public readonly Type Type; private object? boxedValue; public object? LocalBaseValue; private CustomRPC customRPC; private static readonly HashSet<CustomSyncedValueBase> SyncedValues = new HashSet<CustomSyncedValueBase>(); private static Harmony harmony = null; public bool IsSourceOfTruth { get; private set; } = true; public object? BoxedValue { get { return boxedValue; } set { boxedValue = value; this.ValueChanged?.Invoke(this, value); } } public event Action<object, object>? ValueChanged; protected CustomSyncedValueBase(string name, Type type, object? initialValue) { //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Expected O, but got Unknown //IL_00af: Expected O, but got Unknown //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown Name = name; Type = type; SyncedValues.Add(this); if (harmony == null) { harmony = (Harmony)AccessTools.Field(typeof(Main), "Harmony").GetValue(null); harmony.PatchAll(typeof(Patches)); Get.Plugin.LogDebugOnly("Jotunn's harmony instance obtained, CustomSyncedValueBase+Patches applied."); } boxedValue = initialValue; customRPC = NetworkManager.Instance.AddRPC(Name + "_CustomSyncedValue_RPC", new CoroutineHandler(OnServerReceive), new CoroutineHandler(OnClientReceive)); SynchronizationManager.Instance.AddInitialSynchronization(customRPC, (Func<ZPackage>)GetPackage); } public void SendPackage() { if ((Object)(object)ZNet.instance == (Object)null) { Get.Plugin.LogDebugOnly("SendPackage called but am not connected."); } if (!SynchronizationManager.Instance.PlayerIsAdmin) { Get.Plugin.LogDebugOnly("SendPackage called but Player is not admin."); } if ((Object)(object)ZNet.instance != (Object)null && SynchronizationManager.Instance.PlayerIsAdmin) { ZPackage package = GetPackage(); if (ZNetExtension.IsClientInstance(ZNet.instance)) { customRPC.SendPackage(ZRoutedRpc.instance.GetServerPeerID(), package); } else { customRPC.SendPackage(ZNet.instance.m_peers, package); } } } private ZPackage GetPackage() { return new PackageEntry { key = Name, type = Type, value = boxedValue }.ToPackage(); } private IEnumerator OnServerReceive(long sender, ZPackage package) { Get.Plugin.LogDebugOnly($"Server received RPC: {sender} {package}"); yield return null; ParsedEntries parsedEntries = package.ReadEntries(); if (parsedEntries.Count > 0 && parsedEntries.TryGetValue(Name, out var value)) { BoxedValue = (LocalBaseValue = value.value); Get.Plugin.LogDebugOnly($"Set local and boxed: {value.value}"); customRPC.SendPackage(ZNet.instance.m_peers.Where((ZNetPeer x) => x.m_uid != sender).ToList(), package); } else { OnReceiveError(parsedEntries); } } private IEnumerator OnClientReceive(long sender, ZPackage package) { Get.Plugin.LogDebugOnly($"Client received RPC: {sender} {package}"); yield return null; ParsedEntries parsedEntries = package.ReadEntries(); if (parsedEntries.Count > 0 && parsedEntries.TryGetValue(Name, out var value)) { IsSourceOfTruth = false; if (LocalBaseValue == null) { LocalBaseValue = BoxedValue; } BoxedValue = value.value; Get.Plugin.LogDebugOnly($"Set source of truth: {IsSourceOfTruth}, {Environment.NewLine}local: {LocalBaseValue} {Environment.NewLine}boxed: {BoxedValue}"); } else { OnReceiveError(parsedEntries); } } private void OnReceiveError(ParsedEntries entries) { Get.Plugin.LogWarning(Name + "_CustomSyncedValue_RPC recieved package without expected key: " + Name); string text = ""; foreach (KeyValuePair<string, PackageEntry> entry in entries) { text += $"{entry.Key} - {entry.Value.type} - {entry.Value.value?.ToString()} {Environment.NewLine}"; } Get.Plugin.LogWarning("Result: " + Environment.NewLine + text); } private void OnResetFromServer() { BoxedValue = LocalBaseValue; IsSourceOfTruth = true; LocalBaseValue = null; } private void OnServerShutdown() { } } internal sealed class CustomSyncedValue<T> : CustomSyncedValueBase { public T Value { get { return (T)base.BoxedValue; } set { base.BoxedValue = value; if (base.BoxedValue != null) { SendPackage(); } } } public CustomSyncedValue(string name, T value = default(T)) : base(name, typeof(T), value) { } public void AssignLocalValue(T value) { if (base.IsSourceOfTruth && SynchronizationManager.Instance.PlayerIsAdmin) { Value = value; } else { LocalBaseValue = value; } } } internal class ParsedEntries : Dictionary<string, PackageEntry> { } internal class PackageEntry { public string key; public Type type; public object? value; } internal class InvalidDeserializationTypeException : Exception { public string expected; public string received; public string field = ""; } internal static class PackageUtils { public static string GetPackageTypeString(Type type) { return type.AssemblyQualifiedName; } public static void AddValue(this ZPackage package, object? value) { Type type = value?.GetType(); if (value is Enum) { value = ((IConvertible)value).ToType(Enum.GetUnderlyingType(value.GetType()), CultureInfo.InvariantCulture); } else { if (value is ICollection collection) { package.Write(collection.Count); { foreach (object item in collection) { package.AddValue(item); } return; } } if ((object)type != null && type.IsValueType && !type.IsPrimitive) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); package.Write(fields.Length); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { package.Write(GetPackageTypeString(fieldInfo.FieldType)); package.AddValue(fieldInfo.GetValue(value)); } return; } } ZRpc.Serialize(new object[1] { value }, ref package); } public static void AddEntry(this ZPackage package, PackageEntry entry) { package.Write(entry.key); package.Write((entry.value == null) ? "" : GetPackageTypeString(entry.type)); package.AddValue(entry.value); } public static ZPackage ToPackage(this PackageEntry packageEntry) { return new List<PackageEntry> { packageEntry }.ToPackage(); } public static ZPackage ToPackage(this IEnumerable<PackageEntry> packageEntries) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(packageEntries?.Count() ?? 0); foreach (PackageEntry item in packageEntries ?? Array.Empty<PackageEntry>()) { val.AddEntry(item); } return val; } public static object ReadValueWithType(this ZPackage package, Type type) { if ((object)type != null && type.IsValueType && !type.IsPrimitive && !type.IsEnum) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); int num = package.ReadInt(); if (num != fields.Length) { throw new InvalidDeserializationTypeException { received = $"(field count: {num})", expected = $"(field count: {fields.Length})" }; } object uninitializedObject = FormatterServices.GetUninitializedObject(type); FieldInfo[] array = fields; foreach (FieldInfo fieldInfo in array) { string text = package.ReadString(); if (text != GetPackageTypeString(fieldInfo.FieldType)) { throw new InvalidDeserializationTypeException { received = text, expected = GetPackageTypeString(fieldInfo.FieldType), field = fieldInfo.Name }; } fieldInfo.SetValue(uninitializedObject, package.ReadValueWithType(fieldInfo.FieldType)); } return uninitializedObject; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<, >)) { int num2 = package.ReadInt(); IDictionary dictionary = (IDictionary)Activator.CreateInstance(type); Type type2 = typeof(KeyValuePair<, >).MakeGenericType(type.GenericTypeArguments); FieldInfo field = type2.GetField("key", BindingFlags.Instance | BindingFlags.NonPublic); FieldInfo field2 = type2.GetField("value", BindingFlags.Instance | BindingFlags.NonPublic); for (int j = 0; j < num2; j++) { object obj = package.ReadValueWithType(type2); dictionary.Add(field.GetValue(obj), field2.GetValue(obj)); } return dictionary; } if (type != typeof(List<string>) && type.IsGenericType) { Type type3 = typeof(ICollection<>).MakeGenericType(type.GenericTypeArguments[0]); if ((object)type3 != null && type3.IsAssignableFrom(type)) { int num3 = package.ReadInt(); object obj2 = Activator.CreateInstance(type); MethodInfo method = type3.GetMethod("Add"); for (int k = 0; k < num3; k++) { method.Invoke(obj2, new object[1] { package.ReadValueWithType(type.GenericTypeArguments[0]) }); } return obj2; } } ParameterInfo parameterInfo = (ParameterInfo)FormatterServices.GetUninitializedObject(typeof(ParameterInfo)); AccessTools.DeclaredField(typeof(ParameterInfo), "ClassImpl").SetValue(parameterInfo, type); List<object> source = new List<object>(); ZRpc.Deserialize(new ParameterInfo[2] { null, parameterInfo }, package, ref source); return source.FirstOrDefault(); } public static ParsedEntries ReadEntries(this ZPackage package) { ParsedEntries parsedEntries = new ParsedEntries(); int num = package.ReadInt(); for (int i = 0; i < num; i++) { string key = package.ReadString(); string text = package.ReadString(); Type type = Type.GetType(text); if (text == "" || type != null) { object obj; try { obj = ((text == "") ? null : package.ReadValueWithType(type)); } catch (InvalidDeserializationTypeException ex) { Get.Plugin.LogWarning("Got unexpected struct internal type " + ex.received + " for field " + ex.field + " struct " + text); continue; } if (obj != null) { parsedEntries[key] = new PackageEntry { key = key, type = type, value = obj }; } continue; } Get.Plugin.LogWarning("Got invalid type " + text + ", abort reading of received configs"); return new ParsedEntries(); } return parsedEntries; } public static void Test() { } } internal interface IPlugin { ConfigFile Config { get; } bool Debug { get; } ManualLogSource Logger { get; } } internal static class LoggingExtensions { private static readonly Dictionary<string, ManualLogSource> logSources = new Dictionary<string, ManualLogSource>(); internal static ManualLogSource GetLogger(this IPlugin mod) { if (mod.Debug) { Type declaringType = new StackFrame(2).GetMethod().DeclaringType; if (!logSources.TryGetValue(declaringType.FullName, out var value)) { value = Logger.CreateLogSource(declaringType.FullName); logSources.Add(declaringType.FullName, value); } return value; } return mod.Logger; } public static void LogDebugOnly(this IPlugin mod, object data) { if (mod.Debug) { mod.GetLogger().LogDebug(data); } } public static void LogDebug(this IPlugin mod, object data) { if (mod.Debug) { mod.GetLogger().LogDebug(data); } else { mod.Logger.LogDebug(data); } } public static void LogInfo(this IPlugin mod, object data) { if (mod.Debug) { mod.GetLogger().LogInfo(data); } else { mod.Logger.LogInfo(data); } } public static void LogMessage(this IPlugin mod, object data) { if (mod.Debug) { mod.GetLogger().LogMessage(data); } else { mod.Logger.LogMessage(data); } } public static void LogWarning(this IPlugin mod, object data) { if (mod.Debug) { mod.GetLogger().LogWarning(data); } else { mod.Logger.LogWarning(data); } } public static void LogError(this IPlugin mod, object data) { if (mod.Debug) { mod.GetLogger().LogError(data); } else { mod.Logger.LogError(data); } } public static void LogFatal(this IPlugin mod, object data) { if (mod.Debug) { mod.GetLogger().LogFatal(data); } else { mod.Logger.LogFatal(data); } } } internal static class Get { private static IPlugin cachedModRef; public static IPlugin Plugin { get { if (cachedModRef == null) { cachedModRef = AccessTools.Field((from p in new StackFrame(0).GetMethod().DeclaringType.Assembly.GetTypes() where typeof(IPlugin).IsAssignableFrom(p) select p).FirstOrDefault(), "Instance").GetValue(null) as IPlugin; cachedModRef.LogDebugOnly("Caching static mod reference"); } return cachedModRef; } } } internal class Watcher { private FileSystemWatcher fileSystemWatcher; public bool EnableRaisingEvents { get { if (fileSystemWatcher != null) { return fileSystemWatcher.EnableRaisingEvents; } return false; } set { if (fileSystemWatcher != null) { fileSystemWatcher.EnableRaisingEvents = value; } } } public event Action<object, FileSystemEventArgs>? FileChanged; public Watcher(string path, string filter) { if (path == null) { throw new ArgumentNullException("path"); } if (filter == null) { throw new ArgumentNullException("filter"); } Get.Plugin.LogDebugOnly("Watcher created for " + path + ", " + filter); fileSystemWatcher = new FileSystemWatcher(path, filter); fileSystemWatcher.Changed += OnCreatedChangedOrRenamed; fileSystemWatcher.Created += OnCreatedChangedOrRenamed; fileSystemWatcher.Renamed += OnCreatedChangedOrRenamed; fileSystemWatcher.IncludeSubdirectories = true; fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; fileSystemWatcher.EnableRaisingEvents = true; } private void OnCreatedChangedOrRenamed(object sender, FileSystemEventArgs args) { Get.Plugin.LogDebugOnly($"OnCreatedChangedOrRenamed triggered {args.Name}, {args.ChangeType}"); this.FileChanged?.Invoke(sender, args); } } }