Decompiled source of DeathChest v2.0.0

BepInEx/plugins/DeathChest/DeathChest.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.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("DeathChest")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("DeathChest")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("b8ff20b6-2efb-4726-91c3-fb3b0fc22a3e")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
[BepInPlugin("com.robertomontanas.muck.deathchest", "Death Chest", "2.0.0")]
public class DeathChestPlugin : BaseUnityPlugin
{
	private class DroppedItem
	{
		public int ItemId;

		public int Amount;

		public int ObjectId;

		public GameObject Object;

		public DroppedItem(int itemId, int amount, int objectId, GameObject droppedObject)
		{
			Object = droppedObject;
			ItemId = itemId;
			Amount = amount;
			ObjectId = objectId;
		}
	}

	private class DropBatch
	{
		public ItemManager Manager;

		public int ClientId;

		public float FirstDropTime;

		public float LastDropTime;

		public readonly List<DroppedItem> Items = new List<DroppedItem>();

		public DropBatch(int clientId, float time, ItemManager manager)
		{
			Manager = manager;
			ClientId = clientId;
			FirstDropTime = time;
			LastDropTime = time;
		}
	}

	private class ProtectedChest
	{
		public int ObjectId;

		public BuildDestruction BuildDestruction;

		public ProtectedChest(int objectId, BuildDestruction buildDestruction)
		{
			ObjectId = objectId;
			BuildDestruction = buildDestruction;
		}
	}

	private const string PluginGuid = "com.robertomontanas.muck.deathchest";

	private const string PluginName = "Death Chest";

	private const string PluginVersion = "2.0.0";

	private const float DeathTimeout = 2f;

	private const float DropBatchWindow = 0.05f;

	private const int DropRemovalRetries = 2;

	private const float DropRemovalRetryDelay = 0.1f;

	private const int ProtectedDeathChestCount = 16;

	private const int DeathChestExtraHP = 10000;

	private const int ChestSize = 21;

	private static DeathChestPlugin Instance;

	private Harmony harmony;

	private bool modActive;

	private int sessionSceneHandle = -1;

	private ItemManager sessionItemManager;

	private ConfigEntry<bool> instantRespawn;

	private DeathChestPacketHooks packetHooks;

	private readonly HashSet<int> queuedRespawns = new HashSet<int>();

	private readonly Dictionary<int, DropBatch> pendingDrops = new Dictionary<int, DropBatch>();

	private readonly Queue<ProtectedChest> protectedDeathChests = new Queue<ProtectedChest>();

	private void Awake()
	{
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Expected O, but got Unknown
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		Instance = this;
		instantRespawn = ((BaseUnityPlugin)this).Config.Bind<bool>("Testing", "InstantRespawn", false, "Automatically revive after death for testing. F7 toggles this setting.");
		packetHooks = new DeathChestPacketHooks(HandleDropPacket, HandleDeathPacket);
		harmony = new Harmony("com.robertomontanas.muck.deathchest");
		SceneManager.sceneLoaded += OnSceneLoaded;
		SceneManager.sceneUnloaded += OnSceneUnloaded;
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Death Chest v2.0.0 - host only, packet tracking, expandable chests, test respawn");
		OnSceneLoaded(SceneManager.GetActiveScene(), (LoadSceneMode)0);
	}

