Decompiled source of WorldCleaner v0.31.0

BepInEx/plugins/WorldCleaner/WorldCleaner.dll

Decompiled 5 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("WorldCleaner")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("WorldCleaner")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("c4fa187a-d091-4b48-aa9b-d09eb8b13e7c")]
[assembly: AssemblyFileVersion("0.31.0.1")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.31.0.1")]
namespace WorldCleaner;

public class Class1
{
}
internal sealed class ActiveCleanerAreas
{
	internal static ActiveCleanerAreas Active;

	private static readonly FieldInfo Sectors = AccessTools.Field(typeof(ZDOMan), "m_objectsBySector");

	private static readonly FieldInfo Portals = AccessTools.Field(typeof(ZDOMan), "m_portalObjects");

	private static readonly MethodInfo Generated = AccessTools.Method(typeof(ZoneSystem), "IsZoneGenerated", new Type[1] { typeof(Vector2s) }, (Type[])null);

	private static readonly MethodInfo Poke = AccessTools.Method(typeof(ZoneSystem), "PokeLocalZone", new Type[1] { typeof(Vector2s) }, (Type[])null);

	private static readonly MethodInfo Create = AccessTools.Method(typeof(ZNetScene), "CreateObject", new Type[1] { typeof(ZDO) }, (Type[])null);

	private List<ZDO>[] sectors;

	private Dictionary<SectorIndex, List<ZDO>> portals;

	private CleanerAreaPlan plan = new CleanerAreaPlan();

	private List<ZDO> records = new List<ZDO>();

	private IEnumerator<object> rebuilding;

	private int pokeCursor;

	private int createCursor;

	private float nextPlan;

	private float nextPoke;

	private float nextCreate;

	private float nextWarning;

	private float nextReadyReport;

	private readonly Dictionary<int, bool> retainedPrefabs = new Dictionary<int, bool>();

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

	private HashSet<ZDOID> missingObjects = new HashSet<ZDOID>();

	private CleanerRegionReadiness<ZDOID> readiness = new CleanerRegionReadiness<ZDOID>(new GridPoint[0]);

	private readonly HashSet<ZDOID> failedCreations = new HashSet<ZDOID>();

	private bool allowed;

	private bool warmingPaused;

	private int objectCap = 4096;

	private Action<string> warning;

	internal ActiveCleanerAreas()
	{
		Active = this;
	}

	internal void Reset()
	{
		rebuilding?.Dispose();
		rebuilding = null;
		allowed = false;
		warmingPaused = false;
		plan = new CleanerAreaPlan();
		records.Clear();
		retainedPrefabs.Clear();
		wardPrefabs.Clear();
		missingObjects.Clear();
		failedCreations.Clear();
		sectors = null;
		portals = null;
		readiness = new CleanerRegionReadiness<ZDOID>(plan.Zones);
		nextPlan = (nextPoke = (nextCreate = 0f));
		pokeCursor = (createCursor = 0);
		nextReadyReport = Time.realtimeSinceStartup + 30f;
	}

	internal void Dispose()
	{
		Reset();
		if (Active == this)
		{
			Active = null;
		}
	}

	private void Warn(string message)
	{
		if (!(Time.realtimeSinceStartup < nextWarning))
		{
			nextWarning = Time.realtimeSinceStartup + 15f;
			warning?.Invoke(message);
		}
	}

	internal void Tick(bool enabled, Vector3[] anchors, float radius, int zoneLimit, int objectLimit, double milliseconds, Action<string> warn)
	{
		//IL_05ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_0665: Unknown result type (might be due to invalid IL or missing references)
		//IL_06a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0684: Unknown result type (might be due to invalid IL or missing references)
		//IL_04dd: Unknown result type (might be due to invalid IL or missing references)
		//IL_04c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_04f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_034d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0565: Unknown result type (might be due to invalid IL or missing references)
		//IL_056c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0517: Unknown result type (might be due to invalid IL or missing references)
		//IL_0371: Unknown result type (might be due to invalid IL or missing references)
		warning = warn;
		if (!enabled || !CollectionAuthority.IsServer || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null || anchors == null || anchors.Length == 0)
		{
			if (allowed)
			{
				Reset();
			}
			return;
		}
		if (Sectors == null || Portals == null || Poke == null || Create == null || Generated == null)
		{
			Warn("Cleaner background area API unavailable; automatic area activity disabled.");
			return;
		}
		allowed = true;
		objectCap = objectLimit;
		Stopwatch stopwatch = Stopwatch.StartNew();
		try
		{
			if (rebuilding == null && Time.realtimeSinceStartup >= nextPlan)
			{
				sectors = Sectors.GetValue(ZDOMan.instance) as List<ZDO>[];
				portals = Portals.GetValue(ZDOMan.instance) as Dictionary<SectorIndex, List<ZDO>>;
				if (sectors == null || portals == null)
				{
					return;
				}
				rebuilding = Rebuild(anchors, radius, zoneLimit, objectLimit).GetEnumerator();
				nextPlan = Time.realtimeSinceStartup + 1f;
			}
			if (rebuilding != null)
			{
				if (!rebuilding.MoveNext())
				{
					rebuilding.Dispose();
					rebuilding = null;
				}
				if (stopwatch.Elapsed.TotalMilliseconds >= milliseconds)
				{
					return;
				}
			}
			if (plan.Zones.Count == 0)
			{
				return;
			}
			if (missingObjects.Count > 0 && Time.realtimeSinceStartup >= nextReadyReport)
			{
				nextReadyReport = Time.realtimeSinceStartup + 30f;
				Warn("Background storage waiting for " + missingObjects.Count + " objects across " + plan.Zones.Count + " retained zones; cursor " + createCursor + "/" + records.Count + ", failed initializations " + failedCreations.Count + ", warmup paused=" + warmingPaused + ".");
			}
			if (Time.realtimeSinceStartup >= nextPoke)
			{
				GridPoint gridPoint = plan.Zones[pokeCursor % plan.Zones.Count];
				Vector2s val = default(Vector2s);
				((Vector2s)(ref val))..ctor(gridPoint.X, gridPoint.Z);
				if (!warmingPaused || ZoneSystem.instance.IsZoneLoaded(val))
				{
					try
					{
						Poke.Invoke(ZoneSystem.instance, new object[1] { val });
					}
					catch (Exception ex)
					{
						Warn("Cleaner zone initialization deferred; other zones continue: " + ex.Message);
					}
				}
				pokeCursor++;
				if (pokeCursor >= plan.Zones.Count)
				{
					pokeCursor = 0;
					nextPoke = Time.realtimeSinceStartup + 1f;
				}
				if (stopwatch.Elapsed.TotalMilliseconds >= milliseconds)
				{
					return;
				}
			}
			if (warmingPaused || Time.realtimeSinceStartup < nextCreate)
			{
				return;
			}
			int num = Math.Min(128, records.Count);
			for (int i = 0; i < num; i++)
			{
				if (!(stopwatch.Elapsed.TotalMilliseconds < milliseconds))
				{
					break;
				}
				if (records.Count == 0)
				{
					break;
				}
				createCursor %= records.Count;
				ZDO val2 = records[createCursor++];
				if (val2 == null)
				{
					continue;
				}
				if (!val2.IsValid())
				{
					RemoveMissing(val2.m_uid);
				}
				else
				{
					if (failedCreations.Contains(val2.m_uid))
					{
						continue;
					}
					GameObject val3 = ZNetScene.instance.FindInstance(val2.m_uid);
					if ((Object)(object)val3 != (Object)null)
					{
						RemoveMissing(val2.m_uid);
						Container component = val3.GetComponent<Container>();
						if (!CollectionAuthority.PeerOwns(val2.GetOwner()) && (Object)(object)component != (Object)null)
						{
							OwnershipCoordinator.Acquire(val3.GetComponent<ZNetView>(), component);
						}
					}
					else
					{
						if (!ZoneSystem.instance.IsZoneReadyForType(val2.GetSector(), val2.Type) || (Object)(object)ZNetScene.instance.GetPrefab(val2.GetPrefab()) == (Object)null)
						{
							continue;
						}
						if (!CollectionAuthority.PeerOwns(val2.GetOwner()))
						{
							val2.SetOwner(ZDOMan.GetSessionID());
						}
						try
						{
							Create.Invoke(ZNetScene.instance, new object[1] { val2 });
						}
						catch (Exception ex2)
						{
							failedCreations.Add(val2.m_uid);
							Warn("Cleaner object initialization failed; other objects continue: " + ex2.Message);
						}
						finally
						{
							AccessTools.Field(typeof(ZNetView), "m_useInitZDO")?.SetValue(null, false);
							AccessTools.Field(typeof(ZNetView), "m_initZDO")?.SetValue(null, null);
						}
						if ((Object)(object)ZNetScene.instance.FindInstance(val2.m_uid) == (Object)null)
						{
							failedCreations.Add(val2.m_uid);
							Warn("A cleaner-region object failed to initialize; physical deliveries remain deferred until the region is ready.");
						}
						else
						{
							RemoveMissing(val2.m_uid);
						}
						break;
					}
				}
			}
		}
		catch (Exception ex3)
		{
			warmingPaused = true;
			Warn("Cleaner area warmup paused: " + ex3);
		}
		finally
		{
			double totalMilliseconds = stopwatch.Elapsed.TotalMilliseconds;
			if (totalMilliseconds > 100.0)
			{
				nextCreate = (nextPoke = Time.realtimeSinceStartup + 2f);
				Warn("Cleaner area warmup cooling down for two seconds after " + totalMilliseconds.ToString("F1") + " ms, then resuming other objects.");
			}
			else if (totalMilliseconds > 20.0)
			{
				nextCreate = (nextPoke = Time.realtimeSinceStartup + 1f);
				Warn("Cleaner area warmup backing off after " + totalMilliseconds.ToString("F1") + " ms.");
			}
		}
	}

	private int Index(GridPoint p)
	{
		//IL_004c: 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_0077: 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)
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		if (p.X < -32768 || p.X > 32767 || p.Z < -32768 || p.Z > 32767)
		{
			return -1;
		}
		uint sector = ZoneSystem.SectorToIndex(p.X, p.Z).Sector;
		if (sector >= sectors.Length)
		{
			return -1;
		}
		Vector2s val = ZoneSystem.IndexToSector(sector);
		return (val.x == p.X && val.y == p.Z) ? ((int)sector) : (-1);
	}

	private List<ZDO> Portal(int index)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		List<ZDO> value;
		return portals.TryGetValue(new SectorIndex((uint)index), out value) ? value : null;
	}

	private int Count(GridPoint p)
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		int num = Index(p);
		if (num < 0 || !(bool)Generated.Invoke(ZoneSystem.instance, new object[1] { (object)new Vector2s(p.X, p.Z) }))
		{
			return -1;
		}
		return (sectors[num]?.Count ?? 0) + (Portal(num)?.Count ?? 0);
	}

	private IEnumerable<object> Rebuild(Vector3[] anchors, float radius, int zoneLimit, int objectLimit)
	{
		CleanerAreaPlan next = new CleanerAreaPlan();
		foreach (Vector3 anchor in anchors)
		{
			yield return null;
			Vector2s center = ZoneSystem.GetZone(anchor);
			Vector2s lo = ZoneSystem.GetZone(anchor - new Vector3(radius, 0f, radius));
			Vector2s hi = ZoneSystem.GetZone(anchor + new Vector3(radius, 0f, radius));
			int reach = Math.Max(1, Math.Max(Math.Max(center.x - lo.x, hi.x - center.x), Math.Max(center.y - lo.y, hi.y - center.y)));
			int val = reach;
			SimulationDistance syncedSimulationDistance = ZNet.instance.GetSyncedSimulationDistance();
			if (!next.TryAdd(reach: Math.Max(val, ((SimulationDistance)(ref syncedSimulationDistance)).NearSimulationDistance), center: new GridPoint(center.x, center.y), maximumZones: zoneLimit, maximumObjects: objectLimit, count: Count))
			{
				Warn("Cleaner background region deferred: ungenerated terrain or configured zone/object cap. Its archive stays intact.");
			}
		}
		List<ZDO> snapshot = new List<ZDO>(Math.Min(next.Objects, objectLimit));
		CleanerRegionReadiness<ZDOID> nextReadiness = new CleanerRegionReadiness<ZDOID>(next.Zones);
		HashSet<ZDOID> missing = new HashSet<ZDOID>();
		int batch = 0;
		foreach (GridPoint cell in next.Zones)
		{
			int index = Index(cell);
			if (index < 0)
			{
				continue;
			}
			for (int layer = 0; layer < 2; layer++)
			{
				List<ZDO> list = ((layer == 0) ? sectors[index] : Portal(index));
				if (list == null)
				{
					continue;
				}
				for (int j = 0; j < list.Count; j++)
				{
					if (snapshot.Count >= objectLimit)
					{
						Warn("Cleaner background region changed beyond object cap; keeping the previous complete plan.");
						yield break;
					}
					if (list[j] != null && RetainRecord(list[j]))
					{
						snapshot.Add(list[j]);
						if ((Object)(object)ZNetScene.instance.FindInstance(list[j].m_uid) == (Object)null)
						{
							missing.Add(list[j].m_uid);
							nextReadiness.Add(list[j].m_uid, cell);
						}
					}
					int num = batch + 1;
					batch = num;
					if (num >= 128)
					{
						batch = 0;
						yield return null;
					}
				}
			}
		}
		yield return null;
		snapshot.Sort((ZDO a, ZDO b) => Priority(b).CompareTo(Priority(a)));
		plan = next;
		records = snapshot;
		missingObjects = missing;
		readiness = nextReadiness;
		pokeCursor = ((plan.Zones.Count != 0) ? (pokeCursor % plan.Zones.Count) : 0);
		createCursor = ((records.Count != 0) ? (createCursor % records.Count) : 0);
	}

	private bool RetainRecord(ZDO zdo)
	{
		if (zdo == null || !zdo.IsValid() || !zdo.Persistent)
		{
			return false;
		}
		int prefab = zdo.GetPrefab();
		if (retainedPrefabs.TryGetValue(prefab, out var value))
		{
			return value;
		}
		GameObject prefab2 = ZNetScene.instance.GetPrefab(prefab);
		if ((Object)(object)prefab2 == (Object)null)
		{
			return false;
		}
		value = (Object)(object)prefab2.GetComponent<Character>() == (Object)null && (Object)(object)prefab2.GetComponent<CreatureSpawner>() == (Object)null && (Object)(object)prefab2.GetComponent<SpawnArea>() == (Object)null;
		if (value && (Object)(object)prefab2.GetComponent<PrivateArea>() != (Object)null)
		{
			wardPrefabs.Add(prefab);
		}
		retainedPrefabs[prefab] = value;
		return value;
	}

	private int Priority(ZDO zdo)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Expected I4, but got Unknown
		return zdo.Type * 2 + (wardPrefabs.Contains(zdo.GetPrefab()) ? 1 : 0);
	}

	private void RemoveMissing(ZDOID id)
	{
		//IL_0007: 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)
		missingObjects.Remove(id);
		readiness.Remove(id);
	}

	internal static bool StorageReady(Vector3 position)
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		if (Active == null || !Active.Contains(position))
		{
			return true;
		}
		Vector2s zone = ZoneSystem.GetZone(position);
		return Active.readiness.Ready(new GridPoint(zone.x, zone.y));
	}

	private bool Contains(Vector3 point)
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//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_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		if (!allowed || plan.Zones.Count == 0)
		{
			return false;
		}
		Vector2s zone = ZoneSystem.GetZone(point);
		return plan.Contains(new GridPoint(zone.x, zone.y));
	}

	internal static bool Inside(Vector3 point)
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		return Active != null && Active.Contains(point);
	}

	internal static void Retain(List<ZDO> near)
	{
		ActiveCleanerAreas active = Active;
		if (active == null || !active.allowed || near == null || active.sectors == null)
		{
			return;
		}
		long num = 0L;
		foreach (GridPoint zone in active.plan.Zones)
		{
			int num2 = active.Index(zone);
			if (num2 >= 0)
			{
				num += (active.sectors[num2]?.Count ?? 0) + (active.Portal(num2)?.Count ?? 0);
			}
		}
		if (num > active.objectCap)
		{
			active.Warn("Cleaner regions exceeded the object cap; releasing complete regions through native unloading. Archive delivery will wait.");
			active.Reset();
			return;
		}
		foreach (GridPoint zone2 in active.plan.Zones)
		{
			int num3 = active.Index(zone2);
			if (num3 < 0)
			{
				continue;
			}
			for (int i = 0; i < 2; i++)
			{
				List<ZDO> list = ((i == 0) ? active.sectors[num3] : active.Portal(num3));
				if (list == null)
				{
					continue;
				}
				for (int j = 0; j < list.Count; j++)
				{
					if (active.RetainRecord(list[j]))
					{
						near.Add(list[j]);
					}
				}
			}
		}
	}
}
[HarmonyPatch(typeof(ZNetScene), "RemoveObjects")]
internal static class RetainCleanerObjectsPatch
{
	[HarmonyPrefix]
	private static void Prefix(List<ZDO> __0)
	{
		ActiveCleanerAreas.Retain(__0);
	}
}
[HarmonyPatch(typeof(ZNetScene), "PointInsideActiveArea")]
internal static class CleanerActiveAreaPatch
{
	[HarmonyPostfix]
	private static void Postfix(Vector2s __0, Vector3 __1, ref bool __result)
	{
		//IL_0005: 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_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		if (!__result && ActiveCleanerAreas.Inside(__1) && (Object)(object)ZNet.instance != (Object)null && __0 == ZoneSystem.GetZone(ZNet.instance.GetReferencePosition()))
		{
			__result = true;
		}
	}
}
internal sealed class ArchiveCatalog : IDisposable
{
	private readonly ItemArchive archive;

