Decompiled source of LLBTweaker v0.1.4

plugins/LLBTweaker/LLBTweaker.dll

Decompiled 9 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using LLBML.GameEvents;
using LLBML.Messages;
using LLBML.Networking;
using LLBML.Players;
using LLBML.States;
using LLBML.Utils;
using LLBT.Tweaks;
using Multiplayer;
using TinyJson;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("LLBTweaker (fr.glomzubuk.plugins.llb.llbtweaker)")]
[assembly: AssemblyProduct("LLBTweaker")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.4.0")]
[module: UnverifiableCode]
namespace LLBT
{
	[BepInPlugin("fr.glomzubuk.plugins.llb.llbtweaker", "LLBTweaker", "0.1.4")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class LLBTweaker : BaseUnityPlugin
	{
		private int currentTweakIndex;

		internal ConfigEntry<KeyCode> cycleTweaksNextKey;

		internal ConfigEntry<KeyCode> cycleTweaksPreviousKey;

		internal ConfigEntry<bool> enableLiveTweaking;

		internal ConfigEntry<bool> restoreConfigsOnEnteringLobby;

		internal ConfigEntry<KeyCode> reloadTweaksStateKey;

		internal ConfigEntry<KeyCode> toggleCurrentTweakKey;

		internal List<TweakBase> tweaks = new List<TweakBase>();

		internal bool showVanillaWarning;

		internal static ManualLogSource Log { get; private set; }

		internal static ManualLogSource TweakLog { get; private set; }

		internal static LLBTweaker Instance { get; private set; }

		internal ConfigFile TweakConfig { get; private set; }

		private void Awake()
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Hello, world!");
			InitConfigs();
			TweakLog = Logger.CreateLogSource("Tweaks");
			TweakConfig = new ConfigFile(Path.Combine(Paths.ConfigPath, "TweakConfigs.cfg"), true, ((BaseUnityPlugin)this).Info.Metadata);
		}

		private void Start()
		{
			TweakConfig.SaveOnConfigSet = false;
			foreach (TweakBase item in tweaks.Where((TweakBase tweak) => tweak.ShouldPatch()))
			{
				((BaseUnityPlugin)this).Logger.LogDebug((object)("Applying tweak " + item.ID));
				item.Enable();
			}
			Network.Init();
			ModDependenciesUtils.RegisterToModMenu(((BaseUnityPlugin)this).Info, (List<string>)null);
		}

		private void Update()
		{
			if (GameStates.IsInOnlineLobby())
			{
				Network.OnUpdate();
			}
			else
			{
				showVanillaWarning = false;
			}
			if (enableLiveTweaking.Value && tweaks.Count > 0)
			{
				HandleLiveTweaking();
			}
		}

		private void OnGUI()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			if (enableLiveTweaking.Value && tweaks.Count != 0)
			{
				GUIStyle val = new GUIStyle
				{
					fontStyle = (FontStyle)1,
					fontSize = 24
				};
				val.normal.textColor = Color.red;
				GUIStyle val2 = val;
				TweakBase tweakBase = tweaks[currentTweakIndex];
				GUI.Label(new Rect(20f, 20f, 400f, 25f), "Current Tweak: " + tweakBase.Name, val2);
				GUI.Label(new Rect(20f, 50f, 200f, 25f), "Is Tweak Enabled: " + tweakBase.IsEnabled, val2);
			}
		}

		private void HandleLiveTweaking()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			if (Input.GetKeyDown(toggleCurrentTweakKey.Value))
			{
				if (NetworkApi.IsOnline)
				{
					Player localPlayer = Player.GetLocalPlayer();
					if (localPlayer.nr == 0 && GameStates.IsInLobby())
					{
						tweaks[currentTweakIndex].Toggle();
						GameStatesLobbyUtils.SendPlayerState(localPlayer);
					}
				}
				else
				{
					tweaks[currentTweakIndex].Toggle();
				}
			}
			if (Input.GetKeyDown(cycleTweaksNextKey.Value))
			{
				currentTweakIndex--;
				if (currentTweakIndex < 0)
				{
					currentTweakIndex = tweaks.Count - 1;
				}
			}
			if (Input.GetKeyDown(cycleTweaksPreviousKey.Value))
			{
				currentTweakIndex++;
				if (currentTweakIndex >= tweaks.Count)
				{
					currentTweakIndex = 0;
				}
			}
			if (Input.GetKeyDown(reloadTweaksStateKey.Value))
			{
				ReloadAllTweaksStates();
			}
		}

		public static void AddTweak(TweakBase tweak)
		{
			Instance.tweaks.Add(tweak);
		}

		internal void DisableAll()
		{
			((BaseUnityPlugin)this).Logger.LogDebug((object)"Disabling all tweaks.");
			foreach (TweakBase tweak in tweaks)
			{
				tweak.Disable();
			}
		}

		internal void ReloadAllTweaksStates()
		{
			TweakConfig.Reload();
			((BaseUnityPlugin)this).Logger.LogDebug((object)"Reloading all tweaks states.");
			foreach (TweakBase tweak in tweaks)
			{
				tweak.Reload();
			}
		}

		private void InitConfigs()
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			toggleCurrentTweakKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "toggleCurrentTweak", (KeyCode)92, (ConfigDescription)null);
			cycleTweaksNextKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "cycleTweaksNext", (KeyCode)91, (ConfigDescription)null);
			cycleTweaksPreviousKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "cycleTweaksPrevious", (KeyCode)93, (ConfigDescription)null);
			reloadTweaksStateKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Keybinds", "reloadTweaksState", (KeyCode)8, (ConfigDescription)null);
			((BaseUnityPlugin)this).Config.Bind<string>("General", "headerGeneral", "General:", new ConfigDescription("", (AcceptableValueBase)null, new object[1] { "modmenu_header" }));
			enableLiveTweaking = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "enableLiveTweaking", false, (ConfigDescription)null);
			restoreConfigsOnEnteringLobby = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "restoreConfigsOnEnteringLobby", true, (ConfigDescription)null);
		}
	}
	public static class PluginInfos
	{
		public const string PLUGIN_NAME = "LLBTweaker";

		public const string PLUGIN_ID = "fr.glomzubuk.plugins.llb.llbtweaker";

		public const string PLUGIN_VERSION = "0.1.4";
	}
	internal enum LLBT_MSGCODES : ushort
	{
		CHECKIN = 380,
		CHECKIN_ACK,
		UNKNOWN_TWEAKS,
		ABORT
	}
	internal static class Network
	{
		private static readonly ManualLogSource Logger = LLBTweaker.Log;

		private static bool[] playerStatuses = new bool[4];

		private static bool InVanillaLobby = true;

		internal static void Init()
		{
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Expected O, but got Unknown
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Expected O, but got Unknown
			PluginInfo info = ((BaseUnityPlugin)LLBTweaker.Instance).Info;
			PlayerLobbyState.RegisterPayload(info, (Func<PlayerLobbyState, byte[]>)OnSendLobbyState, (Action<PlayerLobbyState, byte[]>)OnReceiveLobbyState);
			MessageApi.RegisterCustomMessage(info, (ushort)380, "LLBTCheckIn", (Action<Message>)ReceiveCheckIn);
			MessageApi.RegisterCustomMessage(info, (ushort)381, "LLBTCheckInAck", (Action<Message>)ReceiveCheckInAck);
			MessageApi.RegisterCustomMessage(info, (ushort)382, "LLBTUnknownTweaks", (Action<Message>)ReceiveUnknownTweaks);
			MessageApi.RegisterCustomMessage(info, (ushort)383, "LLBTAbort", (Action<Message>)ReceiveAbort);
			LobbyEvents.OnLobbyEntered += new OnLobbyEnteredHandler(OnLobbyEnteredHandler);
			LobbyEvents.OnLobbyReady += new OnLobbyReadyHandler(OnLobbyReadyHandler);
			LobbyEvents.OnStageSelectOpen += new OnStageSelectOpenHandler(OnStageSelectOpened);
		}

		internal static void OnUpdate()
		{
			LLBTweaker.Instance.showVanillaWarning = !AllPeersTweaked();
		}

		internal static bool AllPeersTweaked()
		{
			if (P2P.localPeer == null)
			{
				return true;
			}
			bool allTweaked = true;
			((Peer)P2P.localPeer).ForAllOthers((Action<Peer>)delegate(Peer peer)
			{
				PlayerStatus playerStatus = Player.GetPlayer(peer.playerNr).playerStatus;
				if ((EnumWrapper<AKNMBDLMNJM>)(object)playerStatus != (EnumWrapper<AKNMBDLMNJM>)(object)PlayerStatus.NONE && (EnumWrapper<AKNMBDLMNJM>)(object)playerStatus != (EnumWrapper<AKNMBDLMNJM>)(object)PlayerStatus.DISCONNECTED && !playerStatuses[peer.playerNr])
				{
					allTweaked = false;
				}
			});
			return allTweaked;
		}

		internal static byte[] OnSendLobbyState(PlayerLobbyState pls)
		{
			byte[] result = null;
			if (Player.GetPlayer(pls.playerNr).peer is LocalHost)
			{
				Dictionary<string, TweakConfigJson> dictionary = new Dictionary<string, TweakConfigJson>();
				foreach (TweakBase item in LLBTweaker.Instance.tweaks.Where((TweakBase tweak) => tweak.IsEnabled))
				{
					dictionary.Add(item.ID, item.ConfigToJson());
				}
				using MemoryStream memoryStream = new MemoryStream();
				using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream))
				{
					string text = JSONWriter.ToJson((object)dictionary);
					Logger.LogDebug((object)("Sending Lobby State: " + text));
					binaryWriter.Write(text);
				}
				result = memoryStream.ToArray();
			}
			return result;
		}

		internal static void OnReceiveLobbyState(PlayerLobbyState pls, byte[] payload)
		{
			List<string> list = new List<string>();
			using (MemoryStream input = new MemoryStream(payload))
			{
				using BinaryReader binaryReader = new BinaryReader(input);
				LLBTweaker.Instance.DisableAll();
				string text = binaryReader.ReadString();
				Logger.LogDebug((object)("Receiving LobbyState: " + text));
				foreach (KeyValuePair<string, TweakConfigJson> jTweakConfig in JSONParser.FromJson<Dictionary<string, TweakConfigJson>>(text))
				{
					Logger.LogDebug((object)$"{jTweakConfig.Key}:\n{jTweakConfig.Value}");
					TweakBase tweakBase = LLBTweaker.Instance.tweaks.Find((TweakBase _tweak) => _tweak.ID == jTweakConfig.Key);
					if (tweakBase != null)
					{
						tweakBase.LoadJsonConfig(jTweakConfig.Value);
					}
					else
					{
						list.Add(jTweakConfig.Key);
					}
				}
			}
			if (list.Count > 0)
			{
				SendUnkownTweaksMessage(list);
			}
		}

		internal static void OnLobbyEnteredHandler(object source, LobbyEventArgs args)
		{
			if (LLBTweaker.Instance.restoreConfigsOnEnteringLobby.Value)
			{
				LLBTweaker.Instance.ReloadAllTweaksStates();
			}
			ArrayExtension.Fill<bool>(playerStatuses, false);
			InVanillaLobby = true;
		}

		internal static void OnLobbyReadyHandler(object source, LobbyReadyArgs args)
		{
			if (args.isOnline)
			{
				SendCheckIn();
			}
		}

		internal static void OnStageSelectOpened(HDLIJDBFGKN source, OnStageSelectOpenArgs args)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			if (NetworkApi.IsOnline)
			{
				if (InVanillaLobby)
				{
					Logger.LogDebug((object)"Entering Stage Select and current lobby is vanilla.");
					LLBTweaker.Instance.DisableAll();
				}
				else if (Player.GetLocalPlayer().peer is LocalHost && !AllPeersTweaked())
				{
					P2P.SendAll(new Message((Msg)383, 0, -1, (object)null, -1));
				}
			}
		}

		internal static void SendCheckIn()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			int playerNr = ((Peer)P2P.localPeer).playerNr;
			Logger.LogDebug((object)$"[P{playerNr}] Sending CheckIn to P0.");
			P2P.SendToPlayerNr(0, new Message((Msg)380, playerNr, 0, (object)null, -1));
		}

		internal static void ReceiveCheckIn(Message payload)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			int playerNr = ((Peer)P2P.localPeer).playerNr;
			int playerNr2 = payload.playerNr;
			Logger.LogDebug((object)$"[P{playerNr}] Received CheckIn from P{playerNr2}.");
			playerStatuses[playerNr2] = true;
			Logger.LogDebug((object)$"[P{playerNr}] Sending CheckInAck to P{playerNr2}.");
			P2P.SendToPlayerNr(playerNr2, new Message((Msg)381, playerNr, playerNr2, (object)null, -1));
		}

		internal static void ReceiveCheckInAck(Message payload)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			int playerNr = ((Peer)P2P.localPeer).playerNr;
			int playerNr2 = payload.playerNr;
			Logger.LogDebug((object)$"[P{playerNr}] Received CheckInAck from P{playerNr2}.");
			InVanillaLobby = false;
		}

		internal static void SendUnkownTweaksMessage(List<string> unknownTweaks)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			int playerNr = ((Peer)P2P.localPeer).playerNr;
			string text = JSONWriter.ToJson((object)unknownTweaks);
			Logger.LogDebug((object)$"[P{playerNr}] Sending UnkownTweaks to P0 with content:\n{text}");
			byte[] bytes = Encoding.ASCII.GetBytes(text);
			P2P.SendToPlayerNr(0, new Message((Msg)382, ((Peer)P2P.localPeer).playerNr, 0, (object)bytes, bytes.Length));
		}

		internal static void ReceiveUnknownTweaks(Message payload)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			if (payload.ob == null)
			{
				LLBTweaker.Log.LogWarning((object)"Received a null payload on an UnkownTweaks message. Ignoring.");
				return;
			}
			int playerNr = ((Peer)P2P.localPeer).playerNr;
			int playerNr2 = payload.playerNr;
			string @string = Encoding.ASCII.GetString(payload.ob as byte[]);
			Logger.LogDebug((object)$"[P{playerNr}] Received UnknownTweaks from P{playerNr2} with content:\n{@string}");
			List<string> missingTweakIDs = JSONParser.FromJson<List<string>>(@string);
			GameStatesLobbyUtils.MakeSureReadyIs(false, true);
			TmpRemoveMissingTweaks(missingTweakIDs);
			GameStatesLobbyUtils.SendPlayerState(Player.GetLocalPlayer());
		}

		internal static void TmpRemoveMissingTweaks(List<string> missingTweakIDs)
		{
			foreach (TweakBase tweak in LLBTweaker.Instance.tweaks)
			{
				if (missingTweakIDs.Contains(tweak.ID))
				{
					Logger.LogDebug((object)("Unpatching " + tweak.Name + " on request."));
					tweak.Disable();
				}
			}
		}

		internal static void ReceiveAbort(Message payload)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			int playerNr = ((Peer)P2P.localPeer).playerNr;
			int playerNr2 = payload.playerNr;
			Logger.LogDebug((object)$"[P{playerNr}] Received Abort from P{playerNr2}.");
			LLBTweaker.Instance.DisableAll();
		}
	}
}
namespace LLBT.Tweaks
{
	public abstract class TweakBase
	{
		protected ConfigEntry<bool> tweakEnabled;

