using 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
{
}
}