	private readonly ArchiveCheckout checkout;

	private readonly Dictionary<string, VirtualStacks.Row> byKey = new Dictionary<string, VirtualStacks.Row>(StringComparer.Ordinal);

	private readonly Dictionary<string, VirtualStacks.Row> byToken = new Dictionary<string, VirtualStacks.Row>(StringComparer.Ordinal);

	private readonly Dictionary<string, int> quantities = new Dictionary<string, int>(StringComparer.Ordinal);

	private readonly HashSet<string> counted = new HashSet<string>(StringComparer.Ordinal);

	private readonly Dictionary<string, LinkedListNode<ItemArchive.Entry>> nodes = new Dictionary<string, LinkedListNode<ItemArchive.Entry>>(StringComparer.Ordinal);

	private readonly Queue<ItemArchive.Entry> pending = new Queue<ItemArchive.Entry>();

	private int cursor;

	internal readonly List<VirtualStacks.Row> Rows = new List<VirtualStacks.Row>();

	internal readonly List<VirtualStacks.Row> ActiveRows = new List<VirtualStacks.Row>();

	internal int AvailableCount { get; private set; }

	internal int Revision { get; private set; }

	internal bool Ready => cursor >= archive.RecordCount && pending.Count == 0;

	internal int IndexedCount => cursor;

	internal VirtualStacks.Row RowFor(string token)
	{
		VirtualStacks.Row value;
		return byToken.TryGetValue(token, out value) ? value : null;
	}

	internal ArchiveCatalog(ItemArchive archive, ArchiveCheckout checkout)
	{
		this.archive = archive;
		this.checkout = checkout;
		archive.RecordAvailable += Enqueue;
		checkout.TokenChanged += Changed;
	}

	public void Dispose()
	{
		archive.RecordAvailable -= Enqueue;
		checkout.TokenChanged -= Changed;
	}

	private void Enqueue(ItemArchive.Entry record)
	{
		pending.Enqueue(record);
	}

	private void Changed(string token)
	{
		Revision++;
		if (!byToken.TryGetValue(token, out var value))
		{
			return;
		}
		bool flag = checkout.IsBlocked(token);
		if (flag && counted.Remove(token))
		{
			value.Total -= quantities[token];
			AvailableCount--;
			value.ActiveCount--;
			value.Available.Remove(nodes[token]);
			nodes.Remove(token);
			if (value.ActiveCount == 0)
			{
				ActiveRows.Remove(value);
			}
		}
		else if (!flag && counted.Add(token))
		{
			if (value.ActiveCount == 0)
			{
				ActiveRows.Add(value);
			}
			value.Total += quantities[token];
			AvailableCount++;
			value.ActiveCount++;
			nodes[token] = value.Available.AddLast(archive.FindRecord(token));
		}
	}

	private void Index(ItemArchive.Entry record)
	{
		if (record == null || record.State != "REMOVED" || byToken.ContainsKey(record.Token))
		{
			return;
		}
		Revision++;
		if (!checkout.IsBlocked(record.Token))
		{
			ItemData val = VirtualStacks.Inspect(record);
			ItemIdentity.Snapshot cachedIdentity = record.CachedIdentity;
			string key = ((val == null) ? ("invalid:" + record.Token) : (cachedIdentity.MayMerge ? cachedIdentity.Sha256 : ("token:" + record.Token)));
			if (!byKey.TryGetValue(key, out var value))
			{
				value = new VirtualStacks.Row
				{
					Example = val,
					Key = key,
					DisplayName = ((val == null) ? "Unavailable item" : ((Object)val.m_dropPrefab).name),
					MaxStack = ((val == null) ? 1 : Math.Max(1, val.m_shared.m_maxStackSize))
				};
				byKey.Add(key, value);
				Rows.Add(value);
			}
			value.Records.Add(record);
			byToken.Add(record.Token, value);
			quantities.Add(record.Token, val?.m_stack ?? 0);
			counted.Add(record.Token);
			nodes.Add(record.Token, value.Available.AddLast(record));
			if (value.ActiveCount == 0)
			{
				ActiveRows.Add(value);
			}
			value.Total += quantities[record.Token];
			AvailableCount++;
			value.ActiveCount++;
		}
	}

	internal int Tick(int maximumRecords, double milliseconds)
	{
		Stopwatch stopwatch = Stopwatch.StartNew();
		int i;
		for (i = 0; i < maximumRecords; i++)
		{
			if (!(stopwatch.Elapsed.TotalMilliseconds < milliseconds))
			{
				break;
			}
			if (pending.Count > 0)
			{
				Index(pending.Dequeue());
				continue;
			}
			if (cursor < archive.RecordCount)
			{
				Index(archive.RecordAt(cursor++));
				continue;
			}
			break;
		}
		return i;
	}
}
internal sealed class ArchiveCheckout
{
	internal sealed class Operation
	{
		internal string Id;

		internal string Token;

		internal string State;

		internal string Receipt;

		internal string IntentContext;

		internal long Ticks;
	}

	private const int Magic = 1464025905;

	private const int MaxFrame = 1048576;

	private readonly string file;

	private readonly object gate = new object();

	private readonly Dictionary<string, Operation> byId = new Dictionary<string, Operation>();

	private readonly Dictionary<string, string> byToken = new Dictionary<string, string>();

	private readonly List<string> orderedIds = new List<string>();

	private bool healthy;

	private int ambiguousCount;

	internal bool Healthy
	{
		get
		{
			lock (gate)
			{
				return healthy;
			}
		}
	}

	internal int OperationCount
	{
		get
		{
			lock (gate)
			{
				return orderedIds.Count;
			}
		}
	}

	internal int AmbiguousCount
	{
		get
		{
			lock (gate)
			{
				return ambiguousCount;
			}
		}
	}

	internal event Action<string> TokenChanged;

	internal ArchiveCheckout(string directory)
	{
		file = Path.Combine(directory, "checkouts.wcc");
	}