		public Dictionary<ConfigDefinition, ConfigEntryBase> tweakConfigs;

		public static ManualLogSource Logger => LLBTweaker.TweakLog;

		protected static ConfigFile Config => LLBTweaker.Instance.TweakConfig;

		public string ID { get; protected set; }

		public string Name { get; protected set; }

		public bool IsEnabled { get; protected set; }

		protected TweakBase(string tweakID, string tweakName)
		{
			ID = tweakID;
			Name = tweakName;
			tweakEnabled = Config.Bind<bool>("TweakToggles", tweakID, true, (ConfigDescription)null);
			tweakConfigs = new Dictionary<ConfigDefinition, ConfigEntryBase>();
			Logger.LogInfo((object)(ID + " settedUp"));
		}

		protected abstract void DoPatch();

		protected abstract void DoUnpatch();

		public bool ShouldPatch()
		{
			if (!IsEnabled)
			{
				return tweakEnabled.Value;
			}
			return false;
		}

		public bool ShouldUnpatch()
		{
			if (IsEnabled)
			{
				return !tweakEnabled.Value;
			}
			return false;
		}

		public void UpdateState()
		{
			if (ShouldPatch())
			{
				DoPatch();
			}
			if (ShouldUnpatch())
			{
				DoUnpatch();
			}
		}

