Decompiled source of EpicLootChestBridge v1.0.0

EpicLootChestBridge.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("EpicLootChestBridge")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("EpicLootChestBridge")]
[assembly: AssemblyTitle("EpicLootChestBridge")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace EpicLootChestBridge;

[BepInPlugin("burksheim.epiclootchestbridge", "Epic Loot Chest Bridge", "1.0.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public sealed class EpicLootChestBridgePlugin : BaseUnityPlugin
{
	public const string PluginGuid = "burksheim.epiclootchestbridge";

	public const string PluginName = "Epic Loot Chest Bridge";

	public const string PluginVersion = "1.0.0";

	public const string EpicLootGuid = "randyknapp.mods.epicloot";

	public const string ValheimPlusGuid = "org.bepinex.plugins.valheim_plus";

	private const string ProviderId = "ValheimPlus.CraftFromChest";

	private ConfigEntry<bool> _enabled;

	private readonly ValheimPlusChests _chests = new ValheimPlusChests();

	private MethodInfo _unregister;

	private bool _registered;

	private MethodInfo _tableVisible;

	private Func<object, object> _tableInstance;

	private Func<object, object> _tableSource;

	private FieldInfo _localPlayer;

	private void Awake()
	{
		_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Let the Epic Loot enchanting table use materials from chests in Valheim Plus's Craft From Chest range.");
		Type type = Reflect.FindType("EpicLoot.API");
		Type type2 = Reflect.FindType("EpicLoot_UnityLib.EnchantingTableUI");
		Type type3 = Reflect.FindType("Player");
		if (type == null || type2 == null)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Epic Loot not found; nothing to do.");
		}
		else if (!(type3 == null) && _chests.TryResolve(((BaseUnityPlugin)this).Logger))
		{
			_tableVisible = Reflect.Method(type2, "IsVisible");
			_tableInstance = Reflect.Getter(type2, "instance");
			_tableSource = Reflect.Getter(type2, "SourceTable");
			_localPlayer = Reflect.Field(type3, "m_localPlayer");
			if (_tableVisible == null || _localPlayer == null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"This Epic Loot version's enchanting table can't be detected; bridge disabled.");
			}
			else
			{
				Register(type);
			}
		}
	}

	private void Register(Type api)
	{
		MethodInfo method = api.GetMethod("RegisterInventoryProvider", BindingFlags.Static | BindingFlags.Public);
		_unregister = api.GetMethod("UnregisterInventoryProvider", BindingFlags.Static | BindingFlags.Public);
		ParameterInfo[] array = method?.GetParameters();
		if (array == null || array.Length != 5 || array[0].ParameterType != typeof(string))
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Epic Loot's inventory provider API is missing or changed (needs Epic Loot 0.14+); bridge disabled.");
			return;
		}
		Type type = array[4].ParameterType.GetGenericArguments()[0];
		Type typeFromHandle = typeof(EpicLootChestBridgePlugin);
		Delegate obj = Delegate.CreateDelegate(array[1].ParameterType, typeFromHandle.GetMethod("NoItems", BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(type));
		Delegate obj2 = Delegate.CreateDelegate(array[2].ParameterType, this, typeFromHandle.GetMethod("CountItem", BindingFlags.Instance | BindingFlags.NonPublic));
		Delegate obj3 = Delegate.CreateDelegate(array[3].ParameterType, this, typeFromHandle.GetMethod("RemoveItem", BindingFlags.Instance | BindingFlags.NonPublic));
		Delegate obj4 = Delegate.CreateDelegate(array[4].ParameterType, typeFromHandle.GetMethod("RemoveNothing", BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(type));
		_registered = (bool)method.Invoke(null, new object[5] { "ValheimPlus.CraftFromChest", obj, obj2, obj3, obj4 });
		if (_registered)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Enchanting table can now use materials from Valheim Plus Craft From Chest chests.");
		}
		else
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)"Epic Loot refused the chest provider registration.");
		}
	}

	private static List<T> NoItems<T>()
	{
		return new List<T>();
	}

	private static int RemoveNothing<T>(T item, int amount)
	{
		return 0;
	}

	private int CountItem(string itemName)
	{
		if (!Active(out var origin))
		{
			return 0;
		}
		return _chests.Count(origin, itemName);
	}

	private int RemoveItem(string itemName, int amount)
	{
		if (amount <= 0 || !Active(out var origin))
		{
			return 0;
		}
		int num = _chests.Remove(origin, itemName, amount);
		if (num > 0)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Took " + num + " x " + itemName + " from nearby chests."));
		}
		return num;
	}

	private bool Active(out GameObject origin)
	{
		origin = null;
		if (!_enabled.Value || !_chests.Enabled)
		{
			return false;
		}
		if (!(bool)_tableVisible.Invoke(null, null))
		{
			return false;
		}
		object? value = _localPlayer.GetValue(null);
		Component val = (Component)((value is Component) ? value : null);
		if ((Object)(object)val == (Object)null)
		{
			return false;
		}
		origin = val.gameObject;
		if (_chests.CheckFromWorkbench && _tableInstance != null && _tableSource != null)
		{
			object obj = _tableInstance(null);
			Component val2 = (Component)((obj != null) ? /*isinst with value type is only supported in some contexts*/: null);
			if ((Object)(object)val2 != (Object)null)
			{
				origin = val2.gameObject;
			}
		}
		return true;
	}

	private void OnDestroy()
	{
		if (_registered)
		{
			_unregister?.Invoke(null, new object[1] { "ValheimPlus.CraftFromChest" });
		}
	}
}
internal sealed class ValheimPlusChests
{
	private Func<object, object> _current;

