Decompiled source of EpicLoot Craft From Chests v1.2.0

EpicLootCraftFromChests.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace EpicLootCraftFromChests;

[BepInPlugin("skiter.epicloot.craftfromchests", "EpicLoot Craft From Chests", "1.2.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
	public const string PluginGuid = "skiter.epicloot.craftfromchests";

	public const string PluginName = "EpicLoot Craft From Chests";

	public const string PluginVersion = "1.2.0";

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

	internal static ManualLogSource Log;

	internal static ConfigEntry<bool> Enabled;

	internal static ConfigEntry<float> Range;

	internal static ConfigEntry<bool> RespectWards;

	internal static ConfigEntry<bool> SkipChestsInUse;

	internal static ConfigEntry<bool> OnlyChestsIOwn;

	internal static ConfigEntry<bool> DebugLog;

	internal static readonly List<Container> KnownContainers = new List<Container>();

	private static FieldInfo _fNview;

	private static FieldInfo _fPiece;

	private static MethodInfo _mCheckAccess;

	private static MethodInfo _mTableIsVisible;

	private static PropertyInfo _pTableInstance;

	private static PropertyInfo _pTableSource;

	private Harmony _harmony;

	private void Awake()
	{
		//IL_0217: Unknown result type (might be due to invalid IL or missing references)
		//IL_0221: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "[Synced with Server] Let the Epic Loot enchanting table use materials from nearby chests.");
		Range = ((BaseUnityPlugin)this).Config.Bind<float>("General", "Range", 20f, "[Synced with Server] How far from the enchanting table (in meters) chests are searched.");
		RespectWards = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Respect wards", true, "[Synced with Server] Skip chests inside a ward you do not have access to.");
		SkipChestsInUse = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Skip chests in use", true, "[Synced with Server] Skip chests that someone currently has open.");
		OnlyChestsIOwn = ((BaseUnityPlugin)this).Config.Bind<bool>("Multiplayer", "Only chests I already own", false, "[Synced with Server] Safer but less convenient: only use chests whose network ownership already belongs to you (normally the ones nearest to you when nobody else is around). When off, the mod takes ownership of a chest right before taking items from it, the same way opening a chest does.");
		DebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Debug log", false, "Write every count and removal to the BepInEx log.");
		_fNview = AccessTools.Field(typeof(Container), "m_nview");
		_fPiece = AccessTools.Field(typeof(Container), "m_piece");
		_mCheckAccess = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null);
		if (_fNview == null || _fPiece == null || _mCheckAccess == null)
		{
			Log.LogError((object)"Could not find Container internals (Valheim update?). The mod stays disabled.");
			return;
		}
		Type type = AccessTools.TypeByName("EpicLoot.API");
		Type type2 = ((!(type == null)) ? type.Assembly.GetType("EpicLoot_UnityLib.EnchantingTableUI") : null);
		if (type2 == null)
		{
			type2 = AccessTools.TypeByName("EpicLoot_UnityLib.EnchantingTableUI");
		}
		if (type2 != null)
		{
			_mTableIsVisible = AccessTools.Method(type2, "IsVisible", (Type[])null, (Type[])null);
			_pTableInstance = AccessTools.Property(type2, "instance");
			_pTableSource = AccessTools.Property(type2, "SourceTable");
		}
		if (_mTableIsVisible == null)
		{
			Log.LogError((object)"Could not find Epic Loot's EnchantingTableUI. The mod stays disabled.");
			return;
		}
		_harmony = new Harmony("skiter.epicloot.craftfromchests");
		_harmony.PatchAll(typeof(ContainerPatches));
		_harmony.PatchAll(typeof(ServerSync));
		ServerSync.Init(this);
		RegisterWithEpicLoot();
	}

	private void Update()
	{
		ServerSync.Update();
	}

	private void OnDestroy()
	{
		ServerSync.Shutdown();
		try
		{
			Type type = AccessTools.TypeByName("EpicLoot.API");
			MethodInfo methodInfo = ((!(type == null)) ? AccessTools.Method(type, "UnregisterInventoryProvider", (Type[])null, (Type[])null) : null);
			if (methodInfo != null)
			{
				methodInfo.Invoke(null, new object[1] { "skiter.epicloot.craftfromchests" });
			}
		}
		catch (Exception)
		{
		}
		if (_harmony != null)
		{
			_harmony.UnpatchSelf();
		}
	}

	private static void RegisterWithEpicLoot()
	{
		Type type = AccessTools.TypeByName("EpicLoot.API");
		MethodInfo methodInfo = ((!(type == null)) ? AccessTools.Method(type, "RegisterInventoryProvider", (Type[])null, (Type[])null) : null);
		if (methodInfo == null)
		{
			Log.LogError((object)"This Epic Loot version has no RegisterInventoryProvider (needs 0.14.10 or newer). The mod stays disabled.");
			return;
		}
		object[] parameters = new object[5]
		{
			"skiter.epicloot.craftfromchests",
			null,
			new Func<string, int>(CountItem),
			new Func<string, int, int>(RemoveItem),
			null
		};
		object obj = methodInfo.Invoke(null, parameters);
		if (obj is bool && (bool)obj)
		{
			Log.LogInfo((object)"Registered with Epic Loot: the enchanting table can now use nearby chests.");
		}
		else
		{
			Log.LogError((object)"Epic Loot refused the registration (already registered?).");
		}
	}

	private static int CountItem(string itemName)
	{
		if (string.IsNullOrEmpty(itemName))
		{
			return 0;
		}
		int num = 0;
		foreach (Container usableContainer in GetUsableContainers())
		{
			Inventory inventory = usableContainer.GetInventory();
			if (inventory != null)
			{
				num += inventory.CountItems(itemName, -1, true);
			}
		}
		if (DebugLog.Value)
		{
			Log.LogInfo((object)("Count " + itemName + " in chests: " + num));
		}
		return num;
	}

	private static int RemoveItem(string itemName, int amount)
	{
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Expected O, but got Unknown
		if (string.IsNullOrEmpty(itemName) || amount <= 0)
		{
			return 0;
		}
		int num = amount;
		foreach (Container usableContainer in GetUsableContainers())
		{
			if (num <= 0)
			{
				break;
			}
			Inventory inventory = usableContainer.GetInventory();
			if (inventory == null)
			{
				continue;
			}
			int num2 = inventory.CountItems(itemName, -1, true);
			if (num2 <= 0)
			{
				continue;
			}
			ZNetView val = (ZNetView)_fNview.GetValue(usableContainer);
			if (!val.IsOwner())
			{
				val.ClaimOwnership();
				if (!val.IsOwner())
				{
					continue;
				}
			}
			int num3 = Math.Min(num2, num);
			inventory.RemoveItem(itemName, num3, -1, true);
			int num4 = num2 - inventory.CountItems(itemName, -1, true);
			num -= num4;
			if (DebugLog.Value)
			{
				Log.LogInfo((object)("Took " + num4 + "x " + itemName + " from " + usableContainer.m_name));
			}
		}
		return amount - num;
	}

	private static List<Container> GetUsableContainers()
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		List<Container> list = new List<Container>();
		Player localPlayer = Player.m_localPlayer;
		if (!ServerSync.Enabled || !Object.op_Implicit((Object)(object)localPlayer) || !IsTableOpen())
		{
			return list;
		}
		Vector3 tablePosition = GetTablePosition(localPlayer);
		float range = ServerSync.Range;
		long playerID = localPlayer.GetPlayerID();
		KnownContainers.RemoveAll((Container c) => !Object.op_Implicit((Object)(object)c));
		List<KeyValuePair<float, Container>> list2 = new List<KeyValuePair<float, Container>>();
		foreach (Container knownContainer in KnownContainers)
		{
			try
			{
				float num = Vector3.Distance(((Component)knownContainer).transform.position, tablePosition);
				if (num <= range && IsUsable(knownContainer, playerID))
				{
					list2.Add(new KeyValuePair<float, Container>(num, knownContainer));
				}
			}
			catch (Exception ex)
			{
				if (DebugLog.Value)
				{
					Log.LogWarning((object)("Skipped a chest: " + ex.Message));
				}
			}
		}
		list2.Sort((KeyValuePair<float, Container> a, KeyValuePair<float, Container> b) => a.Key.CompareTo(b.Key));
		foreach (KeyValuePair<float, Container> item in list2)
		{
			list.Add(item.Value);
		}
		return list;
	}

	private static bool IsUsable(Container container, long playerId)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Expected O, but got Unknown
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Expected O, but got Unknown
		//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
		ZNetView val = (ZNetView)_fNview.GetValue(container);
		if (!Object.op_Implicit((Object)(object)val) || !val.IsValid())
		{
			return false;
		}
		if (Object.op_Implicit((Object)(object)container.m_wagon) || Object.op_Implicit((Object)(object)container.m_rootObjectOverride))
		{
			return false;
		}
		Piece val2 = (Piece)_fPiece.GetValue(container);
		if (!Object.op_Implicit((Object)(object)val2) || !val2.IsPlacedByPlayer())
		{
			return false;
		}
		if (ServerSync.SkipChestsInUse && (container.IsInUse() || val.GetZDO().GetInt(ZDOVars.s_inUse, 0) == 1))
		{
			return false;
		}
		if (ServerSync.OnlyChestsIOwn && !val.IsOwner())
		{
			return false;
		}
		if (!(bool)_mCheckAccess.Invoke(container, new object[1] { playerId }))
		{
			return false;
		}
		if (ServerSync.RespectWards && container.m_checkGuardStone && !PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false))
		{
			return false;
		}
		return true;
	}

	private static bool IsTableOpen()
	{
		try
		{
			return (bool)_mTableIsVisible.Invoke(null, null);
		}
		catch (Exception)
		{
			return false;
		}
	}

	private static Vector3 GetTablePosition(Player player)
	{
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			object obj = ((!(_pTableInstance == null)) ? _pTableInstance.GetValue(null, null) : null);
			Component val = (Component)((obj != null && !(_pTableSource == null)) ? /*isinst with value type is only supported in some contexts*/: null);
			if (Object.op_Implicit((Object)(object)val))
			{
				return val.transform.position;
			}
		}
		catch (Exception)
		{
		}
		return ((Component)player).transform.position;
	}
}
[HarmonyPatch]
internal static class ContainerPatches
{
	private static int _addsSincePrune;

