Decompiled source of BetterMagnets v1.0.0

BetterMagnets.dll

Decompiled 2 months ago
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text.Json;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using BetterMagnets.Data;
using BetterMagnets.Settings;
using HarmonyLib;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using UnityEngine;
using Utils.Logger;
using Utils.Settings;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("BetterMagnets")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("BetterMagnets is a Deep Rock Galactic: Survivor plugin that improves the artifact magnet experience. With this mod, you can earn magnets at the end of each run without losing experience, and the Company Issued Magnet artifact finally gets a real use.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+a493ab356cb4551c1d1d5bb6b2dd7a5fbfa4842e")]
[assembly: AssemblyProduct("BetterMagnets")]
[assembly: AssemblyTitle("BetterMagnets")]
[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 Utils.Settings
{
	public class Config
	{
		public static string PluginGUID;

		public static string PluginFolderPath;

		internal static ConfigFile cfg;

		private static readonly List<Action> configActions = new List<Action>();

		public static ConfigElement<T> Bind<T>(string section, string key, T defaultValue, string description)
		{
			return new ConfigElement<T>(cfg.Bind<T>(section, key, defaultValue, description), section, key, defaultValue, description);
		}

		public static void Save()
		{
			cfg.Save();
		}

		public static void Reload()
		{
			cfg.Reload();
		}

		public static void Setup(string pluginGUID, ConfigFile config, int skipCaller = 4, params Action[] actions)
		{
			PluginGUID = pluginGUID;
			cfg = config;
			PluginFolderPath = Paths.ConfigPath + "\\" + PluginGUID;
			if (!Directory.Exists(PluginFolderPath))
			{
				Directory.CreateDirectory(PluginFolderPath);
			}
			ENV.Debug.Setup(skipCaller);
			AddConfigActions(actions);
		}

		public static void Load()
		{
			if (configActions == null || configActions.Count == 0)
			{
				return;
			}
			foreach (Action configAction in configActions)
			{
				configAction();
			}
		}

		public static void AddConfigActions(params Action[] actions)
		{
			configActions.AddRange(actions);
		}
	}
	public class ConfigElement<T>
	{
		public List<Action<T>> OnValueChanged;

		public readonly ConfigEntry<T> OriginalConfig;

		public string Section { get; }

		public string Key { get; }

		public string Description { get; }

		public Type ElementType => typeof(T);

		public T DefaultValue { get; }

		public T Value
		{
			get
			{
				return GetValue();
			}
			set
			{
				SetValue(value);
			}
		}

		public ConfigElement(ConfigEntry<T> original, string section, string key, T defaultValue, string description)
		{
			Section = section;
			Key = key;
			Description = description;
			OnValueChanged = new List<Action<T>>();
			OriginalConfig = original;
			DefaultValue = defaultValue;
			base..ctor();
		}

		public T ValueFromConfig()
		{
			Config.Reload();
			return Value;
		}

		private T GetValue()
		{
			return OriginalConfig.Value;
		}

		private void SetValue(T value)
		{
			if ((Value == null && value == null) || (Value != null && Value.Equals(value)))
			{
				return;
			}
			OriginalConfig.Value = value;
			foreach (Action<T> item in OnValueChanged)
			{
				item?.Invoke(value);
			}
		}
	}
	public static class ENV
	{
		public class Debug
		{
			private static string debugSection = "\ud83e\udeb2Debug";

			private static ConfigElement<bool> DebugLogOnTempFile;

			private static ConfigElement<bool> DebugEnableTraceLogs;

			public static void Setup(int skipCaller)
			{
				Config.AddConfigActions(delegate
				{
					load(skipCaller);
				});
			}

			private static void load(int skipCaller)
			{
				if (enableDebugConfigs(skipCaller))
				{
					DebugLogOnTempFile = Config.Bind(debugSection, "LogOnTempFile", defaultValue: false, "Enabled, will log every plugin log on a temp file");
					DebugEnableTraceLogs = Config.Bind(debugSection, "EnableTraceLogs", defaultValue: false, "Enabled, will print Trace logs (Debug output in BepInEx)");
				}
				validateValues();
			}

			private static void validateValues()
			{
				if (DebugLogOnTempFile != null)
				{
					LogOnTempFile = DebugLogOnTempFile.Value;
				}
				if (DebugEnableTraceLogs != null)
				{
					EnableTraceLogs = DebugEnableTraceLogs.Value;
				}
				Config.cfg.Save();
			}

			private static bool enableDebugConfigs(int skipCaller)
			{
				return new StackTrace().GetFrame(skipCaller).GetMethod().DeclaringType.Assembly.GetCustomAttribute<AssemblyConfigurationAttribute>()?.Configuration != "Release";
			}
		}

		public static bool LogOnTempFile;

		public static bool EnableTraceLogs;
	}
}
namespace Utils.Logger
{
	public class Config
	{
		internal static ManualLogSource logger;

		private static string tempLogFile;

		public static void Setup(ManualLogSource logger, string worldType = "")
		{
			Config.logger = logger;
			string value = "";
			if (worldType != "")
			{
				value = "-" + worldType;
			}
			tempLogFile = $"{Utils.Settings.Config.PluginFolderPath}\\{Utils.Settings.Config.PluginGUID}{value}.txt";
			Log.Start("Using \"" + tempLogFile + "\" to save logs.");
		}

		public static void TestSetup()
		{
		}

		internal static void logFile(object data, string level, string prefix = "")
		{
			if (Utils.Settings.ENV.LogOnTempFile)
			{
				using (StreamWriter streamWriter = File.AppendText(tempLogFile))
				{
					string value = $"{prefix}{DateTime.Now.ToString("hh:mm:ss")} [{level} {Utils.Settings.Config.PluginGUID}]: {data}";
					streamWriter.WriteLine(value);
				}
			}
		}
	}
	public class Log
	{
		private static ConcurrentDictionary<string, long> timedLog = new ConcurrentDictionary<string, long>();

		private static List<string> firstLog = new List<string>();

		public static void Info(object data)
		{
			Config.logger.LogInfo(data);
			Config.logFile(data, "Info:   ");
		}

		public static void Error(object data)
		{
			Config.logger.LogError(data);
			Config.logFile(data, "Error:  ");
		}

		public static void Debug(object data)
		{
			Config.logger.LogDebug(data);
			Config.logFile(data, "Debug:  ");
		}

		public static void Fatal(object data)
		{
			Config.logger.LogFatal(data);
			Config.logFile(data, "Fatal:  ");
		}

		public static void Warning(object data)
		{
			Config.logger.LogWarning(data);
			Config.logFile(data, "Warning:");
		}

		public static void Message(object data)
		{
			Config.logger.LogMessage(data);
			Config.logFile(data, "Message:");
		}

		public static void Start(object data)
		{
			Config.logger.LogMessage(data);
			Config.logFile(data, "Start:  ", "\n");
		}

		public static void Trace(object data)
		{
			if (Utils.Settings.ENV.EnableTraceLogs)
			{
				Config.logger.LogDebug(data);
				Config.logFile(data, "Trace:  ");
			}
		}

		public static void Struct<T>(T data)
		{
			if (Utils.Settings.ENV.EnableTraceLogs)
			{
				string text = structToString(data);
				Config.logger.LogDebug((object)text);
				Config.logFile(text, "Struct: ");
			}
		}

		public static void Timed(Action action, int ms, string id = "")
		{
			if (!blocked(ms, id))
			{
				action();
			}
		}

		public static void First(Action action, string id = "")
		{
			if (first(id))
			{
				action();
			}
		}

		private static string structToString<T>(T data)
		{
			Type type = data.GetType();
			FieldInfo[] fields = type.GetFields();
			PropertyInfo[] properties = type.GetProperties();
			Dictionary<string, object> values = new Dictionary<string, object>();
			Array.ForEach(properties, delegate(PropertyInfo property)
			{
				values.TryAdd(property.Name, property.GetValue(data));
			});
			Array.ForEach(fields, delegate(FieldInfo field)
			{
				values.TryAdd(field.Name, field.GetValue(data));
			});
			List<string> list = new List<string>();
			foreach (KeyValuePair<string, object> item in values)
			{
				list.Add($"\"{item.Key}\":\"{item.Value}\"");
			}
			return "\"" + type.ToString() + "\": {" + string.Join(",", list) + "}";
		}

		private static bool first(string id)
		{
			if (firstLog.Contains(id))
			{
				return false;
			}
			firstLog.Add(id);
			return true;
		}

		private static bool blocked(int ms, string id)
		{
			long num = DateTimeOffset.Now.ToUnixTimeMilliseconds();
			if (!timedLog.TryGetValue(id, out var value))
			{
				long newTimestamp = DateTimeOffset.Now.AddMilliseconds(ms).ToUnixTimeMilliseconds();
				timedLog.AddOrUpdate(id, newTimestamp, (string key, long oldValue) => newTimestamp);
				return true;
			}
			if (num < value)
			{
				return true;
			}
			timedLog.TryRemove(id, out var _);
			return false;
		}
	}
}
namespace Utils.Database
{
	public static class Cache
	{
		public static ConcurrentDictionary<string, long> LastUpdate = new ConcurrentDictionary<string, long>();

		public static ConcurrentDictionary<string, bool> Cached = new ConcurrentDictionary<string, bool>();

		public static bool IsBlocked(string key, long blockedDuration)
		{
			long now = DateTimeOffset.Now.ToUnixTimeMilliseconds();
			if (LastUpdate.TryGetValue(key, out var value) && now - value < blockedDuration)
			{
				return true;
			}
			LastUpdate.AddOrUpdate(key, now, (string _, long _) => now);
			return false;
		}

		public static bool Key(string key, bool cache = true)
		{
			if (!cache)
			{
				return false;
			}
			return Cached.AddOrUpdate(key, addValue: true, (string _, bool _) => true);
		}

		public static bool Exists(string key)
		{
			if (Cached.TryGetValue(key, out var value) && value)
			{
				return true;
			}
			return false;
		}

		public static bool RemoveKey(string key)
		{
			bool value;
			return Cached.TryRemove(key, out value);
		}

		public static void Clear()
		{
			Cached.Clear();
			LastUpdate.Clear();
		}
	}
	public static class DB
	{
		private static JsonSerializerOptions JSONOptions = new JsonSerializerOptions
		{
			WriteIndented = false,
			IncludeFields = false
		};

		private static JsonSerializerOptions Pretty_JSON_options = new JsonSerializerOptions
		{
			WriteIndented = true,
			IncludeFields = true
		};

		private static List<Action> loadActions = new List<Action>();

		private static List<Action> saveActions = new List<Action>();

		private static List<Action> cleanActions = new List<Action>();

		public static void Setup(List<Action> load, List<Action> save, List<Action> clean)
		{
			if (load != null)
			{
				loadActions.AddRange(load);
			}
			if (save != null)
			{
				saveActions.AddRange(save);
			}
			if (clean != null)
			{
				cleanActions.AddRange(clean);
			}
		}

		public static void Load()
		{
			if (loadActions == null)
			{
				return;
			}
			foreach (Action loadAction in loadActions)
			{
				loadAction();
			}
			Log.Info($"All({loadActions.Count}) database actions loaded.");
		}

		public static void Save()
		{
			if (saveActions == null)
			{
				return;
			}
			foreach (Action saveAction in saveActions)
			{
				saveAction();
			}
			Log.Info($"All({saveActions.Count}) database actions saved.");
		}

		public static void Clean()
		{
			if (cleanActions == null)
			{
				return;
			}
			foreach (Action cleanAction in cleanActions)
			{
				cleanAction();
			}
			Log.Info($"All({cleanActions.Count}) database actions cleaned.");
		}

		public static void AddLoadActions(params Action[] actions)
		{
			loadActions.AddRange(actions);
		}

		public static void AddSaveActions(params Action[] actions)
		{
			saveActions.AddRange(actions);
		}

		public static void AddCleanActions(params Action[] actions)
		{
			cleanActions.AddRange(actions);
		}

		public static void saveFile<T>(string fileName, T data, bool pretty = false, string extension = ".json")
		{
			JsonSerializerOptions options = JSONOptions;
			if (pretty)
			{
				options = Pretty_JSON_options;
			}
			File.WriteAllText(Utils.Settings.Config.PluginFolderPath + "\\" + fileName + extension, JsonSerializer.Serialize(data, options));
		}

		public static void loadFile<T>(string fileName, ref T data, string extension = ".json") where T : new()
		{
			string path = Utils.Settings.Config.PluginFolderPath + "\\" + fileName + extension;
			if (!File.Exists(path))
			{
				File.Create(path).Dispose();
			}
			string json = File.ReadAllText(path);
			try
			{
				data = JsonSerializer.Deserialize<T>(json);
				Log.Trace(fileName + " DB Populated");
			}
			catch
			{
				data = new T();
				Log.Trace(fileName + " DB Created");
			}
		}
	}
}
namespace BetterMagnets
{
	[BepInPlugin("BetterMagnets", "BetterMagnets", "1.0.0")]
	public class Plugin : BasePlugin
	{
		public static readonly Harmony Harmony = new Harmony("BetterMagnets");

		public override void Load()
		{
			BetterMagnets.Settings.Config.Load(((BasePlugin)this).Config, ((BasePlugin)this).Log, "Client");
			Log.Trace("Patching harmony");
			Harmony.PatchAll();
			Log.Info("Plugin BetterMagnets v1.0.0 loaded!");
		}

		public override bool Unload()
		{
			Log.Trace("Unpatching harmony");
			Harmony.UnpatchSelf();
			Log.Info("Plugin BetterMagnets v1.0.0 unloaded!");
			return true;
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "BetterMagnets";

		public const string PLUGIN_NAME = "BetterMagnets";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace BetterMagnets.Settings
{
	public class Config
	{
		public static void Load(ConfigFile configFile, ManualLogSource logger, string worldType)
		{
			ENV.General.Setup();
			Utils.Settings.Config.Setup("BetterMagnets", configFile, 4);
			Utils.Settings.Config.Load();
			Utils.Logger.Config.Setup(logger, worldType);
		}
	}
	public static class ENV
	{
		public static class General
		{
			public static void Setup()
			{
				Utils.Settings.Config.AddConfigActions(Load);
			}

			private static void Load()
			{
				Utils.Settings.Config.Save();
			}
		}

		private static readonly string general = "⚙\ufe0f General";

		public static readonly ConfigElement<bool> OnlyGoodMagnets = Utils.Settings.Config.Bind(general, "OnlyGoodMagnets", defaultValue: true, "Only spawn good magnets (no company issued magnet magnets).");

		public static readonly ConfigElement<bool> CompanyIssuedMagnetEnabled = Utils.Settings.Config.Bind(general, "CompanyIssuedMagnetEnabled", defaultValue: false, "If enabled, the company issued magnet artifact will be added to the player's inventory.");
	}
}
namespace BetterMagnets.Hooks
{
	[Harmony]
	public class GameControllerPatch
	{
		[HarmonyPatch(typeof(GameController), "Start")]
		public class Start
		{
			public static void Postfix(GameController __instance)
			{
				Initialize(__instance);
			}
		}

		[HarmonyPatch(typeof(GameController), "Update")]
		public class Update
		{
			public static void Postfix(GameController __instance)
			{
				Initialize(__instance);
			}
		}

		private static void Initialize(GameController __instance)
		{
			if ((Object)(object)Controllers.GameController == (Object)null)
			{
				Controllers.GameController = __instance;
			}
			if (!Artifacts.DataList.IsEmpty)
			{
				return;
			}
			foreach (ArtifactSkillData item in (Il2CppArrayBase<ArtifactSkillData>)(object)Controllers.GameController.skillCollectionManager.artifactDataCollection.Data)
			{
				Artifacts.DataList.TryAdd(((ASkillData)item).Id, item);
			}
		}
	}
	[Harmony]
	public class PickupSpawnerPatch
	{
		[HarmonyPatch(typeof(PickupSpawner), "SpawnArtifactMagnet")]
		public class SpawnArtifactMagnet
		{
			public static bool Prefix(PickupSpawner __instance, Vector3 position, float pickThreshold)
			{
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_004c: Unknown result type (might be due to invalid IL or missing references)
				//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_0066: Unknown result type (might be due to invalid IL or missing references)
				//IL_0069: Unknown result type (might be due to invalid IL or missing references)
				//IL_0087: Unknown result type (might be due to invalid IL or missing references)
				Log.Trace($"SpawnArtifactMagnet called with position: {position}, threshold: {pickThreshold}");
				if (!BetterMagnets.Settings.ENV.OnlyGoodMagnets.ValueFromConfig())
				{
					return true;
				}
				position -= new Vector3(2f, 0f, 7f);
				__instance.SpawnMagnet(position);
				Log.Trace($"SpawnArtifactMagnet ignored and spawned normal magnet at position: {position}");
				return false;
			}
		}
	}
	[Harmony]
	public class PlayerPatch
	{
		[HarmonyPatch(typeof(Player), "OnExitDropPod")]
		public static class OnExitDropPod
		{
			public static void Prefix(Player __instance)
			{
				Log.Info("player Exited DropPod");
				if (!BetterMagnets.Settings.ENV.CompanyIssuedMagnetEnabled.ValueFromConfig())
				{
					return;
				}
				if (!Artifacts.DataList.TryGetValue(39198, out var value))
				{
					Log.Error("CompanyIssuedMagnetArtifactData not found in Artifacts.DataList");
					return;
				}
				Enumerator<ArtifactStateHandler> enumerator = __instance.EquippedArtifacts.GetEnumerator();
				while (enumerator.MoveNext())
				{
					if (((ASkillData)enumerator.Current.Data).Id == 39198)
					{
						Log.Trace("CompanyIssuedMagnetArtifactData already equipped on player: " + ((Object)__instance).name);
						return;
					}
				}
				__instance.EquipArtifact(value, true);
			}
		}
	}
}
namespace BetterMagnets.Data
{
	public static class Artifacts
	{
		public static readonly ConcurrentDictionary<int, ArtifactSkillData> DataList = new ConcurrentDictionary<int, ArtifactSkillData>();

		public const int CompanyIssuedMagnetArtifactData = 39198;
	}
	public static class Controllers
	{
		public static GameController GameController { get; set; }
	}
}