	private Func<object, object> _section;

	private Func<object, object> _isEnabled;

	private Func<object, object> _range;

	private Func<object, object> _checkFromWorkbench;

	private Func<object, object> _ignoreWards;

	private Func<object, object> _lookupInterval;

	private MethodInfo _getNearbyChests;

	private MethodInfo _chestItems;

	private MethodInfo _removeFromChest;

	private FieldInfo _itemShared;

	private FieldInfo _sharedName;

	private FieldInfo _itemStack;

	private IList _cachedChests;

	private GameObject _cachedOrigin;

	private float _cachedAt = float.MinValue;

	public bool Enabled => Get(_isEnabled, fallback: false);

	public bool CheckFromWorkbench => Get(_checkFromWorkbench, fallback: false);

	public bool TryResolve(ManualLogSource log)
	{
		Type type = Reflect.FindType("ValheimPlus.Configurations.Configuration");
		Type type2 = Reflect.FindType("ValheimPlus.InventoryAssistant");
		Type type3 = Reflect.FindType("ItemDrop+ItemData");
		Type type4 = Reflect.FindType("Container");
		if (type == null || type2 == null)
		{
			log.LogWarning((object)"Valheim Plus not found; nothing to do.");
			return false;
		}
		_current = Reflect.Getter(type, "Current");
		_section = Reflect.Getter(type, "CraftFromChest");
		Type type5 = Reflect.FindType("ValheimPlus.Configurations.Sections.CraftFromChestConfiguration");
		if (type5 != null)
		{
			_isEnabled = Reflect.Getter(type5, "IsEnabled");
			_range = Reflect.Getter(type5, "range");
			_checkFromWorkbench = Reflect.Getter(type5, "checkFromWorkbench");
			_ignoreWards = Reflect.Getter(type5, "ignorePrivateAreaCheck");
			_lookupInterval = Reflect.Getter(type5, "lookupInterval");
		}
		_getNearbyChests = Reflect.Method(type2, "GetNearbyChests", typeof(GameObject), typeof(float), typeof(bool), typeof(bool));
		if (type3 != null)
		{
			MethodInfo[] methods = type2.GetMethods(BindingFlags.Static | BindingFlags.Public);
			foreach (MethodInfo methodInfo in methods)
			{
				ParameterInfo[] parameters = methodInfo.GetParameters();
				if (methodInfo.Name == "GetNearbyChestItemsByContainerList" && parameters.Length == 1)
				{
					_chestItems = methodInfo;
				}
				if (methodInfo.Name == "RemoveItemFromChest" && parameters.Length == 3 && parameters[1].ParameterType == typeof(string) && parameters[2].ParameterType == typeof(int) && (type4 == null || parameters[0].ParameterType == type4))
				{
					_removeFromChest = methodInfo;
				}
			}
			_itemShared = Reflect.Field(type3, "m_shared");
			_itemStack = Reflect.Field(type3, "m_stack");
			if (_itemShared != null)
			{
				_sharedName = Reflect.Field(_itemShared.FieldType, "m_name");
			}
		}
		if (_current == null || _section == null || _isEnabled == null || _range == null || _getNearbyChests == null || _chestItems == null || _removeFromChest == null || _sharedName == null || _itemStack == null)
		{
			log.LogWarning((object)"This Valheim Plus version's Craft From Chest API is different than expected; bridge disabled.");
			return false;
		}
		return true;
	}

