Decompiled source of CraftFromContainersEvolved v1.0.3

CraftFromContainersEvolved.Core.dll

Decompiled 21 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("KodeMan")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 KodeMan")]
[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: AssemblyInformationalVersion("1.0.3+bfaa1afefa5f05c6be8b24c474b2b23abced2cbf")]
[assembly: AssemblyProduct("Craft From Containers Evolved")]
[assembly: AssemblyTitle("CraftFromContainersEvolved.Core")]
[assembly: AssemblyVersion("1.0.3.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
	[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 CraftFromContainersEvolved.Core
{
	public readonly struct AccessDecision
	{
		public static readonly AccessDecision Allow = new AccessDecision(allowed: true, DenyReason.None);

		public bool Allowed { get; }

		public DenyReason Reason { get; }

		private AccessDecision(bool allowed, DenyReason reason)
		{
			Allowed = allowed;
			Reason = reason;
		}

		public static AccessDecision Deny(DenyReason reason)
		{
			return new AccessDecision(allowed: false, reason);
		}
	}
	public enum AccessMode
	{
		Vanilla,
		WardStrict,
		OwnerStrict
	}
	public static class AccessPolicy
	{
		public static AccessDecision Evaluate(AccessMode mode, ContainerCandidate candidate, long playerId, bool includeWardPermitted)
		{
			if (!candidate.HasInventory)
			{
				return AccessDecision.Deny(DenyReason.NoInventory);
			}
			if (candidate.IsInUse)
			{
				return AccessDecision.Deny(DenyReason.InUse);
			}
			if (!candidate.TypeAllowed)
			{
				return AccessDecision.Deny(DenyReason.FilteredType);
			}
			if (!candidate.InRange)
			{
				return AccessDecision.Deny(DenyReason.OutOfRange);
			}
			if (candidate.Privacy switch
			{
				ContainerPrivacy.Public => 1, 
				ContainerPrivacy.Private => (candidate.HasPiece && candidate.CreatorId == playerId) ? 1 : 0, 
				_ => 0, 
			} == 0)
			{
				if (candidate.Privacy != ContainerPrivacy.Private || !candidate.HasPiece)
				{
					return AccessDecision.Deny(DenyReason.OpenAccessDenied);
				}
				return AccessDecision.Deny(DenyReason.NotOwner);
			}
			if ((candidate.CheckGuardStone || mode >= AccessMode.WardStrict) && !candidate.WardAllows)
			{
				return AccessDecision.Deny(DenyReason.WardDenied);
			}
			if (mode == AccessMode.OwnerStrict)
			{
				bool num = candidate.HasPiece && candidate.CreatorId == playerId;
				bool flag = includeWardPermitted && candidate.InsideWard && candidate.WardAllows;
				if (!num && !flag)
				{
					if (!candidate.HasPiece)
					{
						return AccessDecision.Deny(DenyReason.NoCreator);
					}
					return AccessDecision.Deny(DenyReason.NotOwner);
				}
			}
			return AccessDecision.Allow;
		}
	}
	public sealed class ContainerCandidate
	{
		public bool HasInventory = true;

		public bool TypeAllowed = true;

		public bool InRange = true;

		public bool IsInUse;

		public bool HasPiece;

		public long CreatorId;

		public bool CheckGuardStone;

		public bool WardAllows = true;

		public bool InsideWard;

		public ContainerPrivacy Privacy = ContainerPrivacy.Public;
	}
	public enum ContainerPrivacy
	{
		Private,
		Group,
		Public
	}
	public enum DenyReason
	{
		None,
		NoInventory,
		InUse,
		FilteredType,
		OutOfRange,
		OpenAccessDenied,
		NotOwner,
		NoCreator,
		WardDenied
	}
	public static class PeerClaim
	{
		public static bool IsValid(long claimedPlayerId, long actualPlayerId)
		{
			if (claimedPlayerId != 0L && actualPlayerId != 0L)
			{
				return claimedPlayerId == actualPlayerId;
			}
			return false;
		}
	}
	public static class StackMath
	{
		public static int UsableStackCount(int stackSize, bool leaveOne)
		{
			if (stackSize <= 0)
			{
				return 0;
			}
			if (!leaveOne)
			{
				return stackSize;
			}
			return stackSize - 1;
		}

		public static int CountUsable(IReadOnlyList<int> stackSizes, bool leaveOne)
		{
			if (stackSizes == null)
			{
				return 0;
			}
			int num = 0;
			for (int i = 0; i < stackSizes.Count; i++)
			{
				num += UsableStackCount(stackSizes[i], leaveOne);
			}
			return num;
		}
	}
}

CraftFromContainersEvolved.dll

Decompiled 21 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CraftFromContainersEvolved.Core;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("KodeMan")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 KodeMan")]
[assembly: AssemblyFileVersion("1.0.3.0")]
[assembly: AssemblyInformationalVersion("1.0.3+bfaa1afefa5f05c6be8b24c474b2b23abced2cbf")]
[assembly: AssemblyProduct("Craft From Containers Evolved")]
[assembly: AssemblyTitle("CraftFromContainersEvolved")]
[assembly: AssemblyVersion("1.0.3.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 CraftFromContainersEvolved
{
	internal static class AccessGate
	{
		internal static bool TryGetDecision(Container container, long playerId, out AccessDecision decision)
		{
			//IL_0002: 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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: 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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Expected O, but got Unknown
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			decision = AccessDecision.Deny((DenyReason)1);
			if ((Object)(object)container == (Object)null || playerId == 0L)
			{
				return false;
			}
			ContainerInfo containerInfo = ContainerInfo.FromContainer(container);
			Piece componentInParent = ((Component)container).GetComponentInParent<Piece>();
			WardProbe.Probe(((Component)container).transform.position, playerId, out var wardAllows, out var insideWard);
			ContainerCandidate val = new ContainerCandidate
			{
				HasInventory = (container.GetInventory() != null),
				TypeAllowed = ContainerTypeFilter.IsAllowed(containerInfo.Name, containerInfo.IsWagon, containerInfo.IsShip, containerInfo.IsTombstone),
				InRange = true,
				IsInUse = container.IsInUse(),
				HasPiece = ((Object)(object)componentInParent != (Object)null),
				CreatorId = (((Object)(object)componentInParent != (Object)null) ? componentInParent.GetCreator() : 0),
				Privacy = containerInfo.Privacy,
				CheckGuardStone = containerInfo.CheckGuardStone,
				WardAllows = wardAllows,
				InsideWard = insideWard
			};
			decision = AccessPolicy.Evaluate(PolicyState.EffectiveMode, val, playerId, PolicyState.EffectiveIncludeWardPermitted);
			return ((AccessDecision)(ref decision)).Allowed;
		}
	}
	internal static class ContainerRegistry
	{
		private static readonly List<Container> Containers = new List<Container>();

		internal static int Count => Containers.Count;

		internal static void Add(Container container)
		{
			if (!((Object)(object)container == (Object)null))
			{
				Containers.Add(container);
				ContainerScanner.Invalidate();
			}
		}

		internal static void Remove(Container container)
		{
			if (!((Object)(object)container == (Object)null) && Containers.Remove(container))
			{
				ContainerScanner.Invalidate();
			}
		}

		internal static void Collect(Vector3 center, float range, List<Container> results)
		{
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			results.Clear();
			if (Containers.Count == 0)
			{
				return;
			}
			float num = ((range > 0f) ? (range * range) : (-1f));
			for (int num2 = Containers.Count - 1; num2 >= 0; num2--)
			{
				Container val = Containers[num2];
				if ((Object)(object)val == (Object)null)
				{
					Containers.RemoveAt(num2);
				}
				else
				{
					if (!((Behaviour)val).isActiveAndEnabled)
					{
						continue;
					}
					if (num > 0f)
					{
						Vector3 val2 = ((Component)val).transform.position - center;
						if (((Vector3)(ref val2)).sqrMagnitude > num)
						{
							continue;
						}
					}
					results.Add(val);
				}
			}
		}
	}
	internal static class ContainerScanner
	{
		private const float MoveToleranceSquared = 0.25f;

		private const int CacheRefreshFrames = 30;

		private static readonly List<Container> Candidates = new List<Container>();

		private static readonly List<Container> Allowed = new List<Container>();

		private static Vector3 _cacheCenter;

		private static Vector3 _sortCenter;

		private static float _cacheRange = -1f;

		private static int _cacheRegistryCount = -1;

		private static int _cacheFrame = -2147483647;

		private static bool _cacheValid;

		internal static List<Container> GetAllowedContainers(Player player)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: 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_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				Allowed.Clear();
				return Allowed;
			}
			float effectiveRange = PolicyState.EffectiveRange;
			Vector3 position = ((Component)player).transform.position;
			if (_cacheValid && _cacheRegistryCount == ContainerRegistry.Count)
			{
				Vector3 val = position - _cacheCenter;
				if (!(((Vector3)(ref val)).sqrMagnitude > 0.25f) && !(Mathf.Abs(effectiveRange - _cacheRange) > 0.01f) && Time.frameCount - _cacheFrame <= 30)
				{
					return Allowed;
				}
			}
			ContainerRegistry.Collect(position, effectiveRange, Candidates);
			Allowed.Clear();
			long playerID = player.GetPlayerID();
			for (int i = 0; i < Candidates.Count; i++)
			{
				Container val2 = Candidates[i];
				if (AccessGate.TryGetDecision(val2, playerID, out var decision))
				{
					Allowed.Add(val2);
				}
				else if (ModConfig.Debug.Value)
				{
					CraftFromContainersEvolvedPlugin.Dbg("Skipping container " + ((Object)val2).name + ": " + ((object)((AccessDecision)(ref decision)).Reason/*cast due to .constrained prefix*/).ToString());
				}
			}
			if (ModConfig.PullByDistance.Value && Allowed.Count > 1)
			{
				_sortCenter = position;
				Allowed.Sort(CompareByDistance);
			}
			_cacheCenter = position;
			_cacheRange = effectiveRange;
			_cacheRegistryCount = ContainerRegistry.Count;
			_cacheFrame = Time.frameCount;
			_cacheValid = true;
			return Allowed;
		}

		internal static void Invalidate()
		{
			_cacheValid = false;
			_cacheRegistryCount = -1;
			_cacheFrame = -2147483647;
		}

		private static int CompareByDistance(Container a, Container b)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//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_0035: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = ((Component)a).transform.position - _sortCenter;
			float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
			val = ((Component)b).transform.position - _sortCenter;
			return sqrMagnitude.CompareTo(((Vector3)(ref val)).sqrMagnitude);
		}
	}
	internal static class ContainerTypeFilter
	{
		internal static bool IsAllowed(string name, bool isWagon, bool isShip, bool isTombstone)
		{
			if (isShip && PolicyState.IgnoreShipContainers)
			{
				return false;
			}
			if (isWagon && PolicyState.IgnoreWagonContainers)
			{
				return false;
			}
			if (isTombstone && PolicyState.IgnoreTombstones)
			{
				return false;
			}
			string text = name ?? string.Empty;
			if (PolicyState.IgnorePrivateChests && text.StartsWith("piece_chest_private"))
			{
				return false;
			}
			if (PolicyState.IgnoreWoodChests && text.StartsWith("piece_chest_wood"))
			{
				return false;
			}
			if (PolicyState.IgnoreBlackMetalChests && text.StartsWith("piece_chest_blackmetal"))
			{
				return false;
			}
			if (PolicyState.IgnoreReinforcedChests && text.StartsWith("piece_chest") && !text.StartsWith("piece_chest_private") && !text.StartsWith("piece_chest_wood") && !text.StartsWith("piece_chest_blackmetal"))
			{
				return false;
			}
			return true;
		}
	}
	internal static class EnforcementSelfTest
	{
		private static readonly string[,] Targets = new string[12, 2]
		{
			{ "ZDOMan", "RPC_ZDOData" },
			{ "ZDO", "Deserialize" },
			{ "ZDO", "set_OwnerRevision" },
			{ "ZDO", "set_DataRevision" },
			{ "ZDO", "SetOwnerInternal" },
			{ "Container", "RPC_RequestOpen" },
			{ "Container", "RPC_RequestStack" },
			{ "Container", "RPC_RequestTakeAll" },
			{ "PrivateArea", "IsEnabled" },
			{ "PrivateArea", "IsInside" },
			{ "PrivateArea", "IsPermitted" },
			{ "PrivateArea", "m_allAreas" }
		};

		internal static void Run()
		{
			int length = Targets.GetLength(0);
			int num = 0;
			for (int i = 0; i < length; i++)
			{
				string text = Targets[i, 0];
				string text2 = Targets[i, 1];
				Type type = AccessTools.TypeByName(text);
				if ((!(text2 == "m_allAreas")) ? (type != null && AccessTools.Method(type, text2, (Type[])null, (Type[])null) != null) : (type != null && AccessTools.Field(type, text2) != null))
				{
					num++;
				}
				else
				{
					CraftFromContainersEvolvedPlugin.Warn("Server enforcement target missing: " + text + "." + text2);
				}
			}
			length++;
			if (WardProbe.DelegatesReady)
			{
				num++;
			}
			else
			{
				CraftFromContainersEvolvedPlugin.Warn("Server enforcement target missing: ward probe delegates (PrivateArea reflection).");
			}
			if (num == length)
			{
				CraftFromContainersEvolvedPlugin.LogMessage("Server enforcement targets resolved: " + num + "/" + length + ".");
			}
			else
			{
				CraftFromContainersEvolvedPlugin.Warn("Server enforcement targets resolved: " + num + "/" + length + ". Container write protection may be incomplete.");
			}
		}
	}
	internal static class InventoryUtil
	{
		private static readonly List<ItemData> StackBuffer = new List<ItemData>();

		internal static int CountInContainers(IReadOnlyList<Container> containers, string sharedName, int quality, bool worldLevelBased, bool leaveOne)
		{
			int num = 0;
			for (int i = 0; i < containers.Count; i++)
			{
				Inventory val = (((Object)(object)containers[i] != (Object)null) ? containers[i].GetInventory() : null);
				if (val != null)
				{
					num += CountUsable(val, sharedName, quality, worldLevelBased, leaveOne);
				}
			}
			return num;
		}

		internal static int BestQualityCount(Player player, IReadOnlyList<Container> containers, string sharedName, int maxQuality, bool leaveOne)
		{
			int num = 0;
			Inventory inventory = ((Humanoid)player).GetInventory();
			for (int i = 1; i <= maxQuality; i++)
			{
				int num2 = inventory.CountItems(sharedName, i, true) + CountInContainers(containers, sharedName, i, worldLevelBased: true, leaveOne);
				if (num2 > num)
				{
					num = num2;
				}
			}
			return num;
		}

		internal static int CountUsable(Inventory inventory, string sharedName, int quality, bool worldLevelBased, bool leaveOne)
		{
			if (!leaveOne)
			{
				return inventory.CountItems(sharedName, quality, worldLevelBased);
			}
			StackBuffer.Clear();
			inventory.GetAllItems(sharedName, StackBuffer);
			int num = 0;
			for (int i = 0; i < StackBuffer.Count; i++)
			{
				ItemData val = StackBuffer[i];
				if (val != null && val.m_shared != null && !(val.m_shared.m_name != sharedName) && (quality < 0 || val.m_quality == quality) && (!worldLevelBased || val.m_worldLevel >= Game.m_worldLevel) && val.m_stack > 1)
				{
					num += val.m_stack - 1;
				}
			}
			return num;
		}

		internal static int Consume(Inventory inventory, string sharedName, int quality, int amount)
		{
			if (inventory == null || amount <= 0)
			{
				return 0;
			}
			int num = inventory.CountItems(sharedName, quality, true);
			if (num <= 0)
			{
				return 0;
			}
			int num2 = Math.Min(num, amount);
			inventory.RemoveItem(sharedName, num2, quality, true);
			int num3 = inventory.CountItems(sharedName, quality, true);
			return num - num3;
		}

		internal static int ConsumeFromContainers(IReadOnlyList<Container> containers, string sharedName, int quality, int amount)
		{
			int num = amount;
			for (int i = 0; i < containers.Count; i++)
			{
				if (num <= 0)
				{
					break;
				}
				Container val = containers[i];
				if (!((Object)(object)val == (Object)null))
				{
					num -= Consume(val.GetInventory(), sharedName, quality, num);
				}
			}
			return amount - num;
		}
	}
	internal static class ModConfig
	{
		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> Debug;

		internal static ConfigEntry<AccessMode> Mode;

		internal static ConfigEntry<bool> IncludeWardPermitted;

		internal static ConfigEntry<float> ContainerRange;

		internal static ConfigEntry<bool> ApplyToCrafting;

		internal static ConfigEntry<bool> ApplyToBuilding;

		internal static ConfigEntry<bool> PullByDistance;

		internal static ConfigEntry<bool> LeaveOne;

		internal static ConfigEntry<bool> IgnoreShipContainers;

		internal static ConfigEntry<bool> IgnoreWagonContainers;

		internal static ConfigEntry<bool> IgnoreWoodChests;

		internal static ConfigEntry<bool> IgnoreReinforcedChests;

		internal static ConfigEntry<bool> IgnoreBlackMetalChests;

		internal static ConfigEntry<bool> IgnorePrivateChests;

		internal static ConfigEntry<bool> IgnoreTombstones;

		internal static ConfigEntry<bool> ShowContainerCounts;

		internal static ConfigEntry<string> ResourceCostString;

		internal static ConfigEntry<string> ContainerAvailableColor;

		internal static ConfigEntry<string> InsufficientColor;

		internal static ConfigEntry<bool> EnforceServerPolicy;

		internal static ConfigEntry<bool> ServerEnforcement;

		internal static ConfigEntry<bool> EnforceModeOnWrites;

		internal static ConfigEntry<bool> LogDeniedAttempts;

		internal static ConfigEntry<bool> NotifyDeniedPlayers;

		internal static Color AvailableColor = new Color(1f, 0.92f, 0.02f, 1f);

		internal static Color MissingColor = new Color(1f, 0.25f, 0.25f, 1f);

		internal static void Init(ConfigFile config)
		{
			Enabled = config.Bind<bool>("General", "Enabled", true, "Enable this mod.");
			Debug = config.Bind<bool>("General", "Debug", false, "Log verbose diagnostics.");
			Mode = config.Bind<AccessMode>("Security", "AccessMode", (AccessMode)2, "Vanilla: only mirror the game's own container access checks.\nWardStrict: additionally never use containers inside an enabled ward without permission.\nOwnerStrict: only use containers created by you (plus ward-shared containers when enabled below).");
			IncludeWardPermitted = config.Bind<bool>("Security", "IncludeWardPermitted", true, "In OwnerStrict mode, also allow containers inside an enabled ward you have permission in.");
			ContainerRange = config.Bind<float>("General", "ContainerRange", 10f, "Maximum distance to pull from.");
			ApplyToCrafting = config.Bind<bool>("General", "ApplyToCrafting", true, "Use container resources when crafting and upgrading.");
			ApplyToBuilding = config.Bind<bool>("General", "ApplyToBuilding", true, "Use container resources when building.");
			PullByDistance = config.Bind<bool>("General", "PullByDistance", true, "Pull from closest containers first.");
			LeaveOne = config.Bind<bool>("General", "LeaveOne", false, "Keep at least one item in every container stack. Recipes that would need the last item of a stack are treated as not craftable.");
			IgnoreShipContainers = config.Bind<bool>("Container Types", "IgnoreShipContainers", false, "Ignore containers on ships.");
			IgnoreWagonContainers = config.Bind<bool>("Container Types", "IgnoreWagonContainers", false, "Ignore containers on carts.");
			IgnoreWoodChests = config.Bind<bool>("Container Types", "IgnoreWoodChests", false, "Ignore wood chests.");
			IgnoreReinforcedChests = config.Bind<bool>("Container Types", "IgnoreReinforcedChests", false, "Ignore reinforced chests.");
			IgnoreBlackMetalChests = config.Bind<bool>("Container Types", "IgnoreBlackMetalChests", false, "Ignore black metal chests.");
			IgnorePrivateChests = config.Bind<bool>("Container Types", "IgnorePrivateChests", false, "Ignore personal chests.");
			IgnoreTombstones = config.Bind<bool>("Container Types", "IgnoreTombstones", true, "Ignore tombstones.");
			ShowContainerCounts = config.Bind<bool>("UI", "ShowContainerCounts", true, "Show combined player and container resource counts in the crafting UI.");
			ResourceCostString = config.Bind<string>("UI", "ResourceCostString", "{0}/{1}", "{0} is the available amount including containers, {1} is the required amount.");
			ContainerAvailableColor = config.Bind<string>("UI", "ContainerAvailableColor", "FFEB04", "Hex colour used when a resource is only available in containers.");
			InsufficientColor = config.Bind<string>("UI", "InsufficientColor", "FF4040", "Hex colour used when a resource is missing entirely.");
			EnforceServerPolicy = config.Bind<bool>("Server", "EnforceServerPolicy", true, "When connected to a server running this mod, apply the server's security policy (clients may only be stricter, never looser).");
			ServerEnforcement = config.Bind<bool>("Server", "ServerEnforcement", true, "When running on a server (host or dedicated), validate container writes and container RPCs server-side so modified clients cannot bypass access rules.");
			EnforceModeOnWrites = config.Bind<bool>("Server", "EnforceModeOnWrites", true, "Apply the AccessMode policy to container writes, not just the game's own checks. With OwnerStrict, only creators (and ward-permitted players) can modify a container. Set false for pure vanilla access parity.");
			LogDeniedAttempts = config.Bind<bool>("Server", "LogDeniedAttempts", true, "Log denied container writes and RPCs with player, position and reason (rate limited).");
			NotifyDeniedPlayers = config.Bind<bool>("Server", "NotifyDeniedPlayers", true, "Send a message to modded clients when the server denies access to a container. Clients without the mod still get no message.");
			RefreshColors();
			config.SettingChanged += delegate
			{
				RefreshColors();
			};
		}

		internal static void RefreshColors()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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)
			AvailableColor = ParseColor((ContainerAvailableColor != null) ? ContainerAvailableColor.Value : null, new Color(1f, 0.92f, 0.02f, 1f));
			MissingColor = ParseColor((InsufficientColor != null) ? InsufficientColor.Value : null, new Color(1f, 0.25f, 0.25f, 1f));
		}

		private static Color ParseColor(string value, Color fallback)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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)
			if (string.IsNullOrEmpty(value))
			{
				return fallback;
			}
			Color result = default(Color);
			if (!ColorUtility.TryParseHtmlString((value[0] == '#') ? value : ("#" + value), ref result))
			{
				return fallback;
			}
			return result;
		}
	}
	internal sealed class ContainerInfo
	{
		internal bool IsContainer;

		internal ContainerPrivacy Privacy = (ContainerPrivacy)2;

		internal bool CheckGuardStone;

		internal string Name = string.Empty;

		internal bool IsShip;

		internal bool IsWagon;

		internal bool IsTombstone;

		internal static ContainerInfo FromContainer(Container container)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			GameObject gameObject = ((Component)container).gameObject;
			return new ContainerInfo
			{
				IsContainer = true,
				Privacy = (ContainerPrivacy)container.m_privacy,
				CheckGuardStone = container.m_checkGuardStone,
				Name = (((Object)gameObject).name ?? string.Empty),
				IsShip = ((Object)(object)gameObject.GetComponentInParent<Ship>() != (Object)null),
				IsWagon = ((Object)(object)container.m_wagon != (Object)null || (Object)(object)gameObject.GetComponentInParent<Vagon>() != (Object)null),
				IsTombstone = ((Object)(object)gameObject.GetComponentInParent<TombStone>() != (Object)null)
			};
		}
	}
	internal static class PeerDirectory
	{
		internal static long GetPlayerId(long peerUid)
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			if (peerUid == 0L)
			{
				return 0L;
			}
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return 0L;
			}
			if (peerUid == ZDOMan.GetSessionID())
			{
				if (instance.IsServer() && instance.IsDedicated())
				{
					return 0L;
				}
				PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null);
				if (val != null)
				{
					return val.GetPlayerID();
				}
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer != (Object)null))
				{
					return 0L;
				}
				return localPlayer.GetPlayerID();
			}
			ZNetPeer peer = instance.GetPeer(peerUid);
			if (peer == null)
			{
				return 0L;
			}
			ZNetScene instance2 = ZNetScene.instance;
			if ((Object)(object)instance2 != (Object)null)
			{
				GameObject val2 = instance2.FindInstance(peer.m_characterID);
				if ((Object)(object)val2 != (Object)null)
				{
					Player component = val2.GetComponent<Player>();
					if ((Object)(object)component != (Object)null)
					{
						long playerID = component.GetPlayerID();
						if (playerID != 0L)
						{
							return playerID;
						}
					}
				}
			}
			return peer.m_playerID;
		}

		internal static string GetPlayerName(long peerUid)
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || peerUid == 0L)
			{
				return "unknown";
			}
			ZNetPeer peer = instance.GetPeer(peerUid);
			if (peer != null && !string.IsNullOrEmpty(peer.m_playerName))
			{
				return peer.m_playerName;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null && peerUid == ZDOMan.GetSessionID())
			{
				return localPlayer.GetPlayerName();
			}
			return "unknown";
		}
	}
	internal static class PrefabInfoCache
	{
		private static readonly Dictionary<int, ContainerInfo> Cache = new Dictionary<int, ContainerInfo>();

		internal static ContainerInfo Get(int prefabHash)
		{
			if (prefabHash == 0)
			{
				return null;
			}
			if (Cache.TryGetValue(prefabHash, out var value))
			{
				return value;
			}
			ContainerInfo containerInfo = Build(prefabHash);
			Cache[prefabHash] = containerInfo;
			return containerInfo;
		}

		private static ContainerInfo Build(int prefabHash)
		{
			ZNetScene instance = ZNetScene.instance;
			GameObject val = (((Object)(object)instance != (Object)null) ? instance.GetPrefab(prefabHash) : null);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Container component = val.GetComponent<Container>();
			if (!((Object)(object)component == (Object)null))
			{
				return ContainerInfo.FromContainer(component);
			}
			return new ContainerInfo
			{
				IsContainer = false
			};
		}
	}
	internal static class SenderContext
	{
		private static long _current;

		private static int _depth;

		internal static bool HasContext => _depth > 0;

		internal static long Current => _current;

		internal static void Push(long senderUid)
		{
			if (_depth == 0)
			{
				_current = senderUid;
			}
			_depth++;
		}

		internal static void Pop()
		{
			if (_depth <= 0)
			{
				_depth = 0;
				_current = 0L;
				return;
			}
			_depth--;
			if (_depth == 0)
			{
				_current = 0L;
			}
		}
	}
	internal static class ServerAccess
	{
		internal static AccessDecision Evaluate(ContainerInfo info, long creatorId, Vector3 position, long playerId)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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_0096: Expected O, but got Unknown
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			if (info == null || !info.IsContainer)
			{
				return AccessDecision.Allow;
			}
			WardProbe.Probe(position, playerId, out var wardAllows, out var insideWard);
			AccessMode val = (AccessMode)0;
			bool flag = false;
			if (creatorId != 0L && ModConfig.EnforceModeOnWrites.Value)
			{
				val = PolicyState.EffectiveMode;
				flag = PolicyState.EffectiveIncludeWardPermitted;
			}
			ContainerCandidate val2 = new ContainerCandidate
			{
				HasInventory = true,
				TypeAllowed = true,
				InRange = true,
				IsInUse = false,
				HasPiece = (creatorId != 0),
				CreatorId = creatorId,
				Privacy = info.Privacy,
				CheckGuardStone = info.CheckGuardStone,
				WardAllows = wardAllows,
				InsideWard = insideWard
			};
			return AccessPolicy.Evaluate(val, val2, playerId, flag);
		}

		internal static AccessDecision EvaluateForContainer(Container container, long playerId)
		{
			//IL_000a: 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_006b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)container == (Object)null)
			{
				return AccessDecision.Deny((DenyReason)1);
			}
			long num = 0L;
			ZNetView component = ((Component)container).GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null && component.GetZDO() != null)
			{
				num = component.GetZDO().GetLong(ZDOVars.s_creator, 0L);
			}
			if (num == 0L)
			{
				Piece componentInParent = ((Component)container).GetComponentInParent<Piece>();
				if ((Object)(object)componentInParent != (Object)null)
				{
					num = componentInParent.GetCreator();
				}
			}
			return Evaluate(ContainerInfo.FromContainer(container), num, ((Component)container).transform.position, playerId);
		}
	}
	internal static class ServerAudit
	{
		private const float Interval = 2f;

		private const float PruneAge = 60f;

		private const int PruneThreshold = 64;

		private static readonly Dictionary<string, float> LastEntry = new Dictionary<string, float>();

		private static float _nextPrune;

		internal unsafe static void Denied(string action, long peerUid, string playerName, ZDO zdo, DenyReason reason)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			if (ModConfig.LogDeniedAttempts != null && ModConfig.LogDeniedAttempts.Value)
			{
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				Prune(realtimeSinceStartup);
				string key = action + "|" + peerUid;
				if (!LastEntry.TryGetValue(key, out var value) || !(realtimeSinceStartup - value < 2f))
				{
					LastEntry[key] = realtimeSinceStartup;
					Vector3 val = ((zdo != null) ? zdo.GetPosition() : Vector3.zero);
					string[] obj = new string[10]
					{
						"Denied container ",
						action,
						" for ",
						playerName,
						" (peer ",
						peerUid.ToString(),
						") at ",
						null,
						null,
						null
					};
					Vector3 val2 = val;
					obj[7] = ((object)(*(Vector3*)(&val2))/*cast due to .constrained prefix*/).ToString();
					obj[8] = ": ";
					obj[9] = ((object)(*(DenyReason*)(&reason))/*cast due to .constrained prefix*/).ToString();
					CraftFromContainersEvolvedPlugin.Warn(string.Concat(obj));
				}
			}
		}

		private static void Prune(float now)
		{
			if (LastEntry.Count < 64 || now < _nextPrune)
			{
				return;
			}
			_nextPrune = now + 60f;
			List<string> list = null;
			foreach (KeyValuePair<string, float> item in LastEntry)
			{
				if (now - item.Value > 60f)
				{
					if (list == null)
					{
						list = new List<string>();
					}
					list.Add(item.Key);
				}
			}
			if (list != null)
			{
				for (int i = 0; i < list.Count; i++)
				{
					LastEntry.Remove(list[i]);
				}
			}
		}
	}
	internal static class ServerGate
	{
		internal static bool IsActive
		{
			get
			{
				if (ModConfig.Enabled != null && ModConfig.Enabled.Value && ModConfig.ServerEnforcement != null && ModConfig.ServerEnforcement.Value && (Object)(object)ZNet.instance != (Object)null)
				{
					return ZNet.instance.IsServer();
				}
				return false;
			}
		}

		internal static bool AllowZdoWrite(ZDO zdo, string action)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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_0081: Unknown result type (might be due to invalid IL or missing references)
			if (!IsActive || !SenderContext.HasContext || zdo == null)
			{
				return true;
			}
			ContainerInfo containerInfo = PrefabInfoCache.Get(zdo.GetPrefab());
			if (containerInfo == null || !containerInfo.IsContainer)
			{
				return true;
			}
			long current = SenderContext.Current;
			long playerId = PeerDirectory.GetPlayerId(current);
			if (playerId == 0L)
			{
				ServerAudit.Denied(action, current, PeerDirectory.GetPlayerName(current), zdo, (DenyReason)7);
				return false;
			}
			long creatorId = zdo.GetLong(ZDOVars.s_creator, 0L);
			AccessDecision val = ServerAccess.Evaluate(containerInfo, creatorId, zdo.GetPosition(), playerId);
			if (((AccessDecision)(ref val)).Allowed)
			{
				return true;
			}
			ServerAudit.Denied(action, current, PeerDirectory.GetPlayerName(current), zdo, ((AccessDecision)(ref val)).Reason);
			return false;
		}

		internal static bool AllowContainerRpc(Container container, long senderUid, long claimedPlayerId, string action)
		{
			//IL_004b: 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_006c: Unknown result type (might be due to invalid IL or missing references)
			if (!IsActive || (Object)(object)container == (Object)null)
			{
				return true;
			}
			long playerId = PeerDirectory.GetPlayerId(senderUid);
			if (!PeerClaim.IsValid(claimedPlayerId, playerId))
			{
				ServerAudit.Denied(action + ":spoofed-claim", senderUid, PeerDirectory.GetPlayerName(senderUid), GetZdo(container), (DenyReason)5);
				ServerNotifications.NotifyAccessDenied(senderUid, action);
				return false;
			}
			AccessDecision val = ServerAccess.EvaluateForContainer(container, playerId);
			if (((AccessDecision)(ref val)).Allowed)
			{
				return true;
			}
			ServerAudit.Denied(action, senderUid, PeerDirectory.GetPlayerName(senderUid), GetZdo(container), ((AccessDecision)(ref val)).Reason);
			ServerNotifications.NotifyAccessDenied(senderUid, action);
			return false;
		}

		internal static long PeerUidFromRpc(ZRpc rpc)
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || rpc == null)
			{
				return 0L;
			}
			List<ZNetPeer> peers = instance.GetPeers();
			if (peers == null)
			{
				return 0L;
			}
			for (int i = 0; i < peers.Count; i++)
			{
				ZNetPeer val = peers[i];
				if (val != null && val.m_rpc == rpc)
				{
					return val.m_uid;
				}
			}
			return 0L;
		}

		private static ZDO GetZdo(Container container)
		{
			ZNetView component = ((Component)container).GetComponent<ZNetView>();
			if (!((Object)(object)component != (Object)null))
			{
				return null;
			}
			return component.GetZDO();
		}
	}
	internal static class ServerNotifications
	{
		private const string DeniedRpc = "CFCE_AccessDenied";

		private const float MessageCooldown = 2f;

		private static ZRoutedRpc _registeredInstance;

		private static float _nextMessageTime;

		internal static void EnsureRegistered()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null && instance != _registeredInstance)
			{
				instance.Register<string>("CFCE_AccessDenied", (Action<long, string>)OnAccessDenied);
				_registeredInstance = instance;
			}
		}

		internal static void NotifyAccessDenied(long targetPeerUid, string action)
		{
			if (ModConfig.NotifyDeniedPlayers.Value)
			{
				ZRoutedRpc instance = ZRoutedRpc.instance;
				if (instance != null)
				{
					instance.InvokeRoutedRPC(targetPeerUid, "CFCE_AccessDenied", new object[1] { action ?? string.Empty });
				}
			}
		}

		private static void OnAccessDenied(long sender, string action)
		{
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null))
			{
				float time = Time.time;
				if (!(time < _nextMessageTime))
				{
					_nextMessageTime = time + 2f;
					((Character)localPlayer).Message((MessageType)2, "You do not have access to this container", 0, (Sprite)null, false);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Container), "Awake")]
	internal static class ContainerAwakePatch
	{
		[HarmonyPostfix]
		private static void Postfix(Container __instance)
		{
			ContainerRegistry.Add(__instance);
		}
	}
	[HarmonyPatch(typeof(Container), "OnDestroyed")]
	internal static class ContainerDestroyedPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Container __instance)
		{
			ContainerRegistry.Remove(__instance);
		}
	}
	[HarmonyPatch(typeof(Container), "RPC_RequestOpen")]
	internal static class ContainerOpenRpcGate
	{
		[HarmonyPrefix]
		private static bool Prefix(Container __instance, long uid, long playerID)
		{
			return ServerGate.AllowContainerRpc(__instance, uid, playerID, "open");
		}
	}
	[HarmonyPatch(typeof(Container), "RPC_RequestStack")]
	internal static class ContainerStackRpcGate
	{
		[HarmonyPrefix]
		private static bool Prefix(Container __instance, long uid, long playerID)
		{
			return ServerGate.AllowContainerRpc(__instance, uid, playerID, "stack");
		}
	}
	[HarmonyPatch(typeof(Container), "RPC_RequestTakeAll")]
	internal static class ContainerTakeAllRpcGate
	{
		[HarmonyPrefix]
		private static bool Prefix(Container __instance, long uid, long playerID)
		{
			return ServerGate.AllowContainerRpc(__instance, uid, playerID, "take-all");
		}
	}
	[HarmonyPatch]
	internal static class InventoryGuiPatches
	{
		private sealed class TextRef
		{
			internal TMP_Text Text;
		}

		private static readonly ConditionalWeakTable<Transform, TextRef> AmountTextCache = new ConditionalWeakTable<Transform, TextRef>();

		[HarmonyPostfix]
		[HarmonyPatch(typeof(InventoryGui), "SetupRequirement")]
		private static void SetupRequirement_Postfix(Transform elementRoot, Requirement req, Player player, bool craft, int quality, int craftMultiplier, bool __result)
		{
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			if (!__result || req == null || (Object)(object)req.m_resItem == (Object)null || (Object)(object)player == (Object)null || (Object)(object)elementRoot == (Object)null || !ModConfig.Enabled.Value || !ModConfig.ShowContainerCounts.Value || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			TMP_Text text = AmountTextCache.GetValue(elementRoot, CreateTextRef).Text;
			if ((Object)(object)text == (Object)null)
			{
				return;
			}
			int num = req.GetAmount(quality) * craftMultiplier;
			if (num > 0)
			{
				string name = req.m_resItem.m_itemData.m_shared.m_name;
				Inventory inventory = ((Humanoid)player).GetInventory();
				int num2 = ((inventory != null) ? inventory.CountItems(name, -1, true) : 0);
				List<Container> allowedContainers = ContainerScanner.GetAllowedContainers(player);
				int num3 = num2 + InventoryUtil.CountInContainers(allowedContainers, name, -1, worldLevelBased: true, ModConfig.LeaveOne.Value);
				text.text = string.Format(CultureInfo.InvariantCulture, ModConfig.ResourceCostString.Value, num3, num);
				if (IsNoCostActive())
				{
					((Graphic)text).color = Color.white;
				}
				else if (num3 < num)
				{
					((Graphic)text).color = ModConfig.MissingColor;
				}
				else if (num2 < num)
				{
					((Graphic)text).color = ModConfig.AvailableColor;
				}
				else
				{
					((Graphic)text).color = Color.white;
				}
			}
		}

		private static TextRef CreateTextRef(Transform elementRoot)
		{
			Transform val = elementRoot.Find("res_amount");
			return new TextRef
			{
				Text = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<TMP_Text>() : null)
			};
		}

		private static bool IsNoCostActive()
		{
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			if (!instance.GetGlobalKey((GlobalKeys)24))
			{
				return instance.GetGlobalKey((GlobalKeys)25);
			}
			return true;
		}
	}
	[HarmonyPatch]
	internal static class PlayerPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "HaveRequirementItems")]
		private static void HaveRequirementItems_Postfix(Player __instance, Recipe piece, bool discover, int qualityLevel, int amount, ref bool __result)
		{
			if (__result || discover || (Object)(object)piece == (Object)null || (Object)(object)__instance == (Object)null || piece.m_requireOnlyOneIngredient || !ModConfig.Enabled.Value || !ModConfig.ApplyToCrafting.Value)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)__instance != (Object)(object)localPlayer))
			{
				List<Container> allowedContainers = ContainerScanner.GetAllowedContainers(localPlayer);
				if (allowedContainers.Count != 0 && RequirementLogic.RecipeSatisfied(localPlayer, piece, qualityLevel, amount, allowedContainers))
				{
					__result = true;
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
		{
			typeof(Piece),
			typeof(RequirementMode)
		})]
		private static void HaveRequirements_Piece_Postfix(Player __instance, Piece piece, RequirementMode mode, ref bool __result)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: 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)
			if (__result || (int)mode != 0 || (Object)(object)piece == (Object)null || (Object)(object)__instance == (Object)null || !ModConfig.Enabled.Value || !ModConfig.ApplyToBuilding.Value)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)__instance != (Object)(object)localPlayer) && (!((Object)(object)piece.m_craftingStation != (Object)null) || !((Object)(object)CraftingStation.HaveBuildStationInRange(piece.m_craftingStation.m_name, ((Component)__instance).transform.position) == (Object)null) || ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey())) && (string.IsNullOrEmpty(piece.m_dlc) || DLCMan.instance.IsDLCInstalled(piece.m_dlc)))
			{
				List<Container> allowedContainers = ContainerScanner.GetAllowedContainers(localPlayer);
				if (allowedContainers.Count != 0 && RequirementLogic.PieceSatisfied(localPlayer, piece, allowedContainers))
				{
					__result = true;
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "ConsumeResources", new Type[]
		{
			typeof(Requirement[]),
			typeof(int),
			typeof(int),
			typeof(int)
		})]
		private static bool ConsumeResources_Prefix(Player __instance, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier)
		{
			if (requirements == null || requirements.Length == 0 || (Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (!ModConfig.Enabled.Value)
			{
				return true;
			}
			if (!ModConfig.ApplyToCrafting.Value && !ModConfig.ApplyToBuilding.Value)
			{
				return true;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)__instance != (Object)(object)localPlayer)
			{
				return true;
			}
			Inventory inventory = ((Humanoid)__instance).GetInventory();
			if (inventory == null)
			{
				return true;
			}
			if (!NeedsContainers(__instance, inventory, requirements, qualityLevel, itemQuality, multiplier))
			{
				return true;
			}
			List<Container> allowedContainers = ContainerScanner.GetAllowedContainers(localPlayer);
			ConsumeExactly(__instance, inventory, allowedContainers, requirements, qualityLevel, itemQuality, multiplier);
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "OnSpawned")]
		private static void OnSpawned_Postfix()
		{
			ServerPolicySync.RequestFromServer();
		}

		private static bool NeedsContainers(Player player, Inventory inventory, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier)
		{
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			foreach (Requirement val in requirements)
			{
				if (RequirementLogic.IncludeRequirement(currentCraftingStation, val))
				{
					int num = val.GetAmount(qualityLevel) * multiplier;
					if (num > 0 && inventory.CountItems(RequirementLogic.GetSharedName(val), itemQuality, true) < num)
					{
						return true;
					}
				}
			}
			return false;
		}

		private static void ConsumeExactly(Player player, Inventory inventory, IReadOnlyList<Container> containers, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier)
		{
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			foreach (Requirement val in requirements)
			{
				if (!RequirementLogic.IncludeRequirement(currentCraftingStation, val))
				{
					continue;
				}
				int num = val.GetAmount(qualityLevel) * multiplier;
				if (num > 0)
				{
					string sharedName = RequirementLogic.GetSharedName(val);
					int num2 = num - InventoryUtil.Consume(inventory, sharedName, itemQuality, num);
					if (num2 > 0 && containers.Count > 0)
					{
						num2 -= InventoryUtil.ConsumeFromContainers(containers, sharedName, itemQuality, num2);
					}
					if (num2 > 0)
					{
						CraftFromContainersEvolvedPlugin.Warn("Could not consume " + num2 + " " + sharedName + " for a craft; containers may have changed mid-frame.");
					}
				}
			}
		}
	}
	[HarmonyPatch(typeof(ZDOMan), "RPC_ZDOData")]
	internal static class ZdoDataSenderContextPatch
	{
		[HarmonyPrefix]
		private static void Prefix(ZRpc rpc)
		{
			if (ServerGate.IsActive)
			{
				long num = 0L;
				try
				{
					num = ServerGate.PeerUidFromRpc(rpc);
				}
				catch (Exception)
				{
					num = 0L;
				}
				SenderContext.Push(num);
			}
		}

		[HarmonyFinalizer]
		private static Exception Finalizer(Exception __exception)
		{
			SenderContext.Pop();
			return __exception;
		}
	}
	[HarmonyPatch(typeof(ZDO), "Deserialize")]
	internal static class ZdoDeserializeWriteGate
	{
		[HarmonyPrefix]
		private static bool Prefix(ZDO __instance)
		{
			return ServerGate.AllowZdoWrite(__instance, "write");
		}
	}
	[HarmonyPatch(typeof(ZDO), "set_OwnerRevision")]
	internal static class ZdoOwnerRevisionGate
	{
		[HarmonyPrefix]
		private static bool Prefix(ZDO __instance)
		{
			return ServerGate.AllowZdoWrite(__instance, "owner-revision");
		}
	}
	[HarmonyPatch(typeof(ZDO), "set_DataRevision")]
	internal static class ZdoDataRevisionGate
	{
		[HarmonyPrefix]
		private static bool Prefix(ZDO __instance)
		{
			return ServerGate.AllowZdoWrite(__instance, "data-revision");
		}
	}
	[HarmonyPatch(typeof(ZDO), "SetOwnerInternal")]
	internal static class ZdoOwnerChangeGate
	{
		[HarmonyPrefix]
		private static bool Prefix(ZDO __instance)
		{
			return ServerGate.AllowZdoWrite(__instance, "owner-change");
		}
	}
	[BepInPlugin("CraftFromContainersEvolved", "Craft From Containers Evolved", "1.0.3")]
	public sealed class CraftFromContainersEvolvedPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "CraftFromContainersEvolved";

		public const string PluginName = "Craft From Containers Evolved";

		public const string PluginVersion = "1.0.3";

		internal static CraftFromContainersEvolvedPlugin Instance;

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			ModConfig.Init(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("CraftFromContainersEvolved");
			_harmony.PatchAll();
			EnforcementSelfTest.Run();
			ServerPolicySync.Init();
			Log.LogInfo((object)"Craft From Containers Evolved 1.0.3 loaded.");
		}

		private void Update()
		{
			ServerPolicySync.Tick();
		}

		private void OnDestroy()
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
				_harmony = null;
			}
			Instance = null;
		}

		internal static void Dbg(string message)
		{
			if (ModConfig.Debug != null && ModConfig.Debug.Value && Log != null)
			{
				Log.LogInfo((object)("[debug] " + message));
			}
		}

		internal static void Warn(string message)
		{
			if (Log != null)
			{
				Log.LogWarning((object)message);
			}
		}

		internal static void LogMessage(string message)
		{
			if (Log != null)
			{
				Log.LogInfo((object)message);
			}
		}
	}
	internal static class PolicyState
	{
		internal static ServerPolicy Server;

		internal static bool ServerPolicyActive
		{
			get
			{
				if (ModConfig.EnforceServerPolicy != null && ModConfig.EnforceServerPolicy.Value)
				{
					return Server != null;
				}
				return false;
			}
		}

		internal static AccessMode EffectiveMode
		{
			get
			{
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Expected I4, but got Unknown
				//IL_0024: Expected I4, but got Unknown
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				AccessMode value = ModConfig.Mode.Value;
				if (!ServerPolicyActive)
				{
					return value;
				}
				return (AccessMode)Math.Max((int)value, (int)Server.Mode);
			}
		}

		internal static float EffectiveRange
		{
			get
			{
				float value = ModConfig.ContainerRange.Value;
				if (!ServerPolicyActive || Server.Range <= 0f)
				{
					return value;
				}
				if (!(value > 0f))
				{
					return Server.Range;
				}
				return Math.Min(value, Server.Range);
			}
		}

		internal static bool EffectiveIncludeWardPermitted
		{
			get
			{
				bool value = ModConfig.IncludeWardPermitted.Value;
				if (!ServerPolicyActive)
				{
					return value;
				}
				if (value)
				{
					return Server.IncludeWardPermitted;
				}
				return false;
			}
		}

		internal static bool IgnoreShipContainers
		{
			get
			{
				if (!Local(ModConfig.IgnoreShipContainers))
				{
					return ServerFlag((ServerPolicy p) => p.IgnoreShipContainers);
				}
				return true;
			}
		}

		internal static bool IgnoreWagonContainers
		{
			get
			{
				if (!Local(ModConfig.IgnoreWagonContainers))
				{
					return ServerFlag((ServerPolicy p) => p.IgnoreWagonContainers);
				}
				return true;
			}
		}

		internal static bool IgnoreWoodChests
		{
			get
			{
				if (!Local(ModConfig.IgnoreWoodChests))
				{
					return ServerFlag((ServerPolicy p) => p.IgnoreWoodChests);
				}
				return true;
			}
		}

		internal static bool IgnoreReinforcedChests
		{
			get
			{
				if (!Local(ModConfig.IgnoreReinforcedChests))
				{
					return ServerFlag((ServerPolicy p) => p.IgnoreReinforcedChests);
				}
				return true;
			}
		}

		internal static bool IgnoreBlackMetalChests
		{
			get
			{
				if (!Local(ModConfig.IgnoreBlackMetalChests))
				{
					return ServerFlag((ServerPolicy p) => p.IgnoreBlackMetalChests);
				}
				return true;
			}
		}

		internal static bool IgnorePrivateChests
		{
			get
			{
				if (!Local(ModConfig.IgnorePrivateChests))
				{
					return ServerFlag((ServerPolicy p) => p.IgnorePrivateChests);
				}
				return true;
			}
		}

		internal static bool IgnoreTombstones
		{
			get
			{
				if (!Local(ModConfig.IgnoreTombstones))
				{
					return ServerFlag((ServerPolicy p) => p.IgnoreTombstones);
				}
				return true;
			}
		}

		private static bool Local(ConfigEntry<bool> entry)
		{
			return entry?.Value ?? false;
		}

		private static bool ServerFlag(Func<ServerPolicy, bool> selector)
		{
			if (ServerPolicyActive)
			{
				return selector(Server);
			}
			return false;
		}
	}
	internal static class RequirementLogic
	{
		internal static bool IncludeRequirement(CraftingStation station, Requirement requirement)
		{
			if (requirement == null || (Object)(object)requirement.m_resItem == (Object)null)
			{
				return false;
			}
			if ((Object)(object)station != (Object)null)
			{
				if (station.m_upgrader != requirement.m_upgraderResource)
				{
					return false;
				}
			}
			else if (requirement.m_upgraderResource)
			{
				return false;
			}
			return true;
		}

		internal static string GetSharedName(Requirement requirement)
		{
			return requirement.m_resItem.m_itemData.m_shared.m_name;
		}

		internal static bool RecipeSatisfied(Player player, Recipe recipe, int qualityLevel, int craftMultiplier, IReadOnlyList<Container> containers)
		{
			if ((Object)(object)recipe == (Object)null || recipe.m_resources == null)
			{
				return false;
			}
			CraftingStation currentCraftingStation = player.GetCurrentCraftingStation();
			bool requireOnlyOneIngredient = recipe.m_requireOnlyOneIngredient;
			bool value = ModConfig.LeaveOne.Value;
			for (int i = 0; i < recipe.m_resources.Length; i++)
			{
				Requirement val = recipe.m_resources[i];
				if (!IncludeRequirement(currentCraftingStation, val))
				{
					continue;
				}
				int num = val.GetAmount(qualityLevel) * craftMultiplier;
				if (InventoryUtil.BestQualityCount(player, containers, GetSharedName(val), val.m_resItem.m_itemData.m_shared.m_maxQuality, value) >= num)
				{
					if (requireOnlyOneIngredient)
					{
						return true;
					}
				}
				else if (!requireOnlyOneIngredient)
				{
					return false;
				}
			}
			return !requireOnlyOneIngredient;
		}

		internal static bool PieceSatisfied(Player player, Piece piece, IReadOnlyList<Container> containers)
		{
			if ((Object)(object)piece == (Object)null || piece.m_resources == null)
			{
				return false;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return false;
			}
			bool value = ModConfig.LeaveOne.Value;
			for (int i = 0; i < piece.m_resources.Length; i++)
			{
				Requirement val = piece.m_resources[i];
				if (IncludeRequirement(null, val) && val.m_amount > 0)
				{
					string sharedName = GetSharedName(val);
					if (inventory.CountItems(sharedName, -1, true) + InventoryUtil.CountInContainers(containers, sharedName, -1, worldLevelBased: true, value) < val.m_amount)
					{
						return false;
					}
				}
			}
			return true;
		}
	}
	internal sealed class ServerPolicy
	{
		internal AccessMode Mode = (AccessMode)2;

		internal float Range = 10f;

		internal bool IncludeWardPermitted = true;

		internal bool IgnoreShipContainers;

		internal bool IgnoreWagonContainers;

		internal bool IgnoreWoodChests;

		internal bool IgnoreReinforcedChests;

		internal bool IgnoreBlackMetalChests;

		internal bool IgnorePrivateChests;

		internal bool IgnoreTombstones = true;

		internal static ServerPolicy FromConfig()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			return new ServerPolicy
			{
				Mode = ModConfig.Mode.Value,
				Range = ModConfig.ContainerRange.Value,
				IncludeWardPermitted = ModConfig.IncludeWardPermitted.Value,
				IgnoreShipContainers = ModConfig.IgnoreShipContainers.Value,
				IgnoreWagonContainers = ModConfig.IgnoreWagonContainers.Value,
				IgnoreWoodChests = ModConfig.IgnoreWoodChests.Value,
				IgnoreReinforcedChests = ModConfig.IgnoreReinforcedChests.Value,
				IgnoreBlackMetalChests = ModConfig.IgnoreBlackMetalChests.Value,
				IgnorePrivateChests = ModConfig.IgnorePrivateChests.Value,
				IgnoreTombstones = ModConfig.IgnoreTombstones.Value
			};
		}

		internal string Serialize()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected I4, but got Unknown
			return string.Join("|", "CFCE1", ((int)Mode).ToString(CultureInfo.InvariantCulture), Range.ToString("0.##", CultureInfo.InvariantCulture), IncludeWardPermitted ? "1" : "0", IgnoreShipContainers ? "1" : "0", IgnoreWagonContainers ? "1" : "0", IgnoreWoodChests ? "1" : "0", IgnoreReinforcedChests ? "1" : "0", IgnoreBlackMetalChests ? "1" : "0", IgnorePrivateChests ? "1" : "0", IgnoreTombstones ? "1" : "0");
		}

		internal static bool TryParse(string payload, out ServerPolicy policy)
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			policy = null;
			if (string.IsNullOrEmpty(payload))
			{
				return false;
			}
			string[] array = payload.Split(new char[1] { '|' });
			if (array.Length < 4 || array[0] != "CFCE1")
			{
				return false;
			}
			if (!int.TryParse(array[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return false;
			}
			if (result < 0)
			{
				result = 0;
			}
			else if (result > 2)
			{
				result = 2;
			}
			float result2 = 10f;
			if (array.Length > 2)
			{
				float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out result2);
			}
			ServerPolicy serverPolicy = new ServerPolicy
			{
				Mode = (AccessMode)result,
				Range = result2,
				IncludeWardPermitted = IsTrue(array, 3),
				IgnoreShipContainers = IsTrue(array, 4),
				IgnoreWagonContainers = IsTrue(array, 5),
				IgnoreWoodChests = IsTrue(array, 6),
				IgnoreReinforcedChests = IsTrue(array, 7),
				IgnoreBlackMetalChests = IsTrue(array, 8),
				IgnorePrivateChests = IsTrue(array, 9),
				IgnoreTombstones = IsTrue(array, 10)
			};
			policy = serverPolicy;
			return true;
		}

		private static bool IsTrue(string[] parts, int index)
		{
			if (parts.Length > index)
			{
				return parts[index] == "1";
			}
			return false;
		}
	}
	internal static class ServerPolicySync
	{
		private const string RequestRpc = "CFCE_RequestPolicy";

		private const string PolicyRpc = "CFCE_ServerPolicy";

		private const float BroadcastInterval = 10f;

		private static ZRoutedRpc _registeredInstance;

		private static float _nextBroadcast;

		private static string _lastPayload;

		internal static void Init()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null)
			{
				if (instance != _registeredInstance)
				{
					instance.Register("CFCE_RequestPolicy", (Action<long>)OnPolicyRequested);
					instance.Register<string>("CFCE_ServerPolicy", (Action<long, string>)OnPolicyReceived);
					_registeredInstance = instance;
				}
				ServerNotifications.EnsureRegistered();
			}
		}

		internal static void Tick()
		{
			if (_registeredInstance != null && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && !(Time.unscaledTime < _nextBroadcast))
			{
				_nextBroadcast = Time.unscaledTime + 10f;
				string text = (PolicyState.Server = ServerPolicy.FromConfig()).Serialize();
				if (!(text == _lastPayload))
				{
					_lastPayload = text;
					ZRoutedRpc.instance.InvokeRoutedRPC("CFCE_ServerPolicy", new object[1] { text });
					CraftFromContainersEvolvedPlugin.Dbg("Broadcast server policy: " + text);
				}
			}
		}

		internal static void RequestFromServer()
		{
			Init();
			if (_registeredInstance == null || (Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer())
			{
				return;
			}
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null)
			{
				long num = 1L;
				ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
				if (serverPeer != null)
				{
					num = serverPeer.m_uid;
				}
				instance.InvokeRoutedRPC(num, "CFCE_RequestPolicy", Array.Empty<object>());
				CraftFromContainersEvolvedPlugin.Dbg("Requested server policy from peer " + num);
			}
		}

		private static void OnPolicyRequested(long sender)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				_lastPayload = (PolicyState.Server = ServerPolicy.FromConfig()).Serialize();
				ZRoutedRpc.instance.InvokeRoutedRPC(sender, "CFCE_ServerPolicy", new object[1] { _lastPayload });
			}
		}

		private static void OnPolicyReceived(long sender, string payload)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && ModConfig.EnforceServerPolicy.Value)
			{
				ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
				if (serverPeer == null || sender != serverPeer.m_uid)
				{
					CraftFromContainersEvolvedPlugin.Dbg("Ignored policy payload from non-server peer " + sender);
					return;
				}
				if (!ServerPolicy.TryParse(payload, out var policy))
				{
					CraftFromContainersEvolvedPlugin.Warn("Ignored malformed server policy payload.");
					return;
				}
				PolicyState.Server = policy;
				ContainerScanner.Invalidate();
				CraftFromContainersEvolvedPlugin.LogMessage("Applied server policy: " + payload);
			}
		}
	}
	internal static class WardProbe
	{
		private static readonly FieldInfo AllAreasField = AccessTools.Field(typeof(PrivateArea), "m_allAreas");

		private static readonly FieldRef<List<PrivateArea>> AllAreasRef = ((AllAreasField != null) ? AccessTools.StaticFieldRefAccess<List<PrivateArea>>(AllAreasField) : null);

		private static readonly Func<PrivateArea, bool> IsEnabledDelegate = CreateDelegate<Func<PrivateArea, bool>>("IsEnabled");

		private static readonly Func<PrivateArea, Vector3, float, bool> IsInsideDelegate = CreateDelegate<Func<PrivateArea, Vector3, float, bool>>("IsInside");

		private static readonly Func<PrivateArea, long, bool> IsPermittedDelegate = CreateDelegate<Func<PrivateArea, long, bool>>("IsPermitted");

		private static bool _fallbackLogged;

		internal static bool DelegatesReady
		{
			get
			{
				if ((AllAreasRef != null || AllAreasField != null) && IsEnabledDelegate != null && IsInsideDelegate != null)
				{
					return IsPermittedDelegate != null;
				}
				return false;
			}
		}

		internal static void Probe(Vector3 point, long playerId, out bool wardAllows, out bool insideWard)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			insideWard = false;
			wardAllows = true;
			List<PrivateArea> areas = GetAreas();
			if (areas == null || IsEnabledDelegate == null || IsInsideDelegate == null || IsPermittedDelegate == null)
			{
				Fallback(point, playerId, ref wardAllows, ref insideWard);
				return;
			}
			bool flag = false;
			bool flag2 = false;
			for (int i = 0; i < areas.Count; i++)
			{
				PrivateArea val = areas[i];
				if (!((Object)(object)val == (Object)null) && IsEnabledDelegate(val) && IsInsideDelegate(val, point, 0f))
				{
					flag = true;
					insideWard = true;
					if (HasAccess(val, playerId))
					{
						flag2 = true;
						break;
					}
				}
			}
			if (flag && !flag2)
			{
				wardAllows = false;
			}
		}

		private static List<PrivateArea> GetAreas()
		{
			if (AllAreasRef != null)
			{
				return AllAreasRef.Invoke();
			}
			if (!(AllAreasField != null))
			{
				return null;
			}
			return AllAreasField.GetValue(null) as List<PrivateArea>;
		}

		private static bool HasAccess(PrivateArea area, long playerId)
		{
			if (playerId == 0L)
			{
				return false;
			}
			Piece component = ((Component)area).GetComponent<Piece>();
			if ((Object)(object)component != (Object)null && component.GetCreator() == playerId)
			{
				return true;
			}
			return IsPermittedDelegate(area, playerId);
		}

		private static void Fallback(Vector3 point, long playerId, ref bool wardAllows, ref bool insideWard)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			long num = (((Object)(object)localPlayer != (Object)null) ? localPlayer.GetPlayerID() : 0);
			if (!_fallbackLogged)
			{
				_fallbackLogged = true;
				CraftFromContainersEvolvedPlugin.Warn("Ward reflection unavailable; using reduced ward checks. Container access may be denied more strictly than intended.");
			}
			if (playerId != 0L && playerId == num)
			{
				wardAllows = PrivateArea.CheckAccess(point, 0f, false, false);
				insideWard = !wardAllows;
			}
			else
			{
				wardAllows = false;
				insideWard = true;
			}
		}

		private static T CreateDelegate<T>(string methodName) where T : class
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(PrivateArea), methodName, (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				return null;
			}
			try
			{
				return methodInfo.CreateDelegate(typeof(T)) as T;
			}
			catch (Exception)
			{
				return null;
			}
		}
	}
}