Decompiled source of Infinite Torch v1.0.0

plugins/InfiniteTorch.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using HarmonyLib;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("BenjSammy")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Unlimited durability for handheld Valheim torches only.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Infinite Torch by BenjSammy")]
[assembly: AssemblyTitle("InfiniteTorch")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 BenjSammy.InfiniteTorch
{
	[BepInPlugin("benjsammy.infinitetorch", "Infinite Torch by BenjSammy", "1.0.0")]
	public sealed class InfiniteTorchPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "benjsammy.infinitetorch";

		public const string PluginName = "Infinite Torch by BenjSammy";

		public const string PluginVersion = "1.0.0";

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			_harmony = new Harmony("benjsammy.infinitetorch");
			try
			{
				Type type = AccessTools.TypeByName("ItemDrop");
				Type itemData = type?.GetNestedType("ItemData", BindingFlags.Public | BindingFlags.NonPublic);
				Type humanoid = AccessTools.TypeByName("Humanoid");
				Type inventory = AccessTools.TypeByName("Inventory");
				int num = TorchPatches.Install(_harmony, itemData, humanoid, inventory, type, delegate(string message)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)message);
				});
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Infinite Torch 1.0.0 by BenjSammy active (" + num + " hooks). Handheld torches only. Install on every player's client; server-only installation is insufficient."));
			}
			catch (Exception ex)
			{
				_harmony.UnpatchSelf();
				TorchPatches.Reset();
				((BaseUnityPlugin)this).Logger.LogError((object)("Infinite Torch could not start: incompatible game API or missing dependency. " + ex));
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			TorchPatches.Reset();
		}
	}
	internal sealed class TorchController
	{
		private const BindingFlags Fields = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private readonly Type _itemDataType;

		private readonly FieldInfo _shared;

		private readonly FieldInfo _itemType;

		private readonly FieldInfo _durability;

		private readonly FieldInfo _usesDurability;

		private readonly FieldInfo _timeDrain;

		private readonly FieldInfo _attackDrain;

		private readonly MethodInfo _getMaxDurability;

		private readonly object _torchType;

		internal TorchController(Type itemDataType)
		{
			_itemDataType = itemDataType ?? throw new TypeLoadException("ItemDrop.ItemData was not found.");
			_shared = RequiredField(itemDataType, "m_shared");
			Type fieldType = _shared.FieldType;
			_itemType = RequiredField(fieldType, "m_itemType");
			if (!_itemType.FieldType.IsEnum || !Enum.IsDefined(_itemType.FieldType, "Torch"))
			{
				throw new MissingMemberException("ItemDrop.ItemData.ItemType.Torch was not found.");
			}
			_torchType = Enum.Parse(_itemType.FieldType, "Torch");
			_durability = RequiredField(itemDataType, "m_durability", typeof(float));
			_usesDurability = RequiredField(fieldType, "m_useDurability", typeof(bool));
			_timeDrain = RequiredField(fieldType, "m_durabilityDrain", typeof(float));
			_attackDrain = RequiredField(fieldType, "m_useDurabilityDrain", typeof(float));
			_getMaxDurability = itemDataType.GetMethod("GetMaxDurability", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
			if (_getMaxDurability == null || _getMaxDurability.ReturnType != typeof(float))
			{
				throw new MissingMethodException(itemDataType.FullName, "GetMaxDurability()");
			}
		}

		internal bool Protect(object item)
		{
			if (item == null || !_itemDataType.IsInstanceOfType(item))
			{
				return false;
			}
			object value = _shared.GetValue(item);
			if (value == null || !_torchType.Equals(_itemType.GetValue(value)))
			{
				return false;
			}
			float num = (float)_getMaxDurability.Invoke(item, null);
			if (float.IsNaN(num) || float.IsInfinity(num) || num <= 0f)
			{
				return false;
			}
			_durability.SetValue(item, num);
			_usesDurability.SetValue(value, false);
			_timeDrain.SetValue(value, 0f);
			_attackDrain.SetValue(value, 0f);
			return true;
		}

		private static FieldInfo RequiredField(Type type, string name, Type expectedType = null)
		{
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null || (expectedType != null && field.FieldType != expectedType))
			{
				throw new MissingFieldException(type.FullName, name);
			}
			return field;
		}
	}
	internal static class TorchPatches
	{
		private static TorchController _controller;

		private static FieldInfo _dropItem;

		private static MethodInfo _getAllItems;

		private static Action<string> _warning;

		private static bool _reportedError;

		internal static int Install(Harmony harmony, Type itemData, Type humanoid, Type inventory, Type itemDrop, Action<string> warning)
		{
			_controller = new TorchController(itemData);
			_warning = warning;
			_reportedError = false;
			if (humanoid == null)
			{
				throw new TypeLoadException("Humanoid was not found.");
			}
			MethodInfo[] array = WithItemFirstArgument(humanoid, "EquipItem", itemData);
			MethodInfo[] array2 = WithItemFirstArgument(humanoid, "DrainEquipedItemDurability", itemData);
			if (array.Length == 0)
			{
				throw new MissingMethodException("Humanoid.EquipItem(ItemData, ...)");
			}
			if (array2.Length == 0)
			{
				throw new MissingMethodException("Humanoid.DrainEquipedItemDurability(ItemData, ...)");
			}
			int num = 0;
			MethodInfo[] array3 = array;
			foreach (MethodInfo methodInfo in array3)
			{
				harmony.Patch((MethodBase)methodInfo, Hook("BeforeEquip"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				num++;
			}
			array3 = array2;
			foreach (MethodInfo methodInfo2 in array3)
			{
				harmony.Patch((MethodBase)methodInfo2, Hook("BeforeDrain"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				num++;
			}
			_dropItem = ((itemDrop == null) ? null : AccessTools.Field(itemDrop, "m_itemData"));
			MethodInfo methodInfo3 = ((itemDrop == null) ? null : AccessTools.DeclaredMethod(itemDrop, "Awake", Type.EmptyTypes, (Type[])null));
			if (_dropItem != null && _dropItem.FieldType == itemData && methodInfo3 != null)
			{
				harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, Hook("AfterItemAwake"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				num++;
			}
			else
			{
				warning?.Invoke("ItemDrop.Awake hook unavailable; handheld torches are still protected when equipped.");
			}
			_getAllItems = ((inventory == null) ? null : AccessTools.Method(inventory, "GetAllItems", Type.EmptyTypes, (Type[])null));
			MethodInfo[] array4 = ((inventory == null) ? new MethodInfo[0] : (from method in AccessTools.GetDeclaredMethods(inventory)
				where method.Name == "Load" && !method.IsStatic
				select method).ToArray());
			if (_getAllItems != null && typeof(IEnumerable).IsAssignableFrom(_getAllItems.ReturnType) && array4.Length != 0)
			{
				array3 = array4;
				foreach (MethodInfo methodInfo4 in array3)
				{
					harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, Hook("AfterInventoryLoad"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
			}
			else
			{
				warning?.Invoke("Inventory.Load hook unavailable; existing handheld torches are restored when equipped.");
			}
			return num;
		}

		private static MethodInfo[] WithItemFirstArgument(Type type, string name, Type itemData)
		{
			return (from method in AccessTools.GetDeclaredMethods(type)
				where method.Name == name && !method.IsStatic && method.GetParameters().Length != 0 && method.GetParameters()[0].ParameterType == itemData
				select method).ToArray();
		}

		private static HarmonyMethod Hook(string name)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			return new HarmonyMethod(typeof(TorchPatches).GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic));
		}

		private static bool ProtectSafely(object item)
		{
			try
			{
				return _controller != null && _controller.Protect(item);
			}
			catch (Exception exception)
			{
				Report(exception);
				return false;
			}
		}

		private static void BeforeEquip(object __0)
		{
			ProtectSafely(__0);
		}

		private static bool BeforeDrain(object __0)
		{
			return !ProtectSafely(__0);
		}

		private static void AfterItemAwake(object __instance)
		{
			try
			{
				if (__instance != null && _dropItem != null)
				{
					ProtectSafely(_dropItem.GetValue(__instance));
				}
			}
			catch (Exception exception)
			{
				Report(exception);
			}
		}

		private static void AfterInventoryLoad(object __instance)
		{
			try
			{
				if (__instance == null || _getAllItems == null || !(_getAllItems.Invoke(__instance, null) is IEnumerable enumerable))
				{
					return;
				}
				foreach (object item in enumerable)
				{
					ProtectSafely(item);
				}
			}
			catch (Exception exception)
			{
				Report(exception);
			}
		}

		private static void Report(Exception exception)
		{
			if (!_reportedError)
			{
				_reportedError = true;
				_warning?.Invoke("Infinite Torch encountered an incompatible item; default game behaviour is retained. " + exception);
			}
		}

		internal static void Reset()
		{
			_controller = null;
			_dropItem = null;
			_getAllItems = null;
			_warning = null;
			_reportedError = false;
		}
	}
}