	[HarmonyPatch(typeof(Container), "Awake")]
	[HarmonyPostfix]
	private static void AwakePostfix(Container __instance)
	{
		ZNet instance = ZNet.instance;
		if (Object.op_Implicit((Object)(object)instance) && instance.IsDedicated())
		{
			return;
		}
		if (++_addsSincePrune >= 256)
		{
			_addsSincePrune = 0;
			Plugin.KnownContainers.RemoveAll((Container c) => !Object.op_Implicit((Object)(object)c));
		}
		if (Object.op_Implicit((Object)(object)__instance) && !Plugin.KnownContainers.Contains(__instance))
		{
			Plugin.KnownContainers.Add(__instance);
		}
	}
}
[HarmonyPatch]
internal static class ServerSync
{
	private const string RpcName = "skiter.epicloot.craftfromchests.config";

	private const int ProtocolVersion = 1;

	private static Plugin _plugin;

	private static FileSystemWatcher _watcher;

	private static volatile bool _fileChanged;

	private static bool _hasServerValues;

	private static bool _sEnabled;

	private static float _sRange;

	private static bool _sRespectWards;

	private static bool _sSkipChestsInUse;

	private static bool _sOnlyChestsIOwn;

	internal static bool Enabled => (!_hasServerValues) ? Plugin.Enabled.Value : _sEnabled;