	internal void Load()
	{
		lock (gate)
		{
			healthy = false;
			byId.Clear();
			byToken.Clear();
			orderedIds.Clear();
			ambiguousCount = 0;
			if (!File.Exists(file))
			{
				healthy = true;
				return;
			}
			using (FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
			{
				using BinaryReader binaryReader = new BinaryReader(fileStream, Encoding.UTF8);
				while (fileStream.Position < fileStream.Length)
				{
					long position = fileStream.Position;
					if (fileStream.Length - position < 12)
					{
						throw new InvalidDataException("Truncated checkout frame at " + position);
					}
					if (binaryReader.ReadInt32() != 1464025905)
					{
						throw new InvalidDataException("Checkout frame magic mismatch at " + position);
					}
					int num = binaryReader.ReadInt32();
					byte[] bytes = BitConverter.GetBytes(binaryReader.ReadUInt32());
					if (num <= 0 || num > 1048576 || fileStream.Length - fileStream.Position < num)
					{
						throw new InvalidDataException("Truncated or oversized checkout frame at " + position);
					}
					byte[] buffer = binaryReader.ReadBytes(num);
					using (SHA256 sHA = SHA256.Create())
					{
						byte[] source = sHA.ComputeHash(buffer);
						if (!source.Take(4).SequenceEqual(bytes))
						{
							throw new InvalidDataException("Checkout checksum mismatch at " + position);
						}
					}
					using MemoryStream memoryStream = new MemoryStream(buffer);
					using BinaryReader binaryReader2 = new BinaryReader(memoryStream, Encoding.UTF8);
					Operation operation = new Operation
					{
						Id = binaryReader2.ReadString(),
						Token = binaryReader2.ReadString(),
						State = binaryReader2.ReadString(),
						Receipt = binaryReader2.ReadString(),
						Ticks = binaryReader2.ReadInt64()
					};
					if (memoryStream.Position != memoryStream.Length || !Valid(operation.State) || string.IsNullOrWhiteSpace(operation.Id) || string.IsNullOrWhiteSpace(operation.Token))
					{
						throw new InvalidDataException("Invalid checkout record at " + position);
					}
					if (!byId.ContainsKey(operation.Id) && operation.State != "INTENT")
					{
						throw new InvalidDataException("Checkout has no initial intent at " + position);
					}
					if (byId.TryGetValue(operation.Id, out var value) && (value.Token != operation.Token || value.Ticks != operation.Ticks || !CanAdvance(value.State, operation.State)))
					{
						throw new InvalidDataException("Invalid checkout transition at " + position);
					}
					if (byToken.TryGetValue(operation.Token, out var value2) && value2 != operation.Id)
					{
						throw new InvalidDataException("Archive token has multiple checkouts: " + operation.Token);
					}
					if (byId.TryGetValue(operation.Id, out value))
					{
						operation.IntentContext = value.IntentContext;
					}
					else
					{
						operation.IntentContext = operation.Receipt;
						orderedIds.Add(operation.Id);
					}
					byId[operation.Id] = operation;
					if (operation.State == "CANCELLED")
					{
						byToken.Remove(operation.Token);
					}
					else
					{
						byToken[operation.Token] = operation.Id;
					}
				}
			}
			healthy = true;
			ambiguousCount = byId.Values.Count((Operation v) => v.State == "INTENT");
		}
	}

	internal string Begin(string token)
	{
		if (string.IsNullOrWhiteSpace(token))
		{
			throw new ArgumentException("Missing archive token");
		}
		lock (gate)
		{
			if (!healthy)
			{
				throw new InvalidOperationException("Checkout journal unavailable; reload and review required");
			}
			if (byToken.ContainsKey(token))
			{
				throw new InvalidOperationException("Token is already reserved or spent");
			}
			Operation operation = new Operation
			{
				Id = Guid.NewGuid().ToString("N"),
				Token = token,
				State = "INTENT",
				Receipt = "",
				Ticks = DateTime.UtcNow.Ticks
			};
			Write(operation);
			byId.Add(operation.Id, operation);
			byToken.Add(token, operation.Id);
			orderedIds.Add(operation.Id);
			ambiguousCount++;
			this.TokenChanged?.Invoke(token);
			return operation.Id;
		}
	}

	internal string[] BeginBatch(string[] tokens, string intentContext = "")
	{
		if (intentContext == null || intentContext.Length > 4096)
		{
			throw new ArgumentException("Invalid intent context");
		}
		lock (gate)
		{
			if (!healthy || tokens.Length == 0 || tokens.Distinct<string>(StringComparer.Ordinal).Count() != tokens.Length || tokens.Any((string t) => string.IsNullOrWhiteSpace(t) || byToken.ContainsKey(t)))
			{
				throw new InvalidOperationException("Batch has unavailable or repeated tokens");
			}
			Operation[] array = tokens.Select((string t) => new Operation
			{
				Id = Guid.NewGuid().ToString("N"),
				Token = t,
				State = "INTENT",
				Receipt = intentContext,
				IntentContext = intentContext,
				Ticks = DateTime.UtcNow.Ticks
			}).ToArray();
			WriteBatch(array);
			Operation[] array2 = array;
			foreach (Operation operation in array2)
			{
				byId.Add(operation.Id, operation);
				byToken.Add(operation.Token, operation.Id);
				ambiguousCount++;
				orderedIds.Add(operation.Id);
				this.TokenChanged?.Invoke(operation.Token);
			}
			return array.Select((Operation o) => o.Id).ToArray();
		}
	}

	internal Operation[] Snapshot()
	{
		lock (gate)
		{
			return byId.Values.Select((Operation op) => Copy(op, op.State, op.Receipt)).ToArray();
		}
	}

	internal Operation OperationAt(int index)
	{
		lock (gate)
		{
			if (index < 0 || index >= orderedIds.Count)
			{
				return null;
			}
			Operation operation = byId[orderedIds[index]];
			return Copy(operation, operation.State, operation.Receipt);
		}
	}

	internal Operation ForToken(string token)
	{
		lock (gate)
		{
			if (!byToken.TryGetValue(token, out var value))
			{
				return null;
			}
			Operation operation = byId[value];
			return Copy(operation, operation.State, operation.Receipt);
		}
	}

	internal void CommitBatch(string[] ids, string receipt)
	{
		lock (gate)
		{
			if (ids.Length == 0 || ids.Distinct<string>(StringComparer.Ordinal).Count() != ids.Length)
			{
				throw new ArgumentException("Batch receipt must contain unique operations");
			}
			if (string.IsNullOrWhiteSpace(receipt))
			{
				throw new ArgumentException("Receipt required");
			}
			Operation[] array = ids.Select(delegate(string id)
			{
				if (!byId.TryGetValue(id, out var value) || value.State != "INTENT")
				{
					throw new InvalidOperationException("Cannot commit batch");
				}
				return Copy(value, "COMMITTED", receipt);
			}).ToArray();
			WriteBatch(array);
			Operation[] array2 = array;
			foreach (Operation operation in array2)
			{
				byId[operation.Id] = operation;
				ambiguousCount--;
			}
		}
	}

	internal void CommitReceipt(string id, string destinationReceipt)
	{
		if (string.IsNullOrWhiteSpace(destinationReceipt))
		{
			throw new ArgumentException("Destination receipt required");
		}
		lock (gate)
		{
			if (!byId.TryGetValue(id, out var value) || value.State != "INTENT")
			{
				throw new InvalidOperationException("Cannot commit checkout");
			}
			Operation operation = Copy(value, "COMMITTED", destinationReceipt);
			Write(operation);
			byId[id] = operation;
			ambiguousCount--;
		}
	}

	internal void CancelBeforeDelivery(string id)
	{
		lock (gate)
		{
			if (!byId.TryGetValue(id, out var value) || value.State != "INTENT")
			{
				throw new InvalidOperationException("Cannot cancel checkout");
			}
			Operation operation = Copy(value, "CANCELLED", "");
			Write(operation);
			byId[id] = operation;
			byToken.Remove(value.Token);
			ambiguousCount--;
			this.TokenChanged?.Invoke(value.Token);
		}
	}

	internal bool IsBlocked(string token)
	{
		lock (gate)
		{
			return !healthy || byToken.ContainsKey(token);
		}
	}

	private static Operation Copy(Operation o, string state, string receipt)
	{
		return new Operation
		{
			Id = o.Id,
			Token = o.Token,
			State = state,
			Receipt = receipt,
			IntentContext = o.IntentContext,
			Ticks = o.Ticks
		};
	}

	private static bool Valid(string s)
	{
		return s == "INTENT" || s == "COMMITTED" || s == "CANCELLED";
	}

	private static bool CanAdvance(string a, string b)
	{
		return a == "INTENT" && (b == "COMMITTED" || b == "CANCELLED");
	}

	private void Write(Operation op)
	{
		WriteBatch(new Operation[1] { op });
	}

	private void WriteBatch(Operation[] operations)
	{
		if (!healthy)
		{
			throw new InvalidOperationException("Checkout journal is write-locked");
		}
		try
		{
			WriteCore(operations);
		}
		catch
		{
			healthy = false;
			throw;
		}
	}

	private void WriteCore(Operation[] operations)
	{
		byte[] array2;
		using (MemoryStream memoryStream = new MemoryStream())
		{
			using BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true);
			foreach (Operation operation in operations)
			{
				byte[] array;
				using (MemoryStream memoryStream2 = new MemoryStream())
				{
					using (BinaryWriter binaryWriter2 = new BinaryWriter(memoryStream2, Encoding.UTF8, leaveOpen: true))
					{
						binaryWriter2.Write(operation.Id);
						binaryWriter2.Write(operation.Token);
						binaryWriter2.Write(operation.State);
						binaryWriter2.Write(operation.Receipt);
						binaryWriter2.Write(operation.Ticks);
					}
					array = memoryStream2.ToArray();
				}
				if (array.Length > 1048576)
				{
					throw new IOException("Checkout frame too large");
				}
				byte[] value;
				using (SHA256 sHA = SHA256.Create())
				{
					value = sHA.ComputeHash(array);
				}
				binaryWriter.Write(1464025905);
				binaryWriter.Write(array.Length);
				binaryWriter.Write(BitConverter.ToUInt32(value, 0));
				binaryWriter.Write(array);
			}
			binaryWriter.Flush();
			array2 = memoryStream.ToArray();
		}
		Directory.CreateDirectory(Path.GetDirectoryName(file));
		using FileStream fileStream = new FileStream(file, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
		long length = fileStream.Length;
		try
		{
			fileStream.Position = length;
			fileStream.Write(array2, 0, array2.Length);
			fileStream.Flush(flushToDisk: true);
		}
		catch
		{
			try
			{
				fileStream.SetLength(length);
				fileStream.Flush(flushToDisk: true);
			}
			catch
			{
			}
			throw;
		}
	}
}
internal sealed class ArchiveChestDistributor
{
	private ArchiveCatalog catalog;

	private ItemArchive indexedArchive;

	private ArchiveCheckout indexedCheckout;

	private int cursor;

	private readonly Dictionary<VirtualStacks.Row, LinkedListNode<ItemArchive.Entry>> rowCursors = new Dictionary<VirtualStacks.Row, LinkedListNode<ItemArchive.Entry>>();

	internal void Reset()
	{
		catalog?.Dispose();
		catalog = null;
		indexedArchive = null;
		indexedCheckout = null;
		cursor = 0;
		rowCursors.Clear();
	}