	private void InstallPatches()
	{
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Expected O, but got Unknown
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Expected O, but got Unknown
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Expected O, but got Unknown
		//IL_0185: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: Expected O, but got Unknown
		//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0207: Expected O, but got Unknown
		//IL_028a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0298: Expected O, but got Unknown
		//IL_032c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0339: Expected O, but got Unknown
		//IL_0398: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a5: Expected O, but got Unknown
		//IL_0405: Unknown result type (might be due to invalid IL or missing references)
		//IL_0413: Expected O, but got Unknown
		harmony.Patch((MethodBase)AccessTools.Method(typeof(Server), "InitializeServerPackets", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(DeathChestPlugin), "ServerPacketsInitializedPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)AccessTools.Method(typeof(GameManager), "CheckIfGameOver", (Type[])null, (Type[])null), new HarmonyMethod(typeof(DeathChestPlugin), "CheckGameOverPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		MethodInfo methodInfo = AccessTools.Method(typeof(ItemManager), "DropItem", new Type[4]
		{
			typeof(int),
			typeof(int),
			typeof(int),
			typeof(int)
		}, (Type[])null);
		if (methodInfo == null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"NO SE ENCONTRÓ ItemManager.DropItem(int, int, int, int).");
			throw new MissingMethodException("ItemManager.DropItem");
		}
		harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(DeathChestPlugin), "ItemManagerDropItemPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Hook instalado: ItemManager.DropItem(...)");
		harmony.Patch((MethodBase)AccessTools.Method(typeof(ServerSend), "DropItem", new Type[4]
		{
			typeof(int),
			typeof(int),
			typeof(int),
			typeof(int)
		}, (Type[])null), new HarmonyMethod(typeof(DeathChestPlugin), "ServerDropBroadcastPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		harmony.Patch((MethodBase)AccessTools.Method(typeof(ServerSend), "PlayerDied", new Type[4]
		{
			typeof(int),
			typeof(Vector3),
			typeof(Vector3),
			typeof(int)
		}, (Type[])null), new HarmonyMethod(typeof(DeathChestPlugin), "ServerDeathBroadcastPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		MethodInfo methodInfo2 = AccessTools.Method(typeof(Hitable), "Damage", new Type[4]
		{
			typeof(int),
			typeof(int),
			typeof(int),
			typeof(Vector3)
		}, (Type[])null);
		if (methodInfo2 == null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"NO SE ENCONTRÓ Hitable.Damage(int, int, int, Vector3).");
		}
		else
		{
			harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(DeathChestPlugin), "HitableDamagePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Hook instalado: Hitable.Damage(...)");
		}
		MethodInfo methodInfo3 = AccessTools.Method(typeof(ChestManager), "UpdateChest", new Type[4]
		{
			typeof(int),
			typeof(int),
			typeof(int),
			typeof(int)
		}, (Type[])null);
		if (methodInfo3 == null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"NO SE ENCONTRÓ ChestManager.UpdateChest(int, int, int, int).");
		}
		else
		{
			harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(typeof(DeathChestPlugin), "ChestManagerUpdateChestPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Hook instalado: ChestManager.UpdateChest(...)");
		}
		MethodInfo methodInfo4 = AccessTools.Method(typeof(MobServerEnemy), "FindNextPosition", Type.EmptyTypes, (Type[])null);
		if (methodInfo4 == null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"NO SE ENCONTRÓ MobServerEnemy.FindNextPosition().");
		}
		else
		{
			harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(typeof(DeathChestPlugin), "MobServerEnemyFindNextPositionPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Hook instalado: MobServerEnemy.FindNextPosition(...)");
		}
		MethodInfo methodInfo5 = AccessTools.Method(typeof(MobServerEnemy), "TryAttack", Type.EmptyTypes, (Type[])null);
		if (methodInfo5 == null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"NO SE ENCONTRÓ MobServerEnemy.TryAttack().");
			return;
		}
		harmony.Patch((MethodBase)methodInfo5, new HarmonyMethod(typeof(DeathChestPlugin), "MobServerEnemyTryAttackPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Hook instalado: MobServerEnemy.TryAttack(...)");
	}

	private static bool HitableDamagePrefix(Hitable __instance, int newHp, int fromClient, int hitEffect, Vector3 pos)
	{
		if ((Object)(object)Instance == (Object)null)
		{
			return true;
		}
		if (!Instance.modActive)
		{
			return true;
		}
		if ((Object)(object)__instance == (Object)null)
		{
			return true;
		}
		try
		{
			HitableChest val = (HitableChest)(object)((__instance is HitableChest) ? __instance : null);
			if ((Object)(object)val == (Object)null)
			{
				return true;
			}
			int num = -1;
			try
			{
				num = ((Hitable)val).GetId();
			}
			catch (Exception)
			{
				num = -1;
			}
			if (!Instance.IsProtectedDeathChest(num))
			{
				return true;
			}
			((BaseUnityPlugin)Instance).Logger.LogInfo((object)("[CHEST DAMAGE PROTECTION] Daño bloqueado. ChestID=" + num + " CurrentHP=" + __instance.hp + " RequestedHP=" + newHp));
			return false;
		}
		catch (Exception ex2)
		{
			if ((Object)(object)Instance != (Object)null)
			{
				((BaseUnityPlugin)Instance).Logger.LogError((object)"[CHEST DAMAGE PROTECTION] Error interceptando Hitable.Damage:");
				((BaseUnityPlugin)Instance).Logger.LogError((object)ex2);
			}
			return true;
		}
	}

	private static void ChestManagerUpdateChestPostfix(int chestId, int cellId, int itemId, int amount)
	{
		if ((Object)(object)Instance == (Object)null || !Instance.modActive)
		{
			return;
		}
		try
		{
			Instance.CheckProtectedChestAfterUpdate(chestId);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)Instance).Logger.LogError((object)"[CHEST CONTENT] Error comprobando Death Chest vacío:");
			((BaseUnityPlugin)Instance).Logger.LogError((object)ex);
		}
	}

	private void CheckProtectedChestAfterUpdate(int chestId)
	{
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		if (!IsProtectedDeathChest(chestId))
		{
			return;
		}
		ChestManager chestManager = GetChestManager();
		if (chestManager == null || chestManager.chests == null)
		{
			return;
		}
		if (!chestManager.chests.TryGetValue(chestId, out var value))
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("[CHEST CONTENT] Death Chest protegido ya no existe en ChestManager. ChestID=" + chestId));
			RemoveProtectedDeathChest(chestId);
		}
		else
		{
			if (ChestHasContents(value))
			{
				return;
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[CHEST CONTENT] Death Chest quedó vacío. ChestID=" + chestId + ". Eliminando protección."));
			RemoveProtectedDeathChest(chestId);
			HitableChest val = GetChestFromTransform(((Component)value).transform);
			if ((Object)(object)val == (Object)null)
			{
				val = ((Component)value).GetComponentInParent<HitableChest>();
			}
			if ((Object)(object)val == (Object)null)
			{
				val = ((Component)value).GetComponentInChildren<HitableChest>(true);
			}
			if (!((Object)(object)val == (Object)null))
			{
				try
				{
					((Hitable)val).KillObject(Vector3.zero);
					((BaseUnityPlugin)this).Logger.LogInfo((object)("[CHEST CONTENT] ChestID=" + chestId + " destruido porque quedó vacío."));
					return;
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)("[CHEST CONTENT] Error destruyendo ChestID=" + chestId + ":"));
					((BaseUnityPlugin)this).Logger.LogError((object)ex);
					return;
				}
			}
			((BaseUnityPlugin)this).Logger.LogWarning((object)("[CHEST CONTENT] No se encontró HitableChest para ChestID=" + chestId));
		}
	}

	private void RemoveProtectedDeathChest(int objectId)
	{
		if (protectedDeathChests.Count == 0)
		{
			return;
		}
		Queue<ProtectedChest> queue = new Queue<ProtectedChest>();
		bool flag = false;
		while (protectedDeathChests.Count > 0)
		{
			ProtectedChest protectedChest = protectedDeathChests.Dequeue();
			if (protectedChest != null)
			{
				if (protectedChest.ObjectId == objectId)
				{
					flag = true;
				}
				else
				{
					queue.Enqueue(protectedChest);
				}
			}
		}
		while (queue.Count > 0)
		{
			protectedDeathChests.Enqueue(queue.Dequeue());
		}
		if (flag)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[STABILITY] Death Chest eliminado de la protección. ObjectID=" + objectId + " ProtectedCount=" + protectedDeathChests.Count));
		}
	}

	private static void MobServerEnemyFindNextPositionPostfix(MobServerEnemy __instance, ref Vector3 __result)
	{
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)Instance == (Object)null || !Instance.modActive || (Object)(object)__instance == (Object)null)
		{
			return;
		}
		try
		{
			Mob component = ((Component)__instance).GetComponent<Mob>();
			if ((Object)(object)component == (Object)null || (Object)(object)component.target == (Object)null || !Instance.IsChestTarget(component.target))
			{
				return;
			}
			HitableChest chestFromTransform = Instance.GetChestFromTransform(component.target);
			int num = -1;
			if ((Object)(object)chestFromTransform != (Object)null)
			{
				try
				{
					num = ((Hitable)chestFromTransform).GetId();
				}
				catch (Exception)
				{
					num = -1;
				}
			}
			((BaseUnityPlugin)Instance).Logger.LogInfo((object)("[MOB PROTECTION] Mob intentó seleccionar un Chest. Objetivo cancelado. ChestID=" + num));
			component.target = null;
			component.targetPlayerId = -1;
			__result = Vector3.zero;
		}
		catch (Exception ex2)
		{
			((BaseUnityPlugin)Instance).Logger.LogError((object)"[MOB PROTECTION] Error en FindNextPosition:");
			((BaseUnityPlugin)Instance).Logger.LogError((object)ex2);
		}
	}

	private static bool MobServerEnemyTryAttackPrefix(MobServerEnemy __instance)
	{
		if ((Object)(object)Instance == (Object)null)
		{
			return true;
		}
		if (!Instance.modActive)
		{
			return true;
		}
		if ((Object)(object)__instance == (Object)null)
		{
			return true;
		}
		try
		{
			Mob component = ((Component)__instance).GetComponent<Mob>();
			if ((Object)(object)component == (Object)null)
			{
				return true;
			}
			if ((Object)(object)component.target == (Object)null)
			{
				return true;
			}
			if (!Instance.IsChestTarget(component.target))
			{
				return true;
			}
			HitableChest chestFromTransform = Instance.GetChestFromTransform(component.target);
			int num = -1;
			if ((Object)(object)chestFromTransform != (Object)null)
			{
				try
				{
					num = ((Hitable)chestFromTransform).GetId();
				}
				catch (Exception)
				{
					num = -1;
				}
			}
			((BaseUnityPlugin)Instance).Logger.LogInfo((object)("[MOB PROTECTION] Ataque contra Chest bloqueado. ChestID=" + num));
			component.target = null;
			component.targetPlayerId = -1;
			return false;
		}
		catch (Exception ex2)
		{
			((BaseUnityPlugin)Instance).Logger.LogError((object)"[MOB PROTECTION] Error en TryAttack:");
			((BaseUnityPlugin)Instance).Logger.LogError((object)ex2);
			return true;
		}
	}

	private bool IsChestTarget(Transform target)
	{
		if ((Object)(object)target == (Object)null)
		{
			return false;
		}
		if ((Object)(object)((Component)target).GetComponent<HitableChest>() != (Object)null)
		{
			return true;
		}
		if ((Object)(object)((Component)target).GetComponentInChildren<HitableChest>(true) != (Object)null)
		{
			return true;
		}
		if ((Object)(object)((Component)target).GetComponentInParent<HitableChest>() != (Object)null)
		{
			return true;
		}
		return false;
	}

	private HitableChest GetChestFromTransform(Transform target)
	{
		if ((Object)(object)target == (Object)null)
		{
			return null;
		}
		HitableChest component = ((Component)target).GetComponent<HitableChest>();
		if ((Object)(object)component != (Object)null)
		{
			return component;
		}
		component = ((Component)target).GetComponentInChildren<HitableChest>(true);
		if ((Object)(object)component != (Object)null)
		{
			return component;
		}
		return ((Component)target).GetComponentInParent<HitableChest>();
	}

	private bool IsProtectedDeathChest(int objectId)
	{
		foreach (ProtectedChest protectedDeathChest in protectedDeathChests)
		{
			if (protectedDeathChest != null && protectedDeathChest.ObjectId == objectId)
			{
				return true;
			}
		}
		return false;
	}

	private bool ChestHasContents(Chest chest)
	{
		if ((Object)(object)chest == (Object)null)
		{
			return false;
		}
		if (chest.cells == null)
		{
			return false;
		}
		InventoryItem[] cells = chest.cells;
		foreach (InventoryItem val in cells)
		{
			if ((Object)(object)val != (Object)null && val.amount > 0)
			{
				return true;
			}
		}
		return false;
	}

	private static void ItemManagerDropItemPostfix(ItemManager __instance, int fromClient, int itemId, int amount, int objectID)
	{
		if ((Object)(object)Instance == (Object)null || !Instance.modActive || !LocalClient.serverOwner)
		{
			return;
		}
		try
		{
			Instance.RegisterDrop(__instance, fromClient, itemId, amount, objectID);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)Instance).Logger.LogError((object)"ERROR capturando ItemManager.DropItem:");
			((BaseUnityPlugin)Instance).Logger.LogError((object)ex);
		}
	}

	private void RegisterDrop(ItemManager manager, int clientId, int itemId, int amount, int objectId)
	{
		if (!modActive || manager == null || manager.list == null || amount <= 0 || !manager.list.TryGetValue(objectId, out var droppedObject) || (Object)(object)droppedObject == (Object)null)
		{
			return;
		}
		if (sessionItemManager != manager)
		{
			pendingDrops.Clear();
			sessionItemManager = manager;
		}
		if (pendingDrops.TryGetValue(clientId, out var value) && value.Items.Exists((DroppedItem droppedItem) => droppedItem.ObjectId == objectId && (Object)(object)droppedItem.Object == (Object)(object)droppedObject))
		{
			return;
		}
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		if (pendingDrops.TryGetValue(clientId, out var value2))
		{
			float num = realtimeSinceStartup - value2.LastDropTime;
			if (num > 0.05f)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[DROP BATCH RESET] ClientID=" + clientId + " Gap=" + (num * 1000f).ToString("0.000") + "ms"));
				pendingDrops.Remove(clientId);
				value2 = null;
			}
			else
			{
				value2.LastDropTime = realtimeSinceStartup;
			}
		}
		if (!pendingDrops.TryGetValue(clientId, out var value3))
		{
			value3 = new DropBatch(clientId, realtimeSinceStartup, manager);
			pendingDrops.Add(clientId, value3);
		}
		DroppedItem item = new DroppedItem(itemId, amount, objectId, droppedObject);
		if (!value3.Items.Exists((DroppedItem droppedItem) => droppedItem.ObjectId == objectId))
		{
			value3.Items.Add(item);
			value3.LastDropTime = realtimeSinceStartup;
			string itemName = GetItemName(itemId);
			float num2 = realtimeSinceStartup - value3.FirstDropTime;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[DROP] ClientID=" + clientId + " ItemID=" + itemId + " ItemName=\"" + itemName + "\" Amount=" + amount + " ObjectID=" + objectId + " BatchDrops=" + value3.Items.Count + " SinceFirstDrop=" + (num2 * 1000f).ToString("0.000") + "ms"));
		}
	}

	private static void ServerDropBroadcastPrefix(int fromClient, int itemId, int amount, int objectID)
	{
		if ((Object)(object)Instance == (Object)null || !Instance.modActive || !LocalClient.serverOwner)
		{
			return;
		}
		try
		{
			Instance.RegisterDrop(Instance.GetItemManager(), fromClient, itemId, amount, objectID);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)Instance).Logger.LogError((object)("[DROP BROADCAST] " + ex));
		}
	}

	private static void ServerDeathBroadcastPrefix(int deadPlayerId, ref Vector3 gravePos)
	{
		if ((Object)(object)Instance == (Object)null || !Instance.modActive || !LocalClient.serverOwner)
		{
			return;
		}
		try
		{
			Instance.ProcessDeath(deadPlayerId, ref gravePos);
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)Instance).Logger.LogError((object)("[DEATH] " + ex));
		}
	}

	private void ProcessDeath(int clientId, ref Vector3 gravePosition)
	{
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00db: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_029e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0155: Unknown result type (might be due to invalid IL or missing references)
		//IL_0157: Unknown result type (might be due to invalid IL or missing references)
		//IL_015c: Unknown result type (might be due to invalid IL or missing references)
		//IL_010a: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_011b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0143: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01da: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_020c: Unknown result type (might be due to invalid IL or missing references)
		//IL_020e: Unknown result type (might be due to invalid IL or missing references)
		if (!pendingDrops.TryGetValue(clientId, out var batch))
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[DEATH] Client=" + clientId + " No tracked drops. Host=" + LocalClient.serverOwner));
			return;
		}
		pendingDrops.Remove(clientId);
		if (Time.realtimeSinceStartup - batch.LastDropTime > 2f)
		{
			return;
		}
		batch.Items.RemoveAll((DroppedItem item) => batch.Manager == null || batch.Manager.list == null || !batch.Manager.list.TryGetValue(item.ObjectId, out var value2) || (Object)(object)value2 == (Object)null || (Object)(object)value2 != (Object)(object)item.Object);
		if (batch.Items.Count == 0)
		{
			return;
		}
		int num = FindChestItemId();
		if (num < 0)
		{
			return;
		}
		Vector3 val = gravePosition;
		List<int> list = new List<int>();
		int chestCount = GetChestCount(batch.Items.Count);
		float num2 = 3f;
		RaycastHit val3 = default(RaycastHit);
		for (int num3 = 0; num3 < chestCount; num3++)
		{
			Vector3 val2 = val + Vector3.right * ((float)num3 * num2);
			GameManager gameManager = GetGameManager();
			if (num3 > 0 && (Object)(object)gameManager != (Object)null && Physics.Raycast(val2 + Vector3.up * 10f, Vector3.down, ref val3, 30f, LayerMask.op_Implicit(gameManager.whatIsGround)))
			{
				val2.y = ((RaycastHit)(ref val3)).point.y;
			}
			int num4 = CreateChest(clientId, num, val2 + Vector3.up);
			if (num4 < 0)
			{
				break;
			}
			ResourceManager resourceManager = GetResourceManager();
			if (resourceManager == null || !resourceManager.list.TryGetValue(num4, out var value) || (Object)(object)value == (Object)null)
			{
				break;
			}
			Bounds chestBounds = GetChestBounds(value);
			if (num3 == 0)
			{
				num2 = Mathf.Max(3f, ((Bounds)(ref chestBounds)).size.x + 0.5f);
			}
			CalculatePlacement(val2, value.transform.position, chestBounds, out var finalChestPosition, out var gravePosition2);
			value.transform.position = finalChestPosition;
			ServerSend.SendBuild(clientId, num, num4, finalChestPosition, 0);
			list.Add(num4);
			if (num3 == 0)
			{
				gravePosition = gravePosition2;
			}
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[DEATH] Client=" + clientId + " Drops=" + batch.Items.Count + " Chests=" + list.Count + "/" + chestCount + " Grave=" + FormatVector3(gravePosition)));
		if (list.Count > 0)
		{
			((MonoBehaviour)this).StartCoroutine(FillChestsNextFrame(clientId, list, batch));
		}
	}

	private static int GetChestCount(int itemCount)
	{
		if (itemCount > 0)
		{
			return (itemCount - 1) / 21 + 1;
		}
		return 0;
	}

	private static Bounds GetChestBounds(GameObject chestObject)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: 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_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		Bounds bounds = default(Bounds);
		((Bounds)(ref bounds))..ctor(chestObject.transform.position, Vector3.one);
		bool flag = false;
		Renderer[] componentsInChildren = chestObject.GetComponentsInChildren<Renderer>();
		foreach (Renderer val in componentsInChildren)
		{
			if (val.enabled && !(val is ParticleSystemRenderer))
			{
				if (!flag)
				{
					bounds = val.bounds;
					flag = true;
				}
				else
				{
					((Bounds)(ref bounds)).Encapsulate(val.bounds);
				}
			}
		}
		return bounds;
	}

	private static void CalculatePlacement(Vector3 groundPosition, Vector3 chestPosition, Bounds bounds, out Vector3 finalChestPosition, out Vector3 gravePosition)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: 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_002c: 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_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		float num = groundPosition.y + 0.05f - ((Bounds)(ref bounds)).min.y;
		finalChestPosition = chestPosition + Vector3.up * num;
		gravePosition = new Vector3(groundPosition.x, ((Bounds)(ref bounds)).max.y + num + 0.05f, groundPosition.z);
	}

	private int CreateChest(int clientId, int chestItemId, Vector3 position)
	{
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			ResourceManager resourceManager = GetResourceManager();
			if (resourceManager == null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"[CHEST] No se pudo recuperar ResourceManager.");
				return -1;
			}
			BuildManager buildManager = GetBuildManager();
			if (buildManager == null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"[CHEST] No se pudo recuperar BuildManager.");
				return -1;
			}
			int nextId = resourceManager.GetNextId();
			GameObject val = buildManager.BuildItem(clientId, chestItemId, nextId, position, 0);
			if ((Object)(object)val == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("[CHEST] BuildItem devolvió null. ObjectID=" + nextId));
				return -1;
			}
			Hitable val2 = val.GetComponent<Hitable>();
			if ((Object)(object)val2 == (Object)null)
			{
				val2 = val.GetComponentInChildren<Hitable>(true);
			}
			if ((Object)(object)val2 != (Object)null)
			{
				int hp = val2.hp;
				int maxHp = val2.maxHp;
				Hitable obj = val2;
				obj.hp += 10000;
				Hitable obj2 = val2;
				obj2.maxHp += 10000;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[CHEST HP] ObjectID=" + nextId + " HP=" + hp + " -> " + val2.hp + " MaxHP=" + maxHp + " -> " + val2.maxHp));
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[CHEST HP] No se encontró Hitable para ObjectID=" + nextId));
			}
			ProtectDeathChest(nextId, val);
			return nextId;
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("[CHEST] Error creando Chest: " + ex));
			return -1;
		}
	}

	private void ProtectDeathChest(int objectId, GameObject chestObject)
	{
		if ((Object)(object)chestObject == (Object)null)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("[STABILITY] ObjectID=" + objectId + " no tiene GameObject."));
			return;
		}
		BuildDestruction val = chestObject.GetComponent<BuildDestruction>();
		if ((Object)(object)val == (Object)null)
		{
			val = chestObject.GetComponentInChildren<BuildDestruction>(true);
		}
		if ((Object)(object)val == (Object)null)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("[STABILITY] ObjectID=" + objectId + " no tiene BuildDestruction."));
			protectedDeathChests.Enqueue(new ProtectedChest(objectId, null));
			while (protectedDeathChests.Count > 16)
			{
				ProtectedChest protectedChest = protectedDeathChests.Dequeue();
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[STABILITY] ObjectID=" + protectedChest.ObjectId + " dejó de estar dentro de los últimos " + 16 + " Death Chests protegidos."));
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[STABILITY] ObjectID=" + objectId + " protegido mediante ObjectID aunque no tenga BuildDestruction."));
			return;
		}
		val.connectedToGround = true;
		val.directlyGrounded = true;
		protectedDeathChests.Enqueue(new ProtectedChest(objectId, val));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[STABILITY] Death Chest protegido. ObjectID=" + objectId + " ConnectedToGround=true DirectlyGrounded=true ProtectedCount=" + protectedDeathChests.Count));
		while (protectedDeathChests.Count > 16)
		{
			ProtectedChest protectedChest2 = protectedDeathChests.Dequeue();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[STABILITY] ObjectID=" + protectedChest2.ObjectId + " dejó de estar dentro de los últimos " + 16 + " Death Chests protegidos."));
		}
	}

	private void MaintainProtectedDeathChests()
	{
		if (protectedDeathChests.Count == 0)
		{
			return;
		}
		foreach (ProtectedChest protectedDeathChest in protectedDeathChests)
		{
			if (protectedDeathChest == null)
			{
				continue;
			}
			BuildDestruction buildDestruction = protectedDeathChest.BuildDestruction;
			if (!((Object)(object)buildDestruction == (Object)null))
			{
				try
				{
					buildDestruction.connectedToGround = true;
					buildDestruction.directlyGrounded = true;
				}
				catch (Exception)
				{
				}
			}
		}
	}

	private IEnumerator FillChestsNextFrame(int clientId, List<int> chestIds, DropBatch batch)
	{
		yield return null;
		if (!modActive || !LocalClient.serverOwner)
		{
			yield break;
		}
		ChestManager chestManager = GetChestManager();
		if (chestManager == null || chestManager.chests == null)
		{
			yield break;
		}
		List<DroppedItem> list = new List<DroppedItem>();
		int num = 0;
		foreach (int chestId in chestIds)
		{
			if (!chestManager.chests.TryGetValue(chestId, out var value) || (Object)(object)value == (Object)null || value.cells == null)
			{
				continue;
			}
			int num2 = 0;
			while (num2 < Math.Min(21, value.cells.Length) && num < batch.Items.Count)
			{
				DroppedItem droppedItem = batch.Items[num++];
				if (batch.Manager != null && batch.Manager.list != null && batch.Manager.list.TryGetValue(droppedItem.ObjectId, out var value2) && !((Object)(object)value2 == (Object)null) && !((Object)(object)value2 != (Object)(object)droppedItem.Object))
				{
					try
					{
						chestManager.UpdateChest(chestId, num2, droppedItem.ItemId, droppedItem.Amount);
						ServerSend.UpdateChest(clientId, chestId, num2, droppedItem.ItemId, droppedItem.Amount);
						list.Add(droppedItem);
						num2++;
					}
					catch (Exception ex)
					{
						value.cells[num2] = null;
						((BaseUnityPlugin)this).Logger.LogError((object)("[CHEST CONTENT] Item left on ground: " + ex));
						break;
					}
				}
			}
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[CHEST CONTENT] Chests=" + chestIds.Count + " Stored=" + list.Count + " Unstored=" + (batch.Items.Count - list.Count)));
		yield return ((MonoBehaviour)this).StartCoroutine(RemoveOriginalDropsWithRetries(clientId, batch.Manager, list));
	}

	private IEnumerator RemoveOriginalDropsWithRetries(int clientId, ItemManager itemManager, List<DroppedItem> objectIds)
	{
		if (!modActive)
		{
			yield break;
		}
		if (objectIds == null || objectIds.Count == 0)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"[DROP REMOVE] No hay drops para eliminar.");
			yield break;
		}
		List<DroppedItem> remainingObjectIds = new List<DroppedItem>(objectIds);
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[DROP REMOVE] Objetos a eliminar=" + remainingObjectIds.Count));
		int totalRemoved = 0;
		int totalFailed = 0;
		int totalPasses = 3;
		for (int pass = 0; pass < totalPasses; pass++)
		{
			if (remainingObjectIds.Count == 0)
			{
				break;
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[DROP REMOVE] Iniciando pasada " + (pass + 1) + "/" + totalPasses + ". Objetos pendientes=" + remainingObjectIds.Count));
			List<DroppedItem> list = new List<DroppedItem>();
			foreach (DroppedItem item in remainingObjectIds)
			{
				int objectId = item.ObjectId;
				if (itemManager == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("[DROP REMOVE] Pasada=" + (pass + 1) + "/" + totalPasses + " ObjectID=" + objectId + " ItemManager no disponible."));
					list.Add(item);
				}
				else if (itemManager.list == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("[DROP REMOVE] Pasada=" + (pass + 1) + "/" + totalPasses + " ObjectID=" + objectId + " ItemManager.list es null."));
					list.Add(item);
				}
				else if (!itemManager.list.ContainsKey(objectId))
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("[DROP REMOVE] Pasada=" + (pass + 1) + "/" + totalPasses + " ObjectID=" + objectId + " no existe en ItemManager.list."));
					list.Add(item);
				}
				else
				{
					if (!itemManager.list.TryGetValue(objectId, out var value) || (Object)(object)value == (Object)null || (Object)(object)value != (Object)(object)item.Object)
					{
						continue;
					}
					bool flag = false;
					try
					{
						flag = itemManager.PickupItem(objectId);
					}
					catch (Exception ex)
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)("[DROP REMOVE] Pasada=" + (pass + 1) + "/" + totalPasses + " ObjectID=" + objectId + " PickupItem lanzó excepción: " + ex.Message));
					}
					if (flag)
					{
						try
						{
							ServerSend.PickupItem(-1, objectId);
						}
						catch (Exception ex2)
						{
							((BaseUnityPlugin)this).Logger.LogWarning((object)("[DROP REMOVE] ObjectID=" + objectId + " eliminado, pero ServerSend.PickupItem falló: " + ex2.Message));
						}
						totalRemoved++;
						((BaseUnityPlugin)this).Logger.LogInfo((object)("[DROP REMOVE] ObjectID=" + objectId + " eliminado. Pasada=" + (pass + 1) + "/" + totalPasses));
					}
					else
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)("[DROP REMOVE] Pasada=" + (pass + 1) + "/" + totalPasses + " ObjectID=" + objectId + " PickupItem devolvió false."));
						list.Add(item);
					}
				}
			}
			remainingObjectIds = list;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[DROP REMOVE] Pasada " + (pass + 1) + "/" + totalPasses + " finalizada. Eliminados acumulados=" + totalRemoved + " Pendientes=" + remainingObjectIds.Count));
			if (remainingObjectIds.Count > 0 && pass < totalPasses - 1)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[DROP REMOVE] Esperando " + 100f.ToString("0") + "ms antes de la siguiente pasada."));
				yield return (object)new WaitForSecondsRealtime(0.1f);
			}
		}
		int count = remainingObjectIds.Count;
		if (count > 0)
		{
			totalFailed = count;
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)"----------------------------------------");
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[DROP REMOVE] Resultado para ClientID=" + clientId));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[DROP REMOVE] Objetos originales=" + objectIds.Count));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[DROP REMOVE] Eliminados=" + totalRemoved));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[DROP REMOVE] Pendientes/no eliminados=" + count));
		((BaseUnityPlugin)this).Logger.LogInfo((object)("[DROP REMOVE] Fallidos=" + totalFailed));
		((BaseUnityPlugin)this).Logger.LogInfo((object)"----------------------------------------");
	}

	private int FindChestItemId()
	{
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Invalid comparison between Unknown and I4
		ItemManager itemManager = GetItemManager();
		if (itemManager == null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"[CHEST SEARCH] No se pudo recuperar ItemManager.");
			return -1;
		}
		if (itemManager.allItems == null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)"[CHEST SEARCH] ItemManager.allItems es null.");
			return -1;
		}
		foreach (KeyValuePair<int, InventoryItem> allItem in itemManager.allItems)
		{
			InventoryItem value = allItem.Value;
			if (!((Object)(object)value == (Object)null) && (int)value.type == 6 && !((Object)(object)value.prefab == (Object)null) && (Object)(object)value.prefab.GetComponentInChildren<Chest>(true) != (Object)null)
			{
				return allItem.Key;
			}
		}
		return -1;
	}

	private string GetItemName(int itemId)
	{
		try
		{
			ItemManager itemManager = GetItemManager();
			if (itemManager == null)
			{
				return "<ItemManager null>";
			}
			if (itemManager.allItems == null)
			{
				return "<allItems null>";
			}
			if (!itemManager.allItems.TryGetValue(itemId, out var value))
			{
				return "<ID desconocido>";
			}
			if ((Object)(object)value == (Object)null)
			{
				return "<InventoryItem null>";
			}
			return value.name;
		}
		catch (Exception)
		{
			return "<error>";
		}
	}

	private static bool HasItemManagerData(ItemManager manager)
	{
		if (manager != null && manager.list != null)
		{
			return manager.allItems != null;
		}
		return false;
	}

	private ItemManager GetItemManager()
	{
		if (HasItemManagerData(ItemManager.Instance))
		{
			return ItemManager.Instance;
		}
		if (HasItemManagerData(sessionItemManager))
		{
			return sessionItemManager;
		}
		return FindSceneManager<ItemManager>();
	}

	private ResourceManager GetResourceManager()
	{
		if (ResourceManager.Instance != null && ResourceManager.Instance.list != null)
		{
			return ResourceManager.Instance;
		}
		return FindSceneManager<ResourceManager>();
	}

	private BuildManager GetBuildManager()
	{
		if (BuildManager.Instance == null)
		{
			return FindSceneManager<BuildManager>();
		}
		return BuildManager.Instance;
	}

	private ChestManager GetChestManager()
	{
		if (ChestManager.Instance != null && ChestManager.Instance.chests != null)
		{
			return ChestManager.Instance;
		}
		return FindSceneManager<ChestManager>();
	}

	private GameManager GetGameManager()
	{
		if (!((Object)(object)GameManager.instance != (Object)null))
		{
			return FindSceneManager<GameManager>();
		}
		return GameManager.instance;
	}

	private static T FindSceneManager<T>() where T : MonoBehaviour
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		T[] array = Resources.FindObjectsOfTypeAll<T>();
		foreach (T val in array)
		{
			if (!((Object)(object)val != (Object)null))
			{
				continue;
			}
			Scene scene = ((Component)(object)val).gameObject.scene;
			if (((Scene)(ref scene)).IsValid())
			{
				scene = ((Component)(object)val).gameObject.scene;
				if (((Scene)(ref scene)).isLoaded)
				{
					return val;
				}
			}
		}
		return default(T);
	}

	private void Update()
	{
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		if (Input.GetKeyDown((KeyCode)288))
		{
			instantRespawn.Value = !instantRespawn.Value;
			((BaseUnityPlugin)this).Config.Save();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[TEST] Instant respawn=" + instantRespawn.Value));
		}
		if (modActive && LocalClient.serverOwner)
		{
			EnsurePacketHooks();
		}
		if (Input.GetKeyDown((KeyCode)289))
		{
			Scene activeScene = SceneManager.GetActiveScene();
			if (((Scene)(ref activeScene)).name == "GameAfterLobby")
			{
				RestartHooks();
			}
		}
		if (!modActive)
		{
			return;
		}
		MaintainProtectedDeathChests();
		if (pendingDrops.Count == 0)
		{
			return;
		}
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		List<int> list = new List<int>();
		foreach (KeyValuePair<int, DropBatch> pendingDrop in pendingDrops)
		{
			DropBatch value = pendingDrop.Value;
			if (realtimeSinceStartup - value.FirstDropTime > 2f)
			{
				list.Add(pendingDrop.Key);
			}
		}
		foreach (int item in list)
		{
			if (pendingDrops.TryGetValue(item, out var value2))
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[DROP CLEANUP] ClientID=" + item + " Drops=" + value2.Items.Count + " No apareció una muerte dentro de " + 2f.ToString("0.00") + "s."));
			}
			pendingDrops.Remove(item);
		}
	}

	private string FormatVector3(Vector3 position)
	{
		return "(" + position.x.ToString("0.00") + ", " + position.y.ToString("0.00") + ", " + position.z.ToString("0.00") + ")";
	}

	private static void ServerPacketsInitializedPostfix()
	{
		if ((Object)(object)Instance != (Object)null && Instance.modActive && LocalClient.serverOwner)
		{
			Instance.EnsurePacketHooks();
		}
	}

	private void EnsurePacketHooks()
	{
		if (packetHooks.Bind(Server.PacketHandlers))
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)"[NETWORK] Bound drop/death handlers for this session.");
		}
	}

	private void HandleDropPacket(int clientId, Packet packet, PacketHandler original)
	{
		if (!modActive || !LocalClient.serverOwner || packet.UnreadLength() < 8)
		{
			original.Invoke(clientId, packet);
			return;
		}
		byte[] value = packet.ReadBytes(8, false);
		int num = BitConverter.ToInt32(value, 0);
		int num2 = BitConverter.ToInt32(value, 4);
		ItemManager itemManager = GetItemManager();
		Dictionary<int, GameObject> dictionary = ((itemManager != null && itemManager.list != null) ? new Dictionary<int, GameObject>(itemManager.list) : new Dictionary<int, GameObject>());
		original.Invoke(clientId, packet);
		try
		{
			ItemManager itemManager2 = GetItemManager();
			int num3 = 0;
			int num4 = 0;
			if (itemManager2 != null && itemManager2.list != null)
			{
				foreach (KeyValuePair<int, GameObject> item in itemManager2.list)
				{
					if ((Object)(object)item.Value == (Object)null || (itemManager2 == itemManager && dictionary.TryGetValue(item.Key, out var value2) && (Object)(object)value2 == (Object)(object)item.Value))
					{
						continue;
					}
					num4++;
					Item component = item.Value.GetComponent<Item>();
					if (!((Object)(object)component == (Object)null) && !((Object)(object)component.item == (Object)null))
					{
						if (component.item.id != num || component.item.amount != num2)
						{
							((BaseUnityPlugin)this).Logger.LogWarning((object)("[NETWORK DROP] Metadata mismatch. Requested=" + num + ":" + num2 + " Actual=" + component.item.id + ":" + component.item.amount + " Object=" + item.Key));
						}
						else
						{
							RegisterDrop(itemManager2, clientId, num, num2, item.Key);
							num3++;
						}
					}
				}
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[NETWORK DROP] Client=" + clientId + " Item=" + num + " Amount=" + num2 + " Created=" + num3 + " NewObjects=" + num4 + " Managed=" + (itemManager2 != null) + " NativeAlive=" + ((Object)(object)itemManager2 != (Object)null) + " Before=" + dictionary.Count + " After=" + ((itemManager2 != null && itemManager2.list != null) ? itemManager2.list.Count : (-1))));
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)("[NETWORK DROP] " + ex));
		}
	}

	private void HandleDeathPacket(int clientId, Packet packet, PacketHandler original)
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Invalid comparison between Unknown and I4
		Client value;
		bool flag = modActive && LocalClient.serverOwner && instantRespawn.Value && (int)GameManager.gameSettings.gameMode != 1 && (Object)(object)GameManager.instance != (Object)null && !GameManager.instance.boatLeft && Server.clients.TryGetValue(clientId, out value) && value.player != null && !value.player.dead && queuedRespawns.Add(clientId);
		try
		{
			original.Invoke(clientId, packet);
		}
		catch
		{
			if (flag)
			{
				queuedRespawns.Remove(clientId);
			}
			throw;
		}
		if (flag)
		{
			((MonoBehaviour)this).StartCoroutine(RespawnForTesting(clientId));
		}
	}

	private static bool CheckGameOverPrefix()
	{
		if (!((Object)(object)Instance == (Object)null) && Instance.modActive && LocalClient.serverOwner)
		{
			return Instance.queuedRespawns.Count == 0;
		}
		return true;
	}

	private IEnumerator RespawnForTesting(int clientId)
	{
		try
		{
			yield return (object)new WaitForSecondsRealtime(0.2f);
			float deadline = Time.realtimeSinceStartup + 3f;
			while (modActive && LocalClient.serverOwner && Time.realtimeSinceStartup < deadline)
			{
				if (!Server.clients.TryGetValue(clientId, out var value) || value.player == null || !value.player.dead)
				{
					yield break;
				}
				if (GameManager.players.TryGetValue(clientId, out var value2) && (Object)(object)value2 != (Object)null && (Object)(object)ResourceManager.Instance != (Object)null && ResourceManager.Instance.list.ContainsKey(value2.graveId))
				{
					int graveId = value2.graveId;
					Packet val = new Packet();
					try
					{
						val.Write(clientId);
						val.Write(true);
						val.Write(graveId);
						val.ToArray();
						ServerHandle.RevivePlayer(LocalClient.instance.myId, val);
					}
					finally
					{
						((IDisposable)val)?.Dispose();
					}
					((BaseUnityPlugin)this).Logger.LogInfo((object)("[TEST] Revived Client=" + clientId));
					yield break;
				}
				yield return null;
			}
			((BaseUnityPlugin)this).Logger.LogWarning((object)("[TEST] Revive timed out for Client=" + clientId));
		}
		finally
		{
			DeathChestPlugin deathChestPlugin = this;
			deathChestPlugin.queuedRespawns.Remove(clientId);
			if (deathChestPlugin.modActive && LocalClient.serverOwner && deathChestPlugin.queuedRespawns.Count == 0 && (Object)(object)GameManager.instance != (Object)null)
			{
				GameManager.instance.CheckIfGameOver();
			}
		}
	}

	private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		if ((int)mode == 0 || !(((Scene)(ref scene)).name != "GameAfterLobby"))
		{
			StopSession();
			sessionSceneHandle = ((Scene)(ref scene)).handle;
			if (((Scene)(ref scene)).name == "GameAfterLobby")
			{
				((MonoBehaviour)this).StartCoroutine(StartSessionNextFrame(((Scene)(ref scene)).handle));
			}
		}
	}

	private IEnumerator StartSessionNextFrame(int sceneHandle)
	{
		yield return null;
		Scene activeScene = SceneManager.GetActiveScene();
		if (((Scene)(ref activeScene)).handle == sceneHandle && ((Scene)(ref activeScene)).isLoaded && ((Scene)(ref activeScene)).name == "GameAfterLobby")
		{
			RestartHooks();
		}
	}

	private void OnSceneUnloaded(Scene scene)
	{
		if (((Scene)(ref scene)).handle == sessionSceneHandle)
		{
			StopSession();
		}
	}

	private void RestartHooks()
	{
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
		modActive = false;
		try
		{
			harmony.UnpatchSelf();
			pendingDrops.Clear();
			InstallPatches();
			modActive = true;
			EnsurePacketHooks();
			ItemManager itemManager = GetItemManager();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("[MANAGER] ItemManager Managed=" + (itemManager != null) + " NativeAlive=" + ((Object)(object)itemManager != (Object)null) + " UsableData=" + HasItemManagerData(itemManager)));
			ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
			string[] obj = new string[5] { "[LIFECYCLE] Hooks reinstalled. Scene=", null, null, null, null };
			Scene activeScene = SceneManager.GetActiveScene();
			obj[1] = ((Scene)(ref activeScene)).name;
			obj[2] = " Host=";
			obj[3] = LocalClient.serverOwner.ToString();
			obj[4] = " Press F8 to reinstall again.";
			logger.LogInfo((object)string.Concat(obj));
		}
		catch (Exception ex)
		{
			harmony.UnpatchSelf();
			((BaseUnityPlugin)this).Logger.LogError((object)("[LIFECYCLE] Hook restart failed: " + ex));
		}
	}

	private void StopSession()
	{
		modActive = false;
		((MonoBehaviour)this).StopAllCoroutines();
		queuedRespawns.Clear();
		packetHooks?.Detach();
		Harmony obj = harmony;
		if (obj != null)
		{
			obj.UnpatchSelf();
		}
		pendingDrops.Clear();
		protectedDeathChests.Clear();
		sessionItemManager = null;
		sessionSceneHandle = -1;
		((BaseUnityPlugin)this).Logger.LogInfo((object)"[LIFECYCLE] Session stopped; hooks removed.");
	}

	private void OnDestroy()
	{
		SceneManager.sceneLoaded -= OnSceneLoaded;
		SceneManager.sceneUnloaded -= OnSceneUnloaded;
		StopSession();
		if (Instance == this)
		{
			Instance = null;
		}
	}
}
internal sealed class DeathChestPacketHooks
{
	private readonly Action<int, Packet, PacketHandler> onDrop;

