Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LethalModDataLib v1.2.2
BepInEx/plugins/LethalModDataLib/MaxWasUnavailable.LethalModDataLib.dll
Decompiled 2 years agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Logging; using HarmonyLib; using LethalModDataLib.Attributes; using LethalModDataLib.Enums; using LethalModDataLib.Events; using LethalModDataLib.Features; using LethalModDataLib.Helpers; using LethalModDataLib.Interfaces; using LethalModDataLib.Models; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("ClientNetworkTransform")] [assembly: IgnoresAccessChecksTo("DissonanceVoip")] [assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")] [assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")] [assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")] [assembly: IgnoresAccessChecksTo("Unity.Burst")] [assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")] [assembly: IgnoresAccessChecksTo("Unity.Collections")] [assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")] [assembly: IgnoresAccessChecksTo("Unity.Jobs")] [assembly: IgnoresAccessChecksTo("Unity.Mathematics")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")] [assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")] [assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")] [assembly: IgnoresAccessChecksTo("Unity.Services.QoS")] [assembly: IgnoresAccessChecksTo("Unity.Services.Relay")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("Unity.Timeline")] [assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")] [assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: AssemblyCompany("MaxWasUnavailable")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("A library for Lethal Company, providing a standardised way to save and load modded data.")] [assembly: AssemblyFileVersion("1.2.2.0")] [assembly: AssemblyInformationalVersion("1.2.2+3f6ed0364c80cbd6b139486b6efd40e4051ff736")] [assembly: AssemblyProduct("MaxWasUnavailable.LethalModDataLib")] [assembly: AssemblyTitle("MaxWasUnavailable.LethalModDataLib")] [assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/MaxWasUnavailable/LobbyCompatibility")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] 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] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] 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 LethalModDataLib { [BepInPlugin("MaxWasUnavailable.LethalModDataLib", "MaxWasUnavailable.LethalModDataLib", "1.2.2")] public class LethalModDataLib : BaseUnityPlugin { private const string ModVersionKey = "LMDLVersion"; private bool _isPatched; private Harmony? Harmony { get; set; } internal static ManualLogSource? Logger { get; private set; } public static LethalModDataLib? Instance { get; private set; } private void Awake() { Instance = this; Logger = ((BaseUnityPlugin)this).Logger; PatchAll(); MiscEvents.PostInitializeGameEvent += OnGameInitialized; Logger.LogInfo((object)"Plugin MaxWasUnavailable.LethalModDataLib is loaded!"); } private static void VersionCheck() { string text = SaveLoadHandler.LoadData<string>("LMDLVersion", SaveLocation.GeneralSave); if (string.IsNullOrEmpty(text)) { ManualLogSource? logger = Logger; if (logger != null) { logger.LogInfo((object)"No saved LethalModDataLib version found. This is normal if this is the first time you are running the game with LethalModDataLib installed."); } } else if (text == "1.2.2") { ManualLogSource? logger2 = Logger; if (logger2 != null) { logger2.LogDebug((object)("LethalModDataLib version (1.2.2) matches last saved version (" + text + ").")); } } else { SaveLoadHandler.SaveData(text, "LMDLVersion_old", SaveLocation.GeneralSave); ManualLogSource? logger3 = Logger; if (logger3 != null) { logger3.LogWarning((object)("Mismatch between last saved LethalModDataLib version (" + text + ") and current version (1.2.2). This is normal if you have updated LethalModDataLib. Make sure to check the changelog for breaking changes. ")); } } SaveLoadHandler.SaveData("1.2.2", "LMDLVersion", SaveLocation.GeneralSave); } private static void OnGameInitialized() { ModDataHandler.Initialise(); MiscEvents.PostInitializeGameEvent -= OnGameInitialized; VersionCheck(); } private void PatchAll() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_004c: Expected O, but got Unknown if (_isPatched) { ManualLogSource? logger = Logger; if (logger != null) { logger.LogWarning((object)"Already patched!"); } return; } ManualLogSource? logger2 = Logger; if (logger2 != null) { logger2.LogDebug((object)"Patching..."); } if (Harmony == null) { Harmony val = new Harmony("MaxWasUnavailable.LethalModDataLib"); Harmony val2 = val; Harmony = val; } Harmony.PatchAll(); _isPatched = true; ManualLogSource? logger3 = Logger; if (logger3 != null) { logger3.LogDebug((object)"Patched!"); } } } public static class PluginInfo { public const string PLUGIN_GUID = "MaxWasUnavailable.LethalModDataLib"; public const string PLUGIN_NAME = "MaxWasUnavailable.LethalModDataLib"; public const string PLUGIN_VERSION = "1.2.2"; } } namespace LethalModDataLib.Patches { [HarmonyPatch(typeof(ES3))] [HarmonyPriority(800)] [HarmonyWrapSafe] internal static class ES3Patches { [HarmonyPostfix] [HarmonyPatch("DeleteFile", new Type[] { typeof(string) })] private static void DeleteFilePostfix(string filePath) { if (!filePath.Contains(".moddata")) { SaveLoadEvents.OnPostDeleteSave(filePath); } } } [HarmonyPatch(typeof(GameNetworkManager))] [HarmonyPriority(800)] [HarmonyWrapSafe] internal static class GameNetworkManagerPatches { [HarmonyPostfix] [HarmonyPatch("SaveGame")] private static void SaveGamePostfix(GameNetworkManager __instance) { SaveLoadEvents.OnPostSaveGame(StartOfRound.Instance.isChallengeFile, __instance.currentSaveFileName); } [HarmonyPostfix] [HarmonyPatch("ResetSavedGameValues")] private static void PostResetSavedGameValues(GameNetworkManager __instance) { SaveLoadEvents.OnPostResetSavedGameValues(); } } [HarmonyPatch(typeof(InitializeGame))] [HarmonyPriority(800)] [HarmonyWrapSafe] internal static class InitializeGamePatches { [HarmonyPostfix] [HarmonyPatch("Start")] private static void StartPostfix() { MiscEvents.OnPostInitializeGame(); } } [HarmonyPatch(typeof(StartOfRound))] [HarmonyPriority(800)] [HarmonyWrapSafe] internal static class StartOfRoundPatches { [HarmonyPostfix] [HarmonyPatch("AutoSaveShipData")] private static void PostAutoSaveShipData(StartOfRound __instance) { SaveLoadEvents.OnPostAutoSave(__instance.isChallengeFile, GameNetworkManager.Instance.currentSaveFileName); } [HarmonyPostfix] [HarmonyPatch("Start")] private static void PostStart(StartOfRound __instance) { SaveLoadEvents.OnPostLoadGame(__instance.isChallengeFile, GameNetworkManager.Instance.currentSaveFileName); } } } namespace LethalModDataLib.Models { public record FieldKey(FieldInfo FieldInfo, object? Instance = null) : IModDataKey { public string Name => FieldInfo.Name; public string? AssemblyQualifiedName => FieldInfo.FieldType.AssemblyQualifiedName; public Assembly Assembly => FieldInfo.FieldType.Assembly; public bool TryGetValue(out object? value) { value = FieldInfo.GetValue(Instance); return true; } public bool TrySetValue(object? value) { FieldInfo.SetValue(Instance, value); return true; } public ModDataAttribute GetModDataAttribute() { return FieldInfo.GetCustomAttribute<ModDataAttribute>(); } } public record ModDataValue(ModDataAttribute ModDataAttribute, string? KeySuffix = null, object? OriginalValue = null) { public string? BaseKey { get { return ModDataAttribute.BaseKey; } set { ModDataAttribute.BaseKey = value; } } public SaveWhen SaveWhen => ModDataAttribute.SaveWhen; public LoadWhen LoadWhen => ModDataAttribute.LoadWhen; public ResetWhen ResetWhen => ModDataAttribute.ResetWhen; public SaveLocation SaveLocation => ModDataAttribute.SaveLocation; } public record PropertyKey(PropertyInfo PropertyInfo, object? Instance = null) : IModDataKey { public string Name => PropertyInfo.Name; public string? AssemblyQualifiedName => PropertyInfo.PropertyType.AssemblyQualifiedName; public Assembly Assembly => PropertyInfo.PropertyType.Assembly; public bool TryGetValue(out object? value) { if (PropertyInfo.GetGetMethod(nonPublic: true) == null) { value = null; return false; } value = PropertyInfo.GetValue(Instance); return true; } public bool TrySetValue(object? value) { if (PropertyInfo.GetSetMethod(nonPublic: true) == null) { return false; } PropertyInfo.SetValue(Instance, value); return true; } public ModDataAttribute GetModDataAttribute() { return PropertyInfo.GetCustomAttribute<ModDataAttribute>(); } } } namespace LethalModDataLib.Interfaces { public interface IModDataKey { string Name { get; } string? AssemblyQualifiedName { get; } Assembly Assembly { get; } object? Instance { get; } bool TryGetValue(out object? value); bool TrySetValue(object? value); ModDataAttribute GetModDataAttribute(); } } namespace LethalModDataLib.Helpers { public static class AssemblyExtensions { public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly) { if (assembly == null) { throw new ArgumentNullException("assembly"); } try { return assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { return ex.Types.Where((Type t) => t != null); } } } public static class ModDataHelper { private static readonly Dictionary<Assembly, string> PluginGuids = new Dictionary<Assembly, string>(); public static IModDataKey? GetModDataKey(object instanceOrType, string fieldPropertyName) { object instance = null; Type type2; if (instanceOrType is Type type) { type2 = type; } else { instance = instanceOrType; type2 = instanceOrType.GetType(); } FieldInfo field = type2.GetField(fieldPropertyName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { if (field.IsStatic) { instance = null; } return new FieldKey(field, instance); } PropertyInfo property = type2.GetProperty(fieldPropertyName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property == null) { throw new ArgumentException("Field or property " + fieldPropertyName + " does not exist in " + type2.AssemblyQualifiedName + "!"); } if (property.GetGetMethod(nonPublic: true).IsStatic) { instance = null; } return new PropertyKey(property, instance); } public static void TriggerSaveEvent(SaveWhen saveWhen) { Assembly callingAssembly = Assembly.GetCallingAssembly(); foreach (IModDataKey item in ModDataHandler.ModDataValues.Keys.Where((IModDataKey modDataKey) => modDataKey.Assembly == callingAssembly)) { ModDataValue modDataValue = ModDataHandler.ModDataValues[item]; if ((modDataValue.SaveWhen & saveWhen) != 0) { item.HandleSaveModData(); } if (modDataValue.SaveWhen.HasFlag(SaveWhen.Manual) && saveWhen.HasFlag(SaveWhen.Manual)) { item.HandleSaveModData(); } } } public static void TriggerLoadEvent(LoadWhen loadWhen) { Assembly callingAssembly = Assembly.GetCallingAssembly(); foreach (IModDataKey item in ModDataHandler.ModDataValues.Keys.Where((IModDataKey modDataKey) => modDataKey.Assembly == callingAssembly)) { ModDataValue modDataValue = ModDataHandler.ModDataValues[item]; if ((modDataValue.LoadWhen & loadWhen) != 0) { item.HandleLoadModData(); } if (modDataValue.LoadWhen.HasFlag(LoadWhen.Manual) && loadWhen.HasFlag(LoadWhen.Manual)) { item.HandleLoadModData(); } } } internal static string GetCurrentSaveFileName() { return GameNetworkManager.Instance.currentSaveFileName; } internal static string GetGeneralSaveFileName() { return "LCGeneralSaveData"; } internal static bool IsHost() { return GameNetworkManager.Instance.isHostingGame; } internal static bool IsKBackingField(FieldInfo fieldInfo) { return fieldInfo.Name.Contains("k__BackingField"); } internal static string GenerateBaseKey(Type type, string guid) { return guid + "." + type.FullName; } internal static string GetCallingPluginGuid(Assembly assembly) { Assembly assembly2 = assembly; if (PluginGuids.TryGetValue(assembly2, out string value)) { return value; } PluginInfo val = ((IEnumerable<PluginInfo>)Chainloader.PluginInfos.Values).FirstOrDefault((Func<PluginInfo, bool>)((PluginInfo pluginInfo) => ((object)pluginInfo.Instance)?.GetType().Assembly == assembly2)); if (val == null) { ManualLogSource? logger = LethalModDataLib.Logger; if (logger != null) { logger.LogWarning((object)("Failed to get plugin info for assembly " + assembly2.FullName + "!")); } } Dictionary<Assembly, string> pluginGuids = PluginGuids; Assembly key = assembly2; object obj; if (val == null) { obj = null; } else { BepInPlugin metadata = val.Metadata; obj = ((metadata != null) ? metadata.GUID : null); } if (obj == null) { obj = "Unknown"; } pluginGuids.Add(key, (string)obj); return PluginGuids[assembly2]; } } } namespace LethalModDataLib.Features { public static class ModDataAttributeCollector { internal static void RegisterModDataAttributes() { foreach (PluginInfo value in Chainloader.PluginInfos.Values) { foreach (Type loadableType in ((object)value.Instance).GetType().Assembly.GetLoadableTypes()) { RegisterModDataAttributes(value.Metadata.GUID, loadableType); } } } internal static void DeRegisterModDataAttributes() { foreach (Type item in Chainloader.PluginInfos.Values.SelectMany((PluginInfo pluginInfo) => ((object)pluginInfo.Instance).GetType().Assembly.GetTypes())) { DeRegisterModDataAttributes(item); } } internal static void RegisterModDataAttributes(string guid, Type type, object? instance = null, string? keySuffix = null) { try { AddModDataFields(guid, type, instance, keySuffix); AddModDataProperties(guid, type, instance, keySuffix); } catch (Exception ex) { ManualLogSource? logger = LethalModDataLib.Logger; if (logger != null) { logger.LogError((object)("Failed to register ModData attributes in " + type.FullName + " from " + guid + " plugin: " + ex.Message)); } } } private static void DeRegisterModDataAttributes(Type type) { Type type2 = type; foreach (IModDataKey item in ModDataHandler.ModDataValues.Keys.Where((IModDataKey key) => key.Assembly == type2.Assembly).ToList()) { ModDataHandler.ModDataValues.Remove(item); } } internal static void DeRegisterModDataAttributes(object instance) { object instance2 = instance; foreach (IModDataKey item in ModDataHandler.ModDataValues.Keys.Where((IModDataKey key) => key.Instance == instance2).ToList()) { ModDataHandler.ModDataValues.Remove(item); } } private static BindingFlags GetBindingFlags(object? instance = null) { if (instance != null) { return BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; } return BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; } private static void AddModDataFields(string guid, Type type, object? instance = null, string? keySuffix = null) { FieldInfo[] fields = type.GetFields(GetBindingFlags(instance)); foreach (FieldInfo fieldInfo in fields) { if (Attribute.IsDefined(fieldInfo, typeof(ModDataAttribute))) { FieldKey modDataKey = new FieldKey(fieldInfo, instance); ModDataHandler.AddModData(guid, type, modDataKey, keySuffix); } } } private static void AddModDataProperties(string guid, Type type, object? instance = null, string? keySuffix = null) { PropertyInfo[] properties = type.GetProperties(GetBindingFlags(instance)); foreach (PropertyInfo propertyInfo in properties) { if (Attribute.IsDefined(propertyInfo, typeof(ModDataAttribute))) { PropertyKey modDataKey = new PropertyKey(propertyInfo, instance); ModDataHandler.AddModData(guid, type, modDataKey, keySuffix); } } } } public static class ModDataHandler { internal static Dictionary<IModDataKey, ModDataValue> ModDataValues { get; } = new Dictionary<IModDataKey, ModDataValue>(); internal static string ToES3KeyString(this IModDataKey iModDataKey) { if (!ModDataValues.TryGetValue(iModDataKey, out ModDataValue value)) { throw new ArgumentException("Field or property with name " + iModDataKey.Name + " from " + iModDataKey.AssemblyQualifiedName + " is not registered with the ModDataAttribute!"); } if (value.BaseKey == null) { throw new ArgumentException("Field or property with name " + iModDataKey.Name + " from " + iModDataKey.AssemblyQualifiedName + " has no base key!"); } string text = value.BaseKey + "."; if (!string.IsNullOrEmpty(value.KeySuffix)) { return text + value.KeySuffix + "." + iModDataKey.Name; } return text + iModDataKey.Name; } public static void RegisterInstance(object instance, string? keySuffix = null) { ManualLogSource? logger = LethalModDataLib.Logger; if (logger != null) { logger.LogDebug((object)("Registering instance " + instance.GetType().FullName + "...")); } ModDataAttributeCollector.RegisterModDataAttributes(ModDataHelper.GetCallingPluginGuid(Assembly.GetCallingAssembly()), instance.GetType(), instance, keySuffix); } public static void DeRegisterInstance(object instance) { ManualLogSource? logger = LethalModDataLib.Logger; if (logger != null) { logger.LogDebug((object)("De-registering instance " + instance.GetType().FullName + "...")); } ModDataAttributeCollector.DeRegisterModDataAttributes(instance); } internal static void AddModData(string guid, Type type, IModDataKey modDataKey, string? keySuffix = null) { if (ModDataValues.ContainsKey(modDataKey)) { ManualLogSource? logger = LethalModDataLib.Logger; if (logger != null) { logger.LogWarning((object)("Field or property with name " + modDataKey.Name + " from " + type.AssemblyQualifiedName + " is already registered!")); } return; } modDataKey.TryGetValue(out object value); ModDataValues.Add(modDataKey, new ModDataValue(modDataKey.GetModDataAttribute(), keySuffix, value)); ModDataValue modDataValue = ModDataValues[modDataKey]; if (modDataValue.BaseKey == null) { string text2 = (modDataValue.BaseKey = ModDataHelper.GenerateBaseKey(type, guid)); } ManualLogSource? logger2 = LethalModDataLib.Logger; if (logger2 != null) { logger2.LogDebug((object)("Added field or property with name " + modDataKey.Name + " from " + type.AssemblyQualifiedName + " to the mod data system!")); } if (ModDataValues[modDataKey].LoadWhen.HasFlag(LoadWhen.OnRegister)) { modDataKey.HandleLoadModData(); } } internal static void Initialise() { ManualLogSource? logger = LethalModDataLib.Logger; if (logger != null) { logger.LogInfo((object)"Registering ModDataAttribute fields..."); } ModDataAttributeCollector.RegisterModDataAttributes(); ManualLogSource? logger2 = LethalModDataLib.Logger; if (logger2 != null) { logger2.LogInfo((object)"Hooking up save, load and delete events..."); } SaveLoadEvents.PostSaveGameEvent += OnSave; SaveLoadEvents.PostAutoSaveEvent += OnAutoSave; SaveLoadEvents.PostLoadGameEvent += OnLoad; SaveLoadEvents.PostDeleteSaveEvent += OnDeleteSave; SaveLoadEvents.PostResetSavedGameValuesEvent += OnGameOver; ManualLogSource? logger3 = LethalModDataLib.Logger; if (logger3 != null) { logger3.LogInfo((object)"ModDataHandler initialised!"); } } internal static void HandleSaveModData(this IModDataKey modDataKey) { if (!SaveLoadHandler.SaveData(modDataKey)) { ManualLogSource? logger = LethalModDataLib.Logger; if (logger != null) { logger.LogWarning((object)("Failed to save field or property " + modDataKey.Name + " from " + modDataKey.AssemblyQualifiedName + "!")); } } } internal static void HandleLoadModData(this IModDataKey modDataKey) { if (!SaveLoadHandler.LoadData(modDataKey)) { ManualLogSource? logger = LethalModDataLib.Logger; if (logger != null) { logger.LogWarning((object)("Failed to load field or property " + modDataKey.Name + " from " + modDataKey.AssemblyQualifiedName + "!")); } } } private static void DeleteModDataFile(string saveName) { saveName += ".moddata"; try { ES3.DeleteFile(saveName); } catch (Exception arg) { ManualLogSource? logger = LethalModDataLib.Logger; if (logger != null) { logger.LogError((object)$"Failed to delete file {saveName}! Exception: {arg}"); } } } private static void ResetModData(this IModDataKey modDataKey) { if (!modDataKey.TrySetValue(ModDataValues[modDataKey].OriginalValue)) { ManualLogSource? logger = LethalModDataLib.Logger; if (logger != null) { logger.LogWarning((object)("Failed to reset field or property " + modDataKey.Name + " from " + modDataKey.AssemblyQualifiedName + "!")); } } } private static void OnSave(bool isChallengeFile, string saveFileName) { foreach (IModDataKey item in ModDataValues.Keys.Where((IModDataKey modDataKey) => ModDataValues[modDataKey].SaveWhen.HasFlag(SaveWhen.OnSave))) { item.HandleSaveModData(); } } private static void OnAutoSave(bool isChallengeFile, string saveFileName) { foreach (IModDataKey item in ModDataValues.Keys.Where((IModDataKey modDataKey) => ModDataValues[modDataKey].SaveWhen.HasFlag(SaveWhen.OnAutoSave))) { item.HandleSaveModData(); } } private static void OnLoad(bool isChallengeFile, string saveFileName) { foreach (IModDataKey item in ModDataValues.Keys.Where((IModDataKey modDataKey) => ModDataValues[modDataKey].LoadWhen.HasFlag(LoadWhen.OnLoad))) { item.HandleLoadModData(); } } private static void OnDeleteSave(string filePath) { DeleteModDataFile(filePath); } private static void OnGameOver() { foreach (IModDataKey item in ModDataValues.Keys.Where((IModDataKey modDataKey) => ModDataValues[modDataKey].ResetWhen.HasFlag(ResetWhen.OnGameOver))) { item.ResetModData(); } } } public static class SaveLoadHandler { private static void VerifyKeyAndFileName(string key, string fileName) { if (string.IsNullOrEmpty(key)) { throw new ArgumentException("Key cannot be null or empty!"); } if (string.IsNullOrEmpty(fileName)) { throw new ArgumentException("File name cannot be null or empty!"); } } private static T? LoadData<T>(string key, string fileName, T? defaultValue = default(T?)) { VerifyKeyAndFileName(key, fileName); fileName += ".moddata"; try { ManualLogSource? logger = LethalModDataLib.Logger; if (logger != null) { logger.LogDebug((object)("Loading data from file " + fileName + " with key " + key + "...")); } return (T?)(ES3.KeyExists(key, fileName) ? ((object)ES3.Load<T>(key, fileName)) : ((object)defaultValue)); } catch (Exception arg) { ManualLogSource? logger2 = LethalModDataLib.Logger; if (logger2 != null) { logger2.LogError((object)$"Failed to load data from file {fileName} with key {key}! Exception: {arg}"); } return defaultValue; } } public static T? LoadData<T>(string key, SaveLocation saveLocation = SaveLocation.CurrentSave, T? defaultValue = default(T?), bool autoAddGuid = true) { if (saveLocation == SaveLocation.CurrentSave && !ModDataHelper.IsHost()) { ManualLogSource? logger = LethalModDataLib.Logger; if (logger != null) { logger.LogDebug((object)("Not loading " + key + " from current save file as we're not the host!")); } return defaultValue; } if (autoAddGuid) { string callingPluginGuid = ModDataHelper.GetCallingPluginGuid(Assembly.GetCallingAssembly()); if (!key.StartsWith(callingPluginGuid)) { key = callingPluginGuid + "." + key; } } string key2 = key; return LoadData(key2, saveLocation switch { SaveLocation.CurrentSave => ModDataHelper.GetCurrentSaveFileName(), SaveLocation.GeneralSave => ModDataHelper.GetGeneralSaveFileName(), _ => throw new ArgumentOutOfRangeException("saveLocation", saveLocation, "Invalid load location!"), }, defaultValue); } public static bool LoadData(IModDataKey modDataKey) { if (!ModDataHandler.ModDataValues.TryGetValue(modDataKey, out ModDataValue value)) { ManualLogSource? logger = LethalModDataLib.Logger; if (logger != null) { logger.LogWarning((object)("Field or property " + modDataKey.Name + " from " + modDataKey.AssemblyQualifiedName + " has no registered mod data attribute! If this is an instance of a class, make sure to register it using ModDataHandler.RegisterInstance!")); } return false; } string key = modDataKey.ToES3KeyString(); SaveLocation saveLocation = value.SaveLocation; object value2 = LoadData(key, saveLocation, value.OriginalValue, autoAddGuid: false); if (modDataKey.TrySetValue(value2)) { return true; } ManualLogSource? logger2 = LethalModDataLib.Logger; if (logger2 != null) { logger2.LogDebug((object)("Failed to set value for field or property " + modDataKey.Name + " from " + modDataKey.AssemblyQualifiedName + "! Does it not have a setter?")); } return false; } private static bool SaveData<T>(T? data, string key, string fileName) { VerifyKeyAndFileName(key, fileName); fileName += ".moddata"; try { ManualLogSource? logger = LethalModDataLib.Logger; if (logger != null) { logger.LogDebug((object)("Saving data to file " + fileName + " with key " + key + "...")); } ES3.Save<T>(key, data, fileName); return true; } catch (Exception arg) { ManualLogSource? logger2 = LethalModDataLib.Logger; if (logger2 != null) { logger2.LogError((object)$"Failed to save data to file {fileName} with key {key}! Exception: {arg}"); } return false; } } public static bool SaveData<T>(T? data, string key, SaveLocation saveLocation = SaveLocation.CurrentSave, bool autoAddGuid = true) { if (saveLocation == SaveLocation.CurrentSave && !ModDataHelper.IsHost()) { ManualLogSource? logger = LethalModDataLib.Logger; if (logger != null) { logger.LogDebug((object)("Not saving " + key + " to current save file as we're not the host!")); } return false; } if (autoAddGuid) { string callingPluginGuid = ModDataHelper.GetCallingPluginGuid(Assembly.GetCallingAssembly()); if (!key.StartsWith(callingPluginGuid)) { key = callingPluginGuid + "." + key; } } string key2 = key; return SaveData(data, key2, saveLocation switch { SaveLocation.CurrentSave => ModDataHelper.GetCurrentSaveFileName(), SaveLocation.GeneralSave => ModDataHelper.GetGeneralSaveFileName(), _ => throw new ArgumentOutOfRangeException("saveLocation", saveLocation, "Invalid save location!"), }); } public static bool SaveData(IModDataKey modDataKey) { if (!ModDataHandler.ModDataValues.TryGetValue(modDataKey, out ModDataValue value)) { ManualLogSource? logger = LethalModDataLib.Logger; if (logger != null) { logger.LogWarning((object)("Field or property " + modDataKey.Name + " from " + modDataKey.AssemblyQualifiedName + " has no registered mod data attribute! If this is an instance of a class, make sure to register it using ModDataHandler.RegisterInstance!")); } return false; } string key = modDataKey.ToES3KeyString(); SaveLocation saveLocation = value.SaveLocation; if (modDataKey.TryGetValue(out object value2)) { return SaveData(value2, key, saveLocation, autoAddGuid: false); } ManualLogSource? logger2 = LethalModDataLib.Logger; if (logger2 != null) { logger2.LogDebug((object)("Failed to get value from field or property " + modDataKey.Name + " from " + modDataKey.AssemblyQualifiedName + "! Does it not have a getter?")); } return false; } } } namespace LethalModDataLib.Events { public static class MiscEvents { public delegate void PostInitializeGameEventHandler(); public static event PostInitializeGameEventHandler? PostInitializeGameEvent; internal static void OnPostInitializeGame() { MiscEvents.PostInitializeGameEvent?.Invoke(); } } public static class SaveLoadEvents { public delegate void PostAutoSaveEventHandler(bool isChallenge, string saveFileName); public delegate void PostDeleteSaveEventHandler(string saveFileName); public delegate void PostLoadGameEventHandler(bool isChallenge, string saveFileName); public delegate void PostResetSavedGameValuesEventHandler(); public delegate void PostSaveGameEventHandler(bool isChallenge, string saveFileName); public static event PostSaveGameEventHandler? PostSaveGameEvent; public static event PostAutoSaveEventHandler? PostAutoSaveEvent; public static event PostLoadGameEventHandler? PostLoadGameEvent; public static event PostDeleteSaveEventHandler? PostDeleteSaveEvent; public static event PostResetSavedGameValuesEventHandler? PostResetSavedGameValuesEvent; internal static void OnPostSaveGame(bool isChallenge, string saveFileName) { SaveLoadEvents.PostSaveGameEvent?.Invoke(isChallenge, saveFileName); } internal static void OnPostAutoSave(bool isChallenge, string saveFileName) { SaveLoadEvents.PostAutoSaveEvent?.Invoke(isChallenge, saveFileName); } internal static void OnPostLoadGame(bool isChallenge, string saveFileName) { SaveLoadEvents.PostLoadGameEvent?.Invoke(isChallenge, saveFileName); } internal static void OnPostDeleteSave(string saveFileName) { SaveLoadEvents.PostDeleteSaveEvent?.Invoke(saveFileName); } internal static void OnPostResetSavedGameValues() { SaveLoadEvents.PostResetSavedGameValuesEvent?.Invoke(); } } } namespace LethalModDataLib.Enums { [Flags] public enum IgnoreFlags { None = 0, OnSave = 1, OnLoad = 2, IfNull = 4, IfDefault = 8 } [Flags] public enum LoadWhen { Manual = 0, OnLoad = 1, OnRegister = 2 } [Flags] public enum ResetWhen { Manual = 0, OnGameOver = 1 } public enum SaveLocation { CurrentSave, GeneralSave } [Flags] public enum SaveWhen { Manual = 0, OnAutoSave = 1, OnSave = 2 } } namespace LethalModDataLib.Base { public abstract class ModDataContainer { protected virtual SaveLocation SaveLocation { get; set; } protected virtual string OptionalPrefixSuffix { get; set; } = string.Empty; private List<FieldInfo> GetFields() { return (from field in GetType().GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where !ModDataHelper.IsKBackingField(field) select field).ToArray().ToList(); } private List<PropertyInfo> GetProperties() { return (from property in GetType().GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where property.Name != "SaveLocation" && property.Name != "OptionalPrefixSuffix" select property).ToArray().ToList(); } protected virtual string GetPrefix() { Type type = GetType(); string text = string.Empty; if (!string.IsNullOrEmpty(OptionalPrefixSuffix)) { text = OptionalPrefixSuffix + "."; } return type.Assembly.GetName().Name + "." + type.Name + "." + text; } private void SaveFields() { string prefix = GetPrefix(); foreach (FieldInfo field in GetFields()) { ModDataIgnoreAttribute customAttribute = field.GetCustomAttribute<ModDataIgnoreAttribute>(); if (customAttribute != null) { if (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.None) || customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.OnSave) || (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.IfNull) && field.GetValue(this) == null)) { continue; } if (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.IfDefault)) { object obj = (field.FieldType.IsValueType ? Activator.CreateInstance(field.FieldType) : null); if (field.GetValue(this).Equals(obj)) { continue; } } } SaveLoadHandler.SaveData(field.GetValue(this), prefix + field.Name, SaveLocation, autoAddGuid: false); } } private void SaveProperties() { string prefix = GetPrefix(); foreach (PropertyInfo property in GetProperties()) { if (property.GetGetMethod(nonPublic: true) == null) { continue; } ModDataIgnoreAttribute customAttribute = property.GetCustomAttribute<ModDataIgnoreAttribute>(); if (customAttribute != null) { if (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.None) || customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.OnSave) || (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.IfNull) && property.GetValue(this) == null)) { continue; } if (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.IfDefault)) { object obj = (property.PropertyType.IsValueType ? Activator.CreateInstance(property.PropertyType) : null); if (property.GetValue(this).Equals(obj)) { continue; } } } SaveLoadHandler.SaveData(property.GetValue(this), prefix + property.Name, SaveLocation, autoAddGuid: false); } } public void Save() { PreSave(); SaveFields(); SaveProperties(); PostSave(); } protected virtual void PreSave() { } protected virtual void PostSave() { } private void LoadFields() { string prefix = GetPrefix(); foreach (FieldInfo field in GetFields()) { ModDataIgnoreAttribute customAttribute = field.GetCustomAttribute<ModDataIgnoreAttribute>(); if (customAttribute != null) { if (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.None) || customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.OnLoad) || (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.IfNull) && field.GetValue(this) == null)) { continue; } if (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.IfDefault)) { object obj = (field.FieldType.IsValueType ? Activator.CreateInstance(field.FieldType) : null); if (field.GetValue(this).Equals(obj)) { continue; } } } object value = SaveLoadHandler.LoadData<object>(prefix + field.Name, SaveLocation, null, autoAddGuid: false); field.SetValue(this, value); } } private void LoadProperties() { string prefix = GetPrefix(); foreach (PropertyInfo property in GetProperties()) { if (property.GetSetMethod(nonPublic: true) == null) { continue; } ModDataIgnoreAttribute customAttribute = property.GetCustomAttribute<ModDataIgnoreAttribute>(); if (customAttribute != null) { if (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.None) || customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.OnLoad) || (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.IfNull) && property.GetValue(this) == null)) { continue; } if (customAttribute.IgnoreFlags.HasFlag(IgnoreFlags.IfDefault)) { object obj = (property.PropertyType.IsValueType ? Activator.CreateInstance(property.PropertyType) : null); if (property.GetValue(this).Equals(obj)) { continue; } } } object value = SaveLoadHandler.LoadData(prefix + property.Name, SaveLocation, property.GetValue(this), autoAddGuid: false); property.SetValue(this, value); } } public void Load() { PreLoad(); LoadFields(); LoadProperties(); PostLoad(); } protected virtual void PreLoad() { } protected virtual void PostLoad() { } } } namespace LethalModDataLib.Attributes { [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] public class ModDataAttribute : Attribute { public LoadWhen LoadWhen { get; set; } = LoadWhen.OnLoad; public SaveWhen SaveWhen { get; set; } = SaveWhen.OnSave; public ResetWhen ResetWhen { get; set; } public SaveLocation SaveLocation { get; set; } public string? BaseKey { get; set; } public ModDataAttribute(SaveWhen saveWhen = SaveWhen.OnSave, LoadWhen loadWhen = LoadWhen.OnLoad, SaveLocation saveLocation = SaveLocation.CurrentSave, ResetWhen resetWhen = ResetWhen.Manual, string? baseKey = null) { SaveWhen = saveWhen; LoadWhen = loadWhen; SaveLocation = saveLocation; ResetWhen = resetWhen; BaseKey = baseKey; } public ModDataAttribute() { } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] public class ModDataIgnoreAttribute : Attribute { public IgnoreFlags IgnoreFlags { get; } public ModDataIgnoreAttribute(IgnoreFlags ignoreFlags = IgnoreFlags.None) { IgnoreFlags = ignoreFlags; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } internal static class IsExternalInit { } }