Decompiled source of RunicStorageNetworkFork v1.0.0

plugins/RunicStorageNetwork/RunicStorageNetwork.dll

Decompiled 2 weeks 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.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using RunicStorageNetwork.API;
using RunicStorageNetwork.Logic;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.Rendering;
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("RunicStorageNetwork")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace RunicStorageNetwork
{
	internal static class Actions
	{
		internal sealed class Pending
		{
			internal Operation Op;

			internal Player Player;

			internal InventoryGui Gui;

			internal Recipe Recipe;

			internal Piece Piece;

			internal ItemData Upgrade;

			internal ItemData Tool;

			internal int UpgradeQuality;

			internal int Variant;

			internal bool Multi;

			internal bool Output;

			internal bool Cancelled;

			internal bool Prepared;

			internal Vector3 Position;

			internal Quaternion Rotation;

			internal float Started;

			internal float LastPoll;

			internal bool Warned;

			internal RecoveryAttempt Recovery;

			internal string RollbackFailure;

			internal List<Debit> Plan;

			internal InventoryDelta Delta;
		}

		internal static Pending Waiting;

		internal static Pending Active;

		private static readonly OutcomeReceipts Outcomes = new OutcomeReceipts();

		internal static bool Locked(Inventory inv)
		{
			return Waiting != null && Object.op_Implicit((Object)(object)Waiting.Player) && ((Humanoid)Waiting.Player).GetInventory() == inv;
		}

		internal static void Clear()
		{
			Waiting = null;
			Active = null;
			Outcomes.Clear();
			TerminalTransfer.Clear();
			NetworkTerminal.Close();
			Stockroom.ClearObservations();
			CraftPreparation.Clear();
			RecipeIndex.Invalidate();
			Plugin.ClearCritical();
		}

		internal static PieceTable BuildTable(Player p)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)p) || (Object)(object)p != (Object)(object)Player.m_localPlayer || !Object.op_Implicit((Object)(object)ObjectDB.instance))
			{
				return null;
			}
			ItemData val = (ItemData)R.Call(p, "GetRightItem", Type.EmptyTypes);
			PieceTable val2 = val?.m_shared?.m_buildPieces;
			return (Object.op_Implicit((Object)(object)val2) && (Object)(object)p.GetBuildTool() == (Object)(object)val2 && BuildToolPolicy.Tool(val)) ? val2 : null;
		}

		internal static bool BuildPiece(Player p, Piece piece)
		{
			PieceTable val = BuildTable(p);
			if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)piece) || piece.m_repairPiece || piece.m_removePiece)
			{
				return false;
			}
			GameObject val2 = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab(R.Id(((Component)piece).gameObject)) : ((Component)piece).gameObject);
			return Object.op_Implicit((Object)(object)val2) && val.m_pieces != null && val.m_pieces.Contains(val2) && BuildToolPolicy.PieceReason(val2) == null;
		}

		internal static bool LocalBuildMaterials(Player p, Piece piece, RequirementMode mode)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Invalid comparison between Unknown and I4
			if (!Object.op_Implicit((Object)(object)p) || !Object.op_Implicit((Object)(object)piece))
			{
				return false;
			}
			Requirement[] resources = piece.m_resources;
			foreach (Requirement val in resources)
			{
				if (Object.op_Implicit((Object)(object)val.m_resItem) && val.m_amount > 0)
				{
					int num = (((int)mode == 2) ? 1 : val.m_amount);
					if (((Humanoid)p).GetInventory().CountItems(val.m_resItem.m_itemData.m_shared.m_name, -1, true) < num)
					{
						return false;
					}
				}
			}
			return true;
		}

		internal static bool BuildToolUsable(Player p, ItemData tool)
		{
			return tool != null && (!tool.m_shared.m_useDurability || tool.m_durability > 0f) && ((Character)p).HaveStamina(tool.m_shared.m_attack.m_attackStamina);
		}

		internal static Core Context(Player p, bool craft)
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)p) || (Object)(object)p != (Object)(object)Player.m_localPlayer || !Plugin.Enabled || (!craft && !Object.op_Implicit((Object)(object)BuildTable(p))))
			{
				return null;
			}
			CraftingStation currentCraftingStation = p.GetCurrentCraftingStation();
			if (craft && (!Object.op_Implicit((Object)(object)currentCraftingStation) || currentCraftingStation.m_upgrader))
			{
				return null;
			}
			return craft ? Core.Choose(((Component)currentCraftingStation).transform.position, p.GetPlayerID()) : Topology.Choose(((Component)p).transform.position, p.GetPlayerID(), BuilderCodex.Binding(p));
		}

		internal static bool Craft(InventoryGui gui, Player p)
		{
			if (Active != null)
			{
				return true;
			}
			if (Waiting != null)
			{
				return false;
			}
			if (CraftPreparation.Claimed)
			{
				return CraftPreparation.Execute(gui, p);
			}
			Recipe val = R.Get<Recipe>(gui, "m_craftRecipe");
			Core core = Context(p, craft: true);
			if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)core) || p.NoCostCheat() || ZoneSystem.instance.GetGlobalKey((GlobalKeys)25))
			{
				return true;
			}
			return CraftPreparation.Execute(gui, p);
		}

		internal static bool Build(Player p, Piece piece)
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_028a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0294: Expected O, but got Unknown
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
			switch (FastPathCore.BuildEntry(FastPath.Blocks(p), FastPath.Running, FastPath.Owns(p), FastPath.NativeReady(p)))
			{
			case FastPathBuildEntry.Native:
				return FastPath.ConsumeNativePlacement(p);
			case FastPathBuildEntry.Refuse:
				return false;
			default:
			{
				if (Active != null)
				{
					return true;
				}
				if (!BuildPiece(p, piece))
				{
					return true;
				}
				if (Waiting != null)
				{
					return false;
				}
				if (LocalBuildMaterials(p, piece, (RequirementMode)0))
				{
					return true;
				}
				Core core = Context(p, craft: false);
				if (!Object.op_Implicit((Object)(object)core) || p.NoCostCheat() || R.Get<bool>(p, "m_noPlacementCost") || ZoneSystem.instance.GetGlobalKey(piece.FreeBuildKey()))
				{
					return true;
				}
				Operation operation = Create(p, core, build: true, R.Id(((Component)piece).gameObject), 0, 1);
				if (Object.op_Implicit((Object)(object)piece.m_craftingStation))
				{
					CraftingStation val = CraftingStation.HaveBuildStationInRange(piece.m_craftingStation.m_name, ((Component)p).transform.position);
					if (Object.op_Implicit((Object)(object)val) && R.Valid(R.View((Component)(object)val)))
					{
						operation.Station = R.View((Component)(object)val).GetZDO().m_uid;
					}
				}
				if (!operation.Validate(out var _, out var _, out var _))
				{
					return false;
				}
				List<Debit> list = Planner.Plan(operation.Needs, Stockroom.Available(p, core, operation.Needs), compact: true);
				if (list == null)
				{
					return false;
				}
				if (list.All((Debit d) => d.Source == "player"))
				{
					return true;
				}
				R.Call(p, "UpdatePlacementGhost", new Type[1] { typeof(bool) }, false);
				if (R.Get<object>(p, "m_placementStatus").ToString() != "Valid")
				{
					return true;
				}
				GameObject val2 = R.Get<GameObject>(p, "m_placementGhost");
				if (!Object.op_Implicit((Object)(object)val2))
				{
					return true;
				}
				Pending pending = new Pending
				{
					Op = operation,
					Player = p,
					Piece = piece,
					Tool = (ItemData)R.Call(p, "GetRightItem", Type.EmptyTypes),
					Position = val2.transform.position,
					Rotation = val2.transform.rotation
				};
				if (FastPath.TryBuild(pending, core, list))
				{
					return false;
				}
				Start(pending, list);
				return false;
			}
			}
		}

		internal static Operation Create(Player p, Core core, bool build, string target, int quality, int multiplier)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			return new Operation
			{
				Id = Guid.NewGuid().ToString("N"),
				Target = target,
				Build = build,
				Quality = quality,
				Multiplier = multiplier,
				Actor = ((Character)p).GetZDOID(),
				Station = (build ? ZDOID.None : R.View((Component)(object)p.GetCurrentCraftingStation()).GetZDO().m_uid),
				Core = core.Id,
				Network = ((Component)core).GetComponent<NetworkMember>().SavedNetwork,
				Peer = ZNet.GetUID(),
				PlayerId = p.GetPlayerID()
			};
		}

		internal static bool Propose(Pending pending, List<Debit> preview)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			Core core = Operation.CoreObject(pending.Op.Core);
			string network = (Object.op_Implicit((Object)(object)core) ? ((Component)core).GetComponent<NetworkMember>().Network : "");
			pending.Op.Nodes = Topology.OperationNodes(network);
			if (pending.Op.Nodes.Length == 0 || pending.Op.Nodes.Length > 4096)
			{
				return false;
			}
			pending.Op.Sources = SourceSelection.Sources(preview);
			if (pending.Op.Sources == null)
			{
				return false;
			}
			pending.Op.PlayerStock = Stockroom.Snapshot(((Humanoid)pending.Player).GetInventory(), "player", pending.Op.Needs, chest: false);
			return true;
		}

		private static void Start(Pending pending, List<Debit> preview)
		{
			if (!Propose(pending, preview))
			{
				if (Waiting == pending)
				{
					Waiting = null;
				}
				return;
			}
			Waiting = pending;
			if (pending.Recovery == null)
			{
				pending.Started = Time.unscaledTime;
				pending.Recovery = new RecoveryAttempt(pending.Started);
			}
			pending.Recovery.Sent();
			pending.LastPoll = Time.unscaledTime;
			try
			{
				Transport.Instance.Begin(pending.Op);
			}
			catch (Exception ex)
			{
				Plugin.Debug(pending.Op.Id + " request; will replay same ID: " + ex.Message);
			}
		}

		internal static void RecoverCraft(Pending pending)
		{
			pending.Prepared = false;
			pending.Started = Time.unscaledTime;
			pending.Recovery = new RecoveryAttempt(pending.Started);
			pending.Recovery.Retry("offer expired", pending.Started);
			Waiting = pending;
			Topology.Dirty();
		}

		internal static void Tick()
		{
			Pending waiting = Waiting;
			if (waiting == null)
			{
				return;
			}
			float unscaledTime = Time.unscaledTime;
			if (!Intent(waiting, out var reason))
			{
				waiting.Cancelled = true;
			}
			if (!waiting.Warned && unscaledTime - waiting.Started > 10f)
			{
				waiting.Warned = true;
				Plugin.Critical(waiting.Op.Id, "Craft/build acknowledgement unresolved after 10 seconds; replaying the existing operation");
			}
			if (waiting.Recovery.InFlight)
			{
				if (!(unscaledTime - waiting.LastPoll >= 2f))
				{
					return;
				}
				waiting.LastPoll = unscaledTime;
				try
				{
					if (waiting.Prepared)
					{
						Transport.Instance.UseQuote(waiting.Op.Id);
					}
					else
					{
						Transport.Instance.Begin(waiting.Op);
					}
					return;
				}
				catch (Exception ex)
				{
					Plugin.Debug(waiting.Op.Id + " waiting for coordinator: " + ex.Message);
					return;
				}
			}
			if (!((double)unscaledTime < waiting.Recovery.Due) && !CraftPreparation.Releasing)
			{
				if (!Intent(waiting, out reason) || waiting.Recovery.Expired(unscaledTime))
				{
					Waiting = null;
					Topology.Dirty();
				}
				else
				{
					Retry(waiting);
				}
			}
		}

		internal static void Cancel()
		{
			if (Waiting != null)
			{
				Waiting.Cancelled = true;
			}
		}

		internal static void Refused(string id, string reason)
		{
			Plugin.Debug(id + " terminal refusal (resources released): " + reason);
			Pending waiting = Waiting;
			if (waiting?.Op.Id != id || !waiting.Recovery.InFlight)
			{
				return;
			}
			waiting.Prepared = false;
			if (waiting.Recovery.Retry(reason, Time.unscaledTime) && Intent(waiting, out var _))
			{
				Topology.Dirty();
				Plugin.Debug(id + " rebuilding supply and retrying automatically");
				return;
			}
			if (!waiting.Cancelled && waiting.Recovery.Expired(Time.unscaledTime) && RecoveryAttempt.Transient(reason) && !reason.Contains("insufficient"))
			{
				Plugin.Critical(id, "Automatic recovery exhausted: " + reason);
			}
			Waiting = null;
			Topology.Dirty();
		}

		private static void Retry(Pending p)
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Topology.Dirty();
				Topology.Refresh(force: true);
				Core core = Context(p.Player, !p.Op.Build);
				if (!Object.op_Implicit((Object)(object)core))
				{
					if (!p.Recovery.Retry("network path or coverage changed", Time.unscaledTime))
					{
						Waiting = null;
					}
					return;
				}
				Operation operation = Create(p.Player, core, p.Op.Build, p.Op.Target, p.Op.Quality, p.Op.Multiplier);
				operation.Station = p.Op.Station;
				if (!operation.Validate(out var _, out var _, out var reason))
				{
					if (!p.Recovery.Retry(reason, Time.unscaledTime))
					{
						Waiting = null;
					}
					return;
				}
				Stockroom.Refresh(core, p.Op.Sources);
				List<Stock> stock = Stockroom.Available(p.Player, core, operation.Needs);
				List<Debit> list = (operation.SelectNeeds(stock) ? Planner.Plan(operation.Needs, stock, compact: true) : null);
				if (list == null)
				{
					if (!p.Recovery.Retry("insufficient fresh resources", Time.unscaledTime))
					{
						Waiting = null;
					}
					return;
				}
				Plugin.Debug(p.Op.Id + " retry -> " + operation.Id + " attempt=" + (p.Recovery.Attempts + 1));
				p.Op = operation;
				p.Plan = null;
				p.Delta = null;
				Start(p, list);
			}
			catch (Exception e)
			{
				Plugin.Error(p.Op.Id + " recovery", e);
				Waiting = null;
			}
		}

		private static bool Intent(Pending p, out string reason)
		{
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Invalid comparison between Unknown and O
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Expected O, but got Unknown
			//IL_0166: 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_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			reason = "player context changed";
			if (p.Cancelled || !Object.op_Implicit((Object)(object)p.Player) || (Object)(object)p.Player != (Object)(object)Player.m_localPlayer || ((Character)p.Player).IsDead() || p.Player.NoCostCheat())
			{
				return false;
			}
			if (p.Op.Build)
			{
				reason = "build tool context changed";
				if (!BuildPiece(p.Player, p.Piece) || !((Character)p.Player).InPlaceMode() || (Object)(object)p.Player.GetSelectedPiece() != (Object)(object)p.Piece || (object)(ItemData)R.Call(p.Player, "GetRightItem", Type.EmptyTypes) != p.Tool)
				{
					return false;
				}
				goto IL_026f;
			}
			reason = "recipe changed or disabled";
			if (!RecipeIndex.Matches(p.Recipe, p.Op.Target))
			{
				return false;
			}
			reason = "craft cancelled/changed";
			if (Object.op_Implicit((Object)(object)p.Gui) && InventoryGui.IsVisible())
			{
				CraftingStation currentCraftingStation = p.Player.GetCurrentCraftingStation();
				ZDOID? val = ((currentCraftingStation == null) ? ((ZDOID?)null) : ((Component)currentCraftingStation).GetComponent<ZNetView>().GetZDO()?.m_uid);
				ZDOID station = p.Op.Station;
				if (val.HasValue && !(val.GetValueOrDefault() != station) && !((Object)(object)R.Get<Recipe>(p.Gui, "m_craftRecipe") != (Object)(object)p.Recipe) && R.Get<ItemData>(p.Gui, "m_craftUpgradeItem") == p.Upgrade)
				{
					object obj = R.Get<object>(p.Gui, "m_selectedRecipe");
					if ((Object)(Recipe)obj.GetType().GetProperty("Recipe").GetValue(obj, null) != (Object)(object)p.Recipe)
					{
						return false;
					}
					if (p.Upgrade != null && (!((Humanoid)p.Player).GetInventory().ContainsItem(p.Upgrade) || p.Upgrade.m_quality != p.UpgradeQuality))
					{
						return false;
					}
					goto IL_026f;
				}
			}
			return false;
			IL_026f:
			reason = "ok";
			return true;
		}

		internal static void Ready(string id, List<Debit> plan)
		{
			//IL_04ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_04de: Expected O, but got Unknown
			//IL_0422: Unknown result type (might be due to invalid IL or missing references)
			//IL_0434: Unknown result type (might be due to invalid IL or missing references)
			//IL_0439: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Invalid comparison between Unknown and O
			//IL_0449: Unknown result type (might be due to invalid IL or missing references)
			//IL_044e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0393: Unknown result type (might be due to invalid IL or missing references)
			//IL_0399: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b7: Unknown result type (might be due to invalid IL or missing references)
			if (Outcomes.TryGet(id, out var result))
			{
				Transport.Instance.Result(id, result.Success, result.Reason);
				return;
			}
			Pending waiting = Waiting;
			if (waiting == null || waiting.Op.Id != id)
			{
				Transport.Instance.Result(id, success: false, "no pending action");
				return;
			}
			if (waiting.RollbackFailure != null)
			{
				Transport.InternalMutation++;
				try
				{
					waiting.Delta.Restore();
				}
				catch (Exception e)
				{
					Plugin.Error(id + " retrying local rollback", e);
					return;
				}
				finally
				{
					Transport.InternalMutation--;
				}
				Outcomes.Record(id, result: false, waiting.RollbackFailure);
				Transport.Instance.Result(id, success: false, waiting.RollbackFailure);
				return;
			}
			bool flag = false;
			string text = "action not completed";
			try
			{
				if (!Intent(waiting, out var reason))
				{
					throw new InvalidOperationException(reason);
				}
				if (!waiting.Op.Validate(out var _, out var core, out var reason2))
				{
					throw new InvalidOperationException(reason2);
				}
				if (!GatewayRuntime.ValidatePlan(waiting.Op, plan))
				{
					throw new InvalidOperationException("gateway path changed before result");
				}
				core.Scan();
				foreach (Debit debit in plan.Where((Debit d) => d.Source != "player"))
				{
					Container val = ((IEnumerable<Container>)core.Pool).FirstOrDefault((Func<Container, bool>)((Container source) => Object.op_Implicit((Object)(object)source) && R.Valid(R.View((Component)(object)source)) && R.Key(R.View((Component)(object)source).GetZDO().m_uid) == debit.Source));
					if (!Object.op_Implicit((Object)(object)val) || !Access.Container(val, waiting.Op.PlayerId, core, out var _, ownLease: true))
					{
						throw new InvalidOperationException("source path/access changed before result: " + debit.Source);
					}
				}
				if (!Object.op_Implicit((Object)(object)waiting.Player) || (Object)(object)waiting.Player != (Object)(object)Player.m_localPlayer || ((Character)waiting.Player).IsDead() || waiting.Player.NoCostCheat())
				{
					throw new InvalidOperationException("player context changed");
				}
				if (waiting.Op.Build)
				{
					if (!BuildPiece(waiting.Player, waiting.Piece) || R.Get<bool>(waiting.Player, "m_noPlacementCost") || !((Character)waiting.Player).InPlaceMode() || (Object)(object)waiting.Player.GetSelectedPiece() != (Object)(object)waiting.Piece || (object)(ItemData)R.Call(waiting.Player, "GetRightItem", Type.EmptyTypes) != waiting.Tool || !BuildToolUsable(waiting.Player, waiting.Tool))
					{
						throw new InvalidOperationException("build tool context changed");
					}
					R.Call(waiting.Player, "UpdatePlacementGhost", new Type[1] { typeof(bool) }, false);
					GameObject val2 = R.Get<GameObject>(waiting.Player, "m_placementGhost");
					if (!Object.op_Implicit((Object)(object)val2) || R.Get<object>(waiting.Player, "m_placementStatus").ToString() != "Valid" || Vector3.Distance(val2.transform.position, waiting.Position) > 0.05f || Quaternion.Angle(val2.transform.rotation, waiting.Rotation) > 0.5f)
					{
						throw new InvalidOperationException("placement moved/cancelled");
					}
					goto IL_053d;
				}
				if (Object.op_Implicit((Object)(object)waiting.Gui) && InventoryGui.IsVisible())
				{
					CraftingStation currentCraftingStation = waiting.Player.GetCurrentCraftingStation();
					ZDOID? val3 = ((currentCraftingStation != null) ? new ZDOID?(((Component)currentCraftingStation).GetComponent<ZNetView>().GetZDO().m_uid) : ((ZDOID?)null));
					ZDOID station = waiting.Op.Station;
					if (val3.HasValue && !(val3.GetValueOrDefault() != station) && !((Object)(object)R.Get<Recipe>(waiting.Gui, "m_craftRecipe") != (Object)(object)waiting.Recipe) && R.Get<ItemData>(waiting.Gui, "m_craftUpgradeItem") == waiting.Upgrade)
					{
						object obj = R.Get<object>(waiting.Gui, "m_selectedRecipe");
						if ((Object)(Recipe)obj.GetType().GetProperty("Recipe").GetValue(obj, null) != (Object)(object)waiting.Recipe)
						{
							throw new InvalidOperationException("recipe selection changed");
						}
						if (waiting.Upgrade != null && (!((Humanoid)waiting.Player).GetInventory().ContainsItem(waiting.Upgrade) || waiting.Upgrade.m_quality != waiting.UpgradeQuality))
						{
							throw new InvalidOperationException("upgrade item changed");
						}
						goto IL_053d;
					}
				}
				throw new InvalidOperationException("craft cancelled/changed");
				IL_053d:
				waiting.Plan = plan;
				waiting.Delta = new InventoryDelta(((Humanoid)waiting.Player).GetInventory(), plan.Where((Debit d) => d.Source == "player"), chest: false);
				Active = waiting;
				Transport.InternalMutation++;
				try
				{
					waiting.Delta.Apply();
					Plugin.Debug(id + " player debit confirmed");
					if (waiting.Op.Build)
					{
						if (waiting.Player.TryPlacePiece(waiting.Piece))
						{
							waiting.Output = true;
							FinishBuild(waiting);
						}
					}
					else
					{
						R.Set(waiting.Gui, "m_craftVariant", waiting.Variant);
						R.Set(waiting.Gui, "m_multiCrafting", waiting.Multi);
						waiting.Gui.m_multiCraftAmount = waiting.Op.Multiplier;
						R.Call(waiting.Gui, "DoCrafting", new Type[1] { typeof(Player) }, waiting.Player);
					}
					flag = waiting.Output;
					if (!flag)
					{
						waiting.Delta.Restore();
						Plugin.Debug(id + " player delta restored: no result");
					}
				}
				finally
				{
					Transport.InternalMutation--;
					Active = null;
				}
			}
			catch (Exception ex)
			{
				text = ex.Message;
				if (ex is InvalidOperationException)
				{
					Plugin.Debug(id + " action postponed: " + ex.Message);
				}
				else
				{
					Plugin.Error(id + " action", ex);
				}
				flag = waiting.Output;
				if (!flag)
				{
					InventoryDelta delta = waiting.Delta;
					if (delta != null && delta.Applied)
					{
						Transport.InternalMutation++;
						try
						{
							waiting.Delta.Restore();
						}
						catch (Exception e2)
						{
							waiting.RollbackFailure = text;
							Plugin.Error(id + " local rollback unresolved", e2);
							return;
						}
						finally
						{
							Transport.InternalMutation--;
						}
					}
				}
			}
			Outcomes.Record(id, flag, flag ? "result observed" : text);
			if (flag)
			{
				Waiting = null;
				Stockroom.ClearObservations();
				CraftInspection.Clear();
				CraftOverview.Rescan();
				Topology.Dirty();
			}
			Transport.Instance.Result(id, flag, flag ? "result observed" : text);
		}

		internal static void FinishBuild(Pending p)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Invalid comparison between Unknown and I4
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: 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)
			R.Set(p.Player, "m_lastToolUseTime", Time.time);
			((Character)p.Player).UseStamina((float)R.Call(p.Player, "GetBuildStamina", Type.EmptyTypes));
			PieceTable val = R.Get<PieceTable>(p.Player, "m_buildPieces");
			if ((int)val.m_skill > 0)
			{
				int num = R.Get<int>(p.Player, "m_buildRemoveDebt");
				if (num > 0)
				{
					R.Set(p.Player, "m_buildRemoveDebt", num - 1);
				}
				else
				{
					((Character)p.Player).RaiseSkill(val.m_skill, 1f);
				}
			}
			if (p.Tool.m_shared.m_useDurability)
			{
				ItemData tool = p.Tool;
				tool.m_durability -= (float)R.Call(p.Player, "GetPlaceDurability", new Type[1] { typeof(ItemData) }, p.Tool) * Game.m_durabilityRate;
			}
			p.Tool.m_shared.m_buildEffect.Create(((Component)p.Player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, ((Character)p.Player).GetZDOID());
		}

		internal static bool PaidRequirements(Player p, Recipe recipe)
		{
			return Active != null && (Object)(object)Active.Player == (Object)(object)p && (Object)(object)Active.Recipe == (Object)(object)recipe;
		}

		internal static bool HaveCraft(Player player, Recipe recipe, int quality, int amount, out bool result)
		{
			result = false;
			if (PaidRequirements(player, recipe))
			{
				result = true;
				return true;
			}
			Core core = Context(player, craft: true);
			if (!Object.op_Implicit((Object)(object)core))
			{
				return false;
			}
			result = CraftPreparation.Available(player, recipe, quality, amount);
			return true;
		}

		internal static bool FirstIngredient(Player p, Recipe recipe, int quality, int multiplier, out ItemData item, out int amount, out int extra)
		{
			item = null;
			amount = (extra = 0);
			Core core = Context(p, craft: true);
			if (!Object.op_Implicit((Object)(object)core))
			{
				return false;
			}
			List<Need> list = Stockroom.Requirements(recipe.m_resources, quality, multiplier);
			List<Stock> stock = ((!((Object)(object)Active?.Recipe == (Object)(object)recipe)) ? CraftPreparation.IngredientStock(p, recipe, quality, multiplier, list) : Active.Plan.Select((Debit d) => new Stock(d.Source, d.Item, d.Quality, d.Amount)).ToList());
			foreach (Need n in list)
			{
				List<Need> needs = new List<Need> { n };
				if (!Stockroom.Qualities(needs, stock, craft: true))
				{
					continue;
				}
				Requirement val = recipe.m_resources.First((Requirement r) => Object.op_Implicit((Object)(object)r.m_resItem) && ((Object)r.m_resItem).name == n.Item);
				item = val.m_resItem.m_itemData.Clone();
				item.m_quality = n.Quality;
				amount = n.Amount;
				extra = val.m_extraAmountOnlyOneIngredient;
				return true;
			}
			return false;
		}
	}
	internal sealed class ApiSource
	{
		internal string Token;

		internal ZDO Data;

		internal Container Container;

		internal InventoryDelta Delta;

		internal long PreviousOwner;

		internal long Creator;

		internal ushort OwnerRevision;

		internal uint DataRevision;

		internal bool Ready;

		internal bool Created;

		internal bool Denied;

		internal bool Released;

		internal bool Applied;

		internal byte[] Before;

		internal byte[] Buffer;

		internal byte[] Expected;

		internal int Received;

		internal int ReservedBytes;

		internal double LastSend;

		internal List<Stock> Stock;
	}
	internal static class ApiOwnership
	{
		private const string Fence = "rsn_api_fence";

		private static readonly Dictionary<ZDOID, ApiSource> sources = new Dictionary<ZDOID, ApiSource>();

		private static readonly Dictionary<ZDOID, ApiSource> offers = new Dictionary<ZDOID, ApiSource>();

		private static readonly HashSet<Inventory> blocked = new HashSet<Inventory>();

		private static ZDOID ownerTransition;

		private static bool transition;

		private static long bytes;

		internal static int Count => sources.Count;

		internal static bool Locked(Inventory inv)
		{
			return blocked.Contains(inv);
		}

		internal static bool LockedItem(ItemData item)
		{
			return blocked.Any((Inventory inv) => inv.ContainsItem(item));
		}

		internal static bool? OwnerRule(ZDO z, long uid)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if (transition && ownerTransition == z.m_uid && uid == ZNet.GetUID())
			{
				return true;
			}
			if (sources.TryGetValue(z.m_uid, out var value) && value.Ready)
			{
				return uid == z.GetOwner();
			}
			return null;
		}

		internal static void Install(Harmony h)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(ZDOMan), "RPC_ZDOData", new Type[2]
			{
				typeof(ZRpc),
				typeof(ZPackage)
			}, (Type[])null) ?? throw new MissingMethodException("ZDOMan.RPC_ZDOData");
			h.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ApiOwnership), "FilterReplication", (Type[])null)
			{
				priority = 800
			}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			h.Patch((MethodBase)AccessTools.Method(typeof(ZNetScene), "RemoveObjects", new Type[2]
			{
				typeof(List<ZDO>),
				typeof(List<ZDO>)
			}, (Type[])null), new HarmonyMethod(typeof(ApiOwnership), "Pin", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Transport.ExtraInventoryLock = Locked;
			Transport.ExtraItemLock = LockedItem;
			Transport.ExtraOwnerRule = OwnerRule;
		}

		private static bool FilterReplication(ZDOMan __instance, ZRpc __0, ref ZPackage __1)
		{
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Expected O, but got Unknown
			//IL_0192: 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)
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer())
			{
				return true;
			}
			object obj = R.Call(__instance, "FindPeer", new Type[1] { typeof(ZRpc) }, __0);
			if (obj == null)
			{
				return true;
			}
			long uid = R.Get<ZNetPeer>(obj, "m_peer").m_uid;
			byte[] array = null;
			ZPackage val = __1;
			int pos = val.GetPos();
			val.SetPos(0);
			MemoryStream memoryStream = null;
			try
			{
				int num = val.ReadInt();
				if (num < 0 || num > 65536)
				{
					return false;
				}
				for (int i = 0; i < num; i++)
				{
					val.ReadZDOID();
				}
				for (int j = 0; j < 65536; j++)
				{
					int pos2 = val.GetPos();
					ZDOID val2 = val.ReadZDOID();
					if (val2 == ZDOID.None)
					{
						if (memoryStream != null)
						{
							memoryStream.Write(array, pos2, array.Length - pos2);
							__1 = new ZPackage(memoryStream.ToArray());
						}
						return true;
					}
					ushort incomingRevision = val.ReadUShort();
					val.ReadUInt();
					val.ReadLong();
					val.ReadVector3();
					int num2 = val.ReadInt();
					int pos3 = val.GetPos();
					if (num2 < 0 || num2 > val.Size() - pos3)
					{
						return false;
					}
					val.SetPos(pos3 + num2);
					ZDO zDO = __instance.GetZDO(val2);
					ApiSource value;
					bool flag = sources.TryGetValue(val2, out value) && value.Ready;
					if (zDO != null && (zDO.GetInt("rsn_api_fence", 0) == 1 || flag) && !ApiRules.AcceptSourceFrame(flag, zDO.GetOwner(), zDO.OwnerRevision, uid, incomingRevision))
					{
						if (memoryStream == null)
						{
							array = val.GetArray();
							memoryStream = new MemoryStream(array.Length);
							memoryStream.Write(array, 0, pos2);
						}
					}
					else
					{
						memoryStream?.Write(array, pos2, val.GetPos() - pos2);
					}
				}
				return false;
			}
			catch (Exception ex)
			{
				ApiRuntime.Diagnostic("replication", ex.Message);
				return false;
			}
			finally
			{
				val.SetPos(pos);
				memoryStream?.Dispose();
			}
		}

		private static void Pin(List<ZDO> currentNearObjects)
		{
			foreach (ApiSource item in sources.Values.Concat(offers.Values))
			{
				if (Object.op_Implicit((Object)(object)item.Container) && !Object.op_Implicit((Object)(object)((Component)item.Container).GetComponent<UnloadedReplica>()) && R.Valid(R.View((Component)(object)item.Container)) && !currentNearObjects.Contains(item.Data))
				{
					currentNearObjects.Add(item.Data);
				}
			}
		}

		private static void Block(ApiSource s, bool value)
		{
			if (Object.op_Implicit((Object)(object)s.Container))
			{
				Inventory inventory = s.Container.GetInventory();
				if (value)
				{
					blocked.Add(inventory);
				}
				else
				{
					blocked.Remove(inventory);
				}
				Integrations.Block(inventory, value);
				R.Set(s.Container, "m_inUse", value);
			}
		}

		internal static ApiSource Prepare(ZDO z, string token, long creator, double now)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			if (sources.TryGetValue(z.m_uid, out var value))
			{
				return (value.Token == token) ? value : null;
			}
			if (sources.Count >= 256 || z.GetString("rsn_lease", "") != "" || z.OwnerRevision == ushort.MaxValue || z.DataRevision >= 4294966271u)
			{
				return null;
			}
			ApiSource apiSource = new ApiSource
			{
				Data = z,
				Token = token,
				Creator = creator,
				PreviousOwner = z.GetOwner(),
				OwnerRevision = z.OwnerRevision,
				LastSend = now
			};
			sources[z.m_uid] = apiSource;
			if (z.GetOwner() == ZNet.GetUID() || z.GetOwner() == 0L || ZNet.instance.GetPeer(z.GetOwner()) == null)
			{
				try
				{
					GameObject obj = ZNetScene.instance.FindInstance(z.m_uid);
					Container val = ((obj != null) ? obj.GetComponent<Container>() : null);
					if (Object.op_Implicit((Object)(object)val) && R.View((Component)(object)val).IsOwner() && !val.IsInUse() && !Transport.Locked(val.GetInventory()) && !Integrations.IsBusy(val.GetInventory()))
					{
						R.Call(val, "Save");
					}
					Claim(apiSource, z.GetByteArray(ZDOVars.s_items, (byte[])null) ?? Array.Empty<byte>(), z.DataRevision);
				}
				catch
				{
					apiSource.Denied = true;
				}
			}
			else if (ApiRuntime.HasPeer(z.GetOwner()))
			{
				SendPrepare(apiSource);
			}
			else
			{
				apiSource.Denied = true;
			}
			return apiSource;
		}

		private static bool ReserveBytes(ApiSource s, int size)
		{
			int num = checked(size * 4 + 4096);
			if (size < 0 || size > 1048576 || bytes + num > 67108864)
			{
				return false;
			}
			bytes += num;
			s.ReservedBytes += num;
			return true;
		}

		private static void Claim(ApiSource s, byte[] content, uint revision)
		{
			//IL_0084: 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_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			ZDO data = s.Data;
			if (data == null || !data.IsValid() || data.GetOwner() != s.PreviousOwner || data.OwnerRevision != s.OwnerRevision || !ApiWorld.Eligible(data, s.Creator))
			{
				throw new InvalidOperationException("Source changed before handoff");
			}
			if (s.ReservedBytes == 0 && !ReserveBytes(s, content.Length))
			{
				throw new InvalidOperationException("API recovery budget exhausted");
			}
			ApiWorld.Decode(data, content);
			GameObject obj = ZNetScene.instance.FindInstance(data.m_uid);
			Container val = ((obj != null) ? obj.GetComponent<Container>() : null);
			if (Object.op_Implicit((Object)(object)val) && R.View((Component)(object)val).IsOwner() && (val.IsInUse() || Integrations.IsBusy(val.GetInventory()) || Transport.Locked(val.GetInventory())))
			{
				throw new InvalidOperationException("Source busy");
			}
			ownerTransition = data.m_uid;
			transition = true;
			try
			{
				data.SetOwner(ZNet.GetUID());
			}
			finally
			{
				transition = false;
			}
			if (data.GetOwner() != ZNet.GetUID())
			{
				throw new InvalidOperationException("Ownership handoff refused");
			}
			s.Ready = true;
			data.Set("rsn_api_fence", 1);
			data.DataRevision = Math.Max(data.DataRevision, revision);
			data.Set(ZDOVars.s_items, content);
			data.Set("rsn_lease", s.Token);
			data.Set(ZDOVars.s_inUse, 1, false);
			s.Before = (byte[])content.Clone();
			s.Container = UnloadedNetworks.ApiContainer(data, out s.Created);
			if (!Object.op_Implicit((Object)(object)s.Container))
			{
				throw new InvalidOperationException("Source adapter unavailable");
			}
			Transport.InternalMutation++;
			try
			{
				R.Set(s.Container, "m_inUse", false);
				R.Set(s.Container, "m_lastRevision", uint.MaxValue);
				R.Call(s.Container, "Load");
			}
			finally
			{
				Transport.InternalMutation--;
			}
			s.Stock = Stockroom.Snapshot(s.Container.GetInventory(), R.Key(data.m_uid), null, chest: true);
			Block(s, value: true);
			ZDOMan.instance.ForceSendZDO(data.m_uid);
			ApiWorld.Invalidate(data.m_uid);
		}

		private static void SendPrepare(ApiSource s)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			ZPackage val = new ZPackage();
			val.Write(s.Token);
			val.Write(s.Data.m_uid);
			val.Write((int)s.OwnerRevision);
			val.Write(s.Creator);
			ApiRuntime.Send(s.PreviousOwner, "prepare", val);
		}

		internal static void ReceivePrepare(long sender, ZPackage p)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Unknown result type (might be due to invalid IL or missing references)
			if (sender != Transport.Server)
			{
				return;
			}
			string text = ApiWire.Text(p, 512);
			ZDOID val = p.ReadZDOID();
			int num = p.ReadInt();
			long num2 = p.ReadLong();
			if (!ApiRuntime.Enabled)
			{
				Deny(sender, text, val);
				return;
			}
			if (offers.TryGetValue(val, out var value))
			{
				if (value.Token == text)
				{
					SendOffer(value);
				}
				else
				{
					Deny(sender, text, val);
				}
				return;
			}
			ZDO val2 = RemoteContext.Data(val);
			object obj;
			if (val2 == null)
			{
				obj = null;
			}
			else
			{
				GameObject obj2 = ZNetScene.instance.FindInstance(val);
				obj = ((obj2 != null) ? obj2.GetComponent<Container>() : null);
			}
			Container val3 = (Container)obj;
			if (offers.Count >= 256 || !Object.op_Implicit((Object)(object)val3) || !R.Valid(R.View((Component)(object)val3)) || !R.View((Component)(object)val3).IsOwner() || val2.OwnerRevision != num || !ApiWorld.Eligible(val2, num2) || !(bool)R.Call(val3, "CheckAccess", new Type[1] { typeof(long) }, num2) || Transport.Reserved(val2) || val3.IsInUse() || Integrations.IsBusy(val3.GetInventory()) || Transport.Locked(val3.GetInventory()))
			{
				Deny(sender, text, val);
				return;
			}
			ApiSource apiSource = new ApiSource
			{
				Token = text,
				Data = val2,
				Container = val3,
				Creator = num2,
				OwnerRevision = val2.OwnerRevision,
				PreviousOwner = val2.GetOwner()
			};
			try
			{
				R.Call(val3, "Load");
				R.Call(val3, "Save");
				byte[] array = val2.GetByteArray(ZDOVars.s_items, (byte[])null) ?? Array.Empty<byte>();
				if (!ReserveBytes(apiSource, array.Length))
				{
					throw new InvalidOperationException("Offer budget");
				}
				apiSource.Before = (byte[])array.Clone();
				apiSource.DataRevision = val2.DataRevision;
				Block(apiSource, value: true);
				val2.Set("rsn_lease", text);
				val2.Set(ZDOVars.s_inUse, 1, false);
				offers[val] = apiSource;
				SendOffer(apiSource);
			}
			catch
			{
				Block(apiSource, value: false);
				if (val2.GetString("rsn_lease", "") == text)
				{
					val2.Set("rsn_lease", "");
					val2.Set(ZDOVars.s_inUse, 0, false);
				}
				offers.Remove(val);
				bytes -= apiSource.ReservedBytes;
				Deny(sender, text, val);
			}
		}

		private static void Deny(long peer, string token, ZDOID id)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			ZPackage val = new ZPackage();
			val.Write(token);
			val.Write(id);
			val.Write(false);
			ApiRuntime.Send(peer, "grant", val);
		}

		private static void SendOffer(ApiSource s)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			int num = s.Before.Length;
			for (int i = 0; i < num || i == 0; i += 28000)
			{
				int num2 = Math.Min(28000, num - i);
				byte[] array = new byte[num2];
				Array.Copy(s.Before, i, array, 0, num2);
				ZPackage val = new ZPackage();
				val.Write(s.Token);
				val.Write(s.Data.m_uid);
				val.Write(true);
				val.Write((int)s.OwnerRevision);
				val.Write(s.DataRevision);
				val.Write(num);
				val.Write(i);
				val.Write(array);
				ApiRuntime.Send(Transport.Server, "grant", val);
			}
		}

		internal static void ReceiveGrant(long sender, ZPackage p)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if (!ZNet.instance.IsServer())
			{
				return;
			}
			string text = ApiWire.Text(p, 512);
			ZDOID key = p.ReadZDOID();
			bool flag = p.ReadBool();
			if (!sources.TryGetValue(key, out var value) || value.Token != text || value.PreviousOwner != sender || value.Ready)
			{
				return;
			}
			if (!flag)
			{
				value.Denied = true;
				return;
			}
			int num = p.ReadInt();
			uint num2 = p.ReadUInt();
			int num3 = p.ReadInt();
			int num4 = p.ReadInt();
			byte[] array = p.ReadByteArray();
			if (num != value.OwnerRevision || num3 < 0 || num3 > 1048576 || num4 < 0 || array.Length > 28000 || num4 + array.Length > num3)
			{
				value.Denied = true;
				return;
			}
			if (value.Buffer == null)
			{
				if (num4 != 0 || !ReserveBytes(value, num3))
				{
					value.Denied = true;
					return;
				}
				value.Buffer = new byte[num3];
				value.DataRevision = num2;
			}
			if (value.Buffer.Length != num3 || value.DataRevision != num2)
			{
				value.Denied = true;
			}
			else
			{
				if (num4 < value.Received || num4 != value.Received)
				{
					return;
				}
				Array.Copy(array, 0, value.Buffer, num4, array.Length);
				value.Received += array.Length;
				if (value.Received == num3)
				{
					try
					{
						Claim(value, value.Buffer, num2);
					}
					catch (Exception ex)
					{
						value.Denied = true;
						ApiRuntime.Diagnostic("handoff", ex.Message);
					}
				}
			}
		}

		internal static void Tick(double now)
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			ApiSource[] array = sources.Values.ToArray();
			foreach (ApiSource apiSource in array)
			{
				if (!apiSource.Ready && !apiSource.Denied && now - apiSource.LastSend > 1.0)
				{
					apiSource.LastSend = now;
					SendPrepare(apiSource);
				}
			}
			ApiSource[] array2 = offers.Values.ToArray();
			foreach (ApiSource apiSource2 in array2)
			{
				if (now - apiSource2.LastSend > 2.0)
				{
					apiSource2.LastSend = now;
					ZPackage val = new ZPackage();
					val.Write(apiSource2.Token);
					val.Write(apiSource2.Data.m_uid);
					ApiRuntime.Send(Transport.Server, "ticket", val);
				}
			}
		}

		internal static void ReceiveTicket(long sender, ZPackage p)
		{
			//IL_0023: 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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			if (ZNet.instance.IsServer())
			{
				string text = ApiWire.Text(p, 512);
				ZDOID val = p.ReadZDOID();
				if (!sources.TryGetValue(val, out var value) || !(value.Token == text) || value.Ready)
				{
					ZPackage val2 = new ZPackage();
					val2.Write(text);
					val2.Write(val);
					ApiRuntime.Send(sender, "release", val2);
				}
			}
		}

		internal static void ReceiveRelease(long sender, ZPackage p)
		{
			//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_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			if (sender != Transport.Server)
			{
				return;
			}
			string text = ApiWire.Text(p, 512);
			ZDOID key = p.ReadZDOID();
			if (offers.TryGetValue(key, out var value) && !(value.Token != text))
			{
				Block(value, value: false);
				if (value.Data != null && value.Data.IsValid() && value.Data.GetOwner() == ZNet.GetUID() && value.Data.GetString("rsn_lease", "") == text)
				{
					value.Data.Set("rsn_lease", "");
					value.Data.Set(ZDOVars.s_inUse, 0, false);
				}
				bytes -= value.ReservedBytes;
				offers.Remove(key);
			}
		}

		internal static bool Release(ApiSource s)
		{
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Expected O, but got Unknown
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			if (s.Released)
			{
				return true;
			}
			if (s.Ready)
			{
				if (s.Data == null || !s.Data.IsValid() || s.Data.GetOwner() != ZNet.GetUID())
				{
					return false;
				}
				string text = s.Data.GetString("rsn_lease", "");
				if (text != "" && text != s.Token)
				{
					return false;
				}
				if (Object.op_Implicit((Object)(object)s.Container))
				{
					Block(s, value: false);
					s.Data.Set("rsn_lease", "");
					s.Data.Set(ZDOVars.s_inUse, 0, false);
				}
				else
				{
					s.Data.Set("rsn_lease", "");
					s.Data.Set(ZDOVars.s_inUse, 0, false);
				}
				ZDOMan.instance.ForceSendZDO(s.Data.m_uid);
				ApiWorld.Invalidate(s.Data.m_uid);
				StorageIndex.Changed(R.Key(s.Data.m_uid));
			}
			sources.Remove(s.Data.m_uid);
			bytes -= s.ReservedBytes;
			s.Released = true;
			Transport.Instance.SharedSourceGate.Released(s.Token, R.Key(s.Data.m_uid));
			if (s.PreviousOwner != 0L && s.PreviousOwner != ZNet.GetUID())
			{
				ZPackage val = new ZPackage();
				val.Write(s.Token);
				val.Write(s.Data.m_uid);
				ApiRuntime.Send(s.PreviousOwner, "release", val);
			}
			if (s.Created)
			{
				UnloadedNetworks.ApiReleaseAdapter(s.Data.m_uid);
			}
			s.Before = null;
			s.Buffer = null;
			s.Expected = null;
			s.Delta = null;
			s.Stock = null;
			s.Container = null;
			return true;
		}

		internal static void Clear()
		{
			foreach (ApiSource item in offers.Values.Concat(sources.Values))
			{
				if (Object.op_Implicit((Object)(object)item.Container))
				{
					blocked.Remove(item.Container.GetInventory());
					Integrations.Block(item.Container.GetInventory(), value: false);
				}
			}
			sources.Clear();
			offers.Clear();
			blocked.Clear();
			bytes = 0L;
		}
	}
	internal sealed class ApiPaymentWorld : IApiPayment
	{
		private readonly ApiContext context;

		private readonly ApiLedger.Entry entry;

		private readonly List<Need> needs;

		private readonly List<ApiSource> sources = new List<ApiSource>();

		private readonly HashSet<string> avoid = new HashSet<string>();

		private ApiNetworkView view;

		private List<Debit> plan;

		private string[] keys;

		private int prepare;

		private int apply;

		private long creator;

		private bool captured;

		internal ApiPaymentWorld(ApiContext context, ApiLedger.Entry entry)
		{
			this.context = context;
			this.entry = entry;
			needs = entry.Request.Resources.Select((ResourceRequirement r) => new Need(r.Resource.PrefabName, r.Amount, r.Resource.Quality)).ToList();
		}

		public ApiStatus Prepare()
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b9: Unknown result type (might be due to invalid IL or missing references)
			if (!ApiRuntime.Enabled)
			{
				return ApiStatus.FeatureDisabled;
			}
			ZDO consumer;
			ApiStatus apiStatus = ApiWorld.Context(context, ZNet.GetUID(), receipt: true, out consumer, out creator);
			if (apiStatus != ApiStatus.Ready)
			{
				return apiStatus;
			}
			if (!UnloadedNetworks.Ward(consumer.GetPosition(), creator))
			{
				return ApiStatus.AccessDenied;
			}
			view = ApiRuntime.View(context, consumer, creator);
			if (view == null)
			{
				return ApiStatus.CapacityExceeded;
			}
			if (view.Limited)
			{
				return ApiStatus.SnapshotLimit;
			}
			if (view.Graph == null || view.ObservedAt == 0.0 || view.Structure != UnloadedNetworks.CatalogRevision || (view.Scanning && ApiRuntime.Now - view.ObservedAt > 2.0))
			{
				return ApiStatus.Updating;
			}
			if (string.IsNullOrEmpty(view.Network))
			{
				return ApiStatus.NoNetwork;
			}
			if (keys == null)
			{
				entry.Network = view.Network;
				if (ApiRuntime.InNetwork(entry) > 8)
				{
					return ApiStatus.Busy;
				}
				plan = Planner.Plan(needs, from s in view.Stocks(needs)
					where !avoid.Contains(s.Source)
					select s, compact: true);
				if (plan == null)
				{
					return view.Complete ? ApiStatus.InsufficientResources : ApiStatus.SourcesUnavailable;
				}
				keys = SourceSelection.Sources(plan);
				if (keys == null || keys.Length > 32)
				{
					keys = null;
					return ApiStatus.PlanLimitExceeded;
				}
				if (Transport.Instance.PlayerWaiting(keys) || !Transport.Instance.SharedSourceGate.TryAcquire(entry.Id, keys))
				{
					keys = null;
					return ApiStatus.Busy;
				}
			}
			if (view.Network != entry.Network)
			{
				return ApiStatus.NoNetwork;
			}
			if (prepare < keys.Length)
			{
				ZDO val = RemoteContext.Source(keys[prepare]);
				if (val == null || !ApiWorld.Eligible(val, creator))
				{
					avoid.Add(keys[prepare]);
					return ApiStatus.SourcesUnavailable;
				}
				ApiSource apiSource = ApiOwnership.Prepare(val, entry.Id, creator, ApiRuntime.Now);
				if (apiSource == null)
				{
					avoid.Add(keys[prepare]);
					return ApiStatus.SourcesUnavailable;
				}
				sources.Add(apiSource);
				prepare++;
				return ApiStatus.Updating;
			}
			foreach (ApiSource source in sources)
			{
				if (source.Denied)
				{
					avoid.Add(R.Key(source.Data.m_uid));
					return ApiStatus.SourcesUnavailable;
				}
				if (!source.Ready)
				{
					return ApiStatus.Updating;
				}
			}
			plan = Planner.Plan(needs, sources.SelectMany((ApiSource s) => GatewayRuntime.Filter(view.Graph, view.Network, s.Data.GetPosition(), consumer.GetPosition(), s.Stock)), compact: true);
			return (plan == null) ? ApiStatus.InsufficientResources : ApiStatus.Ready;
		}

		public void Repair()
		{
			view?.Repair();
			view = null;
			keys = null;
			plan = null;
			sources.Clear();
			prepare = (apply = 0);
			captured = false;
		}

		public void Capture()
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			if (!ApiRuntime.Enabled || view.Structure != UnloadedNetworks.CatalogRevision)
			{
				throw new InvalidOperationException("Network changed before debit");
			}
			if (ApiWorld.Context(context, ZNet.GetUID(), receipt: true, out var consumer, out var num) != ApiStatus.Ready || num != creator || !UnloadedNetworks.Ward(consumer.GetPosition(), creator))
			{
				throw new InvalidOperationException("Consumer access changed");
			}
			foreach (ApiSource s in sources)
			{
				Check(s, before: true);
				if (!ApiWorld.Eligible(s.Data, creator) || !view.Graph.Covers(entry.Network, Topology.Position(s.Data.GetPosition()), (NetworkNode n) => true))
				{
					throw new InvalidOperationException("Source access changed");
				}
				if (plan.Where((Debit d) => d.Source == R.Key(s.Data.m_uid)).Any((Debit d) => !view.Graph.CanTransfer(entry.Network, Topology.Position(s.Data.GetPosition()), Topology.Position(consumer.GetPosition()), GatewayRuntime.Teleportable(d.Item))))
				{
					throw new InvalidOperationException("Gateway path changed");
				}
				s.Delta = new InventoryDelta(s.Container.GetInventory(), plan.Where((Debit d) => d.Source == R.Key(s.Data.m_uid)), chest: true);
			}
			captured = true;
		}

		private static void Check(ApiSource s, bool before)
		{
			if (s.Data == null || !s.Data.IsValid() || s.Data.GetOwner() != ZNet.GetUID() || s.Data.GetString("rsn_lease", "") != s.Token || !Object.op_Implicit((Object)(object)s.Container))
			{
				throw new InvalidOperationException("Source ownership or reservation changed");
			}
			byte[] array = (before ? s.Before : s.Expected);
			if (array != null && !(s.Data.GetByteArray(ZDOVars.s_items, (byte[])null) ?? Array.Empty<byte>()).SequenceEqual(array))
			{
				throw new InvalidOperationException("Source changed outside its reservation");
			}
		}

		public bool ApplyNext()
		{
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			if (!captured)
			{
				throw new InvalidOperationException("Missing capture");
			}
			if (apply >= sources.Count)
			{
				return true;
			}
			if (!ApiRuntime.Enabled || view.Structure != UnloadedNetworks.CatalogRevision)
			{
				throw new InvalidOperationException("Network changed before debit");
			}
			ApiSource s = sources[apply];
			Check(s, before: true);
			ZDO consumer = RemoteContext.Data(context.ConsumerId);
			if (consumer == null || plan.Where((Debit d) => d.Source == R.Key(s.Data.m_uid)).Any((Debit d) => !view.Graph.CanTransfer(entry.Network, Topology.Position(s.Data.GetPosition()), Topology.Position(consumer.GetPosition()), GatewayRuntime.Teleportable(d.Item))))
			{
				throw new InvalidOperationException("Gateway path changed before debit");
			}
			s.Applied = true;
			Transport.InternalMutation++;
			try
			{
				s.Delta.Apply();
				R.Call(s.Container, "Save");
				s.Expected = (byte[])(s.Data.GetByteArray(ZDOVars.s_items, (byte[])null) ?? Array.Empty<byte>()).Clone();
				apply++;
			}
			catch
			{
				s.Expected = (byte[])(s.Data.GetByteArray(ZDOVars.s_items, (byte[])null) ?? Array.Empty<byte>()).Clone();
				throw;
			}
			finally
			{
				Transport.InternalMutation--;
			}
			return apply == sources.Count;
		}

		public bool Rollback()
		{
			for (int num = sources.Count - 1; num >= 0; num--)
			{
				ApiSource apiSource = sources[num];
				if (apiSource.Applied)
				{
					Check(apiSource, before: false);
					Transport.InternalMutation++;
					try
					{
						apiSource.Delta.Restore();
						R.Call(apiSource.Container, "Save");
						apiSource.Expected = (byte[])(apiSource.Data.GetByteArray(ZDOVars.s_items, (byte[])null) ?? Array.Empty<byte>()).Clone();
						apiSource.Applied = false;
					}
					catch
					{
						apiSource.Expected = (byte[])(apiSource.Data.GetByteArray(ZDOVars.s_items, (byte[])null) ?? Array.Empty<byte>()).Clone();
						throw;
					}
					finally
					{
						Transport.InternalMutation--;
					}
					return false;
				}
			}
			return true;
		}

		public bool Release()
		{
			foreach (ApiSource source in sources)
			{
				if (!source.Released)
				{
					if (!ApiOwnership.Release(source))
					{
						return false;
					}
					return false;
				}
			}
			Transport.Instance.SharedSourceGate.Cancel(entry.Id);
			return true;
		}
	}
	internal sealed class ApiRuntime : MonoBehaviour
	{
		private sealed class Outgoing
		{
			internal long Peer;

			internal string Name;

			internal ZPackage Data;

			internal int Bytes;
		}

		private sealed class ReadInterest
		{
			internal ApiContext Context;

			internal long Peer;

			internal double Used;
		}

		private sealed class LocalRead
		{
			internal ApiContext Context;

			internal ResourceKey? Filter;

			internal ResourceSnapshot Snapshot;

			internal double Used;

			internal double Next;

			internal double ReceivedAt;

			internal long Serial;

			internal long ReceivedSerial;

			internal int Expected;

			internal int NextPage;

			internal int Bytes;

			internal readonly List<ResourceAmount> Rows = new List<ResourceAmount>();
		}

		private sealed class LocalOperation
		{
			internal ApiContext Context;

			internal ConsumeRequest Request;

			internal ConsumeOperation Handle;

			internal double Next;
		}

		private const string Prefix = "RSN_API1_";

		internal static ApiRuntime Instance;

		private readonly ApiLedger ledger = new ApiLedger();

		private readonly List<ApiNetworkView> views = new List<ApiNetworkView>();

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

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

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

		private readonly Dictionary<long, ApiBucket> writes = new Dictionary<long, ApiBucket>();

		private readonly Dictionary<long, ApiBucket> queries = new Dictionary<long, ApiBucket>();

		private readonly Dictionary<long, ApiBucket> controls = new Dictionary<long, ApiBucket>();

		private readonly Dictionary<long, double> peers = new Dictionary<long, double>();

		private readonly Queue<ConsumeOperation> callbacks = new Queue<ConsumeOperation>();

		private readonly Queue<Outgoing> outgoing = new Queue<Outgoing>();

		private long outgoingBytes;

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

		private ApiBucket writeRate;

		private ZRoutedRpc rpc;

		private ZDOMan world;

		private Harmony harmony;

		private int thread;

		private int scanCursor;

		private int jobCursor;

		private double helloAt;

		private double cleanupAt;

		private string session = "";

		private bool serverEnabled;

		private int serverVersion;

		internal static double Now => Time.realtimeSinceStartupAsDouble;

		internal static bool Enabled => Object.op_Implicit((Object)(object)Instance) && Plugin.Enabled && UnloadedNetworks.Requested && (Server ? UnloadedNetworks.Enabled : (Instance.serverVersion == 1 && Instance.serverEnabled));

		private static bool Main => Instance != null && Instance.thread == Thread.CurrentThread.ManagedThreadId;

		private static bool Server => Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer();

		private void Awake()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			Instance = this;
			thread = Thread.CurrentThread.ManagedThreadId;
			writeRate = new ApiBucket(16.0, 16.0, Now);
			harmony = new Harmony("local.runicstoragenetwork.api");
			try
			{
				ApiOwnership.Install(harmony);
				Transport.ExtraOperations = () => ledger.Entries.Count((ApiLedger.Entry e) => !e.Released);
			}
			catch (Exception ex)
			{
				harmony.UnpatchSelf();
				Transport.ExtraInventoryLock = null;
				Transport.ExtraItemLock = null;
				Transport.ExtraOwnerRule = null;
				Transport.ExtraOperations = null;
				Instance = null;
				((Behaviour)this).enabled = false;
				Plugin.Info("API unavailable: " + ex.Message);
			}
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			Reset();
			Transport.ExtraInventoryLock = null;
			Transport.ExtraItemLock = null;
			Transport.ExtraOwnerRule = null;
			Transport.ExtraOperations = null;
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		private static string Consumer(ApiContext c)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return R.Key(c.ConsumerId) + "/" + c.ModId;
		}

		private static bool Valid(ApiContext c)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			return c != null && c.ConsumerId != ZDOID.None && ApiRules.Text(c.ModId);
		}

		internal static bool HasPeer(long peer)
		{
			double value;
			return peer == ZNet.GetUID() || (Object.op_Implicit((Object)(object)Instance) && Instance.peers.TryGetValue(peer, out value) && Now - value < 15.0);
		}

		internal static void Diagnostic(string category, string message)
		{
			ApiRuntime instance = Instance;
			if (!Object.op_Implicit((Object)(object)instance))
			{
				return;
			}
			double now = Now;
			if (!instance.warnings.TryGetValue(category, out var value) || !(now - value < 15.0))
			{
				if (instance.warnings.Count >= 32)
				{
					instance.warnings.Clear();
				}
				instance.warnings[category] = now;
				Plugin.Info("API " + category + ": " + message);
			}
		}

		private void Reset()
		{
			foreach (LocalOperation value in localOperations.Values)
			{
				if (!value.Handle.IsFinal)
				{
					value.Handle.Complete(new ConsumeResult(value.Handle.RequestId, "", ConsumptionOutcome.OutcomeUnknown, ApiStatus.Unavailable));
					QueueCallback(value.Handle);
				}
			}
			if ((Object)(object)Transport.Instance != (Object)null)
			{
				foreach (ApiLedger.Entry entry in ledger.Entries)
				{
					Transport.Instance.SharedSourceGate.Cancel(entry.Id);
				}
			}
			ApiOwnership.Clear();
			ApiWorld.Clear();
			ledger.Clear();
			views.Clear();
			reads.Clear();
			localReads.Clear();
			localOperations.Clear();
			writes.Clear();
			queries.Clear();
			controls.Clear();
			peers.Clear();
			outgoing.Clear();
			outgoingBytes = 0L;
			rpc = null;
			session = "";
			serverVersion = 0;
			serverEnabled = false;
		}

		private void Update()
		{
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Expected O, but got Unknown
			//IL_03dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e3: Expected O, but got Unknown
			try
			{
				if (world != ZDOMan.instance)
				{
					Reset();
					world = ZDOMan.instance;
					if (world != null && Server)
					{
						session = Guid.NewGuid().ToString("N");
						serverVersion = 1;
						serverEnabled = Enabled;
					}
				}
				if (world == null || ZRoutedRpc.instance == null || !Object.op_Implicit((Object)(object)ZNet.instance))
				{
					DeliverCallbacks();
					return;
				}
				if (rpc != ZRoutedRpc.instance)
				{
					rpc = ZRoutedRpc.instance;
					Register();
				}
				double now = Now;
				if (!Server && now >= helloAt && Transport.Server != 0)
				{
					helloAt = now + 3.0;
					ZPackage val = new ZPackage();
					val.Write(1);
					Send(Transport.Server, "hello", val);
				}
				if (Server)
				{
					ApiLedger.Entry[] array = ledger.Entries.Where((ApiLedger.Entry e) => !e.Released).ToArray();
					int num = Math.Min(8, array.Length);
					for (int num2 = 0; num2 < num; num2++)
					{
						ApiLedger.Entry entry = array[(jobCursor + num2) % array.Length];
						ApiPayment apiPayment = (ApiPayment)entry.Work;
						apiPayment.Tick(now);
						entry.State = apiPayment.State;
						if (apiPayment.Outcome.HasValue)
						{
							ledger.Finish(entry, apiPayment.Outcome.Value, apiPayment.Reason, apiPayment.Released);
							NotifyLocal(entry);
							if (apiPayment.Released)
							{
								entry.Work = null;
							}
						}
						if (apiPayment.State == ConsumptionState.Recovering && now - entry.Started > 10.0)
						{
							Diagnostic("recovery", "Operation " + entry.Id + " is retaining its reservation until its original debit is resolved.");
						}
					}
					if (array.Length != 0)
					{
						jobCursor = (jobCursor + num) % array.Length;
					}
					Stopwatch stopwatch = Stopwatch.StartNew();
					int num3 = 0;
					while (views.Count > 0 && num3++ < 128 && stopwatch.Elapsed.TotalMilliseconds < 0.5)
					{
						if (scanCursor >= views.Count)
						{
							scanCursor = 0;
						}
						ApiNetworkView apiNetworkView = views[scanCursor++];
						if (now - apiNetworkView.Used < 30.0 && Enabled && UnloadedNetworks.ApiCatalogReady)
						{
							apiNetworkView.Tick(now);
						}
					}
				}
				LocalOperation[] array2 = localOperations.Values.ToArray();
				foreach (LocalOperation localOperation in array2)
				{
					if (!localOperation.Handle.IsFinal && now >= localOperation.Next)
					{
						localOperation.Next = now + 1.0;
						if (Server)
						{
							ProcessLocal(localOperation);
						}
						else if (Transport.Server != 0)
						{
							ZPackage p = new ZPackage();
							ApiWire.Context(p, localOperation.Context);
							ApiWire.Request(p, localOperation.Request);
							Send(Transport.Server, "consume", p);
						}
					}
				}
				ApiOwnership.Tick(now);
				for (int num5 = 0; num5 < 16; num5++)
				{
					if (this.outgoing.Count <= 0)
					{
						break;
					}
					Outgoing outgoing = this.outgoing.Dequeue();
					outgoingBytes -= outgoing.Bytes;
					if (outgoing.Peer == ZNet.GetUID())
					{
						Dispatch(outgoing.Name, outgoing.Peer, outgoing.Data);
					}
					else if (outgoing.Peer != 0)
					{
						rpc.InvokeRoutedRPC(outgoing.Peer, "RSN_API1_" + outgoing.Name, new object[1] { outgoing.Data });
					}
				}
				if (now >= cleanupAt)
				{
					cleanupAt = now + 5.0;
					Clean(now);
				}
				DeliverCallbacks();
			}
			catch (Exception ex)
			{
				Diagnostic("tick", ex.Message);
			}
		}

		private void Clean(double now)
		{
			string[] array = (from x in reads
				where now - x.Value.Used > 30.0
				select x.Key).ToArray();
			foreach (string key in array)
			{
				reads.Remove(key);
			}
			string[] array2 = (from x in localReads
				where now - x.Value.Used > 30.0
				select x.Key).ToArray();
			foreach (string key2 in array2)
			{
				localReads.Remove(key2);
			}
			views.RemoveAll((ApiNetworkView v) => now - v.Used > 30.0 && !ledger.Entries.Any((ApiLedger.Entry e) => !e.Released && e.Network == v.Network));
			ApiWorld.Trim(now);
			long[] array3 = peers.Keys.Where((long p) => now - peers[p] > 30.0).ToArray();
			foreach (long key3 in array3)
			{
				peers.Remove(key3);
				writes.Remove(key3);
				queries.Remove(key3);
				controls.Remove(key3);
			}
		}

		internal static int InNetwork(ApiLedger.Entry current)
		{
			return Instance.ledger.Entries.Count((ApiLedger.Entry e) => !e.Released && e.Network == current.Network && e.Order <= current.Order);
		}

		internal static bool AllowViewBytes(ApiNetworkView current, int bytes)
		{
			return Instance.views.Sum((ApiNetworkView v) => (v == current) ? bytes : v.Bytes) <= 4194304;
		}

		internal static ApiNetworkView View(ApiContext context, ZDO consumer, long creator)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			ApiRuntime instance = Instance;
			Point point = Topology.Position(consumer.GetPosition());
			ApiNetworkView apiNetworkView = instance.views.FirstOrDefault((ApiNetworkView v) => v.Creator == creator && (v.Point.Distance2(point) < 0.001 || (v.Graph != null && v.Structure == UnloadedNetworks.CatalogRevision && !string.IsNullOrEmpty(v.Network) && v.Graph.Choose(point, (NetworkNode n) => true) == v.Network)));
			if (apiNetworkView == null)
			{
				if (instance.views.Count >= 128)
				{
					return null;
				}
				instance.views.Add(apiNetworkView = new ApiNetworkView
				{
					Creator = creator,
					Point = point
				});
			}
			apiNetworkView.Used = Now;
			return apiNetworkView;
		}

		private ConsumerRecovery Recovery(ApiContext context)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			ZDO val = RemoteContext.Data(context.ConsumerId);
			ApiLedger.Entry entry = ledger.Find(Consumer(context));
			return new ConsumerRecovery((uint)((val != null) ? val.OwnerRevision : 0), entry?.Request, entry?.Result, entry?.State ?? ConsumptionState.Pending);
		}

		private ResourceSnapshot Snapshot(ApiContext c, long peer, ResourceKey? filter)
		{
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			ApiStatus apiStatus = ApiWorld.Context(c, peer, receipt: false, out var z, out var creator);
			if (apiStatus != ApiStatus.Ready)
			{
				return new ResourceSnapshot(apiStatus, session, "", 0L);
			}
			ConsumerRecovery recovery = Recovery(c);
			if (!Enabled)
			{
				return new ResourceSnapshot(ApiStatus.FeatureDisabled, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, recovery);
			}
			if (!UnloadedNetworks.ApiCatalogReady)
			{
				return new ResourceSnapshot(ApiStatus.NotReady, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, recovery);
			}
			if (!UnloadedNetworks.Ward(z.GetPosition(), creator))
			{
				return new ResourceSnapshot(ApiStatus.AccessDenied, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, recovery);
			}
			if (filter.HasValue && !Known(filter.Value))
			{
				return new ResourceSnapshot(ApiStatus.UnknownItem, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, recovery);
			}
			ApiNetworkView apiNetworkView = View(c, z, creator);
			if (apiNetworkView == null)
			{
				return new ResourceSnapshot(ApiStatus.SnapshotLimit, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, recovery);
			}
			if (apiNetworkView.Graph == null || apiNetworkView.ObservedAt == 0.0)
			{
				return new ResourceSnapshot(ApiStatus.Updating, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, recovery);
			}
			if (apiNetworkView.Limited)
			{
				return new ResourceSnapshot(ApiStatus.SnapshotLimit, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, recovery);
			}
			if (apiNetworkView.Structure != UnloadedNetworks.CatalogRevision)
			{
				return new ResourceSnapshot(ApiStatus.Updating, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, recovery);
			}
			bool flag = apiNetworkView.Structure != UnloadedNetworks.CatalogRevision || Now - apiNetworkView.ObservedAt > 2.0;
			apiStatus = (flag ? ApiStatus.Updating : (string.IsNullOrEmpty(apiNetworkView.Network) ? ApiStatus.NoNetwork : ((!apiNetworkView.Complete) ? ApiStatus.Partial : ApiStatus.Ready)));
			IReadOnlyList<ResourceAmount> resources = apiNetworkView.Resources;
			if (filter.HasValue)
			{
				apiNetworkView.Amounts.TryGetValue(filter.Value, out var value);
				resources = Array.AsReadOnly(new ResourceAmount[1]
				{
					new ResourceAmount(filter.Value, value)
				});
			}
			return new ResourceSnapshot(apiStatus, session, apiNetworkView.Network, apiNetworkView.Revision, apiNetworkView.ObservedAt, Math.Max(0.0, Now - apiNetworkView.ObservedAt), flag, apiNetworkView.Complete && !flag && apiStatus == ApiStatus.Ready, apiNetworkView.Unknown, resources, recovery);
		}

		private static bool Known(ResourceKey k)
		{
			return ApiRules.Text(k.PrefabName) && k.Quality > 0 && k.Quality <= 10000 && Object.op_Implicit((Object)(object)ObjectDB.instance) && Object.op_Implicit((Object)(object)ObjectDB.instance.GetItemPrefab(k.PrefabName));
		}

		internal static ResourceSnapshot Read(ApiContext context, ResourceKey? filter)
		{
			//IL_0304: Unknown result type (might be due to invalid IL or missing references)
			//IL_030b: Expected O, but got Unknown
			if (Instance == null)
			{
				return new ResourceSnapshot(ApiStatus.FeatureDisabled, "", "", 0L);
			}
			if (!Main)
			{
				return new ResourceSnapshot(ApiStatus.WrongThread, "", "", 0L);
			}
			if (!Valid(context))
			{
				return new ResourceSnapshot(ApiStatus.InvalidRequest, "", "", 0L);
			}
			ApiRuntime instance = Instance;
			if (instance.world == null || !Object.op_Implicit((Object)(object)ZNet.instance))
			{
				return new ResourceSnapshot(ApiStatus.Unavailable, "", "", 0L);
			}
			ZDO z;
			long creator;
			ApiStatus apiStatus = ApiWorld.Context(context, ZNet.GetUID(), receipt: false, out z, out creator);
			if (apiStatus != ApiStatus.Ready)
			{
				return new ResourceSnapshot(apiStatus, instance.session, "", 0L);
			}
			if (filter.HasValue && !Known(filter.Value))
			{
				return new ResourceSnapshot(ApiStatus.UnknownItem, instance.session, "", 0L);
			}
			string text = Consumer(context) + (filter.HasValue ? ("/" + filter.Value) : "/*");
			if (!instance.localReads.TryGetValue(text, out var value))
			{
				if (instance.localReads.Count >= 128)
				{
					return new ResourceSnapshot(ApiStatus.SnapshotLimit, instance.session, "", 0L);
				}
				value = (instance.localReads[text] = new LocalRead
				{
					Context = context,
					Filter = filter
				});
			}
			double num = (value.Used = Now);
			if (num >= value.Next)
			{
				value.Next = num + 1.0;
				if (Server)
				{
					value.Snapshot = instance.Snapshot(context, ZNet.GetUID(), filter);
					value.ReceivedAt = num;
				}
				else if (instance.serverVersion == 1 && Transport.Server != 0)
				{
					value.Serial++;
					ZPackage val = new ZPackage();
					val.Write(text);
					val.Write(value.Serial);
					ApiWire.Context(val, context);
					val.Write(filter.HasValue);
					if (filter.HasValue)
					{
						ApiWire.Key(val, filter.Value);
					}
					Send(Transport.Server, "read", val);
				}
			}
			if (!Server && Plugin.Enabled && UnloadedNetworks.Requested && instance.serverVersion == 0)
			{
				return new ResourceSnapshot(ApiStatus.NotReady, instance.session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, value.Snapshot?.Recovery);
			}
			if (!Server && instance.serverVersion != 0 && instance.serverVersion != 1)
			{
				return new ResourceSnapshot(ApiStatus.IncompatibleVersion, instance.session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, value.Snapshot?.Recovery);
			}
			if (!Enabled)
			{
				return new ResourceSnapshot(ApiStatus.FeatureDisabled, instance.session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, value.Snapshot?.Recovery);
			}
			if (value.Snapshot == null)
			{
				return new ResourceSnapshot((instance.serverVersion > 1) ? ApiStatus.IncompatibleVersion : ((instance.serverVersion == 1 && !instance.serverEnabled) ? ApiStatus.FeatureDisabled : ApiStatus.NotReady), instance.session, "", 0L);
			}
			ResourceSnapshot snapshot = value.Snapshot;
			if (num - value.ReceivedAt > 2.0)
			{
				return new ResourceSnapshot(ApiStatus.Updating, instance.session, snapshot.NetworkId, snapshot.Revision, 0.0, snapshot.Age + num - value.ReceivedAt, stale: true, complete: false, 0, snapshot.Resources, snapshot.Recovery);
			}
			return snapshot;
		}

		internal static ResourceAmountResult Amount(ApiContext context, ResourceKey resource)
		{
			ResourceSnapshot resourceSnapshot = Read(context, resource);
			return new ResourceAmountResult(resourceSnapshot, resourceSnapshot.Resources.FirstOrDefault((ResourceAmount r) => r.Resource.Equals(resource))?.ObservedAmount);
		}

		private static ConsumeOperation Reject(ApiStatus status, string id = "", ConsumptionOutcome? outcome = null)
		{
			return new ConsumeOperation
			{
				AdmissionCode = status,
				RequestId = id,
				State = ConsumptionState.Completed,
				IsFinal = true,
				Result = new ConsumeResult(id, "", outcome, status),
				RetryAfter = ((status == ApiStatus.Busy || status == ApiStatus.Throttled) ? 1 : 0)
			};
		}

		internal static ConsumeOperation Consume(ApiContext context, ConsumeRequest request, Action<ConsumeResult> completed)
		{
			if (Instance == null)
			{
				return Reject(ApiStatus.FeatureDisabled);
			}
			if (!Main)
			{
				return Reject(ApiStatus.WrongThread);
			}
			if (!Valid(context) || !ApiRules.Normalize(request, out var normalized))
			{
				return Reject(ApiStatus.InvalidRequest);
			}
			ApiRuntime instance = Instance;
			string text = Consumer(context);
			string text2 = ApiRules.RequestId(text, normalized);
			if (instance.world == null)
			{
				return Reject(ApiStatus.Unavailable, text2);
			}
			if (instance.localOperations.TryGetValue(text, out var value))
			{
				if (value.Request.SessionId == normalized.SessionId && value.Request.Sequence == normalized.Sequence)
				{
					if (!ApiRules.Same(value.Request, normalized))
					{
						return Reject(ApiStatus.RequestConflict, text2);
					}
					if (!value.Handle.IsFinal || value.Handle.Result.Outcome.HasValue)
					{
						value.Handle.Subscribe(completed);
						instance.QueueCallback(value.Handle);
						return value.Handle;
					}
				}
				if (!value.Handle.IsFinal)
				{
					return Reject(ApiStatus.Busy, text2);
				}
			}
			if (normalized.SessionId != instance.session)
			{
				return Reject(ApiStatus.Unavailable, text2, ConsumptionOutcome.OutcomeUnknown);
			}
			if (!instance.localOperations.ContainsKey(text) && instance.localOperations.Count >= 4096)
			{
				return Reject(ApiStatus.CapacityExceeded, text2);
			}
			ZDO z;
			long creator;
			ApiStatus apiStatus = ApiWorld.Context(context, ZNet.GetUID(), Server, out z, out creator);
			if (apiStatus != ApiStatus.Ready)
			{
				return Reject(apiStatus, text2);
			}
			LocalOperation localOperation = new LocalOperation
			{
				Context = context,
				Request = normalized,
				Handle = new ConsumeOperation
				{
					RequestId = text2,
					AdmissionCode = ApiStatus.Ready,
					State = ConsumptionState.Pending
				}
			};
			localOperation.Handle.Subscribe(completed);
			instance.localOperations[text] = localOperation;
			if (Server)
			{
				instance.ProcessLocal(localOperation);
			}
			return localOperation.Handle;
		}

		private ApiStatus Admit(ApiContext context, long peer, ConsumeRequest request, out ApiLedger.Entry entry)
		{
			entry = null;
			if (request.SessionId != session)
			{
				return ApiStatus.Unavailable;
			}
			ApiStatus apiStatus = ApiWorld.Context(context, peer, receipt: true, out var z, out var creator);
			if (apiStatus != ApiStatus.Ready)
			{
				return apiStatus;
			}
			string consumer = Consumer(context);
			ApiStatus apiStatus2 = ledger.Lookup(consumer, request, out entry);
			if (apiStatus2 != ApiStatus.NotReady)
			{
				return apiStatus2;
			}
			apiStatus = ApiWorld.Context(context, peer, receipt: false, out z, out creator);
			if (apiStatus != ApiStatus.Ready)
			{
				return apiStatus;
			}
			if (!Enabled)
			{
				return ApiStatus.FeatureDisabled;
			}
			if (!UnloadedNetworks.ApiCatalogReady)
			{
				return ApiStatus.NotReady;
			}
			if (Transport.Instance.OccupiedOperations + ledger.Entries.Count((ApiLedger.Entry e) => !e.Released) >= 256)
			{
				return ApiStatus.Busy;
			}
			foreach (ResourceRequirement resource in request.Resources)
			{
				if (!Known(resource.Resource))
				{
					return ApiStatus.UnknownItem;
				}
			}
			if (!writes.TryGetValue(peer, out var value))
			{
				if (writes.Count >= 128)
				{
					return ApiStatus.CapacityExceeded;
				}
				value = (writes[peer] = new ApiBucket(4.0, 4.0, Now));
			}
			if (!value.Take(Now) || !writeRate.Take(Now))
			{
				return ApiStatus.Throttled;
			}
			apiStatus = ledger.Admit(consumer, peer, request, Now, out entry);
			if (apiStatus == ApiStatus.Ready && entry.Work == null)
			{
				entry.Work = new ApiPayment(new ApiPaymentWorld(context, entry), Now, request.StartWithin);
			}
			return apiStatus;
		}

		private void ProcessLocal(LocalOperation op)
		{
			ApiLedger.Entry entry;
			ApiStatus apiStatus = Admit(op.Context, ZNet.GetUID(), op.Request, out entry);
			UpdateOperation(op, apiStatus, entry?.State ?? ConsumptionState.Pending, (apiStatus == ApiStatus.Ready) ? entry.Result : null);
		}

		private void UpdateOperation(LocalOperation local, ApiStatus admission, ConsumptionState state, ConsumeResult result)
		{
			ConsumeOperation handle = local.Handle;
			if (!handle.IsFinal)
			{
				handle.AdmissionCode = admission;
				handle.State = state;
				if (admission != ApiStatus.Ready)
				{
					handle.Complete(new ConsumeResult(handle.RequestId, "", (admission == ApiStatus.Unavailable && local.Request.SessionId != session) ? new ConsumptionOutcome?(ConsumptionOutcome.OutcomeUnknown) : ((ConsumptionOutcome?)null), admission));
				}
				else if (result != null)
				{
					handle.Complete(result);
				}
				QueueCallback(handle);
			}
		}

		private void NotifyLocal(ApiLedger.Entry entry)
		{
			if (localOperations.TryGetValue(entry.Consumer, out var value) && value.Request.Sequence == entry.Request.Sequence)
			{
				UpdateOperation(value, ApiStatus.Ready, entry.State, entry.Result);
			}
		}

		private void QueueCallback(ConsumeOperation op)
		{
			if (op.IsFinal && !op.Delivered && !op.Queued && op.Handler != null)
			{
				op.Queued = true;
				callbacks.Enqueue(op);
			}
		}

		private void DeliverCallbacks()
		{
			int count = callbacks.Count;
			for (int i = 0; i < count; i++)
			{
				ConsumeOperation consumeOperation = callbacks.Dequeue();
				consumeOperation.Queued = false;
				if (!consumeOperation.Delivered)
				{
					consumeOperation.Delivered = true;
					try
					{
						consumeOperation.Handler?.Invoke(consumeOperation.Result);
					}
					catch (Exception ex)
					{
						Diagnostic("callback", ex.Message);
					}
					consumeOperation.Handler = null;
				}
			}
		}

		internal static void Send(long peer, string name, ZPackage p)
		{
			ApiRuntime instance = Instance;
			if (Object.op_Implicit((Object)(object)instance) && peer != 0)
			{
				int num = p.Size();
				if (num > 32768)
				{
					throw new InvalidOperationException("API packet limit");
				}
				if (instance.outgoing.Count >= 4096 || instance.outgoingBytes + num > 4194304)
				{
					Diagnostic("backpressure", "Outgoing API queue full; requests retain their IDs for retry.");
					return;
				}
				instance.outgoing.Enqueue(new Outgoing
				{
					Peer = peer,
					Name = name,
					Data = p,
					Bytes = num
				});
				instance.outgoingBytes += num;
			}
		}

		private void Register()
		{
			string[] array = new string[10] { "hello", "welcome", "read", "snapshot", "consume", "result", "prepare", "grant", "ticket", "release" };
			foreach (string text in array)
			{
				string n = text;
				rpc.Register<ZPackage>("RSN_API1_" + n, (Action<long, ZPackage>)delegate(long sender, ZPackage p)
				{
					Dispatch(n, sender, p);
				});
			}
		}

		private void Dispatch(string name, long sender, ZPackage p)
		{
			//IL_03d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d8: Expected O, but got Unknown
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Expected O, but got Unknown
			try
			{
				if (p.Size() > 32768)
				{
					return;
				}
				p.SetPos(0);
				switch (name)
				{
				case "hello":
					if (Server)
					{
						int num3 = p.ReadInt();
						if (num3 == 1 && peers.Count < 128)
						{
							peers[sender] = Now;
						}
						ZPackage val2 = new ZPackage();
						val2.Write(1);
						val2.Write(session);
						val2.Write(Enabled);
						Send(sender, "welcome", val2);
					}
					break;
				case "welcome":
				{
					if (sender != Transport.Server || Server)
					{
						break;
					}
					int num = p.ReadInt();
					string text = ApiWire.Text(p, 64);
					bool flag = p.ReadBool();
					if (session != "" && session != text)
					{
						foreach (LocalOperation value2 in localOperations.Values)
						{
							if (!value2.Handle.IsFinal)
							{
								value2.Handle.Complete(new ConsumeResult(value2.Handle.RequestId, "", ConsumptionOutcome.OutcomeUnknown, ApiStatus.Unavailable));
								QueueCallback(value2.Handle);
							}
						}
						localReads.Clear();
					}
					session = text;
					serverVersion = num;
					serverEnabled = flag;
					break;
				}
				case "read":
					if (Server && RateRead(sender))
					{
						ReceiveRead(sender, p);
					}
					break;
				case "snapshot":
					if (!Server && sender == Transport.Server)
					{
						ReceiveSnapshot(p);
					}
					break;
				case "consume":
					if (Server && RateRead(sender, control: true))
					{
						ApiContext apiContext = ApiWire.Context(p);
						ConsumeRequest consumeRequest = ApiWire.Request(p);
						ApiLedger.Entry entry;
						ApiStatus apiStatus = Admit(apiContext, sender, consumeRequest, out entry);
						ZPackage val = new ZPackage();
						ApiWire.Context(val, apiContext);
						val.Write(consumeRequest.SessionId);
						val.Write(consumeRequest.Sequence);
						val.Write((int)apiStatus);
						val.Write((int)(entry?.State ?? ConsumptionState.Pending));
						ApiWire.Result(val, (apiStatus == ApiStatus.Ready) ? entry.Result : null);
						Send(sender, "result", val);
					}
					break;
				case "result":
					if (!Server && sender == Transport.Server)
					{
						ApiContext c = ApiWire.Context(p);
						string text2 = ApiWire.Text(p, 64);
						long num2 = p.ReadLong();
						ApiStatus admission = (ApiStatus)p.ReadInt();
						ConsumptionState state = (ConsumptionState)p.ReadInt();
						ConsumeResult result = ApiWire.Result(p);
						if (localOperations.TryGetValue(Consumer(c), out var value) && value.Request.SessionId == text2 && value.Request.Sequence == num2)
						{
							UpdateOperation(value, admission, state, result);
						}
					}
					break;
				case "prepare":
					ApiOwnership.ReceivePrepare(sender, p);
					break;
				case "grant":
					ApiOwnership.ReceiveGrant(sender, p);
					break;
				case "ticket":
					if (Server && RateRead(sender, control: true))
					{
						ApiOwnership.ReceiveTicket(sender, p);
					}
					break;
				case "release":
					ApiOwnership.ReceiveRelease(sender, p);
					break;
				}
			}
			catch (Exception ex)
			{
				Diagnostic("protocol", name + ": " + ex.Message);
			}
		}

		private bool RateRead(long peer, bool control = false)
		{
			if (!HasPeer(peer))
			{
				return false;
			}
			Dictionary<long, ApiBucket> dictionary = (control ? controls : queries);
			if (!dictionary.TryGetValue(peer, out var value))
			{
				if (dictionary.Count >= 128)
				{
					return false;
				}
				value = (dictionary[peer] = new ApiBucket(control ? 128 : 16, control ? 256 : 32, Now));
			}
			return value.Take(Now);
		}

		private void ReceiveRead(long peer, ZPackage p)
		{
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Expected O, but got Unknown
			string text = ApiWire.Text(p, 512);
			long num = p.ReadLong();
			ApiContext apiContext = ApiWire.Context(p);
			ResourceKey? filter = (p.ReadBool() ? new ResourceKey?(ApiWire.Key(p)) : ((ResourceKey?)null));
			string key = peer + "/" + Consumer(apiContext);
			if (!reads.TryGetValue(key, out var value))
			{
				if (reads.Count >= 128)
				{
					return;
				}
				value = (reads[key] = new ReadInterest
				{
					Context = apiContext,
					Peer = peer
				});
			}
			value.Used = Now;
			ResourceSnapshot resourceSnapshot = Snapshot(apiContext, peer, filter);
			int num2 = resourceSnapshot.Resources.Count;
			int num3 = Math.Max(1, (num2 + 63) / 64);
			if (num2 > 4096)
			{
				resourceSnapshot = new ResourceSnapshot(ApiStatus.SnapshotLimit, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, resourceSnapshot.Recovery);
				num3 = 1;
				num2 = 0;
			}
			for (int i = 0; i < num3; i++)
			{
				ZPackage val = new ZPackage();
				val.Write(text);
				val.Write(num);
				val.Write(i);
				val.Write(num3);
				val.Write((int)resourceSnapshot.Status);
				val.Write(resourceSnapshot.SessionId);
				val.Write(resourceSnapshot.NetworkId ?? "");
				val.Write(resourceSnapshot.Revision);
				val.Write(resourceSnapshot.ObservedAt);
				val.Write(resourceSnapshot.Age);
				val.Write(resourceSnapshot.IsStale);
				val.Write(resourceSnapshot.IsComplete);
				val.Write(resourceSnapshot.UnknownSources);
				ApiWire.Recovery(val, resourceSnapshot.Recovery);
				int num4 = i * 64;
				int num5 = Math.Min(64, num2 - num4);
				val.Write(num5);
				for (int j = 0; j < num5; j++)
				{
					ResourceAmount resourceAmount = resourceSnapshot.Resources[num4 + j];
					ApiWire.Key(val, resourceAmount.Resource);
					val.Write(resourceAmount.ObservedAmount);
				}
				Send(peer, "snapshot", val);
			}
		}

		private void ReceiveSnapshot(ZPackage p)
		{
			string key = ApiWire.Text(p, 512);
			long num = p.ReadLong();
			int num2 = p.ReadInt();
			int num3 = p.ReadInt();
			if (!localReads.TryGetValue(key, out var value) || num < value.ReceivedSerial || num > value.Serial || num3 < 1 || num3 > 64 || num2 < 0 || num2 >= num3)
			{
				return;
			}
			if (num2 == 0)
			{
				value.ReceivedSerial = num;
				value.NextPage = 0;
				value.Expected = num3;
				value.Rows.Clear();
				value.Bytes = 0;
			}
			if (num != value.ReceivedSerial || num2 != value.NextPage || num3 != value.Expected)
			{
				return;
			}
			ApiStatus status = (ApiStatus)p.ReadInt();
			string text = ApiWire.Text(p, 64);
			string text2 = p.ReadString();
			if (text != session || text2.Length > 256)
			{
				return;
			}
			long revision = p.ReadLong();
			double observedAt = p.ReadDouble();
			double age = p.ReadDouble();
			bool stale = p.ReadBool();
			bool complete = p.ReadBool();
			int unknown = p.ReadInt();
			ConsumerRecovery recovery = ApiWire.Recovery(p);
			int num4 = p.ReadInt();
			if (num4 < 0 || num4 > 64)
			{
				return;
			}
			for (int i = 0; i < num4; i++)
			{
				value.Rows.Add(new ResourceAmount(ApiWire.Key(p), p.ReadLong()));
			}
			value.Bytes += p.Size();
			if (value.Bytes > 1048576)
			{
				value.Rows.Clear();
				return;
			}
			value.NextPage++;
			if (value.NextPage == num3)
			{
				if (localReads.Values.Sum((LocalRead r) => r.Bytes) > 16777216)
				{
					value.Rows.Clear();
					value.Snapshot = new ResourceSnapshot(ApiStatus.SnapshotLimit, session, "", 0L, 0.0, 0.0, stale: false, complete: false, 0, null, recovery);
					value.Bytes = 0;
				}
				else
				{
					value.Snapshot = new ResourceSnapshot(status, text, text2, revision, observedAt, age, stale, complete, unknown, Array.AsReadOnly(value.Rows.ToArray()), recovery);
				}
				value.ReceivedAt = Now;
			}
		}
	}
	internal static class ApiWire
	{
		internal static string Text(ZPackage p, int limit = 128)
		{
			string text = p.ReadString();
			if (!ApiRules.Text(text, limit))
			{
				throw new InvalidOperationException("Invalid API text");
			}
			return text;
		}

		internal static ApiContext Context(ZPackage p)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return new ApiContext(p.ReadZDOID(), Text(p));
		}

		internal static void Context(ZPackage p, ApiContext c)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			p.Write(c.ConsumerId);
			p.Write(c.ModId);
		}

		internal static ResourceKey Key(ZPackage p)
		{
			return new ResourceKey(Text(p), p.ReadInt());
		}

		internal static void Key(ZPackage p, ResourceKey k)
		{
			p.Write(k.PrefabName);
			p.Write(k.Quality);
		}

		internal static ConsumeRequest Request(ZPackage p)
		{
			string sessionId = Text(p, 64);
			long sequence = p.ReadLong();
			string cycleRevision = Text(p);
			ResourceMatchPolicy matchPolicy = (ResourceMatchPolicy)p.ReadInt();
			double startWithin = p.ReadDouble();
			int num = p.ReadInt();
			if (num < 1 || num > 32)
			{
				throw new InvalidOperationException("API requirement limit");
			}
			List<ResourceRequirement> list = new List<ResourceRequirement>();
			for (int i = 0; i < num; i++)
			{
				list.Add(new ResourceRequirement(Key(p), p.ReadInt()));
			}
			ConsumeRequest request = new ConsumeRequest(sessionId, sequence, cycleRevision, list, matchPolicy, startWithin);
			if (!ApiRules.Normalize(request, out var normalized))
			{
				throw new InvalidOperationException("Invalid API request");
			}
			return normalized;
		}

		internal static void Request(ZPackage p, ConsumeRequest r)
		{
			p.Write(r.SessionId);
			p.Write(r.Sequence);
			p.Write(r.CycleRevision);
			p.Write((int)r.MatchPolicy);
			p.Write(r.StartWithin);
			p.Write(r.Resources.Count);
			foreach (ResourceRequirement resource in r.Resources)
			{
				Key(p, resource.Resource);
				p.Write(resource.Amount);
			}
		}

		internal static void Result(ZPackage p, ConsumeResult r)
		{
			p.Write(r != null);
			if (r == null)
			{
				return;
			}
			p.Write(r.RequestId ?? "");
			p.Write(r.NetworkId ?? "");
			p.Write((int)(r.Outcome.HasValue ? r.Outcome.Value : ((ConsumptionOutcome)(-1))));
			p.Write((int)r.Reason);
			p.Write(r.Resources.Count);
			foreach (ResourceRequirement resource in r.Resources)
			{
				Key(p, resource.Resource);
				p.Write(resource.Amount);
			}
		}

		internal static ConsumeResult Result(ZPackage p)
		{
			if (!p.ReadBool())
			{
				return null;
			}
			string text = p.ReadString();
			string text2 = p.ReadString();
			if (text.Length > 512 || text2.Length > 256)
			{
				throw new InvalidOperationException("API receipt limit");
			}
			int num = p.ReadInt();
			ApiStatus reason = (ApiStatus)p.ReadInt();
			int num2 = p.ReadInt();
			if (num < -1 || num > 2 || num2 < 0 || num2 > 32)
			{
				throw new InvalidOperationException("Invalid API receipt");
			}
			List<ResourceRequirement> list = new List<ResourceRequirement>();
			for (int i = 0; i < num2; i++)
			{
				list.Add(new ResourceRequirement(Key(p), p.ReadInt()));
			}
			return new ConsumeResult(text, text2, (num < 0) ? ((ConsumptionOutcome?)null) : new ConsumptionOutcome?((ConsumptionOutcome)num), reason, list);
		}

		internal static void Recovery(ZPackage p, ConsumerRecovery r)
		{
			p.Write(r != null);
			if (r != null)
			{
				p.Write(r.OwnerEpoch);
				p.Write(r.Request != null);
				if (r.Request != null)
				{
					Request(p, r.Request);
				}
				Result(p, r.Result);
				p.Write((int)r.State);
			}
		}

		internal static ConsumerRecovery Recovery(ZPackage p)
		{
			if (!p.ReadBool())
			{
				return null;
			}
			long epoch = p.ReadLong();
			ConsumeRequest request = (p.ReadBool() ? Request(p) : null);
			ConsumeResult result = Result(p);
			return new ConsumerRecovery(epoch, request, result, (ConsumptionState)p.ReadInt());
		}
	}
	internal static class UnloadedNetworks
	{
		private static readonly HashSet<int> chestTypes = new HashSet<int>();

		private static readonly HashSet<int> nodeTypes = new HashSet<int>(new string[3] { "RSN_NetworkCore", "RSN_RunicRelay", "RSN_RunicGateway" }.Select((string s) => StringExtensionMethods.GetStableHashCode(s)));

		private static readonly Dictionary<ZDOID, ZDO> nodes = new Dictionary<ZDOID, ZDO>();

		private static readonly Dictionary<ZDOID, ZDO> chests = new Dictionary<ZDOID, ZDO>();

		private static readonly Dictionary<ZDOID, ZDO> wards = new Dictionary<ZDOID, ZDO>();

		private static readonly Dictionary<Vector2s, HashSet<ZDOID>> chestSectors = new Dictionary<Vector2s, HashSet<ZDOID>>();

		private static readonly Dictionary<ZDOID, Vector2s> positions = new Dictionary<ZDOID, Vector2s>();

		private static readonly Dictionary<ZDOID, (Vector3, string, long, int)> nodeState = new Dictionary<ZDOID, (Vector3, string, long, int)>();

		private static readonly Dictionary<ZDOID, long> creators = new Dictionary<ZDOID, long>();

		private static readonly Dictionary<ZDOID, long> owners = new Dictionary<ZDOID, long>();

		private static readonly Dictionary<(Vector3, long), bool> wardAccess = new Dictionary<(Vector3, long), bool>();

		private static readonly Dictionary<ZDOID, GameObject> replicas = new Dictionary<ZDOID, GameObject>();

		private static readonly Queue<ZDOID> queue = new Queue<ZDOID>();

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

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

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

		private static readonly Dictionary<string, long> requestedNetworks = new Dictionary<string, long>();

		private static readonly HashSet<int> wardTypes = new HashSet<int>();

		private static ZDOMan world;

		private static bool enabled;

		private static bool bootstrapped;

		private static long structure;

		private static long nodesRequested = -1L;

		private static float demandUntil;

		private static Harmony patches;

		internal static bool ApiCatalogReady => Enabled && Authority && bootstrapped;

		internal static IEnumerable<ZDO> ApiNodes => nodes.Values;

		internal static IEnumerable<ZDO> ApiChests => chests.Values;

		internal static bool Installed => patches != null;

		internal static bool Requested => enabled && world == ZDOMan.instance && Object.op_Implicit((Object)(object)ZNet.instance);

		internal static bool Authority => Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer();

		internal static bool Enabled => Requested && (Authority || UnloadedMultiplayer.ServerEnabled);

		internal static long CatalogRevision => structure;

		internal static bool Demand => Enabled && Time.unscaledTime < demandUntil;

		internal static IEnumerable<NetworkMember> Members => from g in replicas.Values
			where Object.op_Implicit((Object)(object)g)
			select g.GetComponent<NetworkMember>() into m
			where Object.op_Implicit((Object)(object)m) && m.Valid
			se