		public void Enable()
		{
			tweakEnabled.Value = true;
			UpdateState();
		}

		public void Disable()
		{
			tweakEnabled.Value = false;
			UpdateState();
		}

		public void Toggle()
		{
			tweakEnabled.Value = !tweakEnabled.Value;
			UpdateState();
		}

		public void Reload()
		{
			DoUnpatch();
			UpdateState();
		}

		public void AddConfig(ConfigEntryBase config)
		{
			tweakConfigs.Add(config.Definition, config);
		}

		public TweakConfigJson ConfigToJson()
		{
			TweakConfigJson tweakConfigJson = default(TweakConfigJson);
			tweakConfigJson.enabled = tweakEnabled.Value;
			TweakConfigJson result = tweakConfigJson;
			List<ConfigEntryJson> list = new List<ConfigEntryJson>();
			foreach (ConfigEntryBase value in tweakConfigs.Values)
			{
				list.Add(new ConfigEntryJson
				{
					Key = value.Definition.Key,
					Section = value.Definition.Section,
					value = value.GetSerializedValue()
				});
			}
			result.configs = list;
			return result;
		}

		public void LoadJsonConfig(TweakConfigJson jTweakObject)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			tweakEnabled.Value = jTweakObject.enabled;
			foreach (ConfigEntryJson config in jTweakObject.configs)
			{
				ConfigDefinition val = new ConfigDefinition(config.Section, config.Key);
				Config[val].SetSerializedValue(config.value);
			}
			Reload();
		}
	}
	public struct TweakConfigJson
	{
		public bool enabled;

		public List<ConfigEntryJson> configs;

		public override string ToString()
		{
			return $"enabled: {enabled}\n" + "Configs:\n\t" + GeneralExtensions.Join<ConfigEntryJson>((IEnumerable<ConfigEntryJson>)configs, (Func<ConfigEntryJson, string>)((ConfigEntryJson ce) => ce.ToString()), "\n\t");
		}
	}
	public struct ConfigEntryJson
	{
		public string Section;

		public string Key;

		public string value;

		public override string ToString()
		{
			return "[" + Section + "." + Key + "]: " + value;
		}
	}
	public class HarmonyTweak : TweakBase
	{
		protected List<Type> patchClasses;

		private readonly Harmony harmonyInstance;

		public static HarmonyTweak Create(Type patchClass)
		{
			string tweakID = "HarmonyTweak_" + patchClass.Name;
			string name = patchClass.Name;
			HarmonyTweak harmonyTweak = new HarmonyTweak(tweakID, name);
			harmonyTweak.AddPatchClass(patchClass);
			return harmonyTweak;
		}

		public HarmonyTweak(string tweakID, string tweakName, Harmony harmonyInstance = null)
			: base(tweakID, tweakName)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			this.harmonyInstance = (Harmony)(((object)harmonyInstance) ?? ((object)new Harmony(tweakID)));
			patchClasses = new List<Type>();
		}

		protected void AddPatchClass(Type patchClass, bool addConfig = false)
		{
			patchClasses.Add(patchClass);
			if (addConfig)
			{
				ConfigEntry<bool> config = TweakBase.Config.Bind<bool>("PatchToggles." + base.ID, patchClass.Name, true, (ConfigDescription)null);
				AddConfig((ConfigEntryBase)(object)config);
			}
		}

		protected override void DoPatch()
		{
			if (base.IsEnabled)
			{
				return;
			}
			TweakBase.Logger.LogDebug((object)("Applying " + base.Name));
			foreach (Type patchClass in patchClasses)
			{
				harmonyInstance.PatchAll(patchClass);
			}
			base.IsEnabled = true;
		}

		protected override void DoUnpatch()
		{
			if (base.IsEnabled)
			{
				TweakBase.Logger.LogDebug((object)("Unpatching " + base.Name));
				harmonyInstance.UnpatchSelf();
				base.IsEnabled = false;
			}
		}
	}
	public class FieldTweak<TClass, TField> : TweakBase where TClass : class
	{
		protected readonly FieldRef<TClass, TField> fieldRef;

		protected readonly TField newValue;

		protected TField initialValue;

		protected TClass instance;

		public static FieldTweak<_TClass, _TField> Create<_TClass, _TField>(FieldInfo _fieldInfo, _TField _newValue) where _TClass : class
		{
			string patchID = "FieldPatch_" + _fieldInfo.Name;
			string name = _fieldInfo.Name;
			return new FieldTweak<_TClass, _TField>(patchID, name, _fieldInfo, _newValue);
		}

		public static FieldTweak<_TClass, _TField> Create<_TClass, _TField>(FieldInfo _fieldInfo, _TField _newValue, _TClass _instance) where _TClass : class
		{
			string patchID = "FieldPatch_" + _fieldInfo.Name;
			string name = _fieldInfo.Name;
			return new FieldTweak<_TClass, _TField>(patchID, name, _fieldInfo, _newValue, _instance);
		}

		public FieldTweak(string _patchID, string _patchName, FieldInfo _fieldInfo, TField _newValue, TClass _instance = null)
			: base(_patchID, _patchName)
		{
			fieldRef = AccessTools.FieldRefAccess<TClass, TField>(_fieldInfo);
			newValue = _newValue;
			instance = _instance;
		}

		public void SetInstance(TClass _instance)
		{
			instance = _instance;
		}

		public TField GetFieldValue()
		{
			return fieldRef.Invoke(instance);
		}

		protected override void DoPatch()
		{
			if (!base.IsEnabled && instance != null)
			{
				try
				{
					TField val = fieldRef.Invoke(instance);
					initialValue = val;
					val = newValue;
				}
				catch (Exception)
				{
				}
			}
			throw new NotImplementedException();
		}

		protected override void DoUnpatch()
		{
			throw new NotImplementedException();
		}
	}
}