	private object Section()
	{
		object obj = _current(null);
		if (obj == null)
		{
			return null;
		}
		return _section(obj);
	}

	private T Get<T>(Func<object, object> getter, T fallback)
	{
		object obj = Section();
		if (obj == null || getter == null)
		{
			return fallback;
		}
		object obj2 = getter(obj);
		if (obj2 is T)
		{
			return (T)obj2;
		}
		return fallback;
	}

	private IList Chests(GameObject origin, bool forceRefresh)
	{
		float num = Math.Max(1, Math.Min(10, Get(_lookupInterval, 3)));
		if (!forceRefresh && _cachedChests != null && (Object)(object)_cachedOrigin == (Object)(object)origin && Time.time - _cachedAt < num)
		{
			return _cachedChests;
		}
		float num2 = Math.Max(1f, Math.Min(50f, Get(_range, 20f)));
		bool flag = !Get(_ignoreWards, fallback: false);
		_cachedChests = _getNearbyChests.Invoke(null, new object[4] { origin, num2, flag, true }) as IList;
		_cachedOrigin = origin;
		_cachedAt = Time.time;
		return _cachedChests;
	}

	public int Count(GameObject origin, string itemName)
	{
		IList list = Chests(origin, forceRefresh: false);
		if (list == null || list.Count == 0)
		{
			return 0;
		}
		if (!(_chestItems.Invoke(null, new object[1] { list }) is IList list2))
		{
			return 0;
		}
		int num = 0;
		foreach (object item in list2)
		{
			object obj = ((item != null) ? _itemShared.GetValue(item) : null);
			if (obj != null && (string)_sharedName.GetValue(obj) == itemName)
			{
				num += (int)_itemStack.GetValue(item);
			}
		}
		return num;
	}

	public int Remove(GameObject origin, string itemName, int amount)
	{
		IList list = Chests(origin, forceRefresh: true);
		if (list == null)
		{
			return 0;
		}
		int num = amount;
		foreach (object item in list)
		{
			if (num <= 0)
			{
				break;
			}
			Object val = (Object)((item is Object) ? item : null);
			if (val == null || !(val == (Object)null))
			{
				num -= (int)_removeFromChest.Invoke(null, new object[3] { item, itemName, num });
			}
		}
		_cachedAt = float.MinValue;
		return amount - num;
	}
}
internal static class Reflect
{
	private const BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

	public static Type FindType(string fullName)
	{
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		foreach (Assembly assembly in assemblies)
		{
			Type type = null;
			try
			{
				type = assembly.GetType(fullName, throwOnError: false);
			}
			catch (Exception)
			{
			}
			if (type != null)
			{
				return type;
			}
		}
		return null;
	}

	public static FieldInfo Field(Type type, string name)
	{
		Type type2 = type;
		while (type2 != null)
		{
			FieldInfo field = type2.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				return field;
			}
			type2 = type2.BaseType;
		}
		return null;
	}

	public static Func<object, object> Getter(Type type, string name)
	{
		Type type2 = type;
		while (type2 != null)
		{
			PropertyInfo property = type2.GetProperty(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.GetIndexParameters().Length == 0 && property.GetGetMethod(nonPublic: true) != null)
			{
				MethodInfo g = property.GetGetMethod(nonPublic: true);
				return (object o) => g.Invoke(g.IsStatic ? null : o, null);
			}
			FieldInfo f = type2.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (f != null)
			{
				return (object o) => f.GetValue(f.IsStatic ? null : o);
			}
			type2 = type2.BaseType;
		}
		return null;
	}

	public static MethodInfo Method(Type type, string name, params Type[] parameters)
	{
		Type type2 = type;
		while (type2 != null)
		{
			MethodInfo method = type2.GetMethod(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, parameters, null);
			if (method != null)
			{
				return method;
			}
			type2 = type2.BaseType;
		}
		return null;
	}
}