	internal static float Range => (!_hasServerValues) ? Plugin.Range.Value : _sRange;

	internal static bool RespectWards => (!_hasServerValues) ? Plugin.RespectWards.Value : _sRespectWards;

	internal static bool SkipChestsInUse => (!_hasServerValues) ? Plugin.SkipChestsInUse.Value : _sSkipChestsInUse;

	internal static bool OnlyChestsIOwn => (!_hasServerValues) ? Plugin.OnlyChestsIOwn.Value : _sOnlyChestsIOwn;

	internal static void Init(Plugin plugin)
	{
		_plugin = plugin;
		((BaseUnityPlugin)plugin).Config.SettingChanged += delegate
		{
			BroadcastIfServer();
		};
		try
		{
			string configFilePath = ((BaseUnityPlugin)plugin).Config.ConfigFilePath;
			_watcher = new FileSystemWatcher(Path.GetDirectoryName(configFilePath), Path.GetFileName(configFilePath));
			_watcher.Changed += delegate
			{
				_fileChanged = true;
			};
			_watcher.Created += delegate
			{
				_fileChanged = true;
			};
			_watcher.Renamed += delegate
			{
				_fileChanged = true;
			};
			_watcher.IncludeSubdirectories = false;
			_watcher.EnableRaisingEvents = true;
		}
		catch (Exception ex)
		{
			Plugin.Log.LogWarning((object)("Config file watcher unavailable (" + ex.Message + "); edits to the .cfg will apply after a restart."));
		}
	}