	internal unsafe IEnumerable<object> Drain(ItemArchive archive, ArchiveCheckout checkout, Container[] chests, Vector3[] anchors, int budget, float radius, Action<string> trace, Action<string> warn, Func<Vector3[]> currentAnchors = null, bool useNearby = true, int archiveMB = 4096)
	{
		if (archive == null || checkout == null || !archive.JournalHealthy || !checkout.Healthy || anchors == null || anchors.Length == 0 || !CollectionAuthority.IsServer)
		{
			yield break;
		}
		if (indexedArchive != archive || indexedCheckout != checkout)
		{
			Reset();
			indexedArchive = archive;
			indexedCheckout = checkout;
			catalog = new ArchiveCatalog(archive, checkout);
		}
		int watermark = archive.RecordCount;
		do
		{
			yield return "archive refill index";
			if (!CollectionAuthority.IsServer || !archive.JournalHealthy || !checkout.Healthy)
			{
				yield break;
			}
			catalog.Tick(32, 0.75);
		}
		while (catalog.IndexedCount < watermark);
		int activeCount = catalog.ActiveRows.Count;
		int count = Math.Min(Mathf.Clamp(budget, 1, 1000), activeCount);
		VirtualStacks.Row[] rows = new VirtualStacks.Row[count];
		for (int i = 0; i < count; i++)
		{
			rows[i] = catalog.ActiveRows[(cursor + i) % activeCount];
		}
		if (activeCount > 0)
		{
			cursor = (cursor + count) % activeCount;
		}
		int sent = 0;
		for (int visit = 0; visit < count; visit++)
		{
			yield return "archive refill";
			if (!CollectionAuthority.IsServer || !archive.JournalHealthy || !checkout.Healthy)
			{
				yield break;
			}
			if (currentAnchors != null)
			{
				anchors = currentAnchors();
			}
			if (anchors == null || anchors.Length == 0)
			{
				yield break;
			}
			VirtualStacks.Row row = rows[visit];
			List<ItemArchive.Entry> candidates = NextWindow(row);
			ItemArchive.Entry record = candidates.FirstOrDefault();
			if (record == null || checkout.IsBlocked(record.Token))
			{
				continue;
			}
			ItemData original = VirtualStacks.Inspect(record);
			if (original == null)
			{
				continue;
			}
			Vector3 anchor = AnchorFor(record, anchors);
			ItemData item = original.Clone();
			item.m_stack = (int)Math.Min(val2: Math.Max(1, item.m_shared.m_maxStackSize), val1: row.Total);
			int stillNeeded = item.m_stack;
			List<Tuple<StorageTarget, int>> plan = new List<Tuple<StorageTarget, int>>();
			foreach (StorageTarget chest in ChestTransfer.StorageRefillDestinations(chests, anchor, radius, useNearby))
			{
				if (stillNeeded == 0 || plan.Count >= 32)
				{
					break;
				}
				int fitting = chest.AmountThatFits(item, stillNeeded);
				if (fitting > 0)
				{
					plan.Add(Tuple.Create(chest, fitting));
					stillNeeded -= fitting;
				}
			}
			int capacity = item.m_stack - stillNeeded;
			if (capacity == 0)
			{
				continue;
			}
			string[] intents = null;
			bool deliveryAttempted = false;
			try
			{
				int selected = 0;
				List<string> tokens = new List<string>();
				foreach (ItemArchive.Entry candidate in candidates)
				{
					int num;
					if (!checkout.IsBlocked(candidate.Token))
					{
						Vector3 val = AnchorFor(candidate, anchors) - anchor;
						num = ((((Vector3)(ref val)).sqrMagnitude > 0.01f) ? 1 : 0);
					}
					else
					{
						num = 1;
					}
					if (num != 0)
					{
						continue;
					}
					ItemData restored = VirtualStacks.Inspect(candidate);
					if (restored != null && candidate.CachedIdentity.CanonicalPayload.SequenceEqual(record.CachedIdentity.CanonicalPayload))
					{
						item.m_cheated |= restored.m_cheated;
						int taking = Math.Min(restored.m_stack, capacity - selected);
						string token = candidate.Token;
						if (taking < restored.m_stack)
						{
							token = ArchiveSplit.TakePortion(archive, checkout, token, restored, taking, archiveMB);
						}
						tokens.Add(token);
						selected += taking;
						if (selected == capacity)
						{
							break;
						}
					}
				}
				if (selected == 0)
				{
					continue;
				}
				item.m_stack = selected;
				intents = checkout.BeginBatch(intentContext: "archive_refill:" + string.Join(",", plan.Select(delegate(Tuple<StorageTarget, int> p)
				{
					//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)
					ZDOID uid = p.Item1.View.GetZDO().m_uid;
					return ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString() + "=" + p.Item2;
				})), tokens: tokens.ToArray());
				int remaining = selected;
				foreach (Tuple<StorageTarget, int> allocation in plan)
				{
					int amount = Math.Min(remaining, allocation.Item2);
					if (amount == 0)
					{
						break;
					}
					deliveryAttempted = true;
					if (!allocation.Item1.TryInsert(item, amount, CollectionAuthority.ActorFor(anchor), out var failure))
					{
						throw new InvalidOperationException(failure ?? "Destination rejected planned insertion");
					}
					remaining -= amount;
					failure = null;
				}
				checkout.CommitBatch(intents, "chests:" + plan.Count + ":" + record.Token);
				sent += selected;
				trace("Virtual archive -> physical storage: " + selected + " " + item.m_shared.m_name + " from " + tokens.Count + " records");
			}
			catch (Exception ex)
			{
				Exception ex2 = ex;
				if (intents != null && !deliveryAttempted)
				{
					string[] array = intents;
					foreach (string intent in array)
					{
						try
						{
							checkout.CancelBeforeDelivery(intent);
						}
						catch
						{
						}
					}
				}
				warn("Virtual archive chest delivery UNCERTAIN; affected records retained for review: " + ex2);
			}
		}
		if (sent > 0)
		{
			trace("Archive refill completed: " + sent + " items");
		}
	}

	private List<ItemArchive.Entry> NextWindow(VirtualStacks.Row row)
	{
		if (!rowCursors.TryGetValue(row, out var value) || value == null || value.List != row.Available)
		{
			value = row.Available.First;
		}
		List<ItemArchive.Entry> list = new List<ItemArchive.Entry>();
		LinkedListNode<ItemArchive.Entry> linkedListNode = value;
		while (value != null && list.Count < 128)
		{
			list.Add(value.Value);
			value = value.Next ?? row.Available.First;
			if (value == linkedListNode)
			{
				break;
			}
		}
		rowCursors[row] = value;
		return list;
	}

	private static Vector3 AnchorFor(ItemArchive.Entry record, Vector3[] anchors)
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: 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_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//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_0067: 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)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		if (!TrySourcePosition(record.Source, out var position))
		{
			position = anchors[0];
		}
		Vector3 val = anchors[0];
		Vector3 val2 = val - position;
		float num = ((Vector3)(ref val2)).sqrMagnitude;
		for (int i = 1; i < anchors.Length; i++)
		{
			val2 = anchors[i] - position;
			float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
			if (sqrMagnitude < num)
			{
				num = sqrMagnitude;
				val = anchors[i];
			}
		}
		return val;
	}

	private static bool TrySourcePosition(string source, out Vector3 position)
	{
		//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_012b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: Unknown result type (might be due to invalid IL or missing references)
		position = Vector3.zero;
		if (string.IsNullOrEmpty(source))
		{
			return false;
		}
		int num = source.LastIndexOf('@');
		if (num < 0)
		{
			return false;
		}
		int num2 = source.IndexOf(':', num + 1);
		if (num2 <= num + 1)
		{
			return false;
		}
		string[] array = source.Substring(num + 1, num2 - num - 1).Split(new char[1] { ',' });
		if (array.Length != 3)
		{
			return false;
		}
		if (!float.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || !float.TryParse(array[1], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) || !float.TryParse(array[2], NumberStyles.Float, CultureInfo.InvariantCulture, out var result3))
		{
			return false;
		}
		if (float.IsNaN(result) || float.IsNaN(result2) || float.IsNaN(result3) || float.IsInfinity(result) || float.IsInfinity(result2) || float.IsInfinity(result3))
		{
			return false;
		}
		position = new Vector3(result, result2, result3);
		return true;
	}
}
internal static class ArchiveDeposit
{
	internal static bool Transfer(ItemArchive archive, string source, ItemData item, int capMB, Action removeAndSave, out string error)
	{
		error = null;
		if (archive == null || !archive.JournalHealthy || item == null || item.m_stack <= 0)
		{
			return false;
		}
		if (!archive.TryPrepare(source, item.Clone(), capMB, out var token))
		{
			return false;
		}
		try
		{
			removeAndSave();
			archive.MarkOriginalRemoved(token);
			return true;
		}
		catch (Exception ex)
		{
			error = "Deposit held for recovery review: " + ex.Message;
			return false;
		}
	}
}
internal static class ArchiveNetworkWithdrawal
{
	internal static int Deliver(ItemArchive archive, ArchiveCheckout checkout, ArchiveCatalog catalog, string key, int capacity, int archiveMB, Func<ItemData, string, string> deliver, long recipient = 0L)
	{
		if (archive == null || checkout == null || catalog == null || !catalog.Ready || !archive.JournalHealthy || !checkout.Healthy || capacity <= 0 || deliver == null)
		{
			return 0;
		}
		VirtualStacks.Row row = catalog.ActiveRows.FirstOrDefault((VirtualStacks.Row r) => r.Key == key);
		ItemArchive.Entry entry = row?.Available.First?.Value;
		if (entry == null || row.Example == null)
		{
			return 0;
		}
		ItemData obj = VirtualStacks.Inspect(entry);
		ItemData val = ((obj != null) ? obj.Clone() : null);
		if (val == null)
		{
			return 0;
		}
		int num = (int)Math.Min(row.Total, Math.Min(capacity, Math.Max(1, val.m_shared.m_maxStackSize)));
		List<string> list = new List<string>();
		int num2 = 0;
		ItemArchive.Entry[] array = row.Available.Take(128).ToArray();
		foreach (ItemArchive.Entry entry2 in array)
		{
			if (!checkout.IsBlocked(entry2.Token))
			{
				ItemData val2 = VirtualStacks.Inspect(entry2);
				if (val2 == null || !entry2.CachedIdentity.CanonicalPayload.SequenceEqual(entry.CachedIdentity.CanonicalPayload))
				{
					throw new InvalidOperationException("Archive delivery metadata mismatch");
				}
				int num4 = Math.Min(val2.m_stack, num - num2);
				string text = entry2.Token;
				if (num4 < val2.m_stack)
				{
					text = ArchiveSplit.TakePortion(archive, checkout, text, val2, num4, archiveMB);
				}
				val.m_cheated |= val2.m_cheated;
				list.Add(text);
				num2 += num4;
				if (num2 == num)
				{
					break;
				}
			}
		}
		if (num2 == 0)
		{
			return 0;
		}
		string text2 = Guid.NewGuid().ToString("N");
		string[] ids = checkout.BeginBatch(list.ToArray(), "network_delivery_intent:" + text2 + ":recipient=" + recipient.ToString(CultureInfo.InvariantCulture));
		val.m_stack = num2;
		string text3 = deliver(val, text2);
		if (string.IsNullOrEmpty(text3))
		{
			throw new InvalidOperationException("Missing network delivery receipt");
		}
		checkout.CommitBatch(ids, "network_delivery:" + text2 + ":" + text3);
		return num2;
	}
}
internal static class ArchiveSplit
{
	internal static string TakePortion(ItemArchive archive, ArchiveCheckout checkout, string token, ItemData item, int amount, int capMB)
	{
		if (amount <= 0 || amount >= item.m_stack)
		{
			throw new ArgumentOutOfRangeException("amount");
		}
		string id = checkout.Begin(token);
		string token2 = null;
		try
		{
			ItemData val = item.Clone();
			val.m_stack = amount;
			ItemData val2 = item.Clone();
			val2.m_stack -= amount;
			string text = "split:" + token + ":";
			string text2 = archive.FindRecord(token)?.Source;
			int num = text2?.LastIndexOf('@') ?? (-1);
			int num2 = ((num < 0) ? (-1) : text2.IndexOf(':', num + 1));
			string text3 = ((num2 > num && num >= 0) ? (text2.Substring(num, num2 - num + 1) + "split") : "");
			if (!archive.TryPrepare(text + "take" + text3, val, capMB, out token2) || !archive.TryPrepare(text + "rest" + text3, val2, capMB, out var token3))
			{
				throw new InvalidOperationException("Cannot prepare both split records; parent reserved for recovery");
			}
			archive.MarkOriginalRemoved(token2);
			archive.MarkOriginalRemoved(token3);
			checkout.CommitReceipt(id, "split:" + token2 + ":" + token3);
			return token2;
		}
		catch
		{
			throw;
		}
	}
}
internal sealed class BufferedDebugLog
{
	private readonly Queue<string> buffer = new Queue<string>();

	private readonly string directory;

	private const int MaxLines = 500;

	private const long MaxBytes = 16777216L;

	private bool failed;

	internal BufferedDebugLog(string directoryPath)
	{
		directory = directoryPath;
	}

	internal void Enqueue(string message)
	{
		if (!failed)
		{
			if (buffer.Count >= 500)
			{
				buffer.Dequeue();
			}
			buffer.Enqueue(DateTime.UtcNow.ToString("O") + " " + message);
		}
	}

