Decompiled source of CupboardWipeProtection v1.2.0

CupboardWipeProtection.dll

Decompiled 2 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Unity.Netcode;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("CupboardWipeProtection")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyInformationalVersion("1.2.0")]
[assembly: AssemblyProduct("CupboardWipeProtection")]
[assembly: AssemblyTitle("CupboardWipeProtection")]
[assembly: AssemblyVersion("1.2.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 CupboardWipeProtection
{
	[BepInPlugin("cupboardwipeprotection.plugin", "Cupboard Wipe Protection", "1.2.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "cupboardwipeprotection.plugin";

		public const string PluginName = "Cupboard Wipe Protection";

		public const string PluginVersion = "1.2.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> EnableTotalValueLimit;

		internal static ConfigEntry<int> MaximumProtectedTotalScrapValue;

		internal static ConfigEntry<bool> EnablePerItemValueLimit;

		internal static ConfigEntry<int> MaximumProtectedItemScrapValue;

		internal static ConfigEntry<float> SurfaceTolerance;

		internal static ConfigEntry<float> CabinetBoundsPadding;

		internal static ConfigEntry<bool> VerboseLogging;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Expected O, but got Unknown
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Expected O, but got Unknown
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Keep items stored in the ship cupboard when the entire crew is lost.");
			EnableTotalValueLimit = ((BaseUnityPlugin)this).Config.Bind<bool>("Value limits", "Enable total value limit", false, "Limit the combined value of all scrap protected by the cupboard.");
			MaximumProtectedTotalScrapValue = ((BaseUnityPlugin)this).Config.Bind<int>("Value limits", "Maximum total protected scrap value", 200, new ConfigDescription("Maximum combined scrap value protected after a wipe when the total value limit is enabled.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000000), Array.Empty<object>()));
			EnablePerItemValueLimit = ((BaseUnityPlugin)this).Config.Bind<bool>("Value limits", "Enable per-item value limit", true, "Remove individual scrap items worth more than the configured per-item limit during a full-crew wipe.");
			MaximumProtectedItemScrapValue = ((BaseUnityPlugin)this).Config.Bind<int>("Value limits", "Maximum protected value per scrap item", 200, new ConfigDescription("Maximum value of a single scrap item that the cupboard can protect when the per-item limit is enabled.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000000), Array.Empty<object>()));
			SurfaceTolerance = ((BaseUnityPlugin)this).Config.Bind<float>("Detection", "Shelf tolerance", 0.14f, "Maximum distance in metres between an item and a cupboard shelf surface.");
			CabinetBoundsPadding = ((BaseUnityPlugin)this).Config.Bind<float>("Detection", "Cabinet bounds padding", 0.08f, "Extra margin in metres around the cupboard model used by fallback detection.");
			VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Diagnostics", "Verbose logging", false, "Log every item classified as inside or outside the cupboard during a wipe.");
			_harmony = new Harmony("cupboardwipeprotection.plugin");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Cupboard Wipe Protection 1.2.0 loaded.");
		}

		private void OnDestroy()
		{
			ProtectionState.Clear();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class ProtectionState
	{
		private sealed class ItemGroup
		{
			internal readonly List<GrabbableObject> Items;

			internal readonly int ScrapValue;

			internal readonly int MaximumIndividualScrapValue;

			internal readonly string Label;

			internal readonly ulong NetworkObjectId;

			private ItemGroup(List<GrabbableObject> items, int scrapValue, int maximumIndividualScrapValue, string label, ulong networkObjectId)
			{
				Items = items;
				ScrapValue = scrapValue;
				MaximumIndividualScrapValue = maximumIndividualScrapValue;
				Label = label;
				NetworkObjectId = networkObjectId;
			}

			internal static ItemGroup FromRoot(GrabbableObject root)
			{
				List<GrabbableObject> list = new List<GrabbableObject>();
				CollectGroupItems(root, list, new HashSet<GrabbableObject>());
				int scrapValue = ((IEnumerable<GrabbableObject>)list).Sum((Func<GrabbableObject, int>)ItemScrapValue);
				int maximumIndividualScrapValue = ((list.Count != 0) ? ((IEnumerable<GrabbableObject>)list).Max((Func<GrabbableObject, int>)ItemScrapValue) : 0);
				ulong networkObjectId = (((Object)(object)((NetworkBehaviour)root).NetworkObject != (Object)null) ? ((NetworkBehaviour)root).NetworkObjectId : ulong.MaxValue);
				return new ItemGroup(list, scrapValue, maximumIndividualScrapValue, ItemName(root), networkObjectId);
			}

			private static void CollectGroupItems(GrabbableObject item, List<GrabbableObject> items, HashSet<GrabbableObject> visited)
			{
				if ((Object)(object)item == (Object)null || !visited.Add(item))
				{
					return;
				}
				items.Add(item);
				BeltBagItem val = (BeltBagItem)(object)((item is BeltBagItem) ? item : null);
				if (val != null && val.objectsInBag != null)
				{
					GrabbableObject[] array = val.objectsInBag.ToArray();
					for (int i = 0; i < array.Length; i++)
					{
						CollectGroupItems(array[i], items, visited);
					}
				}
			}

			private static int ItemScrapValue(GrabbableObject item)
			{
				if (!((Object)(object)item?.itemProperties != (Object)null) || !item.itemProperties.isScrap)
				{
					return 0;
				}
				return Math.Max(0, item.scrapValue);
			}
		}

		private static readonly HashSet<NetworkObject> ProtectedObjects = new HashSet<NetworkObject>();

		private static bool _active;

		internal static bool IsProtected(NetworkObject networkObject)
		{
			if (_active && (Object)(object)networkObject != (Object)null)
			{
				return ProtectedObjects.Contains(networkObject);
			}
			return false;
		}

		internal static void Begin()
		{
			Clear();
			if (!Plugin.Enabled.Value)
			{
				return;
			}
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || !((NetworkBehaviour)instance).IsServer || !instance.allPlayersDead)
			{
				return;
			}
			CabinetDetector cabinetDetector = CabinetDetector.Create(instance);
			if (cabinetDetector == null)
			{
				Plugin.Log.LogWarning((object)"A full-crew wipe was detected, but the ship cupboard could not be located. No items were protected.");
				return;
			}
			GrabbableObject[] array = Object.FindObjectsByType<GrabbableObject>((FindObjectsInactive)1, (FindObjectsSortMode)0);
			HashSet<GrabbableObject> hashSet = FindContainedItems(array);
			List<ItemGroup> list = new List<ItemGroup>();
			GrabbableObject[] array2 = array;
			foreach (GrabbableObject val in array2)
			{
				if (!((Object)(object)val == (Object)null) && !val.deactivated && !val.isHeld && !val.isHeldByEnemy && !hashSet.Contains(val))
				{
					bool flag = cabinetDetector.Contains(val);
					if (Plugin.VerboseLogging.Value)
					{
						Plugin.Log.LogInfo((object)("Cupboard check: " + ItemName(val) + " => " + (flag ? "inside" : "outside")));
					}
					if (flag)
					{
						list.Add(ItemGroup.FromRoot(val));
					}
				}
			}
			if (list.Count == 0)
			{
				Plugin.Log.LogInfo((object)"Full-crew wipe: the cupboard was empty, so no items were protected.");
				Clear();
				return;
			}
			bool perItemLimitEnabled = Plugin.EnablePerItemValueLimit.Value;
			int perItemLimit = Plugin.MaximumProtectedItemScrapValue.Value;
			List<ItemGroup> list2 = list.Where((ItemGroup group) => !perItemLimitEnabled || group.MaximumIndividualScrapValue <= perItemLimit).ToList();
			List<ItemGroup> list3 = list.Where((ItemGroup group) => perItemLimitEnabled && group.MaximumIndividualScrapValue > perItemLimit).ToList();
			bool value = Plugin.EnableTotalValueLimit.Value;
			int value2 = Plugin.MaximumProtectedTotalScrapValue.Value;
			List<ItemGroup> list4 = (value ? SelectGroupsWithinValueLimit(list2, value2) : list2.ToList());
			int num = 0;
			foreach (ItemGroup item in list4)
			{
				foreach (GrabbableObject item2 in item.Items)
				{
					num += (ProtectSingle(item2) ? 1 : 0);
				}
			}
			int num2 = list.Sum((ItemGroup group) => group.ScrapValue);
			int num3 = list4.Sum((ItemGroup group) => group.ScrapValue);
			int num4 = list2.Count - list4.Count;
			int num5 = list3.Count + num4;
			if (Plugin.VerboseLogging.Value && num5 > 0)
			{
				foreach (ItemGroup item3 in list3)
				{
					Plugin.Log.LogInfo((object)$"Per-item limit: removing {item3.Label} group because it contains scrap worth ${item3.MaximumIndividualScrapValue}, above the ${perItemLimit} limit.");
				}
				HashSet<ItemGroup> selectedSet = new HashSet<ItemGroup>(list4);
				foreach (ItemGroup item4 in list2.Where((ItemGroup group) => !selectedSet.Contains(group)))
				{
					Plugin.Log.LogInfo((object)$"Total value limit: removing {item4.Label} group worth ${item4.ScrapValue}.");
				}
			}
			if (num == 0)
			{
				Plugin.Log.LogInfo((object)$"Full-crew wipe: cupboard value was ${num2}, but no item group passed the enabled value limits.");
				Clear();
				return;
			}
			_active = true;
			string text = (perItemLimitEnabled ? $"${perItemLimit}" : "off");
			string text2 = (value ? $"${value2}" : "off");
			Plugin.Log.LogInfo((object)$"Full-crew wipe: protected {num} cupboard item object(s) worth ${num3} of ${num2}. Per-item limit: {text}; total limit: {text2}. {num5} over-limit group(s) will be removed normally.");
		}

		private static HashSet<GrabbableObject> FindContainedItems(IEnumerable<GrabbableObject> items)
		{
			HashSet<GrabbableObject> hashSet = new HashSet<GrabbableObject>();
			foreach (BeltBagItem item in items.OfType<BeltBagItem>())
			{
				AddContainedItems(item, hashSet);
			}
			return hashSet;
		}

		private static void AddContainedItems(BeltBagItem beltBag, HashSet<GrabbableObject> contained)
		{
			if (beltBag.objectsInBag == null)
			{
				return;
			}
			GrabbableObject[] array = beltBag.objectsInBag.ToArray();
			foreach (GrabbableObject val in array)
			{
				if (!((Object)(object)val == (Object)null) && contained.Add(val))
				{
					BeltBagItem val2 = (BeltBagItem)(object)((val is BeltBagItem) ? val : null);
					if (val2 != null)
					{
						AddContainedItems(val2, contained);
					}
				}
			}
		}

		private static List<ItemGroup> SelectGroupsWithinValueLimit(List<ItemGroup> groups, int configuredLimit)
		{
			List<ItemGroup> list = groups.Where((ItemGroup group) => group.ScrapValue == 0).ToList();
			List<ItemGroup> list2 = (from @group in groups
				where @group.ScrapValue > 0
				orderby @group.NetworkObjectId
				select @group).ThenBy<ItemGroup, string>((ItemGroup group) => group.Label, StringComparer.Ordinal).ToList();
			int val = list2.Sum((ItemGroup group) => group.ScrapValue);
			int num = Math.Min(configuredLimit, val);
			if (num <= 0)
			{
				return list;
			}
			bool[] array = new bool[num + 1];
			int[] array2 = Enumerable.Repeat(-1, num + 1).ToArray();
			int[] array3 = Enumerable.Repeat(-1, num + 1).ToArray();
			array[0] = true;
			for (int num2 = 0; num2 < list2.Count; num2++)
			{
				int scrapValue = list2[num2].ScrapValue;
				if (scrapValue > num)
				{
					continue;
				}
				for (int num3 = num - scrapValue; num3 >= 0; num3--)
				{
					int num4 = num3 + scrapValue;
					if (array[num3] && !array[num4])
					{
						array[num4] = true;
						array2[num4] = num3;
						array3[num4] = num2;
					}
				}
			}
			int num5 = num;
			while (num5 > 0 && !array[num5])
			{
				num5--;
			}
			while (num5 > 0)
			{
				int num6 = array3[num5];
				if (num6 < 0)
				{
					break;
				}
				list.Add(list2[num6]);
				num5 = array2[num5];
			}
			return list;
		}

		private static bool ProtectSingle(GrabbableObject item)
		{
			NetworkObject networkObject = ((NetworkBehaviour)item).NetworkObject;
			if ((Object)(object)networkObject == (Object)null || !networkObject.IsSpawned)
			{
				Plugin.Log.LogWarning((object)("Could not protect " + ItemName(item) + " because it has no spawned NetworkObject."));
				return false;
			}
			item.isInShipRoom = true;
			item.isInElevator = true;
			item.isInFactory = false;
			item.scrapPersistedThroughRounds = true;
			return ProtectedObjects.Add(networkObject);
		}

		internal static void Clear()
		{
			_active = false;
			ProtectedObjects.Clear();
		}

		private static string ItemName(GrabbableObject item)
		{
			object obj;
			if (!((Object)(object)item?.itemProperties != (Object)null))
			{
				obj = ((item != null) ? ((Object)item).name : null);
				if (obj == null)
				{
					return "unknown item";
				}
			}
			else
			{
				obj = item.itemProperties.itemName;
			}
			return (string)obj;
		}
	}
	internal sealed class CabinetDetector
	{
		private static readonly string[] CabinetNameHints = new string[4] { "closet", "cupboard", "cabinet", "storage" };

		private readonly Transform _root;

		private readonly PlaceableObjectsSurface[] _surfaces;

		private readonly Bounds? _cabinetBounds;

		private CabinetDetector(Transform root, PlaceableObjectsSurface[] surfaces, Bounds? cabinetBounds)
		{
			_root = root;
			_surfaces = surfaces;
			_cabinetBounds = cabinetBounds;
		}

		internal static CabinetDetector Create(StartOfRound round)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)round.closetLeftDoor == (Object)null || (Object)(object)round.closetRightDoor == (Object)null)
			{
				return null;
			}
			Transform transform = ((Component)round.closetLeftDoor).transform;
			Transform transform2 = ((Component)round.closetRightDoor).transform;
			Transform root = FindCabinetRoot(transform, transform2);
			Vector3 doorMidpoint = (transform.position + transform2.position) * 0.5f;
			Bounds? cabinetBounds = TryGetCabinetBounds(root, transform, transform2);
			if (!cabinetBounds.HasValue)
			{
				root = null;
			}
			PlaceableObjectsSurface[] source = Object.FindObjectsByType<PlaceableObjectsSurface>((FindObjectsInactive)1, (FindObjectsSortMode)0);
			PlaceableObjectsSurface[] array = source.Where((PlaceableObjectsSurface surface) => IsCupboardSurface(surface, root)).ToArray();
			if (array.Length == 0)
			{
				array = (from surface in source
					where (Object)(object)surface != (Object)null && (Object)(object)surface.placeableBounds != (Object)null
					where SurfaceDistanceSquared(surface, doorMidpoint) <= 2.25f
					orderby SurfaceDistanceSquared(surface, doorMidpoint)
					select surface).Take(4).ToArray();
			}
			if (array.Length == 0 && !cabinetBounds.HasValue)
			{
				return null;
			}
			if (Plugin.VerboseLogging.Value)
			{
				string arg = (((Object)(object)root != (Object)null) ? HierarchyPath(root) : "unresolved");
				Plugin.Log.LogInfo((object)string.Format("Cupboard detector: root={0}, shelf surfaces={1}, bounds={2}", arg, array.Length, cabinetBounds.HasValue ? ((object)cabinetBounds.Value/*cast due to .constrained prefix*/).ToString() : "none"));
			}
			return new CabinetDetector(root, array, cabinetBounds);
		}

		internal bool Contains(GrabbableObject item)
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)item.parentObject != (Object)null && IsWithinRoot(item.parentObject, _root))
			{
				return true;
			}
			Transform parent = ((Component)item).transform.parent;
			if ((Object)(object)parent != (Object)null && IsWithinRoot(parent, _root))
			{
				return true;
			}
			PlaceableObjectsSurface[] surfaces = _surfaces;
			foreach (PlaceableObjectsSurface surface in surfaces)
			{
				if (IsOnSurface(item, surface))
				{
					return true;
				}
			}
			if (!_cabinetBounds.HasValue)
			{
				return false;
			}
			Bounds value = _cabinetBounds.Value;
			((Bounds)(ref value)).Expand(Mathf.Max(0f, Plugin.CabinetBoundsPadding.Value) * 2f);
			if (((Bounds)(ref value)).Contains(((Component)item).transform.position))
			{
				return true;
			}
			Renderer mainObjectRenderer = (Renderer)(object)item.mainObjectRenderer;
			if ((Object)(object)mainObjectRenderer != (Object)null)
			{
				Bounds bounds = mainObjectRenderer.bounds;
				return ((Bounds)(ref value)).Contains(((Bounds)(ref bounds)).center);
			}
			return false;
		}

		private static bool IsOnSurface(GrabbableObject item, PlaceableObjectsSurface surface)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)surface == (Object)null || (Object)(object)surface.placeableBounds == (Object)null)
			{
				return false;
			}
			float num = (((Object)(object)item.itemProperties != (Object)null) ? item.itemProperties.verticalOffset : 0f);
			Vector3 val = ((Component)item).transform.position - ((Component)surface.placeableBounds).transform.up * num;
			Vector3 val2 = surface.placeableBounds.ClosestPoint(val);
			float num2 = Mathf.Max(0.01f, Plugin.SurfaceTolerance.Value);
			Vector3 val3 = val2 - val;
			return ((Vector3)(ref val3)).sqrMagnitude <= num2 * num2;
		}

		private static bool IsCupboardSurface(PlaceableObjectsSurface surface, Transform root)
		{
			if ((Object)(object)surface == (Object)null || (Object)(object)surface.placeableBounds == (Object)null || (Object)(object)root == (Object)null)
			{
				return false;
			}
			if (IsWithinRoot(((Component)surface).transform, root) || IsWithinRoot(((Component)surface.placeableBounds).transform, root))
			{
				return true;
			}
			if ((Object)(object)surface.parentTo != (Object)null)
			{
				return IsWithinRoot(((Component)surface.parentTo).transform, root);
			}
			return false;
		}

		private static float SurfaceDistanceSquared(PlaceableObjectsSurface surface, Vector3 point)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: 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_0012: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = surface.placeableBounds.ClosestPoint(point) - point;
			return ((Vector3)(ref val)).sqrMagnitude;
		}

		private static Transform FindCabinetRoot(Transform leftDoor, Transform rightDoor)
		{
			HashSet<Transform> hashSet = new HashSet<Transform>();
			Transform val = rightDoor;
			while ((Object)(object)val != (Object)null)
			{
				hashSet.Add(val);
				val = val.parent;
			}
			Transform val2 = null;
			while ((Object)(object)leftDoor != (Object)null)
			{
				if (hashSet.Contains(leftDoor))
				{
					if (val2 == null)
					{
						val2 = leftDoor;
					}
					if (CabinetNameHints.Any((string hint) => ((Object)leftDoor).name.IndexOf(hint, StringComparison.OrdinalIgnoreCase) >= 0))
					{
						return leftDoor;
					}
				}
				leftDoor = leftDoor.parent;
			}
			return val2;
		}

		private static Bounds? TryGetCabinetBounds(Transform root, Transform leftDoor, Transform rightDoor)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: 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_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)root == (Object)null)
			{
				return null;
			}
			Renderer[] componentsInChildren = ((Component)root).GetComponentsInChildren<Renderer>(true);
			Collider[] componentsInChildren2 = ((Component)root).GetComponentsInChildren<Collider>(true);
			bool flag = false;
			Bounds value = default(Bounds);
			Renderer[] array = componentsInChildren;
			foreach (Renderer val in array)
			{
				if (!((Object)(object)val == (Object)null) && !IsDoorPart(((Component)val).transform, root, leftDoor, rightDoor))
				{
					if (!flag)
					{
						value = val.bounds;
						flag = true;
					}
					else
					{
						((Bounds)(ref value)).Encapsulate(val.bounds);
					}
				}
			}
			if (!flag)
			{
				Collider[] array2 = componentsInChildren2;
				foreach (Collider val2 in array2)
				{
					if (!((Object)(object)val2 == (Object)null) && !IsDoorPart(((Component)val2).transform, root, leftDoor, rightDoor))
					{
						if (!flag)
						{
							value = val2.bounds;
							flag = true;
						}
						else
						{
							((Bounds)(ref value)).Encapsulate(val2.bounds);
						}
					}
				}
			}
			if (!flag)
			{
				return null;
			}
			Vector3 size = ((Bounds)(ref value)).size;
			if (size.x > 7f || size.y > 7f || size.z > 7f)
			{
				Plugin.Log.LogWarning((object)$"Rejected unusually large cupboard bounds ({size}); shelf detection will be used instead.");
				return null;
			}
			return value;
		}

		private static bool IsDoorPart(Transform candidate, Transform root, Transform leftDoor, Transform rightDoor)
		{
			bool num = (Object)(object)leftDoor != (Object)(object)root && IsWithinRoot(candidate, leftDoor);
			bool flag = (Object)(object)rightDoor != (Object)(object)root && IsWithinRoot(candidate, rightDoor);
			return num || flag;
		}

		private static bool IsWithinRoot(Transform candidate, Transform root)
		{
			if ((Object)(object)candidate != (Object)null && (Object)(object)root != (Object)null)
			{
				if (!((Object)(object)candidate == (Object)(object)root))
				{
					return candidate.IsChildOf(root);
				}
				return true;
			}
			return false;
		}

		private static string HierarchyPath(Transform transform)
		{
			List<string> list = new List<string>();
			Transform val = transform;
			while ((Object)(object)val != (Object)null)
			{
				list.Add(((Object)val).name);
				val = val.parent;
			}
			list.Reverse();
			return string.Join("/", list);
		}
	}
	[HarmonyPatch(typeof(RoundManager), "DespawnPropsAtEndOfRound")]
	internal static class RoundCleanupPatch
	{
		[HarmonyPrefix]
		private static void Prefix()
		{
			ProtectionState.Begin();
		}

		[HarmonyFinalizer]
		private static Exception Finalizer(Exception __exception)
		{
			ProtectionState.Clear();
			return __exception;
		}
	}
	[HarmonyPatch(typeof(NetworkObject), "Despawn", new Type[] { typeof(bool) })]
	internal static class NetworkObjectDespawnPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(NetworkObject __instance)
		{
			return !ProtectionState.IsProtected(__instance);
		}
	}
}