	internal static void Update()
	{
		if (_fileChanged)
		{
			_fileChanged = false;
			try
			{
				((BaseUnityPlugin)_plugin).Config.Reload();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not reload the config: " + ex.Message));
				return;
			}
			BroadcastIfServer();
		}
	}

	internal static void Shutdown()
	{
		if (_watcher != null)
		{
			_watcher.EnableRaisingEvents = false;
			_watcher.Dispose();
			_watcher = null;
		}
	}

	private static ZPackage BuildPackage()
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Expected O, but got Unknown
		ZPackage val = new ZPackage();
		val.Write(1);
		val.Write(Plugin.Enabled.Value);
		val.Write(Plugin.Range.Value);
		val.Write(Plugin.RespectWards.Value);
		val.Write(Plugin.SkipChestsInUse.Value);
		val.Write(Plugin.OnlyChestsIOwn.Value);
		return val;
	}

	private static void SendTo(ZRpc rpc)
	{
		if (rpc != null)
		{
			rpc.Invoke("skiter.epicloot.craftfromchests.config", new object[1] { BuildPackage() });
		}
	}

	private static void BroadcastIfServer()
	{
		ZNet instance = ZNet.instance;
		if (!Object.op_Implicit((Object)(object)instance) || !instance.IsServer())
		{
			return;
		}
		foreach (ZNetPeer peer in instance.GetPeers())
		{
			if (peer != null && peer.m_rpc != null && peer.IsReady())
			{
				SendTo(peer.m_rpc);
			}
		}
	}

	private static void RPC_ReceiveConfig(ZRpc rpc, ZPackage pkg)
	{
		ZNet instance = ZNet.instance;
		if (!Object.op_Implicit((Object)(object)instance) || !instance.IsServer())
		{
			int num = pkg.ReadInt();
			if (num != 1)
			{
				Plugin.Log.LogWarning((object)("Server runs a different version of this mod (settings format " + num + "); using local settings. Update the mod on both sides."));
				return;
			}
			_sEnabled = pkg.ReadBool();
			_sRange = pkg.ReadSingle();
			_sRespectWards = pkg.ReadBool();
			_sSkipChestsInUse = pkg.ReadBool();
			_sOnlyChestsIOwn = pkg.ReadBool();
			_hasServerValues = true;
			Plugin.Log.LogInfo((object)("Using the server's settings: Enabled=" + _sEnabled + ", Range=" + _sRange + ", Respect wards=" + _sRespectWards + ", Skip chests in use=" + _sSkipChestsInUse + ", Only chests I already own=" + _sOnlyChestsIOwn));
		}
	}

	[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
	[HarmonyPostfix]
	private static void OnNewConnectionPostfix(ZNet __instance, ZNetPeer peer)
	{
		if (!__instance.IsServer() && peer != null && peer.m_rpc != null)
		{
			peer.m_rpc.Register<ZPackage>("skiter.epicloot.craftfromchests.config", (Action<ZRpc, ZPackage>)RPC_ReceiveConfig);
		}
	}

	[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
	[HarmonyPostfix]
	private static void PeerInfoPostfix(ZNet __instance, ZRpc rpc)
	{
		if (__instance.IsServer())
		{
			SendTo(rpc);
		}
	}

	[HarmonyPatch(typeof(ZNet), "OnDestroy")]
	[HarmonyPostfix]
	private static void ZNetDestroyPostfix()
	{
		_hasServerValues = false;
	}
}