	internal void Flush(Action<string> warn)
	{
		if (failed || buffer.Count == 0)
		{
			return;
		}
		try
		{
			Directory.CreateDirectory(directory);
			string text = Path.Combine(directory, "WorldCleaner-debug.log");
			if (File.Exists(text) && new FileInfo(text).Length > 16777216)
			{
				string text2 = Path.Combine(directory, "WorldCleaner-debug.previous.log");
				if (File.Exists(text2))
				{
					File.Delete(text2);
				}
				File.Move(text, text2);
			}
			string[] value = buffer.ToArray();
			File.AppendAllText(text, string.Join(Environment.NewLine, value) + Environment.NewLine, Encoding.UTF8);
			buffer.Clear();
		}
		catch (Exception ex)
		{
			failed = true;
			buffer.Clear();
			warn?.Invoke("Debug file disabled after I/O error: " + ex.Message);
		}
	}
}
internal static class ChestTransfer
{
	private sealed class DestinationCache
	{
		internal Vector3 Position;

		internal float Radius;

		internal float Expires;

		internal int Revision;

		internal Container[] Chests;
	}

	private static readonly List<DestinationCache> destinations = new List<DestinationCache>();

	private static Container[] cachedChests = (Container[])(object)new Container[0];

	private static float chestExpires;

	private static int chestRevision = -1;

	private static readonly HashSet<ZDOID> failedDestinations = new HashSet<ZDOID>();

	private static readonly MethodInfo SaveMethod = typeof(Container).GetMethod("Save", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);

	private static readonly MethodInfo AccessMethod = typeof(Container).GetMethod("CheckAccess", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(long) }, null);

	internal static Func<Container, bool> ArchiveViewOwnsChest = (Container chest) => false;

	internal static void ResetFailures()
	{
		failedDestinations.Clear();
	}

	internal static void ResetCache()
	{
		destinations.Clear();
		cachedChests = (Container[])(object)new Container[0];
		chestExpires = 0f;
		chestRevision = -1;
	}

	internal static Container[] CachedChests()
	{
		if (Time.realtimeSinceStartup >= chestExpires || chestRevision != LoadedObjects.ChestRevision)
		{
			cachedChests = LoadedObjects.Chests();
			chestRevision = LoadedObjects.ChestRevision;
			chestExpires = Time.realtimeSinceStartup + 2f;
		}
		return cachedChests;
	}

	internal static bool CanUse(Container chest, bool requireReady = true, long? actor = null)
	{
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)chest == (Object)null || (chest.IsInUse() && !ArchiveViewOwnsChest(chest)) || (Object)(object)((Component)chest).GetComponent<Piece>() == (Object)null || AccessMethod == null)
		{
			return false;
		}
		if (requireReady && !ActiveCleanerAreas.StorageReady(((Component)chest).transform.position))
		{
			return false;
		}
		ZNetView component = ((Component)chest).GetComponent<ZNetView>();
		if ((Object)(object)component == (Object)null || !component.IsValid())
		{
			return false;
		}
		if (failedDestinations.Contains(component.GetZDO().m_uid))
		{
			return false;
		}
		try
		{
			if (actor.HasValue)
			{
				return ServerAccess.ChestAccess(chest, actor.Value) && OwnershipCoordinator.Acquire(component, chest);
			}
			if (!component.IsOwner())
			{
				return false;
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return false;
			}
			return PrivateArea.CheckAccess(((Component)chest).transform.position, 0f, false, false) && (bool)AccessMethod.Invoke(chest, new object[1] { Player.m_localPlayer.GetPlayerID() });
		}
		catch
		{
			return false;
		}
	}

	internal static IEnumerable<Container> Destinations(Container[] all, Vector3 anchor, float radius)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_015e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
		float r2 = radius * radius;
		DestinationCache destinationCache = destinations.Find(delegate(DestinationCache c)
		{
			//IL_0001: 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_0011: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = c.Position - anchor;
			return ((Vector3)(ref val)).sqrMagnitude < 0.01f && c.Radius == radius;
		});
		if (destinationCache == null || destinationCache.Revision != LoadedObjects.ChestRevision || Time.realtimeSinceStartup >= destinationCache.Expires)
		{
			if (destinationCache == null)
			{
				if (destinations.Count >= 128)
				{
					destinations.RemoveAll((DestinationCache c) => c.Expires <= Time.realtimeSinceStartup);
				}
				if (destinations.Count >= 128)
				{
					destinations.RemoveAt(0);
				}
				destinationCache = new DestinationCache();
				destinations.Add(destinationCache);
			}
			destinationCache.Position = anchor;
			destinationCache.Radius = radius;
			destinationCache.Revision = LoadedObjects.ChestRevision;
			destinationCache.Expires = Time.realtimeSinceStartup + 2f;
			destinationCache.Chests = (all ?? CachedChests()).Where(delegate(Container c)
			{
				//IL_0017: 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_0022: Unknown result type (might be due to invalid IL or missing references)
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				int result;
				if ((Object)(object)c != (Object)null && !VacuumAnchors.IsVacuum(c))
				{
					Vector3 val = ((Component)c).transform.position - anchor;
					result = ((((Vector3)(ref val)).sqrMagnitude <= r2) ? 1 : 0);
				}
				else
				{
					result = 0;
				}
				return (byte)result != 0;
			}).OrderBy(delegate(Container c)
			{
				//IL_0006: 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_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = ((Component)c).transform.position - anchor;
				return ((Vector3)(ref val)).sqrMagnitude;
			}).ToArray();
		}
		long actor = CollectionAuthority.ActorFor(anchor);
		return destinationCache.Chests.Where(delegate(Container c)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: 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)
			int result;
			if ((Object)(object)c != (Object)null && !VacuumAnchors.IsVacuum(c) && CanUse(c, requireReady: true, actor))
			{
				Vector3 val = ((Component)c).transform.position - anchor;
				result = ((((Vector3)(ref val)).sqrMagnitude <= r2) ? 1 : 0);
			}
			else
			{
				result = 0;
			}
			return (byte)result != 0;
		});
	}

	internal static int AmountThatFits(Inventory inventory, ItemData item, int requested)
	{
		if (inventory == null || item == null || item.m_shared == null || requested <= 0)
		{
			return 0;
		}
		return SafeInventory.AmountThatFits(inventory, item, requested);
	}

	internal static IEnumerable<StorageTarget> StorageDestinations(Container[] all, Vector3 anchor, float radius, bool useNearby = true)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		IEnumerable<StorageTarget> enumerable = SpiritStorageCompat.Destinations(anchor, radius, useNearby);
		if (useNearby)
		{
			enumerable = enumerable.Concat(from c in Destinations(all, anchor, radius)
				select new StorageTarget(c));
		}
		return enumerable.OrderBy(delegate(StorageTarget t)
		{
			//IL_0001: 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_0011: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = t.Position - anchor;
			return ((Vector3)(ref val)).sqrMagnitude;
		});
	}

	internal static IEnumerable<StorageTarget> StorageRefillDestinations(Container[] all, Vector3 anchor, float radius, bool nearby)
	{
		//IL_000f: 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)
		HashSet<ZNetView> used = new HashSet<ZNetView>();
		foreach (Container chest in RefillDestinations(all, anchor, radius, useNearby: false))
		{
			StorageTarget target = new StorageTarget(chest);
			used.Add(target.View);
			yield return target;
		}
		foreach (StorageTarget target2 in SpiritStorageCompat.Destinations(anchor, radius, nearbyEnabled: false))
		{
			if (used.Add(target2.View))
			{
				yield return target2;
			}
		}
		if (!nearby)
		{
			yield break;
		}
		foreach (StorageTarget target3 in StorageDestinations(all, anchor, radius))
		{
			if (used.Add(target3.View))
			{
				yield return target3;
			}
		}
	}

	internal static IEnumerable<Container> RefillDestinations(Container[] all, Vector3 anchor, float radius, bool useNearby)
	{
		//IL_000f: 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)
		Container[] array = all ?? CachedChests();
		foreach (Container chest in array)
		{
			int num2;
			if ((Object)(object)chest != (Object)null && VacuumAnchors.IsVacuum(chest))
			{
				Vector3 val = ((Component)chest).transform.position - anchor;
				if (((Vector3)(ref val)).sqrMagnitude < 0.25f)
				{
					num2 = (CanUse(chest, requireReady: true, CollectionAuthority.ActorFor(anchor)) ? 1 : 0);
					goto IL_00cf;
				}
			}
			num2 = 0;
			goto IL_00cf;
			IL_00cf:
			if (num2 != 0)
			{
				yield return chest;
			}
		}
		if (!useNearby)
		{
			yield break;
		}
		foreach (Container item in Destinations(all, anchor, radius))
		{
			yield return item;
		}
	}

	internal static bool TryInsert(Container chest, ItemData source, int amount, out string failure, long? actor = null)
	{
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		failure = null;
		if (SaveMethod == null)
		{
			failure = "Container.Save unavailable";
			return false;
		}
		if ((Object)(object)chest == (Object)null || source == null || amount <= 0 || !CanUse(chest, requireReady: true, actor))
		{
			return false;
		}
		ZNetView component = ((Component)chest).GetComponent<ZNetView>();
		if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner())
		{
			return false;
		}
		Inventory inventory = chest.GetInventory();
		if (inventory == null)
		{
			return false;
		}
		ItemData val = source.Clone();
		val.m_stack = amount;
		ZDOID uid = component.GetZDO().m_uid;
		try
		{
			if (!SafeInventory.TryInsert(inventory, val, amount))
			{
				return false;
			}
			SaveMethod.Invoke(chest, null);
			return true;
		}
		catch (Exception ex)
		{
			failedDestinations.Add(uid);
			failure = "Destination state uncertain after attempted add/save: " + ex;
			return false;
		}
	}
}
internal sealed class CleanerAreaPlan
{
	internal readonly List<GridPoint> Zones = new List<GridPoint>();

	private readonly HashSet<GridPoint> known = new HashSet<GridPoint>();

	internal int Objects { get; private set; }

	internal bool Contains(GridPoint zone)
	{
		return known.Contains(zone);
	}

	internal bool TryAdd(GridPoint center, int reach, int maximumZones, int maximumObjects, Func<GridPoint, int> count)
	{
		if (reach < 1 || reach > 8 || (2 * reach + 1) * (2 * reach + 1) > maximumZones)
		{
			return false;
		}
		List<GridPoint> list = new List<GridPoint>();
		int num = 0;
		for (int i = center.Z - reach; i <= center.Z + reach; i++)
		{
			for (int j = center.X - reach; j <= center.X + reach; j++)
			{
				GridPoint gridPoint = new GridPoint(j, i);
				if (!known.Contains(gridPoint))
				{
					int num2 = count(gridPoint);
					if (num2 < 0)
					{
						return false;
					}
					if (num > maximumObjects - num2)
					{
						return false;
					}
					num += num2;
					list.Add(gridPoint);
				}
			}
		}
		if (known.Count + list.Count > maximumZones || Objects > maximumObjects - num)
		{
			return false;
		}
		foreach (GridPoint item in list)
		{
			known.Add(item);
			Zones.Add(item);
		}
		Objects += num;
		return true;
	}
}
internal sealed class CleanerRegionReadiness<T>
{
	private readonly Dictionary<GridPoint, int> groups = new Dictionary<GridPoint, int>();

	private readonly Dictionary<T, int> missing = new Dictionary<T, int>();

	private readonly List<int> pending = new List<int>();

	internal CleanerRegionReadiness(IEnumerable<GridPoint> zones)
	{
		HashSet<GridPoint> hashSet = new HashSet<GridPoint>(zones);
		Queue<GridPoint> queue = new Queue<GridPoint>();
		foreach (GridPoint zone in zones)
		{
			if (!hashSet.Remove(zone))
			{
				continue;
			}
			int count = pending.Count;
			pending.Add(0);
			queue.Enqueue(zone);
			while (queue.Count > 0)
			{
				GridPoint key = queue.Dequeue();
				groups.Add(key, count);
				for (int i = -1; i <= 1; i++)
				{
					for (int j = -1; j <= 1; j++)
					{
						GridPoint item = new GridPoint(key.X + j, key.Z + i);
						if (hashSet.Remove(item))
						{
							queue.Enqueue(item);
						}
					}
				}
			}
		}
	}