	private readonly Action<int, Packet, PacketHandler> onDeath;

	private Dictionary<int, PacketHandler> table;

	private PacketHandler oldDrop;

	private PacketHandler oldDeath;

	private PacketHandler dropWrapper;

	private PacketHandler deathWrapper;

	internal DeathChestPacketHooks(Action<int, Packet, PacketHandler> onDrop, Action<int, Packet, PacketHandler> onDeath)
	{
		this.onDrop = onDrop;
		this.onDeath = onDeath;
	}

	internal bool Bind(Dictionary<int, PacketHandler> current)
	{
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: 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
		if (current == null)
		{
			return false;
		}
		if (table != current)
		{
			Detach();
			table = current;
		}
		bool result = false;
		if (table.TryGetValue(10, out var value) && (Delegate?)(object)value != (Delegate?)(object)dropWrapper)
		{
			oldDrop = value;
			PacketHandler upstream = value;
			dropWrapper = (PacketHandler)delegate(int client, Packet packet)
			{
				onDrop(client, packet, upstream);
			};
			table[10] = dropWrapper;
			result = true;
		}
		if (table.TryGetValue(27, out var value2) && (Delegate?)(object)value2 != (Delegate?)(object)deathWrapper)
		{
			oldDeath = value2;
			PacketHandler upstream2 = value2;
			deathWrapper = (PacketHandler)delegate(int client, Packet packet)
			{
				onDeath(client, packet, upstream2);
			};
			table[27] = deathWrapper;
			result = true;
		}
		return result;
	}

	internal void Detach()
	{
		if (table != null)
		{
			if (table.TryGetValue(10, out var value) && (Delegate?)(object)value == (Delegate?)(object)dropWrapper)
			{
				table[10] = oldDrop;
			}
			if (table.TryGetValue(27, out var value2) && (Delegate?)(object)value2 == (Delegate?)(object)deathWrapper)
			{
				table[27] = oldDeath;
			}
		}
		table = null;
		oldDrop = (oldDeath = (dropWrapper = (deathWrapper = null)));
	}
}