	internal void Add(T id, GridPoint zone)
	{
		if (!groups.TryGetValue(zone, out var value))
		{
			throw new InvalidOperationException("Missing object outside retained region");
		}
		if (missing.TryGetValue(id, out var value2))
		{
			if (value2 != value)
			{
				throw new InvalidOperationException("Object occurs in unrelated retained regions");
			}
		}
		else
		{
			missing.Add(id, value);
			pending[value]++;
		}
	}

	internal void Remove(T id)
	{
		if (missing.TryGetValue(id, out var value))
		{
			missing.Remove(id);
			pending[value]--;
		}
	}

	internal bool Ready(GridPoint zone)
	{
		int value;
		return groups.TryGetValue(zone, out value) && pending[value] == 0;
	}
}
internal static class CollectionAuthority
{
	internal static bool IsServer => (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer();

	internal static bool PeerOwns(long owner)
	{
		if ((Object)(object)ZNet.instance == (Object)null)
		{
			return true;
		}
		foreach (ZNetPeer peer in ZNet.instance.GetPeers())
		{
			if (peer.IsReady() && peer.m_uid == owner)
			{
				return true;
			}
		}
		return false;
	}

	internal static long ActorFor(Vector3 anchor)
	{
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_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)
		Container[] array = ChestTransfer.CachedChests();
		foreach (Container val in array)
		{
			if (!((Object)(object)val != (Object)null) || !VacuumAnchors.IsVacuum(val))
			{
				continue;
			}
			Vector3 val2 = ((Component)val).transform.position - anchor;
			if (((Vector3)(ref val2)).sqrMagnitude < 0.25f)
			{
				Piece component = ((Component)val).GetComponent<Piece>();
				if ((Object)(object)component != (Object)null)
				{
					return component.GetCreator();
				}
			}
		}
		long num = SpiritStorageCompat.CreatorAt(anchor);
		return (num != 0L) ? num : (((Object)(object)Player.m_localPlayer == (Object)null) ? 0 : Player.m_localPlayer.GetPlayerID());
	}
}
internal static class CollectionSafety
{
	internal struct Position
	{
		internal readonly float X;

		internal readonly float Y;

		internal readonly float Z;

		internal Position(float x, float y, float z)
		{
			X = x;
			Y = y;
			Z = z;
		}
	}

	internal static bool MayCollect(Position item, IEnumerable<Position> otherConnectedPlayers, bool authoritativeRosterAvailable, float exclusionMeters)
	{
		if (!authoritativeRosterAvailable || otherConnectedPlayers == null || float.IsNaN(exclusionMeters) || float.IsInfinity(exclusionMeters) || exclusionMeters < 0f)
		{
			return false;
		}
		if (exclusionMeters == 0f)
		{
			return true;
		}
		double num = (double)exclusionMeters * (double)exclusionMeters;
		foreach (Position otherConnectedPlayer in otherConnectedPlayers)
		{
			double num2 = (double)otherConnectedPlayer.X - (double)item.X;
			double num3 = (double)otherConnectedPlayer.Y - (double)item.Y;
			double num4 = (double)otherConnectedPlayer.Z - (double)item.Z;
			if (num2 * num2 + num3 * num3 + num4 * num4 <= num)
			{
				return false;
			}
		}
		return true;
	}
}
internal sealed class DropWorkQueue<TKey, TValue>
{
	internal sealed class Entry
	{
		internal TKey Id;

		internal TValue Value;

		internal double Due;

		internal double EligibleAt;

		internal long Sequence;

		internal int Attempts;

		internal int Priority;
	}

	private sealed class Order : IComparer<Entry>
	{
		public int Compare(Entry a, Entry b)
		{
			int num = a.Due.CompareTo(b.Due);
			return (num != 0) ? num : a.Sequence.CompareTo(b.Sequence);
		}
	}

	private readonly Dictionary<TKey, Entry> entries = new Dictionary<TKey, Entry>();

	private readonly SortedSet<Entry>[] ready = new SortedSet<Entry>[2]
	{
		new SortedSet<Entry>(new Order()),
		new SortedSet<Entry>(new Order())
	};

	private long sequence;

	internal int Count => entries.Count;

	internal long Overflow { get; private set; }

	internal bool TryGet(TKey id, out Entry entry)
	{
		return entries.TryGetValue(id, out entry);
	}

	internal bool Observe(TKey id, TValue value, double due, bool fast, int capacity)
	{
		if (entries.TryGetValue(id, out var value2))
		{
			if (fast && value2.Priority != 0)
			{
				ready[1].Remove(value2);
				value2.Priority = 0;
				ready[0].Add(value2);
			}
			return true;
		}
		if (entries.Count >= capacity)
		{
			Overflow++;
			if (!fast || ready[1].Count == 0)
			{
				return false;
			}
			Entry max = ready[1].Max;
			ready[1].Remove(max);
			entries.Remove(max.Id);
		}
		Entry entry = new Entry
		{
			Id = id,
			Value = value,
			Due = due,
			EligibleAt = due,
			Priority = ((!fast) ? 1 : 0),
			Sequence = sequence++
		};
		entries.Add(id, entry);
		ready[entry.Priority].Add(entry);
		return true;
	}

	internal void Wake(TKey id, double now)
	{
		if (entries.TryGetValue(id, out var value) && !(value.Due <= Math.Max(now, value.EligibleAt)))
		{
			ready[value.Priority].Remove(value);
			value.Due = Math.Max(now, value.EligibleAt);
			ready[value.Priority].Add(value);
		}
	}

	internal bool TryTake(double now, bool backgroundTurn, out Entry entry)
	{
		for (int i = 0; i < 2; i++)
		{
			int num = (backgroundTurn ? (1 - i) : i);
			Entry entry2 = ((ready[num].Count == 0) ? null : ready[num].Min);
			if (entry2 != null && !(entry2.Due > now))
			{
				ready[num].Remove(entry2);
				entries.Remove(entry2.Id);
				entry = entry2;
				return true;
			}
		}
		entry = null;
		return false;
	}

	internal void Retry(Entry entry, double due, int capacity)
	{
		entry.Due = due;
		entry.Attempts++;
		entry.Priority = 1;
		if (!entries.ContainsKey(entry.Id))
		{
			if (entries.Count >= capacity)
			{
				Overflow++;
				return;
			}
			entries.Add(entry.Id, entry);
			ready[entry.Priority].Add(entry);
		}
	}

	internal void Clear()
	{
		entries.Clear();
		ready[0].Clear();
		ready[1].Clear();
		sequence = 0L;
		Overflow = 0L;
	}
}
internal sealed class FrameWork
{
	private IEnumerator<object> running;

	private readonly Func<double> clock;

	private double resumeAt;

	private double nextReport;

	private string stage = "scan preparation";

	internal bool Busy => running != null;

	internal bool Suspended { get; private set; }

	internal FrameWork(Func<double> clock = null)
	{
		this.clock = clock ?? ((Func<double>)(() => (double)Stopwatch.GetTimestamp() * 1000.0 / (double)Stopwatch.Frequency));
	}

	internal void Start(IEnumerable<object> work)
	{
		if (!Suspended)
		{
			if (Busy)
			{
				throw new InvalidOperationException("Collection cycle already running");
			}
			running = work.GetEnumerator();
		}
	}

	internal void Cancel()
	{
		running?.Dispose();
		running = null;
		resumeAt = 0.0;
		stage = "scan preparation";
	}

	internal void Reset()
	{
		Cancel();
		Suspended = false;
	}

	internal void Tick(int maximumSteps, double milliseconds, Action<string> warn)
	{
		if (Suspended && clock() >= resumeAt)
		{
			Suspended = false;
		}
		if (!Busy || clock() < resumeAt)
		{
			return;
		}
		double num = clock();
		string text = stage;
		try
		{
			for (int i = 0; i < maximumSteps; i++)
			{
				if (!Busy)
				{
					break;
				}
				text = stage;
				if (!running.MoveNext())
				{
					Cancel();
					break;
				}
				stage = (running.Current as string) ?? stage;
				if (clock() - num >= milliseconds)
				{
					break;
				}
			}
		}
		catch (Exception ex)
		{
			Cancel();
			warn("Collection work stopped: " + ex);
		}
		double num2 = clock() - num;
		if (num2 > 100.0)
		{
			Cancel();
			Suspended = true;
			resumeAt = clock() + 2000.0;
			warn("Performance safeguard: " + text + " took " + num2.ToString("F1") + " ms. Maintenance cooling down for two seconds, then resuming. Other mod callbacks and disk writes cannot be preempted safely.");
		}
		else if (num2 > 20.0)
		{
			resumeAt = clock() + Math.Min(2000.0, num2 * 10.0);
			if (clock() >= nextReport)
			{
				nextReport = clock() + 10000.0;
				warn("Performance: " + text + " took " + num2.ToString("F1") + " ms in one indivisible step; collection backing off. Frame budget=" + milliseconds + " ms.");
			}
		}
	}
}
internal static class GameplayInput
{
	private static readonly MethodInfo TakeInput = typeof(Player).GetMethod("TakeInput", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);

	internal static bool Allowed()
	{
		try
		{
			return (Object)(object)Player.m_localPlayer != (Object)null && TakeInput != null && (bool)TakeInput.Invoke(Player.m_localPlayer, null);
		}
		catch
		{
			return false;
		}
	}
}
internal sealed class HybridDropScanner : IDisposable
{
	private struct Candidate
	{
		internal bool Initialized;

		internal Vector3 Original;
	}

	internal const string OriginalPositionKey = "arcbyte.worldcleaner.origin.v1";

	private static readonly FieldInfo SectorsField = typeof(ZDOMan).GetField("m_objectsBySector", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo PortalsField = typeof(ZDOMan).GetField("m_portalObjects", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private readonly DropWorkQueue<ZDOID, Candidate> queue = new DropWorkQueue<ZDOID, Candidate>();

	private readonly Dictionary<int, bool> prefabs = new Dictionary<int, bool>();

	private readonly Dictionary<ZDOID, Vector3> anchors = new Dictionary<ZDOID, Vector3>();

	private readonly HashSet<ZDOID> quarantined = new HashSet<ZDOID>();

	private readonly List<GridPoint> origins = new List<GridPoint>();

	private readonly HashSet<uint> originSectors = new HashSet<uint>();

	private List<ZDO>[] sectors;

	private Dictionary<SectorIndex, List<ZDO>> portals;

	private ZDOMan manager;

	private SectorSweep<ZDO> sweep;

	private int loadedCursor;

	private int chestCursor;

	private int fairness;

	private double nextDiscovery;

	private double nextAnchors;

	private double nextReport;

	private double resumeAt;

	private Vector3[] cachedAnchors = (Vector3[])(object)new Vector3[0];

	private string manualText;

	private IList<Vector3> manual = (IList<Vector3>)(object)new Vector3[0];

	private bool anchorsDirty = true;

	private bool canStage;

	private bool fallbackSet;

	private GridPoint fallback;

	private long completed;

	private long retries;

	private long staged;

	private long stale;

	private long failures;

	private double scanMs;

	private double transferMs;

	private long lastSectors;

	private long lastExamined;

	private double reportStarted;

	private int boundSectorCount;

	private bool bindingWarning;

	private string anchorCachePath;

	private bool anchorCacheLoaded;

	private bool anchorCacheDirty;

	private double nextAnchorSave;

	internal int Capacity = 10000;

	internal int MaximumRecords = 256;

	internal double GraceSeconds = 5.0;

	internal double ScanMilliseconds = 0.5;

	internal double TransferMilliseconds = 1.0;

	internal double IdleSeconds = 0.15;

	internal double RescanSeconds = 300.0;

	internal bool EventDetection = true;

	internal bool Adaptive = true;

	internal bool Suspended { get; private set; }

	internal Vector3[] Anchors
	{
		get
		{
			if (ZDOMan.instance != null && (anchorsDirty || Now >= nextAnchors))
			{
				RefreshAnchors();
			}
			return cachedAnchors;
		}
	}

	internal int Pending => queue.Count;

	private static double Now => Time.realtimeSinceStartup;

	internal void SetAnchorCache(string folder)
	{
		anchorCachePath = ((folder == null) ? null : Path.Combine(folder, "cleaners.wca"));
		anchorCacheLoaded = false;
		if (anchors.Count > 0)
		{
			anchorCacheDirty = true;
		}
	}

	internal HybridDropScanner()
	{
		LoadedObjects.DropRegistered += Registered;
		VacuumAnchors.Changed += ObserveAnchor;
	}

	public void Dispose()
	{
		LoadedObjects.DropRegistered -= Registered;
		VacuumAnchors.Changed -= ObserveAnchor;
	}

	private void Registered(ZDOID id)
	{
		//IL_0016: 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)
		if (EventDetection)
		{
			queue.Observe(id, default(Candidate), Now + GraceSeconds, fast: true, Capacity);
			queue.Wake(id, Now);
		}
	}

	private bool IsDrop(ZDO zdo)
	{
		int prefab = zdo.GetPrefab();
		if (prefabs.TryGetValue(prefab, out var value))
		{
			return value;
		}
		GameObject prefab2 = ZNetScene.instance.GetPrefab(prefab);
		if ((Object)(object)prefab2 == (Object)null)
		{
			return false;
		}
		value = (Object)(object)prefab2.GetComponent<ItemDrop>() != (Object)null && (Object)(object)prefab2.GetComponent<Container>() == (Object)null;
		prefabs[prefab] = value;
		return value;
	}

	internal void ObserveAnchor(ZDO zdo)
	{
		//IL_009e: 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_0031: 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_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: 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_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		if (zdo == null || !zdo.IsValid())
		{
			return;
		}
		if (zdo.GetBool("arcbyte.worldcleaner.vacuum.v1", false))
		{
			Vector3 position = zdo.GetPosition();
			if (!PersistentDropSweeper.ValidPosition(position))
			{
				return;
			}
			if (anchors.TryGetValue(zdo.m_uid, out var value))
			{
				Vector3 val = value - position;
				if (!(((Vector3)(ref val)).sqrMagnitude > 0.01f))
				{
					return;
				}
			}
			anchors[zdo.m_uid] = position;
			anchorsDirty = true;
			anchorCacheDirty = true;
		}
		else if (anchors.Remove(zdo.m_uid))
		{
			anchorsDirty = true;
			anchorCacheDirty = true;
		}
	}

	private void Inspect(ZDO zdo)
	{
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: 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)
		//IL_0100: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		if (zdo == null || !zdo.IsValid())
		{
			return;
		}
		ObserveAnchor(zdo);
		if (!IsDrop(zdo) || zdo.GetBool("arcbyte.worldcleaner.leftover.v1", false) || quarantined.Contains(zdo.m_uid) || zdo.GetBool("arcbyte.worldcleaner.deposit.pending", false) || (!canStage && (Object)(object)ZNetScene.instance.FindInstance(zdo.m_uid) == (Object)null))
		{
			return;
		}
		DropWorkQueue<ZDOID, Candidate>.Entry entry;
		bool flag = queue.TryGet(zdo.m_uid, out entry);
		if (!flag || !entry.Value.Initialized)
		{
			Candidate value = new Candidate
			{
				Initialized = true,
				Original = (zdo.GetBool("arcbyte.worldcleaner.staged.v1", false) ? zdo.GetVec3("arcbyte.worldcleaner.origin.v1", zdo.GetPosition()) : zdo.GetPosition())
			};
			if (flag)
			{
				entry.Value = value;
			}
			else
			{
				queue.Observe(zdo.m_uid, value, Now + GraceSeconds, fast: false, Capacity);
			}
		}
	}

	internal void SetManual(string text, Func<string, List<Vector3>> parse)
	{
		if (text != manualText)
		{
			manualText = text;
			manual = parse(text);
			anchorsDirty = true;
		}
	}

	internal void Reset()
	{
		queue.Clear();
		prefabs.Clear();
		anchors.Clear();
		quarantined.Clear();
		anchorCacheLoaded = false;
		anchorCacheDirty = false;
		nextAnchorSave = 0.0;
		sweep = null;
		manager = null;
		sectors = null;
		portals = null;
		loadedCursor = (chestCursor = (fairness = 0));
		nextDiscovery = (nextAnchors = (nextReport = (resumeAt = 0.0)));
		cachedAnchors = (Vector3[])(object)new Vector3[0];
		anchorsDirty = true;
		Suspended = false;
		fallbackSet = false;
		completed = (retries = (staged = (stale = (failures = 0L))));
		scanMs = (transferMs = 0.0);
		lastSectors = (lastExamined = 0L);
		reportStarted = Now;
		boundSectorCount = 0;
		bindingWarning = false;
	}

	private bool Bind(Action<string> warn)
	{
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0106: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_0123: Unknown result type (might be due to invalid IL or missing references)
		//IL_0129: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_019a: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
		if (SectorsField == null || PortalsField == null || ZDOMan.instance == null)
		{
			if (!bindingWarning)
			{
				bindingWarning = true;
				warn("Persistent sector API unavailable; sweep disabled, loaded detection remains active.");
			}
			return false;
		}
		sectors = SectorsField.GetValue(ZDOMan.instance) as List<ZDO>[];
		portals = PortalsField.GetValue(ZDOMan.instance) as Dictionary<SectorIndex, List<ZDO>>;
		if (sectors == null || sectors.Length == 0 || portals == null)
		{
			return false;
		}
		if (manager == ZDOMan.instance && sweep != null && boundSectorCount == sectors.Length)
		{
			return true;
		}
		sweep = null;
		Vector2s val = ZoneSystem.IndexToSector(0u);
		Vector2s val2 = ZoneSystem.IndexToSector((uint)(sectors.Length - 1));
		int sector = (int)ZoneSystem.SectorToIndex((int)val.x, (int)val.y).Sector;
		int sector2 = (int)ZoneSystem.SectorToIndex((int)val2.x, (int)val2.y).Sector;
		if (sector != 0 || sector2 != sectors.Length - 1)
		{
			if (!bindingWarning)
			{
				bindingWarning = true;
				warn("Sector API bounds do not round-trip; persistent sweep disabled.");
			}
			return false;
		}
		manager = ZDOMan.instance;
		boundSectorCount = sectors.Length;
		sweep = new SectorSweep<ZDO>(val.x, val.y, val2.x, val2.y, sectors.Length, (int x, int z) => (int)ZoneSystem.SectorToIndex(x, z).Sector, (int index, int layer) => (layer == 0) ? sectors[index] : (portals.TryGetValue(new SectorIndex((uint)index), out var value) ? value : null));
		return true;
	}

	private void RefreshAnchors()
	{
		//IL_0049: 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_016e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_0176: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		//IL_017d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0185: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_019c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_020f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0214: Unknown result type (might be due to invalid IL or missing references)
		//IL_0219: Unknown result type (might be due to invalid IL or missing references)
		//IL_021b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0222: Unknown result type (might be due to invalid IL or missing references)
		//IL_0227: Unknown result type (might be due to invalid IL or missing references)
		//IL_022a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0231: Unknown result type (might be due to invalid IL or missing references)
		if (Now >= nextAnchors)
		{
			nextAnchors = Now + 1.0;
			foreach (ZDOID item in new List<ZDOID>(anchors.Keys))
			{
				ZDO zDO = ZDOMan.instance.GetZDO(item);
				if (zDO == null || !zDO.IsValid() || !zDO.GetBool("arcbyte.worldcleaner.vacuum.v1", false))
				{
					anchors.Remove(item);
					anchorsDirty = true;
				}
				else
				{
					ObserveAnchor(zDO);
				}
			}
		}
		bool flag = anchorsDirty || origins.Count == 0;
		if (anchorsDirty)
		{
			List<Vector3> list = new List<Vector3>(anchors.Values);
			list.AddRange(manual);
			cachedAnchors = list.ToArray();
			anchorsDirty = false;
		}
		if (!flag)
		{
			sweep?.SetOrigins(origins);
			return;
		}
		origins.Clear();
		originSectors.Clear();
		Vector3[] array = cachedAnchors;
		foreach (Vector3 val in array)
		{
			SectorIndex sectorIndex = ZoneSystem.GetSectorIndex(val);
			if (originSectors.Add(sectorIndex.Sector))
			{
				Vector2s val2 = ZoneSystem.IndexToSector(sectorIndex.Sector);
				origins.Add(new GridPoint(val2.x, val2.y));
			}
		}
		if (origins.Count == 0 && (Object)(object)ZNet.instance != (Object)null)
		{
			if (!fallbackSet)
			{
				SectorIndex sectorIndex2 = ZoneSystem.GetSectorIndex(ZNet.instance.GetReferencePosition());
				Vector2s val3 = ZoneSystem.IndexToSector(sectorIndex2.Sector);
				fallback = new GridPoint(val3.x, val3.y);
				fallbackSet = true;
			}
			origins.Add(fallback);
		}
		sweep?.SetOrigins(origins);
	}

	internal unsafe bool Tick(bool persistent, bool allowTransfers, bool stageUnloaded, bool maintenanceTurn, bool collectNearPlayers, float playerRadius, Func<ItemDrop, Vector3, Vector3[], LiveWorldCleaner.Outcome> transfer, Action<string> report, Action<string> warn)
	{
		//IL_09af: Unknown result type (might be due to invalid IL or missing references)
		//IL_09d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_09d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0167: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: 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_0567: Unknown result type (might be due to invalid IL or missing references)
		//IL_05a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_062e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0613: Unknown result type (might be due to invalid IL or missing references)
		//IL_0618: Unknown result type (might be due to invalid IL or missing references)
		//IL_0603: Unknown result type (might be due to invalid IL or missing references)
		//IL_061d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0665: Unknown result type (might be due to invalid IL or missing references)
		//IL_06b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0721: Unknown result type (might be due to invalid IL or missing references)
		//IL_0788: Unknown result type (might be due to invalid IL or missing references)
		//IL_07eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_07f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_07f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_07f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_07fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0800: Unknown result type (might be due to invalid IL or missing references)
		//IL_0813: Unknown result type (might be due to invalid IL or missing references)
		//IL_0820: Unknown result type (might be due to invalid IL or missing references)
		//IL_0825: Unknown result type (might be due to invalid IL or missing references)
		//IL_082a: Unknown result type (might be due to invalid IL or missing references)
		//IL_08a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_08c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_08c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_08ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_08e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_08f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_08fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0902: Unknown result type (might be due to invalid IL or missing references)
		//IL_0904: Unknown result type (might be due to invalid IL or missing references)
		//IL_0909: Unknown result type (might be due to invalid IL or missing references)
		//IL_084e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0853: Unknown result type (might be due to invalid IL or missing references)
		if (Suspended && Now >= resumeAt)
		{
			Suspended = false;
		}
		if (Suspended || Now < resumeAt || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null || !CollectionAuthority.IsServer)
		{
			return false;
		}
		Stopwatch stopwatch = Stopwatch.StartNew();
		if (!anchorCacheLoaded && anchorCachePath != null)
		{
			anchorCacheLoaded = true;
			try
			{
				KnownCleanerId[] array = KnownCleanerCache.Load(anchorCachePath);
				for (int i = 0; i < array.Length; i++)
				{
					KnownCleanerId knownCleanerId = array[i];
					ObserveAnchor(ZDOMan.instance.GetZDO(new ZDOID(knownCleanerId.User, knownCleanerId.Id)));
				}
			}
			catch (Exception ex)
			{
				warn("Cleaner location cache ignored; normal discovery continues: " + ex.Message);
			}
		}
		if (anchorCacheDirty && anchorCachePath != null && Now >= nextAnchorSave)
		{
			nextAnchorSave = Now + 5.0;
			try
			{
				List<KnownCleanerId> list = new List<KnownCleanerId>();
				foreach (ZDOID key in anchors.Keys)
				{
					ZDOID current = key;
					list.Add(new KnownCleanerId
					{
						User = ((ZDOID)(ref current)).UserID,
						Id = ((ZDOID)(ref current)).ID
					});
				}
				KnownCleanerCache.Save(anchorCachePath, list);
				anchorCacheDirty = false;
			}
			catch (Exception ex2)
			{
				warn("Could not save cleaner location cache: " + ex2.Message);
			}
		}
		canStage = stageUnloaded && allowTransfers;
		double num = ((Adaptive && Time.unscaledDeltaTime > 0.033f) ? 0.25 : 1.0);
		if (Now >= nextDiscovery)
		{
			nextDiscovery = Now + IdleSeconds;
			Stopwatch stopwatch2 = Stopwatch.StartNew();
			int num2 = 0;
			int num3 = Math.Min(MaximumRecords / 4, LoadedObjects.DropSlotCount);
			while (num2 < num3 && stopwatch2.Elapsed.TotalMilliseconds < ScanMilliseconds * num * 0.35 && LoadedObjects.DropSlotCount != 0)
			{
				loadedCursor %= LoadedObjects.DropSlotCount;
				ItemDrop val = LoadedObjects.DropAt(loadedCursor);
				loadedCursor = (loadedCursor + 1) % LoadedObjects.DropSlotCount;
				num2++;
				if (!((Object)(object)val == (Object)null))
				{
					ZNetView component = ((Component)val).GetComponent<ZNetView>();
					if ((Object)(object)component != (Object)null && component.IsValid())
					{
						Inspect(component.GetZDO());
					}
				}
			}
			int num4 = Math.Min(16, LoadedObjects.ChestSlotCount);
			foreach (Component item in SpiritStorageCompat.All())
			{
				ObserveAnchor(item.GetComponent<ZNetView>().GetZDO());
			}
			for (int j = 0; j < num4; j++)
			{
				if (!(stopwatch2.Elapsed.TotalMilliseconds < ScanMilliseconds * num * 0.5))
				{
					break;
				}
				chestCursor %= LoadedObjects.ChestSlotCount;
				Container val2 = LoadedObjects.ChestAt(chestCursor);
				chestCursor = (chestCursor + 1) % LoadedObjects.ChestSlotCount;
				if (!((Object)(object)val2 == (Object)null))
				{
					ZNetView component2 = ((Component)val2).GetComponent<ZNetView>();
					if ((Object)(object)component2 != (Object)null && component2.IsValid())
					{
						ObserveAnchor(component2.GetZDO());
					}
				}
			}
			if (persistent && stopwatch2.Elapsed.TotalMilliseconds < ScanMilliseconds * num && Bind(warn))
			{
				RefreshAnchors();
				sweep.Tick(Now, Math.Max(1, MaximumRecords - num2), MaximumRecords, Math.Max(0.0, ScanMilliseconds * num - stopwatch2.Elapsed.TotalMilliseconds), RescanSeconds, Inspect);
			}
			scanMs += stopwatch2.Elapsed.TotalMilliseconds;
		}
		RefreshAnchors();
		bool result = false;
		if (allowTransfers && !maintenanceTurn && cachedAnchors.Length != 0)
		{
			Stopwatch stopwatch3 = Stopwatch.StartNew();
			for (int k = 0; k < MaximumRecords; k++)
			{
				if (!(stopwatch3.Elapsed.TotalMilliseconds < TransferMilliseconds * num))
				{
					break;
				}
				fairness = (fairness + 1) % 4;
				if (!queue.TryTake(Now, fairness == 0, out var entry))
				{
					break;
				}
				ZDO zDO = ZDOMan.instance.GetZDO(entry.Id);
				if (zDO == null || !zDO.IsValid())
				{
					stale++;
				}
				else
				{
					if (quarantined.Contains(entry.Id) || zDO.GetBool("arcbyte.worldcleaner.leftover.v1", false))
					{
						continue;
					}
					Candidate value = entry.Value;
					if (!value.Initialized)
					{
						value.Initialized = true;
						value.Original = (zDO.GetBool("arcbyte.worldcleaner.staged.v1", false) ? zDO.GetVec3("arcbyte.worldcleaner.origin.v1", zDO.GetPosition()) : zDO.GetPosition());
						entry.Value = value;
					}
					if (!PersistentDropSweeper.ValidPosition(value.Original) || !IsDrop(zDO))
					{
						stale++;
						continue;
					}
					if (OtherPlayerProximity.Blocks(value.Original, playerRadius, collectNearPlayers))
					{
						queue.Retry(entry, Now + 2.0, Capacity);
						retries++;
						continue;
					}
					GameObject val3 = ZNetScene.instance.FindInstance(entry.Id);
					ItemDrop val4 = (((Object)(object)val3 != (Object)null) ? val3.GetComponent<ItemDrop>() : null);
					if (!((Object)(object)val4 == (Object)null) || stageUnloaded)
					{
						result = true;
						try
						{
							if ((Object)(object)val4 != (Object)null)
							{
								switch (transfer(val4, value.Original, cachedAnchors))
								{
								case LiveWorldCleaner.Outcome.Retry:
									queue.Retry(entry, Now + 2.0, Capacity);
									retries++;
									break;
								case LiveWorldCleaner.Outcome.Quarantined:
									quarantined.Add(entry.Id);
									failures++;
									break;
								default:
									completed++;
									break;
								}
							}
							else if (stageUnloaded && (Object)(object)val3 == (Object)null && !zDO.GetBool("arcbyte.worldcleaner.staged.v1", false))
							{
								Vector3 val5 = cachedAnchors[0];
								Vector3 val6 = value.Original - val5;
								float num5 = ((Vector3)(ref val6)).sqrMagnitude;
								for (int l = 1; l < cachedAnchors.Length; l++)
								{
									val6 = value.Original - cachedAnchors[l];
									float sqrMagnitude = ((Vector3)(ref val6)).sqrMagnitude;
									if (sqrMagnitude < num5)
									{
										num5 = sqrMagnitude;
										val5 = cachedAnchors[l];
									}
								}
								int num6 = ((object)Unsafe.As<ZDOID, ZDOID>(ref entry.Id)/*cast due to .constrained prefix*/).GetHashCode() & 0x7FFFFFFF;
								float num7 = (float)(num6 % 24) * (float)Math.PI * 2f / 24f;
								Vector3 val7 = val5 + new Vector3(Mathf.Cos(num7) * 13f, 1f, Mathf.Sin(num7) * 13f);
								zDO.SetOwner(ZDOMan.GetSessionID());
								zDO.Set("arcbyte.worldcleaner.origin.v1", value.Original);
								zDO.SetPosition(val7);
								val6 = zDO.GetPosition() - val7;
								if (((Vector3)(ref val6)).sqrMagnitude > 0.25f)
								{
									throw new InvalidOperationException("Staging position verification failed");
								}
								zDO.Set("arcbyte.worldcleaner.staged.v1", true);
								staged++;
								queue.Retry(entry, Now + 1.0, Capacity);
							}
							else
							{
								queue.Retry(entry, Now + 10.0, Capacity);
								retries++;
							}
						}
						catch (Exception ex3)
						{
							quarantined.Add(entry.Id);
							failures++;
							ZDOID id = entry.Id;
							warn("Drop processing quarantined " + ((object)(*(ZDOID*)(&id))/*cast due to .constrained prefix*/).ToString() + ": " + ex3);
						}
						break;
					}
					stale++;
				}
			}
			transferMs += stopwatch3.Elapsed.TotalMilliseconds;
		}
		if (stopwatch.Elapsed.TotalMilliseconds > 100.0)
		{
			Suspended = true;
			resumeAt = Now + 2.0;
			warn("Performance safeguard: scanner cooling down for two seconds after " + stopwatch.Elapsed.TotalMilliseconds.ToString("F1") + " ms, then resuming queued work.");
		}
		else if (stopwatch.Elapsed.TotalMilliseconds > 20.0)
		{
			resumeAt = Now + 0.5;
			warn("Scanner backing off after " + stopwatch.Elapsed.TotalMilliseconds.ToString("F1") + " ms.");
		}
		if (Now >= nextReport)
		{
			nextReport = Now + 30.0;
			long num8 = sweep?.Sectors ?? 0;
			long num9 = sweep?.Examined ?? 0;
			double num10 = Math.Max(0.001, Now - reportStarted);
			report("Scanner: sectors=" + (sweep?.Sectors ?? 0) + " records=" + (sweep?.Examined ?? 0) + " sectorsPerSecond=" + ((double)(num8 - lastSectors) / num10).ToString("F1") + " recordsPerSecond=" + ((double)(num9 - lastExamined) / num10).ToString("F1") + " pending=" + queue.Count + " overflow=" + queue.Overflow + " completed=" + completed + " retries=" + retries + " staged=" + staged + " stale=" + stale + " quarantined=" + failures + " scannerMs=" + scanMs.ToString("F1") + " transferMs=" + transferMs.ToString("F1"));
			scanMs = (transferMs = 0.0);
			lastSectors = num8;
			lastExamined = num9;
			reportStarted = Now;
		}
		return result;
	}
}
internal sealed class ItemArchive
{
	internal sealed class Entry
	{
		internal string Token;

		internal string Source;

		internal string State;

		internal byte[] Payload;

		internal ItemData CachedItem;

		internal ItemIdentity.Snapshot CachedIdentity;

		internal bool DecodeAttempted;
	}

	private const int Magic = 1464027698;

	private const int MaxRecordBytes = 16777216;

	private readonly string directory;

	private readonly string journal;

	private readonly ManualLogSource log;

	private readonly Func<bool> debug;

	private readonly Dictionary<string, Entry> entries = new Dictionary<string, Entry>();

	private readonly List<Entry> ordered = new List<Entry>();

	private readonly Dictionary<string, string> sourceTokens = new Dictionary<string, string>(StringComparer.Ordinal);

	private readonly object gate = new object();

	private bool recovered;

	private bool journalHealthy = true;

	internal int RecordCount
	{
		get
		{
			lock (gate)
			{
				return ordered.Count;
			}
		}
	}

	internal bool JournalHealthy
	{
		get
		{
			lock (gate)
			{
				return recovered && journalHealthy;
			}
		}
	}

	internal event Action<Entry> RecordAvailable;

	internal Entry FindSource(string source)
	{
		lock (gate)
		{
			string value;
			return sourceTokens.TryGetValue(source, out value) ? entries[value] : null;
		}
	}

	internal Entry RecordAt(int index)
	{
		lock (gate)
		{
			return (index < ordered.Count) ? ordered[index] : null;
		}
	}

	internal Entry FindRecord(string token)
	{
		lock (gate)
		{
			Entry value;
			return entries.TryGetValue(token, out value) ? value : null;
		}
	}

	internal ItemArchive(string directory, ManualLogSource log, Func<bool> debug)
	{
		this.directory = directory;
		this.log = log;
		this.debug = debug;
		journal = Path.Combine(directory, "recovery.wcj");
	}

	internal void Recover()
	{
		lock (gate)
		{
			if (recovered)
			{
				return;
			}
			Directory.CreateDirectory(directory);
			if (File.Exists(journal))
			{
				using FileStream fileStream = new FileStream(journal, FileMode.Open, FileAccess.Read, FileShare.Read);
				using BinaryReader binaryReader = new BinaryReader(fileStream, Encoding.UTF8);
				while (fileStream.Position + 12 <= fileStream.Length)
				{
					long position = fileStream.Position;
					try
					{
						if (binaryReader.ReadInt32() != 1464027698)
						{
							throw new InvalidDataException("Journal magic mismatch at " + position);
						}
						int num = binaryReader.ReadInt32();
						uint num2 = binaryReader.ReadUInt32();
						if (num <= 0 || num > 16777216 || fileStream.Length - fileStream.Position < num)
						{
							throw new InvalidDataException("Incomplete or invalid journal record at " + position);
						}
						byte[] array = binaryReader.ReadBytes(num);
						if (Checksum(array) != num2)
						{
							throw new InvalidDataException("Journal checksum mismatch at " + position);
						}
						using MemoryStream memoryStream = new MemoryStream(array);
						using BinaryReader binaryReader2 = new BinaryReader(memoryStream, Encoding.UTF8);
						string text = binaryReader2.ReadString();
						string text2