Decompiled source of RunicProduction v2.3.4

BepInEx/plugins/RunicProduction/RunicProduction.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Runtime.Serialization.Json;
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 Microsoft.CodeAnalysis;
using Runic.Compatibility;
using Runic.Localization;
using RunicAutomation;
using RunicAutomation.Operations;
using RunicProduction.Contracts;
using RunicProduction.Core;
using RunicProduction.Integration;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("RunicMultiAccess.NativeRegression")]
[assembly: AssemblyTitle("Runic Production")]
[assembly: AssemblyDescription("Standalone native-owner production with persistent links and bounded replenishment")]
[assembly: AssemblyCompany("ChazmanMods")]
[assembly: AssemblyProduct("Runic Production")]
[assembly: AssemblyFileVersion("2.3.4.0")]
[assembly: AssemblyInformationalVersion("2.3.4")]
[assembly: InternalsVisibleTo("RunicProduction.Tests")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("2.3.4.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace RunicAutomation
{
	public static class ContainerAuthority
	{
		private sealed class Policy
		{
			public Func<Container, ZDOID, long, long, bool> Allows;

			public Func<Container, bool> Refresh;
		}

		private static readonly Dictionary<string, Policy> Policies = new Dictionary<string, Policy>();

		private static readonly FieldInfo ViewField = AccessTools.Field(typeof(Container), "m_nview");

		private static Dictionary<string, Delegate> Routes => SharedState.Get("authority", () => new Dictionary<string, Delegate>
		{
			["RegisterPolicy"] = new Action<string, Func<Container, ZDOID, long, long, bool>, Func<Container, bool>>(RegisterPolicyCore),
			["UnregisterPolicy"] = (Action<string>)delegate(string name)
			{
				Policies.Remove(name);
			},
			["TryAcquire"] = new Func<Container, string, ZDOID, long, bool, bool>(TryAcquireCore)
		});

		public static void RegisterPolicy(string name, Func<Container, ZDOID, long, long, bool> allows, Func<Container, bool> refresh)
		{
			((Action<string, Func<Container, ZDOID, long, long, bool>, Func<Container, bool>>)Routes["RegisterPolicy"])(name, allows, refresh);
		}

		public static void UnregisterPolicy(string name)
		{
			((Action<string>)Routes["UnregisterPolicy"])(name);
		}

		public static void Register(Container chest)
		{
		}

		public static void Clear()
		{
		}

		public static bool TryAcquire(Container chest, string policy, ZDOID context, long principal, bool allowInUse = false)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			return ((Func<Container, string, ZDOID, long, bool, bool>)Routes["TryAcquire"])(chest, policy, context, principal, allowInUse);
		}

		private static void RegisterPolicyCore(string name, Func<Container, ZDOID, long, long, bool> allows, Func<Container, bool> refresh)
		{
			Policies[name] = new Policy
			{
				Allows = allows,
				Refresh = refresh
			};
		}

		public static ZNetView View(Container chest)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			if (!((Object)(object)chest == (Object)null))
			{
				if (!((Object)(object)chest.m_rootObjectOverride != (Object)null))
				{
					return (ZNetView)ViewField.GetValue(chest);
				}
				return chest.m_rootObjectOverride;
			}
			return null;
		}

		public unsafe static bool Blocked(Container chest)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			ZNetView obj = View(chest);
			ZDO val = ((obj != null) ? obj.GetZDO() : null);
			if (!((Object)(object)chest == (Object)null) && !MutationGate.IsBlocked(chest.GetInventory()))
			{
				if (val != null)
				{
					if (!MutationGate.IsBlocked(val.m_uid))
					{
						ZDOID uid = val.m_uid;
						return MutationGate.IsBlocked("valheim.zdo:" + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString());
					}
					return true;
				}
				return false;
			}
			return true;
		}

		public static bool Writable(Container chest, bool allowInUse = false)
		{
			if ((Object)(object)chest != (Object)null && ((Behaviour)chest).isActiveAndEnabled && !Blocked(chest))
			{
				if (allowInUse)
				{
					goto IL_006f;
				}
				if (!chest.IsInUse())
				{
					ZNetView obj = View(chest);
					if (obj != null)
					{
						ZDO zDO = obj.GetZDO();
						if (((zDO != null) ? new int?(zDO.GetInt(ZDOVars.s_inUse, 0)) : ((int?)null)) == 0)
						{
							goto IL_006f;
						}
					}
				}
			}
			return false;
			IL_006f:
			if (!((Object)(object)chest.m_wagon == (Object)null))
			{
				return !chest.m_wagon.InUse();
			}
			return true;
		}

		public static string Describe(Container chest)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)chest == (Object)null))
			{
				string[] obj = new string[8] { "endpoint=", null, null, null, null, null, null, null };
				ZNetView obj2 = View(chest);
				obj[1] = ((obj2 == null) ? ((ZDOID?)null) : obj2.GetZDO()?.m_uid).ToString();
				obj[2] = "; nativeOwner=";
				ZNetView obj3 = View(chest);
				obj[3] = ((obj3 != null) ? new bool?(obj3.IsOwner()) : ((bool?)null)).ToString();
				obj[4] = "; blocked=";
				obj[5] = Blocked(chest).ToString();
				obj[6] = "; inUse=";
				obj[7] = chest.IsInUse().ToString();
				return string.Concat(obj);
			}
			return "container=missing";
		}

		private static bool TryAcquireCore(Container chest, string policy, ZDOID context, long principal, bool allowInUse = false)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || !Writable(chest, allowInUse) || !Policies.TryGetValue(policy, out var value))
			{
				return false;
			}
			ZNetView val = View(chest);
			ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null);
			if (val2 == null || !value.Allows(chest, context, principal, ZNet.GetUID()))
			{
				return false;
			}
			if (!val.IsOwner())
			{
				val.ClaimOwnership();
				if (!val.IsOwner() || !value.Refresh(chest))
				{
					return false;
				}
			}
			if (val.IsValid() && val.IsOwner() && val.GetZDO() == val2 && Writable(chest, allowInUse))
			{
				return value.Allows(chest, context, principal, ZNet.GetUID());
			}
			return false;
		}

		public static Player FindPlayer(ZDOID context, long principal, long sender)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			ZNetScene instance = ZNetScene.instance;
			GameObject val = ((instance != null) ? instance.FindInstance(context) : null);
			Player val2 = (((Object)(object)val == (Object)null) ? null : val.GetComponent<Player>());
			ZNetView val3 = (((Object)(object)val == (Object)null) ? null : val.GetComponent<ZNetView>());
			if (!((Object)(object)val2 != (Object)null) || !((Object)(object)val3 != (Object)null) || !val3.IsValid() || val3.GetZDO().GetOwner() != sender || val2.GetPlayerID() != principal)
			{
				return null;
			}
			return val2;
		}

		public static ZDOID PlayerContext(Player player)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)player == (Object)null))
			{
				ZNetView component = ((Component)player).GetComponent<ZNetView>();
				return (ZDOID)(((??)((component == null) ? ((ZDOID?)null) : component.GetZDO()?.m_uid)) ?? ZDOID.None);
			}
			return ZDOID.None;
		}

		public static bool PlayerAccess(Container chest, ZDOID context, long principal, long sender, float range, bool requireWard)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			Player val = FindPlayer(context, principal, sender);
			if (!((Object)(object)val == (Object)null) && !((Object)(object)chest == (Object)null) && !(range <= 0f) && !float.IsNaN(range))
			{
				Vector3 val2 = ((Component)val).transform.position - ((Component)chest).transform.position;
				if (!(((Vector3)(ref val2)).sqrMagnitude > range * range))
				{
					MethodInfo methodInfo = AccessTools.Method(typeof(Container), "CheckAccess", (Type[])null, (Type[])null);
					if (methodInfo == null || !(bool)methodInfo.Invoke(chest, new object[1] { principal }))
					{
						return false;
					}
					if (!requireWard && !chest.m_checkGuardStone)
					{
						return true;
					}
					if (!(AccessTools.Field(typeof(PrivateArea), "m_allAreas").GetValue(null) is List<PrivateArea> list))
					{
						return false;
					}
					foreach (PrivateArea item in list)
					{
						if (!((Object)(object)item == (Object)null) && (bool)AccessTools.Method(typeof(PrivateArea), "IsEnabled", (Type[])null, (Type[])null).Invoke(item, null) && (bool)AccessTools.Method(typeof(PrivateArea), "IsInside", (Type[])null, (Type[])null).Invoke(item, new object[2]
						{
							((Component)chest).transform.position,
							0f
						}))
						{
							Piece component = ((Component)item).GetComponent<Piece>();
							if ((Object)(object)component == (Object)null || component.GetCreator() == 0L)
							{
								return false;
							}
							if (component.GetCreator() != principal && !(bool)AccessTools.Method(typeof(PrivateArea), "IsPermitted", (Type[])null, (Type[])null).Invoke(item, new object[1] { principal }))
							{
								return false;
							}
						}
					}
					return true;
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(ZNet), "Start")]
	internal static class RegisterSnapshotProtocol
	{
		private static void Postfix()
		{
			ManagedDrawerInteraction.Register();
		}
	}
	[HarmonyPatch(typeof(ZNet), "OnDestroy")]
	internal static class EndWorld
	{
		private static void Postfix()
		{
			InventoryOperationNetwork.Clear();
			ManagedChestLedger.Clear();
			CharacterOperationEscrow.Clear();
			NativeChestInterop.Clear();
			InventoryWorldLifecycle.Clear();
			WorldInventoryOrigin.Clear();
			ManagedWorldDrops.Clear();
			ManagedDrawerRetrieval.Clear();
			ManagedAnimalConsumption.Clear();
			ContainerAuthority.Clear();
			MutationGate.EndSession();
		}
	}
	[HarmonyPatch(typeof(ZNet), "Update")]
	internal static class InventoryOperationLoop
	{
		private static string lastFailure;

		private static void Postfix()
		{
			try
			{
				InventoryOperationNetwork.Tick();
				if (InventoryOperationNetwork.Ready)
				{
					ManagedChestLedger.Tick();
					NativeChestInterop.Tick();
					CharacterOperationEscrow.Tick();
					ManagedWorldDrops.Tick();
					WorldInventoryOrigin.Tick();
					InventoryWorldLifecycle.Tick();
				}
			}
			catch (Exception ex)
			{
				string text = ex.ToString();
				if (lastFailure != text)
				{
					lastFailure = text;
					Debug.LogError((object)("Runic inventory operation update failed: " + text));
				}
				MutationGate.Report("inventory operation update: " + ex);
			}
		}
	}
	[HarmonyPatch(typeof(PlayerProfile), "LoadPlayerData")]
	internal static class ReloadCharacterOperation
	{
		private static void Postfix()
		{
			CharacterOperationEscrow.Clear();
		}
	}
	[HarmonyPatch(typeof(ZDO), "Deserialize")]
	internal static class PreserveManagedChestProjection
	{
		private static void Postfix(ZDO __instance)
		{
			if (!InventoryOperationNetwork.Ready)
			{
				return;
			}
			try
			{
				ManagedChestLedger.Project(__instance);
				ManagedWorldDrops.Project(__instance);
			}
			catch (Exception ex)
			{
				MutationGate.Report("inventory projection reconciliation: " + ex);
			}
		}
	}
	public enum MutationOutcome
	{
		RejectedWithoutChanges,
		Committed,
		RolledBack,
		Indeterminate
	}
	public sealed class MutationLease : IDisposable
	{
		internal readonly object[] State;

		private HashSet<object> _endpoints => (HashSet<object>)State[4];

		public string Id => (string)State[0];

		public string Purpose => (string)State[1];

		public MutationOutcome Outcome
		{
			get
			{
				return (MutationOutcome)(int)State[2];
			}
			private set
			{
				State[2] = (int)value;
			}
		}

		public bool Active
		{
			get
			{
				return (bool)State[3];
			}
			private set
			{
				State[3] = value;
			}
		}

		internal MutationLease(string purpose)
		{
			State = new object[5]
			{
				Guid.NewGuid().ToString("N"),
				purpose,
				0,
				true,
				new HashSet<object>()
			};
		}

		internal MutationLease(object[] state)
		{
			State = state;
		}

		public void Track(object endpoint)
		{
			if (!Active || MutationGate.Current?.State != State)
			{
				throw new InvalidOperationException("Inactive mutation lease.");
			}
			if (endpoint != null)
			{
				_endpoints.Add(endpoint);
			}
		}

		public void Complete(MutationOutcome outcome)
		{
			if (!Active || Outcome == MutationOutcome.Indeterminate)
			{
				return;
			}
			Outcome = outcome;
			if (outcome == MutationOutcome.Indeterminate)
			{
				foreach (object endpoint in _endpoints)
				{
					MutationGate.Block(endpoint);
				}
			}
			List<string> list = new List<string>();
			foreach (object endpoint2 in _endpoints)
			{
				if (list.Count < 128)
				{
					list.Add(endpoint2.ToString());
				}
			}
			MutationGate.Report(Id + " " + Purpose + " " + outcome.ToString() + " endpoints=[" + string.Join(",", list) + "]");
		}

		public void Dispose()
		{
			lock (MutationGate.Sync)
			{
				if (Active)
				{
					Active = false;
					if (MutationGate.Current?.State == State)
					{
						MutationGate.Current = null;
					}
				}
			}
		}
	}
	public static class MutationGate
	{
		internal static object Sync => SharedState.Sync;

		private static object[] State => SharedState.Get("mutation", () => new object[4]
		{
			null,
			new HashSet<object>(),
			false,
			null
		});

		private static HashSet<object> Blocked => (HashSet<object>)State[1];

		private static bool _overflow
		{
			get
			{
				return (bool)State[2];
			}
			set
			{
				State[2] = value;
			}
		}

		public static MutationLease Current
		{
			get
			{
				if (!(State[0] is object[] state))
				{
					return null;
				}
				return new MutationLease(state);
			}
			internal set
			{
				State[0] = value?.State;
			}
		}

		public static Action<string> Diagnostic
		{
			get
			{
				return (Action<string>)State[3];
			}
			set
			{
				State[3] = value;
			}
		}

		public static bool TryBegin(string purpose, out MutationLease lease)
		{
			lock (Sync)
			{
				lease = null;
				if (Current != null || _overflow)
				{
					return false;
				}
				Current = (lease = new MutationLease(purpose));
				return true;
			}
		}

		public static bool IsBlocked(object endpoint)
		{
			lock (Sync)
			{
				return _overflow || (endpoint != null && Blocked.Contains(endpoint));
			}
		}

		public static void Block(object endpoint)
		{
			lock (Sync)
			{
				if (endpoint != null)
				{
					if (Blocked.Count >= 16384)
					{
						_overflow = true;
					}
					else
					{
						Blocked.Add(endpoint);
					}
				}
			}
		}

		internal static void Report(string text)
		{
			try
			{
				Diagnostic?.Invoke(text);
			}
			catch
			{
			}
		}

		public static void EndSession()
		{
			lock (Sync)
			{
				Current?.Dispose();
				Blocked.Clear();
				_overflow = false;
			}
		}
	}
	public sealed class MutationIndeterminateException : InvalidOperationException
	{
		public MutationIndeterminateException(string detail, Exception inner = null)
			: base("Inventory recovery needs inspection: " + detail, inner)
		{
		}
	}
	internal static class ServerSnapshotBarrier
	{
		private sealed class Check
		{
			public string Nonce;

			public string Digest;

			public int OwnerRevision;

			public uint DataRevision;

			public float Retry;

			public float AcceptedUntil;
		}

		private const string RequestRpc = "RunicAutomation_SnapshotCheck_v2";

		private const string ReplyRpc = "RunicAutomation_SnapshotAck_v2";

		private static readonly Dictionary<ZDOID, Check> Checks = new Dictionary<ZDOID, Check>();

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

		private static ZRoutedRpc Registered;

		private static int Frame = -1;

		private static int Sent;

		internal static void Register()
		{
			if (ZRoutedRpc.instance != null && Registered != ZRoutedRpc.instance)
			{
				Registered = ZRoutedRpc.instance;
				Registered.Register<ZPackage>("RunicAutomation_SnapshotCheck_v2", (Action<long, ZPackage>)ReceiveRequest);
				Registered.Register<ZPackage>("RunicAutomation_SnapshotAck_v2", (Action<long, ZPackage>)ReceiveReply);
			}
		}

		internal static void Clear()
		{
			Checks.Clear();
			Requests.Clear();
			Registered = null;
			Frame = -1;
			Sent = 0;
		}

		internal static void Remove(ZDOID id)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Checks.Remove(id);
		}

		private static string Digest(ZDO zdo)
		{
			byte[] byteArray = zdo.GetByteArray(ZDOVars.s_items, (byte[])null);
			if (byteArray == null || byteArray.Length == 0)
			{
				return null;
			}
			using SHA256 sHA = SHA256.Create();
			return Convert.ToBase64String(sHA.ComputeHash(byteArray));
		}

		internal unsafe static bool Ready(Container chest, string nonce, Func<Container, bool> refresh)
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			Register();
			ZNet instance = ZNet.instance;
			ZNetView val = ContainerAuthority.View(chest);
			ZDO val2 = ((val != null) ? val.GetZDO() : null);
			if ((Object)(object)instance == (Object)null || instance.IsServer() || Registered == null || instance.GetServerPeer() == null || val2 == null || !val.IsOwner() || !ContainerAuthority.Writable(chest))
			{
				return false;
			}
			string text = Digest(val2);
			if (text == null)
			{
				return false;
			}
			if (!Checks.TryGetValue(val2.m_uid, out var value))
			{
				if (Checks.Count >= 16384)
				{
					return false;
				}
				value = (Checks[val2.m_uid] = new Check());
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			ZDOID uid;
			if (value.Nonce == nonce && value.OwnerRevision == val2.OwnerRevision && value.DataRevision == val2.DataRevision && value.Digest == text && realtimeSinceStartup < value.AcceptedUntil)
			{
				if (!refresh(chest))
				{
					return false;
				}
				if (!val.IsValid() || !val.IsOwner() || val2 != val.GetZDO() || !ContainerAuthority.Writable(chest) || value.OwnerRevision != val2.OwnerRevision || value.DataRevision != val2.DataRevision || value.Digest != Digest(val2))
				{
					return false;
				}
				string[] obj = new string[6] { "authority server-snapshot-confirmed; endpoint=", null, null, null, null, null };
				uid = val2.m_uid;
				obj[1] = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString();
				obj[2] = "; ownerRevision=";
				obj[3] = val2.OwnerRevision.ToString();
				obj[4] = "; dataRevision=";
				obj[5] = val2.DataRevision.ToString();
				MutationGate.Report(string.Concat(obj));
				Checks.Remove(val2.m_uid);
				return true;
			}
			if (realtimeSinceStartup < value.Retry)
			{
				return false;
			}
			if (Frame != Time.frameCount)
			{
				Frame = Time.frameCount;
				Sent = 0;
			}
			if (Sent >= 32)
			{
				return false;
			}
			Sent++;
			value.Nonce = nonce;
			value.OwnerRevision = val2.OwnerRevision;
			value.DataRevision = val2.DataRevision;
			value.Digest = text;
			value.AcceptedUntil = 0f;
			value.Retry = realtimeSinceStartup + 3f;
			ZPackage val3 = Pack(val2.m_uid, value);
			Registered.InvokeRoutedRPC(instance.GetServerPeer().m_uid, "RunicAutomation_SnapshotCheck_v2", new object[1] { val3 });
			uid = val2.m_uid;
			MutationGate.Report("authority server-snapshot-requested; endpoint=" + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString());
			return false;
		}

		private static ZPackage Pack(ZDOID id, Check check)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(id);
			val.Write(check.Nonce);
			val.Write(check.OwnerRevision);
			val.Write(check.DataRevision);
			val.Write(check.Digest);
			return val;
		}

		private static bool Read(ZPackage package, out ZDOID id, out Check check)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			id = ZDOID.None;
			check = null;
			if (package == null || package.Size() > 256)
			{
				return false;
			}
			id = package.ReadZDOID();
			string text = package.ReadString();
			int num = package.ReadInt();
			uint dataRevision = package.ReadUInt();
			string text2 = package.ReadString();
			if (((ZDOID)(ref id)).IsNone() || text.Length != 32 || !Guid.TryParseExact(text, "N", out var result) || result == Guid.Empty || num < 0 || num > 65535 || text2.Length != 44)
			{
				return false;
			}
			check = new Check
			{
				Nonce = text,
				OwnerRevision = num,
				DataRevision = dataRevision,
				Digest = text2
			};
			return true;
		}

		private unsafe static void ReceiveRequest(long sender, ZPackage package)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || sender == 0L || ZNet.instance.GetPeer(sender) == null || MutationGate.Current != null || !Read(package, out var id, out var check))
				{
					return;
				}
				string text = sender.ToString();
				ZDOID val = id;
				string key = text + ":" + ((object)(*(ZDOID*)(&val))/*cast due to .constrained prefix*/).ToString();
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				if (Requests.TryGetValue(key, out var value) && realtimeSinceStartup < value)
				{
					return;
				}
				if (Requests.Count >= 16384)
				{
					List<string> list = new List<string>();
					foreach (KeyValuePair<string, float> request in Requests)
					{
						if (realtimeSinceStartup >= request.Value)
						{
							list.Add(request.Key);
						}
					}
					foreach (string item in list)
					{
						Requests.Remove(item);
					}
					if (Requests.Count >= 16384)
					{
						return;
					}
				}
				Requests[key] = realtimeSinceStartup + 2f;
				ZDOMan instance = ZDOMan.instance;
				ZDO val2 = ((instance != null) ? instance.GetZDO(id) : null);
				if (val2 == null || val2.GetOwner() != sender || val2.OwnerRevision != check.OwnerRevision || val2.GetInt(ZDOVars.s_inUse, 0) != 0 || MutationGate.IsBlocked(id))
				{
					return;
				}
				val = id;
				if (!MutationGate.IsBlocked("valheim.zdo:" + ((object)(*(ZDOID*)(&val))/*cast due to .constrained prefix*/).ToString()))
				{
					if (val2.DataRevision != check.DataRevision || Digest(val2) != check.Digest)
					{
						ZDOMan.instance.ForceSendZDO(sender, id);
						return;
					}
					Registered.InvokeRoutedRPC(sender, "RunicAutomation_SnapshotAck_v2", new object[1] { Pack(id, check) });
				}
			}
			catch (Exception ex)
			{
				MutationGate.Report("snapshot check deferred: " + ex.Message);
			}
		}

		private static void ReceiveReply(long sender, ZPackage package)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer())
				{
					ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
					if (serverPeer != null && serverPeer.m_uid == sender && Read(package, out var id, out var check) && Checks.TryGetValue(id, out var value) && !(value.Nonce != check.Nonce) && value.OwnerRevision == check.OwnerRevision && value.DataRevision == check.DataRevision && !(value.Digest != check.Digest))
					{
						value.AcceptedUntil = Time.realtimeSinceStartup + 3f;
					}
				}
			}
			catch (Exception ex)
			{
				MutationGate.Report("snapshot confirmation deferred: " + ex.Message);
			}
		}
	}
	internal static class SharedState
	{
		private const string Key = "Runic.InventorySafety.Embedded.v1";

		internal static readonly object Sync = string.Intern("Runic.InventorySafety.Embedded.v1");

		internal static bool InventoryOperationsActive
		{
			get
			{
				if (TryGet<Dictionary<string, Delegate>>("operations.network.v2", out var value) && value.TryGetValue("ready", out var value2))
				{
					return ((Func<bool>)value2)();
				}
				return false;
			}
		}

		internal static bool TryGet<T>(string name, out T value) where T : class
		{
			lock (Sync)
			{
				value = null;
				if (!(AppDomain.CurrentDomain.GetData("Runic.InventorySafety.Embedded.v1") is Dictionary<string, object> dictionary) || !dictionary.TryGetValue(name, out var value2))
				{
					return false;
				}
				value = value2 as T;
				return value != null;
			}
		}

		internal static T Get<T>(string name, Func<T> create) where T : class
		{
			lock (Sync)
			{
				Dictionary<string, object> dictionary = AppDomain.CurrentDomain.GetData("Runic.InventorySafety.Embedded.v1") as Dictionary<string, object>;
				if (dictionary == null)
				{
					dictionary = new Dictionary<string, object>();
					AppDomain.CurrentDomain.SetData("Runic.InventorySafety.Embedded.v1", dictionary);
				}
				if (!dictionary.TryGetValue(name, out var value))
				{
					value = (dictionary[name] = create());
				}
				return (T)value;
			}
		}
	}
}
namespace RunicAutomation.Operations
{
	internal static class CharacterCheckpointPresentation
	{
		private sealed class Scope : IDisposable
		{
			private Action close;

			internal Scope(Action close)
			{
				this.close = close;
			}

			public void Dispose()
			{
				Action action = close;
				close = null;
				action?.Invoke();
			}
		}

		private const string VaultHarmonyId = "chazman.RunicInventoryCheckpointPresentation.Vault";

		private static FieldInfo vaultPending;

		private static MethodInfo vaultUpload;

		private static readonly FieldInfo PlayerData = AccessTools.Field(typeof(PlayerProfile), "m_playerData");

		private static bool vaultAckValidated;

		private static readonly MethodInfo NativeSave = AccessTools.Method(typeof(Game), "SavePlayerProfile", new Type[2]
		{
			typeof(bool),
			typeof(bool)
		}, (Type[])null);

		private static Dictionary<string, Delegate> Routes => SharedState.Get("operations.checkpoint-presentation.v1", MakeRoutes);

		internal static bool Active => ((Func<bool>)Routes["active"])();

		internal static IDisposable Enter()
		{
			EnsureVaultCompatibility();
			return EnterScope(silent: true);
		}

		private static IDisposable EnterScope(bool silent)
		{
			return ((Func<bool, IDisposable>)Routes["enter"])(silent);
		}

		internal static void EnsureVaultCompatibility()
		{
			((Action)Routes["vault-install"])();
		}

		internal static void RecordVaultUpload(object service, string before, string after, bool silent)
		{
			((Action<object, string, string, bool>)Routes["vault-record"])(service, before, after, silent);
		}

		internal static void ClearVaultOrigin(object service)
		{
			((Action<object>)Routes["vault-clear"])(service);
		}

		internal static bool IsVaultFollowUpSilent(object service, string pending)
		{
			return ((Func<object, string, bool>)Routes["vault-silent"])(service, pending);
		}

		internal static void Verified(PlayerProfile profile, byte[] playerData)
		{
			try
			{
				((Action<PlayerProfile, byte[]>)Routes["verified"])(profile, playerData);
			}
			catch (Exception ex)
			{
				Warn("verified profile proof unavailable: " + ex.GetType().Name);
			}
		}

		private static Dictionary<string, Delegate> MakeRoutes()
		{
			ThreadLocal<LinkedList<bool>> scopes = new ThreadLocal<LinkedList<bool>>(() => new LinkedList<bool>());
			ConditionalWeakTable<object, StrongBox<string>> origins = new ConditionalWeakTable<object, StrongBox<string>>();
			ConditionalWeakTable<PlayerProfile, Tuple<string, int, long, string, byte[]>> verified = new ConditionalWeakTable<PlayerProfile, Tuple<string, int, long, string, byte[]>>();
			bool installed = false;
			bool installing = false;
			bool failed = false;
			StrongBox<string> value;
			return new Dictionary<string, Delegate>
			{
				["active"] = (Func<bool>)(() => scopes.Value.Last?.Value ?? false),
				["verified"] = (Action<PlayerProfile, byte[]>)delegate(PlayerProfile profile, byte[] data)
				{
					//IL_0022: Unknown result type (might be due to invalid IL or missing references)
					//IL_0043: Expected I4, but got Unknown
					if (profile != null)
					{
						verified.Remove(profile);
						if (data != null)
						{
							verified.Add(profile, Tuple.Create(profile.GetFilename(), (int)profile.m_fileSource, profile.GetPlayerID(), profile.GetName(), (byte[])data.Clone()));
						}
					}
				},
				["verified-data"] = (Func<PlayerProfile, byte[]>)((PlayerProfile profile) => (profile == null || !verified.TryGetValue(profile, out value) || !(value.Item1 == profile.GetFilename()) || value.Item2 != (int)profile.m_fileSource || value.Item3 != profile.GetPlayerID() || !(value.Item4 == profile.GetName())) ? null : value.Item5),
				["enter"] = (Func<bool, IDisposable>)delegate(bool silent)
				{
					LinkedList<bool> list = scopes.Value;
					LinkedListNode<bool> entry = list.AddLast(silent);
					return new Scope(delegate
					{
						if (entry.List != null)
						{
							list.Remove(entry);
						}
					});
				},
				["vault-record"] = (Action<object, string, string, bool>)delegate(object service, string before, string after, bool silent)
				{
					if (service != null)
					{
						bool flag = before != null && origins.TryGetValue(service, out value) && value.Value == before;
						origins.Remove(service);
						if (silent && after != null && (before == null || (before == after && flag)))
						{
							origins.Add(service, new StrongBox<string>(after));
						}
					}
				},
				["vault-clear"] = (Action<object>)delegate(object service)
				{
					if (service != null)
					{
						origins.Remove(service);
					}
				},
				["vault-silent"] = (Func<object, string, bool>)((object service, string pending) => service != null && pending != null && origins.TryGetValue(service, out value) && value.Value == pending),
				["vault-install"] = (Action)delegate
				{
					//IL_0093: Unknown result type (might be due to invalid IL or missing references)
					lock (SharedState.Sync)
					{
						if (!(installed || installing || failed))
						{
							Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => a.GetName().Name == "RunicCharacterVault");
							if (!(assembly == null))
							{
								installing = true;
								try
								{
									InstallVault(assembly.GetType("RunicCharacterVault.ProfileTransferService", throwOnError: true));
									installed = true;
									return;
								}
								catch (Exception ex)
								{
									failed = true;
									try
									{
										new Harmony("chazman.RunicInventoryCheckpointPresentation.Vault").UnpatchSelf();
									}
									catch
									{
									}
									Warn("optional Character Vault queued-save compatibility unavailable: " + ex.GetType().Name);
									return;
								}
								finally
								{
									installing = false;
								}
							}
						}
					}
				}
			};
		}

		private static void InstallVault(Type service)
		{
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Expected O, but got Unknown
			vaultPending = AccessTools.Field(service, "_pendingRequest");
			MethodInfo methodInfo = (vaultUpload = AccessTools.Method(service, "UploadSavedProfile", new Type[1] { typeof(PlayerProfile) }, (Type[])null));
			MethodInfo methodInfo2 = AccessTools.Method(service, "ReceiveSaveAck", new Type[2]
			{
				typeof(ZRpc),
				typeof(string)
			}, (Type[])null);
			MethodInfo[] array = new string[4] { "BeginFinalDisconnectSave", "ReceiveSaveRequest", "ResetClientState", "SaveManualClientProfile" }.Select((string name) => AccessTools.Method(service, name, (Type[])null, (Type[])null)).ToArray();
			if (vaultPending?.FieldType != typeof(string) || methodInfo == null || methodInfo2 == null || NativeSave == null || array.Any((MethodInfo method) => method == null))
			{
				throw new MissingMemberException("Character Vault save boundaries changed");
			}
			Harmony val = new Harmony("chazman.RunicInventoryCheckpointPresentation.Vault");
			vaultAckValidated = false;
			val.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, (HarmonyMethod)null, Patch("VaultAckTranspiler"), (HarmonyMethod)null, (HarmonyMethod)null);
			if (!vaultAckValidated)
			{
				throw new MissingMethodException("Character Vault queued-save call changed");
			}
			val.Patch((MethodBase)methodInfo, Patch("VaultUploadPrefix"), Patch("VaultUploadPostfix"), (HarmonyMethod)null, Patch("VaultUploadFinalizer"), (HarmonyMethod)null);
			MethodInfo[] array2 = array;
			foreach (MethodInfo methodInfo3 in array2)
			{
				val.Patch((MethodBase)methodInfo3, Patch("VaultVisiblePrefix"), (HarmonyMethod)null, (HarmonyMethod)null, Patch("VaultVisibleFinalizer"), (HarmonyMethod)null);
			}
			static HarmonyMethod Patch(string name)
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0018: Expected O, but got Unknown
				return new HarmonyMethod(AccessTools.Method(typeof(CharacterCheckpointPresentation), name, (Type[])null, (Type[])null));
			}
		}

		private static void Warn(string message)
		{
			try
			{
				Debug.LogWarning((object)("Runic checkpoint presentation: " + message));
			}
			catch
			{
			}
		}

		private static string Pending(object service)
		{
			return (string)vaultPending.GetValue(service);
		}

		private static void VaultUploadPrefix(object __instance, out Tuple<string, bool> __state)
		{
			__state = null;
			try
			{
				__state = Tuple.Create(Pending(__instance), Active);
			}
			catch (Exception ex)
			{
				ClearVaultOrigin(__instance);
				Warn("upload origin capture failed: " + ex.GetType().Name);
			}
		}

		private static void VaultUploadPostfix(object __instance, Tuple<string, bool> __state)
		{
			try
			{
				if (__state == null)
				{
					ClearVaultOrigin(__instance);
				}
				else
				{
					RecordVaultUpload(__instance, __state.Item1, Pending(__instance), __state.Item2);
				}
			}
			catch (Exception ex)
			{
				ClearVaultOrigin(__instance);
				Warn("upload origin update failed: " + ex.GetType().Name);
			}
		}

		private static Exception VaultUploadFinalizer(object __instance, Exception __exception)
		{
			if (__exception != null)
			{
				ClearVaultOrigin(__instance);
			}
			return __exception;
		}

		private static void VaultVisiblePrefix(object __instance, out IDisposable __state)
		{
			__state = null;
			try
			{
				ClearVaultOrigin(__instance);
				__state = EnterScope(silent: false);
			}
			catch (Exception ex)
			{
				Warn("visible save scope failed: " + ex.GetType().Name);
			}
		}

		private static Exception VaultVisibleFinalizer(IDisposable __state, Exception __exception)
		{
			__state?.Dispose();
			return __exception;
		}

		private static IEnumerable<CodeInstruction> VaultAckTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected O, but got Unknown
			List<CodeInstruction> list = instructions.ToList();
			if (list.Count((CodeInstruction instruction) => CodeInstructionExtensions.Calls(instruction, NativeSave)) != 1)
			{
				return list;
			}
			List<CodeInstruction> list2 = new List<CodeInstruction>(list.Count + 1);
			foreach (CodeInstruction item in list)
			{
				if (CodeInstructionExtensions.Calls(item, NativeSave))
				{
					item.opcode = OpCodes.Ldarg_0;
					item.operand = null;
					list2.Add(item);
					list2.Add(new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(CharacterCheckpointPresentation), "SaveVaultFollowUp", (Type[])null, (Type[])null)));
				}
				else
				{
					list2.Add(item);
				}
			}
			vaultAckValidated = true;
			return list2;
		}

		private static void SaveVaultFollowUp(Game game, bool setLogoutPoint, bool saveEvent, object service)
		{
			bool flag = false;
			try
			{
				flag = IsVaultFollowUpSilent(service, Pending(service));
			}
			catch (Exception ex)
			{
				ClearVaultOrigin(service);
				Warn("queued save origin lookup failed: " + ex.GetType().Name);
			}
			using (EnterScope(flag))
			{
				PlayerProfile val = ((game != null) ? game.GetPlayerProfile() : null);
				if (flag && vaultUpload != null && ReusableVerifiedProfile(val))
				{
					try
					{
						vaultUpload.Invoke(service, new object[1] { val });
						return;
					}
					catch (TargetInvocationException ex2)
					{
						ExceptionDispatchInfo.Capture(ex2.InnerException ?? ex2).Throw();
						return;
					}
				}
				game.SavePlayerProfile(setLogoutPoint, saveEvent);
			}
		}

		private static bool ReusableVerifiedProfile(PlayerProfile profile)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			try
			{
				byte[] array = ((Func<PlayerProfile, byte[]>)Routes["verified-data"])(profile);
				if (array == null || !OwnerOperationQueue.Same(array, (byte[])PlayerData.GetValue(profile)))
				{
					return false;
				}
				PlayerProfile val = new PlayerProfile(profile.GetFilename(), profile.m_fileSource);
				return val.Load() && val.GetPlayerID() == profile.GetPlayerID() && val.GetName() == profile.GetName() && OwnerOperationQueue.Same(array, (byte[])PlayerData.GetValue(val));
			}
			catch
			{
				return false;
			}
		}
	}
	[HarmonyPatch(typeof(Hud), "ProfileSaveStarted")]
	internal static class SilentCharacterCheckpointHudPatch
	{
		private static bool Prefix()
		{
			return !CharacterCheckpointPresentation.Active;
		}
	}
	internal static class CharacterOperationCheckpoint
	{
		private static readonly FieldInfo PlayerData = AccessTools.Field(typeof(PlayerProfile), "m_playerData");

		private const string ReceiptKey = "runic.operations.character.v2";

		internal static string Receipt(Player player)
		{
			if (!player.m_customData.TryGetValue("runic.operations.character.v2", out var value))
			{
				return "";
			}
			return value;
		}

		internal static void SetReceipt(Player player, string value)
		{
			if (value == null || value.Length > 2097152)
			{
				throw new InvalidDataException("Invalid character operation receipt");
			}
			if (value.Length == 0)
			{
				player.m_customData.Remove("runic.operations.character.v2");
			}
			else
			{
				player.m_customData["runic.operations.character.v2"] = value;
			}
		}

		internal static bool SaveAndVerify(Player player, out string error)
		{
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Expected O, but got Unknown
			error = "";
			try
			{
				if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || (Object)(object)Game.instance == (Object)null || SaveSystem.HasSessionFlag((SaveSystemSessionFlags)1))
				{
					error = "character-save-unavailable";
					return false;
				}
				PlayerProfile playerProfile = Game.instance.GetPlayerProfile();
				if (playerProfile == null || string.IsNullOrEmpty(playerProfile.GetFilename()))
				{
					error = "character-profile-unavailable";
					return false;
				}
				playerProfile.SavePlayerData(player);
				byte[] array = (byte[])PlayerData.GetValue(playerProfile);
				bool flag;
				using (CharacterCheckpointPresentation.Enter())
				{
					flag = array != null && playerProfile.Save();
				}
				if (!flag)
				{
					error = "character-save-failed";
					return false;
				}
				PlayerProfile val = new PlayerProfile(playerProfile.GetFilename(), playerProfile.m_fileSource);
				if (!val.Load() || !OwnerOperationQueue.Same(array, (byte[])PlayerData.GetValue(val)))
				{
					error = "character-save-readback-mismatch";
					return false;
				}
				CharacterCheckpointPresentation.Verified(playerProfile, array);
				return true;
			}
			catch (Exception ex)
			{
				error = "character-save-error:" + ex.GetType().Name;
				return false;
			}
		}
	}
	internal static class CharacterOperationEscrow
	{
		private enum Stage
		{
			Prepared,
			Returning,
			Applied
		}

		private sealed class Receipt
		{
			internal string World;

			internal string Id;

			internal string Kind;

			internal string Reason;

			internal byte[] Request;

			internal byte[] Escrow;

			internal byte[] Delivery;

			internal Vector2i[] Origins;

			internal Stage Stage;

			internal bool Accepted;

			internal bool Processed;

			internal string Encode()
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Expected O, but got Unknown
				//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
				ZPackage val = new ZPackage();
				val.Write(4);
				val.Write(World);
				val.Write(Id);
				val.Write(Kind);
				val.Write((int)Stage);
				val.Write(Accepted);
				val.Write(Reason ?? "");
				val.Write(Processed);
				val.Write(Request);
				val.Write(Escrow);
				val.Write(Delivery ?? Array.Empty<byte>());
				Vector2i[] origins = Origins;
				val.Write((origins != null) ? origins.Length : (-1));
				if (Origins != null)
				{
					Vector2i[] origins2 = Origins;
					foreach (Vector2i val2 in origins2)
					{
						val.Write(val2.x);
						val.Write(val2.y);
					}
				}
				return Convert.ToBase64String(val.GetArray());
			}

			internal static Receipt Decode(string value)
			{
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0024: Expected O, but got Unknown
				//IL_0129: Unknown result type (might be due to invalid IL or missing references)
				//IL_0133: Unknown result type (might be due to invalid IL or missing references)
				//IL_013d: Unknown result type (might be due to invalid IL or missing references)
				//IL_014b: Unknown result type (might be due to invalid IL or missing references)
				//IL_015b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0177: Unknown result type (might be due to invalid IL or missing references)
				//IL_0179: Unknown result type (might be due to invalid IL or missing references)
				if (value.Length > 2097152)
				{
					throw new InvalidDataException("Character receipt too large");
				}
				ZPackage val = new ZPackage(Convert.FromBase64String(value));
				int num = val.ReadInt();
				if (num < 2 || num > 4)
				{
					throw new InvalidDataException("Unsupported character receipt");
				}
				Receipt receipt = new Receipt
				{
					World = val.ReadString(),
					Id = val.ReadString(),
					Kind = val.ReadString(),
					Stage = (Stage)val.ReadInt(),
					Accepted = val.ReadBool(),
					Reason = val.ReadString(),
					Processed = (num >= 3 && val.ReadBool()),
					Request = OperationPacket.Bytes(val),
					Escrow = OperationPacket.Bytes(val),
					Delivery = OperationPacket.Bytes(val)
				};
				if (num >= 4)
				{
					int num2 = val.ReadInt();
					if (num2 < -1 || num2 > 65535)
					{
						throw new InvalidDataException("Invalid character origin count");
					}
					if (num2 >= 0)
					{
						receipt.Origins = (Vector2i[])(object)new Vector2i[num2];
						HashSet<Vector2i> hashSet = new HashSet<Vector2i>();
						Vector2i val2 = default(Vector2i);
						for (int i = 0; i < num2; i++)
						{
							((Vector2i)(ref val2))..ctor(val.ReadInt(), val.ReadInt());
							if (val2.x < 0 || val2.y < 0 || val2.x > 255 || val2.y > 255 || !hashSet.Add(val2))
							{
								throw new InvalidDataException("Invalid character origin slot");
							}
							receipt.Origins[i] = val2;
						}
					}
				}
				OperationPacket.End(val);
				if (!Enum.IsDefined(typeof(Stage), receipt.Stage) || !Guid.TryParseExact(receipt.Id, "N", out var _) || receipt.World.Length > 128 || receipt.Kind.Length > 128 || receipt.Reason.Length > 4096 || receipt.Request.Length > 65536 || receipt.Escrow.Length > 65536 || receipt.Delivery.Length > 65536)
				{
					throw new InvalidDataException("Invalid character receipt");
				}
				return receipt;
			}
		}

		private sealed class Session
		{
			private const string PendingKey = "runic.operations.deliveries.v1";

			private Player player;

			private Receipt receipt;

			private Receipt lastCompleted;

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

			private readonly Dictionary<string, Action<bool, string>> pendingCallbacks = new Dictionary<string, Action<bool, string>>();

			private string status = "";

			private bool finished;

			private bool corrupt;

			private float retry;

			private Action<bool, string> completed;

			private readonly Dictionary<string, Func<Player, byte[], Inventory, bool>> processors = new Dictionary<string, Func<Player, byte[], Inventory, bool>>(StringComparer.Ordinal);

			internal Dictionary<string, Delegate> Routes()
			{
				return new Dictionary<string, Delegate>
				{
					["busy"] = (Func<bool>)delegate
					{
						Load();
						return corrupt || (receipt != null && !finished);
					},
					["status"] = (Func<string>)(() => status),
					["completed-counts"] = new Func<Tuple<int, int>>(CompletedItemCounts),
					["tick"] = new Action(Tick),
					["wake-delivery"] = (Func<string, byte[], bool>)delegate(string kind, byte[] request)
					{
						Load();
						if ((Object)(object)player == (Object)null || receipt == null || corrupt || finished || receipt.Stage != Stage.Returning || receipt.Processed || receipt.Kind != kind || request == null || !OwnerOperationQueue.Same(receipt.Request, request))
						{
							return false;
						}
						retry = 0f;
						return true;
					},
					["clear"] = (Action)delegate
					{
						player = null;
						receipt = null;
						lastCompleted = null;
						pending.Clear();
						pendingCallbacks.Clear();
						completed = null;
						finished = false;
						corrupt = false;
						retry = 0f;
						status = "";
					},
					["begin"] = new Func<string, Func<byte[], byte[]>, Tuple<ItemData, int>[], Action<bool, string>, bool>(Begin),
					["processor"] = (Action<string, Func<Player, byte[], Inventory, bool>>)delegate(string kind, Func<Player, byte[], Inventory, bool> process)
					{
						processors[kind] = process;
					}
				};
			}

			private Tuple<int, int> CompletedItemCounts()
			{
				try
				{
					Load();
					Receipt receipt = lastCompleted ?? (finished ? this.receipt : null);
					if ((Object)(object)player == (Object)null || corrupt || receipt == null || receipt.Stage != Stage.Applied)
					{
						return null;
					}
					return Tuple.Create(NativeInventoryCodec.Load(receipt.Escrow, 32, 32).GetAllItems().Sum((ItemData x) => x.m_stack), NativeInventoryCodec.Load(receipt.Delivery, 32, 32).GetAllItems().Sum((ItemData x) => x.m_stack));
				}
				catch
				{
					return null;
				}
			}

			private void Load()
			{
				//IL_009a: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a0: Expected O, but got Unknown
				if (player == Player.m_localPlayer)
				{
					return;
				}
				player = Player.m_localPlayer;
				receipt = null;
				lastCompleted = null;
				pending.Clear();
				pendingCallbacks.Clear();
				completed = null;
				finished = false;
				corrupt = false;
				retry = 0f;
				if ((Object)(object)player == (Object)null)
				{
					return;
				}
				string text = CharacterOperationCheckpoint.Receipt(player);
				try
				{
					if (player.m_customData.TryGetValue("runic.operations.deliveries.v1", out var value))
					{
						ZPackage val = new ZPackage(Convert.FromBase64String(value));
						if (val.ReadInt() != 1)
						{
							throw new InvalidDataException("Unsupported pending deliveries");
						}
						int num = val.ReadInt();
						if (num < 0 || num > 4096)
						{
							throw new InvalidDataException("Invalid pending delivery count");
						}
						for (int i = 0; i < num; i++)
						{
							Receipt held = Receipt.Decode(val.ReadString());
							if (held.Stage != Stage.Returning || pending.Any((Receipt p) => p.Id == held.Id))
							{
								throw new InvalidDataException("Invalid pending delivery");
							}
							pending.Add(held);
						}
						OperationPacket.End(val);
					}
					if (text.Length != 0)
					{
						receipt = Receipt.Decode(text);
						finished = receipt.Stage == Stage.Applied;
						status = (finished ? "" : "Recovering pending inventory operation");
					}
				}
				catch (Exception ex)
				{
					corrupt = true;
					status = "Character operation receipt needs recovery";
					MutationGate.Report(status + ": " + ex);
				}
			}

			private bool Begin(string kind, Func<byte[], byte[]> buildPayload, Tuple<ItemData, int>[] items, Action<bool, string> callback)
			{
				//IL_0068: Unknown result type (might be due to invalid IL or missing references)
				//IL_006e: Expected O, but got Unknown
				//IL_0153: Unknown result type (might be due to invalid IL or missing references)
				//IL_0166: Unknown result type (might be due to invalid IL or missing references)
				//IL_016b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0196: Unknown result type (might be due to invalid IL or missing references)
				//IL_019b: Unknown result type (might be due to invalid IL or missing references)
				Load();
				if ((Object)(object)player == (Object)null || ((Character)player).IsDead() || corrupt || (this.receipt != null && !finished) || !InventoryOperationNetwork.Ready || buildPayload == null || items == null || items.Length > 65535)
				{
					return false;
				}
				Inventory inventory = ((Humanoid)player).GetInventory();
				Inventory val = new Inventory("Runic pending transfer", (Sprite)null, 32, 32);
				HashSet<ItemData> hashSet = new HashSet<ItemData>();
				Vector2i[] array = (Vector2i[])(object)new Vector2i[items.Length];
				HashSet<Vector2i> hashSet2 = new HashSet<Vector2i>();
				for (int i = 0; i < items.Length; i++)
				{
					ItemData val2 = items[i]?.Item1;
					int num = items[i]?.Item2 ?? 0;
					if (val2 == null || val2.m_equipped || num < 1 || num > val2.m_stack || !inventory.ContainsItem(val2) || !hashSet.Add(val2))
					{
						return false;
					}
					if (val2.m_gridPos.x < 0 || val2.m_gridPos.y < 0 || val2.m_gridPos.x >= inventory.GetWidth() || val2.m_gridPos.y >= inventory.GetHeight() || val2.m_gridPos.x > 255 || val2.m_gridPos.y > 255 || !hashSet2.Add(val2.m_gridPos))
					{
						return false;
					}
					array[i] = val2.m_gridPos;
					ItemData val3 = val2.Clone();
					val3.m_stack = num;
					val3.m_equipped = false;
					val3.m_gridPos = new Vector2i(i % 32, i / 32);
					val.GetAllItems().Add(val3);
				}
				byte[] array2 = NativeInventoryCodec.Save(val);
				if (array2.Length > 65536)
				{
					return false;
				}
				byte[] array3 = buildPayload(array2);
				if (array3 == null || array3.Length > 65536)
				{
					return false;
				}
				Receipt receipt = new Receipt
				{
					World = ZNet.instance.GetWorldUID().ToString(CultureInfo.InvariantCulture),
					Id = Guid.NewGuid().ToString("N"),
					Kind = kind,
					Request = array3,
					Escrow = array2,
					Origins = array,
					Stage = Stage.Prepared,
					Reason = ""
				};
				string value = receipt.Encode();
				foreach (Tuple<ItemData, int> tuple in items)
				{
					ItemData item = tuple.Item1;
					item.m_stack -= tuple.Item2;
					if (tuple.Item1.m_stack == 0)
					{
						inventory.GetAllItems().Remove(tuple.Item1);
					}
				}
				this.receipt = receipt;
				completed = callback;
				finished = false;
				retry = 0f;
				CharacterOperationCheckpoint.SetReceipt(player, value);
				Notify(inventory);
				Tick();
				return true;
			}

			private void Tick()
			{
				Load();
				if ((Object)(object)player == (Object)null || corrupt || Time.realtimeSinceStartup < retry)
				{
					return;
				}
				if (((Character)player).IsDead())
				{
					status = "Pending inventory delivery will resume after respawn";
					return;
				}
				if (receipt == null || finished)
				{
					DrainPending();
					return;
				}
				retry = Time.realtimeSinceStartup + 2f;
				if (receipt.Stage == Stage.Applied)
				{
					Finish();
				}
				else if ((Object)(object)ZNet.instance == (Object)null || receipt.World != ZNet.instance.GetWorldUID().ToString(CultureInfo.InvariantCulture))
				{
					status = "Return to the original world to reconcile the pending transfer";
				}
				else if (receipt.Stage == Stage.Prepared)
				{
					status = "Waiting for inventory operation";
					string expectedId = receipt.Id;
					InventoryOperationNetwork.Send(receipt.World, receipt.Id, receipt.Kind, receipt.Request, delegate(int code, string reason, byte[] response)
					{
						Receive(expectedId, code, reason, response);
					});
				}
				else
				{
					Deliver();
				}
			}

			private void Receive(string id, int code, string reason, byte[] payload)
			{
				if ((Object)(object)player == (Object)null || receipt == null || receipt.Id != id || receipt.Stage != Stage.Prepared)
				{
					return;
				}
				if (code != 2 && code != 3)
				{
					status = "Inventory operation requires reconciliation: " + reason;
					corrupt = true;
					return;
				}
				byte[] array = ((code == 2) ? payload : receipt.Escrow);
				try
				{
					NativeInventoryCodec.Load(array, 32, 32);
				}
				catch (Exception ex)
				{
					status = "Inventory delivery codec is incompatible";
					MutationGate.Report(status + ": " + ex);
					return;
				}
				receipt.Accepted = code == 2;
				receipt.Reason = reason;
				receipt.Delivery = array;
				receipt.Stage = Stage.Returning;
				CharacterOperationCheckpoint.SetReceipt(player, receipt.Encode());
				retry = 0f;
			}

			private void Deliver()
			{
				Inventory val = NativeInventoryCodec.Load(receipt.Delivery, 32, 32);
				Inventory inventory = ((Humanoid)player).GetInventory();
				bool flag = false;
				if (receipt.Accepted && !receipt.Processed && processors.TryGetValue(receipt.Kind, out var value))
				{
					NativeInventoryMemoryCheckpoint nativeInventoryMemoryCheckpoint = new NativeInventoryMemoryCheckpoint(inventory);
					Action onChanged = inventory.m_onChanged;
					inventory.m_onChanged = null;
					try
					{
						if (!value(player, receipt.Request, val))
						{
							nativeInventoryMemoryCheckpoint.Restore();
							status = "Waiting to complete the pending action";
							return;
						}
						receipt.Delivery = NativeInventoryCodec.Save(val);
						receipt.Processed = true;
						flag = true;
						CharacterOperationCheckpoint.SetReceipt(player, receipt.Encode());
					}
					catch (Exception ex)
					{
						try
						{
							nativeInventoryMemoryCheckpoint.Restore();
						}
						catch (Exception ex2)
						{
							MutationGate.Report("Action rollback failed: " + ex2);
						}
						corrupt = true;
						status = "Pending action requires reconciliation";
						MutationGate.Report(status + ": " + ex);
						return;
					}
					finally
					{
						inventory.m_onChanged = onChanged;
					}
				}
				if (!AddDelivery(NativeInventoryCodec.Clone(inventory), val, receipt))
				{
					ParkPending();
					if (flag)
					{
						Notify(inventory);
					}
					return;
				}
				NativeInventoryMemoryCheckpoint nativeInventoryMemoryCheckpoint2 = new NativeInventoryMemoryCheckpoint(inventory);
				Action onChanged2 = inventory.m_onChanged;
				inventory.m_onChanged = null;
				bool flag2 = false;
				try
				{
					if (!AddDelivery(inventory, val, receipt))
					{
						nativeInventoryMemoryCheckpoint2.Restore();
						flag2 = true;
					}
					else
					{
						receipt.Stage = Stage.Applied;
						CharacterOperationCheckpoint.SetReceipt(player, receipt.Encode());
					}
				}
				catch (Exception ex3)
				{
					try
					{
						nativeInventoryMemoryCheckpoint2.Restore();
					}
					catch (Exception ex4)
					{
						MutationGate.Report("Inventory delivery rollback failed: " + ex4);
					}
					corrupt = true;
					status = "Inventory delivery requires reconciliation";
					MutationGate.Report(status + ": " + ex3);
					return;
				}
				finally
				{
					inventory.m_onChanged = onChanged2;
				}
				if (flag2)
				{
					ParkPending();
					if (flag)
					{
						Notify(inventory);
					}
				}
				else
				{
					Notify(inventory);
					retry = 0f;
					Finish();
				}
			}

			private void ParkPending()
			{
				pending.Add(receipt);
				if (completed != null)
				{
					Receipt held = receipt;
					byte[] originalDelivery = receipt.Delivery;
					Action<bool, string> callback = completed;
					pendingCallbacks[receipt.Id] = delegate(bool accepted, string reason)
					{
						held.Delivery = originalDelivery;
						callback(accepted, reason);
					};
				}
				PersistPending();
				CharacterOperationCheckpoint.SetReceipt(player, "");
				receipt = null;
				completed = null;
				finished = false;
				retry = 0f;
				status = "Items are waiting for inventory space. You can deposit other items to make room.";
				((Character)player).Message((MessageType)1, status, 0, (Sprite)null, false);
			}

			private static bool AddDelivery(Inventory destination, Inventory delivery, Receipt pending)
			{
				//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e3: 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_00fd: Unknown result type (might be due to invalid IL or missing references)
				//IL_010f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0122: Unknown result type (might be due to invalid IL or missing references)
				//IL_0129: Unknown result type (might be due to invalid IL or missing references)
				//IL_016c: Unknown result type (might be due to invalid IL or missing references)
				//IL_016e: Unknown result type (might be due to invalid IL or missing references)
				if (pending.Origins == null || pending.Origins.Length == 0)
				{
					return NativeInventoryCodec.AddAll(destination, delivery);
				}
				Inventory val = NativeInventoryCodec.Load(pending.Escrow, 32, 32);
				Inventory val2 = NativeInventoryCodec.Clone(delivery);
				ItemData[] array = val2.GetAllItems().ToArray();
				foreach (ItemData val3 in array)
				{
					int num = val3.m_gridPos.y * 32 + val3.m_gridPos.x;
					if (val3.m_gridPos.x >= 32 || num < 0 || num >= pending.Origins.Length)
					{
						continue;
					}
					ItemData itemAt = val.GetItemAt(val3.m_gridPos.x, val3.m_gridPos.y);
					if (itemAt == null || NativeInventoryCodec.ItemIdentity(itemAt) != NativeInventoryCodec.ItemIdentity(val3))
					{
						continue;
					}
					Vector2i val4 = pending.Origins[num];
					if (val4.x < 0 || val4.y < 0 || val4.x >= destination.GetWidth() || val4.y >= destination.GetHeight())
					{
						continue;
					}
					ItemData itemAt2 = destination.GetItemAt(val4.x, val4.y);
					int num2 = Math.Min(val3.m_stack, itemAt.m_stack);
					if (itemAt2 == null)
					{
						ItemData val5 = val3.Clone();
						val5.m_stack = num2;
						val5.m_equipped = false;
						val5.m_gridPos = val4;
						destination.GetAllItems().Add(val5);
					}
					else
					{
						if (NativeInventoryCodec.ItemIdentity(itemAt2) != NativeInventoryCodec.ItemIdentity(val3))
						{
							continue;
						}
						num2 = Math.Min(num2, Math.Max(0, itemAt2.m_shared.m_maxStackSize - itemAt2.m_stack));
						if (num2 == 0)
						{
							continue;
						}
						itemAt2.m_stack += num2;
					}
					val3.m_stack -= num2;
					if (val3.m_stack == 0)
					{
						val2.GetAllItems().Remove(val3);
					}
				}
				return NativeInventoryCodec.AddAll(destination, val2);
			}

			private void Finish()
			{
				lastCompleted = receipt;
				finished = true;
				status = "";
				Action<bool, string> action = completed;
				completed = null;
				action?.Invoke(receipt.Accepted, receipt.Reason);
			}

			private void PersistPending()
			{
				//IL_0024: Unknown result type (might be due to invalid IL or missing references)
				//IL_002a: Expected O, but got Unknown
				if (pending.Count == 0)
				{
					player.m_customData.Remove("runic.operations.deliveries.v1");
					return;
				}
				ZPackage val = new ZPackage();
				val.Write(1);
				val.Write(pending.Count);
				foreach (Receipt item in pending)
				{
					val.Write(item.Encode());
				}
				player.m_customData["runic.operations.deliveries.v1"] = Convert.ToBase64String(val.GetArray());
			}

			private void DrainPending()
			{
				retry = Time.realtimeSinceStartup + 0.1f;
				if (pending.Count == 0 || (Object)(object)ZNet.instance == (Object)null)
				{
					return;
				}
				string world = ZNet.instance.GetWorldUID().ToString(CultureInfo.InvariantCulture);
				Inventory inventory = ((Humanoid)player).GetInventory();
				bool flag = false;
				List<Action> list = new List<Action>();
				Action onChanged = inventory.m_onChanged;
				inventory.m_onChanged = null;
				try
				{
					Receipt[] array = pending.Where((Receipt p) => p.World == world).ToArray();
					foreach (Receipt held in array)
					{
						Inventory val = NativeInventoryCodec.Load(held.Delivery, 32, 32);
						bool flag2 = false;
						ItemData[] array2 = val.GetAllItems().ToArray();
						foreach (ItemData val2 in array2)
						{
							int num3 = Math.Min(val2.m_stack, NativeInventoryCodec.CapacityFor(inventory, val2));
							if (num3 == 0)
							{
								continue;
							}
							NativeInventoryMemoryCheckpoint nativeInventoryMemoryCheckpoint = new NativeInventoryMemoryCheckpoint(inventory);
							ItemData val3 = val2.Clone();
							val3.m_stack = num3;
							val3.m_equipped = false;
							try
							{
								if (!NativeInventoryCodec.AddExact(inventory, val3))
								{
									nativeInventoryMemoryCheckpoint.Restore();
									continue;
								}
							}
							catch
							{
								nativeInventoryMemoryCheckpoint.Restore();
								throw;
							}
							val2.m_stack -= num3;
							if (val2.m_stack == 0)
							{
								val.GetAllItems().Remove(val2);
							}
							held.Delivery = NativeInventoryCodec.Save(val);
							PersistPending();
							flag2 = (flag = true);
						}
						if (!flag2 && val.NrOfItems() != 0)
						{
							continue;
						}
						if (val.NrOfItems() == 0)
						{
							pending.Remove(held);
							held.Stage = Stage.Applied;
							if (pendingCallbacks.TryGetValue(held.Id, out var callback))
							{
								pendingCallbacks.Remove(held.Id);
								list.Add(delegate
								{
									lastCompleted = held;
									callback(held.Accepted, held.Reason);
								});
							}
						}
						else
						{
							held.Delivery = NativeInventoryCodec.Save(val);
						}
						PersistPending();
					}
				}
				finally
				{
					inventory.m_onChanged = onChanged;
				}
				if (flag)
				{
					Notify(inventory);
				}
				if (pending.Count == 0)
				{
					status = "";
				}
				foreach (Action item in list)
				{
					item();
				}
			}

			private static void Notify(Inventory inventory)
			{
				try
				{
					AccessTools.Method(typeof(Inventory), "Changed", new Type[2]
					{
						typeof(bool),
						typeof(bool)
					}, (Type[])null).Invoke(inventory, new object[2] { false, false });
				}
				catch (Exception ex)
				{
					MutationGate.Report("inventory callback after operation: " + ex.GetType().Name);
				}
			}
		}

		private static Dictionary<string, Delegate> Routes => SharedState.Get("operations.character.v2", () => new Session().Routes());

		internal static bool Busy => ((Func<bool>)Routes["busy"])();

		internal static string Status => ((Func<string>)Routes["status"])();

		internal static Tuple<int, int> CompletedItemCounts => ((Func<Tuple<int, int>>)Routes["completed-counts"])();

		internal static void Tick()
		{
			((Action)Routes["tick"])();
		}

		internal static bool WakeDelivery(string kind, byte[] request)
		{
			return ((Func<string, byte[], bool>)Routes["wake-delivery"])(kind, request);
		}

		internal static void Clear()
		{
			((Action)Routes["clear"])();
		}

		internal static void RegisterDeliveryProcessor(string kind, Func<Player, byte[], Inventory, bool> process)
		{
			((Action<string, Func<Player, byte[], Inventory, bool>>)Routes["processor"])(kind, process);
		}

		internal static bool Begin(string kind, Func<byte[], byte[]> payload, Tuple<ItemData, int>[] items, Action<bool, string> completed)
		{
			return ((Func<string, Func<byte[], byte[]>, Tuple<ItemData, int>[], Action<bool, string>, bool>)Routes["begin"])(kind, payload, items, completed);
		}
	}
	internal sealed class FileOperationEndpoints : IOperationEndpoints
	{
		private readonly string directory;

		private readonly string world;

		private readonly Dictionary<string, byte[]> cache = new Dictionary<string, byte[]>();

		internal Action<string, byte[]> Changed = delegate
		{
		};

		internal Func<string, bool> NativeEndpoint = (string _) => false;

		internal Func<string, byte[]> NativeRead = (string _) => (byte[])null;

		public FileOperationEndpoints(string directory, string world)
		{
			this.directory = Path.GetFullPath(directory);
			this.world = world ?? throw new ArgumentNullException("world");
			Directory.CreateDirectory(this.directory);
		}

		public byte[] Import(string endpoint, byte[] inventory)
		{
			byte[] array = Read(endpoint);
			if (array != null)
			{
				return array;
			}
			byte[] array2 = Image(world, endpoint, 0L, inventory);
			FileOperationJournal.DurableReplace(PathFor(endpoint), array2);
			cache[endpoint] = array2;
			return (byte[])array2.Clone();
		}

		public byte[] Read(string endpoint)
		{
			if (NativeEndpoint(endpoint))
			{
				return NativeRead(endpoint);
			}
			return ReadStored(endpoint);
		}

		internal byte[] ReadStored(string endpoint)
		{
			if (cache.TryGetValue(endpoint, out var value))
			{
				return (byte[])value.Clone();
			}
			string text = PathFor(endpoint);
			if (!File.Exists(text))
			{
				return null;
			}
			if (new FileInfo(text).Length > 4194304)
			{
				throw new InvalidDataException("Endpoint too large");
			}
			byte[] array = File.ReadAllBytes(text);
			Parse(array, out var text2, out var endpoint2, out var _, out var _);
			if (world != text2 || endpoint != endpoint2)
			{
				throw new InvalidDataException("Endpoint identity mismatch");
			}
			cache[endpoint] = array;
			return (byte[])array.Clone();
		}

		public bool CompareExchange(string endpoint, byte[] expected, byte[] replacement)
		{
			if (!OwnerOperationQueue.Same(Read(endpoint), expected))
			{
				return false;
			}
			Parse(expected, out var text, out var endpoint2, out var revision, out var inventory);
			Parse(replacement, out var text2, out var endpoint3, out var revision2, out inventory);
			if (text != world || text2 != world || endpoint2 != endpoint || endpoint3 != endpoint || revision == long.MaxValue || revision2 != revision + 1)
			{
				throw new InvalidDataException("Invalid endpoint revision or identity");
			}
			FileOperationJournal.DurableReplace(PathFor(endpoint), replacement);
			cache[endpoint] = (byte[])replacement.Clone();
			Changed(endpoint, (byte[])replacement.Clone());
			return true;
		}

		internal Tuple<string, byte[]>[] Enumerate(string prefix)
		{
			List<Tuple<string, byte[]>> list = new List<Tuple<string, byte[]>>();
			foreach (string item in Directory.EnumerateFiles(directory, "*.inventory"))
			{
				if (new FileInfo(item).Length > 4194304)
				{
					throw new InvalidDataException("Endpoint too large");
				}
				byte[] array = File.ReadAllBytes(item);
				Parse(array, out var text, out var endpoint, out var _, out var _);
				if (text != world || !string.Equals(item, PathFor(endpoint), StringComparison.OrdinalIgnoreCase))
				{
					throw new InvalidDataException("Endpoint identity mismatch");
				}
				if (endpoint.StartsWith(prefix, StringComparison.Ordinal))
				{
					cache[endpoint] = array;
					list.Add(Tuple.Create(endpoint, (byte[])array.Clone()));
				}
			}
			return list.ToArray();
		}

		public static byte[] Next(byte[] image, byte[] inventory)
		{
			Parse(image, out var text, out var endpoint, out var revision, out var _);
			return Image(text, endpoint, checked(revision + 1), inventory);
		}

		internal static byte[] Transient(string world, string endpoint, long revision, byte[] inventory)
		{
			return Image(world, endpoint, revision, inventory);
		}

		public static byte[] Inventory(byte[] image)
		{
			Parse(image, out var _, out var _, out var _, out var inventory);
			return inventory;
		}

		public static long Revision(byte[] image)
		{
			Parse(image, out var _, out var _, out var revision, out var _);
			return revision;
		}

		private string PathFor(string endpoint)
		{
			if (string.IsNullOrWhiteSpace(endpoint) || endpoint.Length > 256)
			{
				throw new ArgumentException("Invalid endpoint");
			}
			return Path.Combine(directory, OperationRequest.Hash(world + "\n" + endpoint) + ".inventory");
		}

		private static byte[] Image(string world, string endpoint, long revision, byte[] inventory)
		{
			if (inventory == null || inventory.Length > 4190208)
			{
				throw new ArgumentException("Invalid inventory size");
			}
			using MemoryStream memoryStream = new MemoryStream();
			using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true))
			{
				binaryWriter.Write(1);
				binaryWriter.Write(world);
				binaryWriter.Write(endpoint);
				binaryWriter.Write(revision);
				binaryWriter.Write(inventory.Length);
				binaryWriter.Write(inventory);
			}
			using (SHA256 sHA = SHA256.Create())
			{
				byte[] array = sHA.ComputeHash(memoryStream.ToArray());
				memoryStream.Write(array, 0, array.Length);
			}
			return memoryStream.ToArray();
		}

		private static void Parse(byte[] image, out string world, out string endpoint, out long revision, out byte[] inventory)
		{
			if (image == null || image.Length < 48 || image.Length > 4194304)
			{
				throw new InvalidDataException("Invalid endpoint image");
			}
			using (SHA256 sHA = SHA256.Create())
			{
				if (!sHA.ComputeHash(image, 0, image.Length - 32).SequenceEqual(image.Skip(image.Length - 32)))
				{
					throw new InvalidDataException("Endpoint checksum mismatch");
				}
			}
			using MemoryStream memoryStream = new MemoryStream(image, 0, image.Length - 32, writable: false);
			using BinaryReader binaryReader = new BinaryReader(memoryStream, Encoding.UTF8);
			if (binaryReader.ReadInt32() != 1)
			{
				throw new InvalidDataException("Unsupported endpoint schema");
			}
			world = binaryReader.ReadString();
			endpoint = binaryReader.ReadString();
			revision = binaryReader.ReadInt64();
			if (world.Length > 256 || endpoint.Length > 256 || revision < 0)
			{
				throw new InvalidDataException("Invalid endpoint identity");
			}
			int num = binaryReader.ReadInt32();
			if (num < 0 || num != memoryStream.Length - memoryStream.Position)
			{
				throw new InvalidDataException("Invalid endpoint payload");
			}
			inventory = binaryReader.ReadBytes(num);
		}
	}
	internal sealed class FileOperationJournal : IOperationJournal, ICompactOperationJournal, IDisposable
	{
		private const int Schema = 2;

		private const int MaximumFile = 67108864;

		private readonly string directory;

		private readonly string world;

		private readonly FileStream ownership;

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

		public FileOperationJournal(string directory, string world)
		{
			this.directory = Path.GetFullPath(directory);
			this.world = world;
			Directory.CreateDirectory(this.directory);
			ownership = new FileStream(Path.Combine(this.directory, "authority.lock"), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
			try
			{
				foreach (string item in Directory.EnumerateFiles(this.directory, "*.operation"))
				{
					OperationRecord operationRecord = Decode(ReadBounded(item));
					if (operationRecord.Request.World != world || Path.GetFileNameWithoutExtension(item) != operationRecord.Request.Key)
					{
						throw new InvalidDataException("Operation journal identity mismatch");
					}
					records.Add(operationRecord.Request.Key, operationRecord);
				}
			}
			catch
			{
				ownership.Dispose();
				throw;
			}
		}

		public OperationRecord Find(string key)
		{
			if (!records.TryGetValue(key, out var value))
			{
				return null;
			}
			return value;
		}

		public IEnumerable<OperationRecord> Pending()
		{
			return records.Values.Where((OperationRecord x) => x.Result.Status == OperationStatus.RecoveryPending).ToArray();
		}

		public void ForgetTerminal(string key)
		{
			if (records.TryGetValue(key, out var value))
			{
				if (value.Result.Status == OperationStatus.RecoveryPending)
				{
					throw new InvalidOperationException("Cannot discard a recovery record");
				}
				File.Delete(Path.Combine(directory, key + ".operation"));
				records.Remove(key);
			}
		}

		public void Save(OperationRecord record)
		{
			if (record.Request.World != world)
			{
				throw new InvalidOperationException("Wrong world");
			}
			if (records.TryGetValue(record.Request.Key, out var value))
			{
				if (value.Request.Fingerprint != record.Request.Fingerprint)
				{
					throw new InvalidOperationException("Reused request id");
				}
				if (value.Result.Status != OperationStatus.RecoveryPending)
				{
					if (!OwnerOperationQueue.Same(Encode(value), Encode(record)))
					{
						throw new InvalidOperationException("Terminal receipt is immutable");
					}
					return;
				}
			}
			DurableReplace(Path.Combine(directory, record.Request.Key + ".operation"), Encode(record));
			records[record.Request.Key] = record;
		}

		internal static void DurableReplace(string destination, byte[] data)
		{
			string text = destination + "." + Guid.NewGuid().ToString("N") + ".pending";
			try
			{
				using (FileStream fileStream = new FileStream(text, FileMode.CreateNew, FileAccess.Write, FileShare.None, 4096, FileOptions.None))
				{
					fileStream.Write(data, 0, data.Length);
				}
				if (File.Exists(destination))
				{
					File.Replace(text, destination, null);
				}
				else
				{
					File.Move(text, destination);
				}
			}
			finally
			{
				if (File.Exists(text))
				{
					File.Delete(text);
				}
			}
		}

		private static byte[] ReadBounded(string path)
		{
			if (new FileInfo(path).Length > 67108864)
			{
				throw new InvalidDataException("Journal record too large");
			}
			return File.ReadAllBytes(path);
		}

		private static byte[] Encode(OperationRecord record)
		{
			using MemoryStream memoryStream = new MemoryStream();
			using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true))
			{
				binaryWriter.Write(2);
				binaryWriter.Write(record.Plan.Native);
				binaryWriter.Write(record.Request.World);
				binaryWriter.Write(record.Request.Actor);
				binaryWriter.Write(record.Request.Id);
				binaryWriter.Write(record.Request.Kind);
				Write(binaryWriter, record.Request.Payload);
				binaryWriter.Write((int)record.Result.Status);
				binaryWriter.Write(record.Result.Reason);
				Write(binaryWriter, record.Result.Payload);
				Write(binaryWriter, record.Plan.Response);
				binaryWriter.Write(record.Plan.Changes.Count);
				foreach (EndpointChange change in record.Plan.Changes)
				{
					binaryWriter.Write(change.Endpoint);
					Write(binaryWriter, change.Before);
					Write(binaryWriter, change.After);
				}
			}
			if (memoryStream.Length > 67108832)
			{
				throw new InvalidDataException("Journal record too large");
			}
			using (SHA256 sHA = SHA256.Create())
			{
				byte[] array = sHA.ComputeHash(memoryStream.ToArray());
				memoryStream.Write(array, 0, array.Length);
			}
			return memoryStream.ToArray();
		}

		private static OperationRecord Decode(byte[] data)
		{
			if (data.Length < 36)
			{
				throw new InvalidDataException("Truncated journal");
			}
			using (SHA256 sHA = SHA256.Create())
			{
				if (!sHA.ComputeHash(data, 0, data.Length - 32).SequenceEqual(data.Skip(data.Length - 32)))
				{
					throw new InvalidDataException("Journal checksum mismatch");
				}
			}
			using MemoryStream memoryStream = new MemoryStream(data, 0, data.Length - 32, writable: false);
			using BinaryReader binaryReader = new BinaryReader(memoryStream, Encoding.UTF8);
			bool native = binaryReader.ReadInt32() switch
			{
				2 => binaryReader.ReadBoolean(), 
				1 => false, 
				_ => throw new InvalidDataException("Unsupported journal schema"), 
			};
			OperationRequest request = new OperationRequest(binaryReader.ReadString(), binaryReader.ReadString(), binaryReader.ReadString(), binaryReader.ReadString(), Read(binaryReader, 65536));
			OperationStatus operationStatus = (OperationStatus)binaryReader.ReadInt32();
			if (operationStatus != OperationStatus.Committed && operationStatus != OperationStatus.Denied && operationStatus != OperationStatus.RecoveryPending)
			{
				throw new InvalidDataException("Invalid receipt status");
			}
			string text = binaryReader.ReadString();
			if (text.Length > 4096)
			{
				throw new InvalidDataException("Invalid receipt reason");
			}
			OperationResult result = new OperationResult(operationStatus, text, Read(binaryReader, 65536));
			byte[] response = Read(binaryReader, 65536);
			int num = binaryReader.ReadInt32();
			if (num < 0 || num > 128)
			{
				throw new InvalidDataException("Invalid endpoint count");
			}
			List<EndpointChange> list = new List<EndpointChange>();
			for (int i = 0; i < num; i++)
			{
				list.Add(new EndpointChange(binaryReader.ReadString(), Read(binaryReader, 4194304), Read(binaryReader, 4194304)));
			}
			if (memoryStream.Position != memoryStream.Length)
			{
				throw new InvalidDataException("Trailing journal data");
			}
			return new OperationRecord(request, new OperationPlan(list, response, native), result);
		}

		private static void Write(BinaryWriter writer, byte[] bytes)
		{
			writer.Write(bytes.Length);
			writer.Write(bytes);
		}

		private static byte[] Read(BinaryReader reader, int maximum)
		{
			int num = reader.ReadInt32();
			if (num < 0 || num > maximum)
			{
				throw new InvalidDataException("Invalid payload length");
			}
			byte[] array = reader.ReadBytes(num);
			if (array.Length != num)
			{
				throw new EndOfStreamException();
			}
			return array;
		}

		public void Dispose()
		{
			ownership.Dispose();
		}
	}
	internal static class InventoryMetadataComparison
	{
		internal static bool SameItem(ItemData left, ItemData right)
		{
			if (left != null && right != null)
			{
				return ((ReadOnlySpan<byte>)ForItem(left)).SequenceEqual((ReadOnlySpan<byte>)ForItem(right));
			}
			return false;
		}

		internal static byte[] ForItem(ItemData item)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			GameObject dropPrefab = item.m_dropPrefab;
			WriteItem(val, (dropPrefab != null) ? StringExtensionMethods.GetStableHashCode(((Object)dropPrefab).name) : 0, item, includeSlot: false);
			return val.GetArray();
		}

		internal static byte[] ForInventory(Inventory inventory)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(109);
			val.Write(inventory.GetAllItems().Count);
			foreach (ItemData item in from i in inventory.GetAllItems()
				orderby i.m_gridPos.y, i.m_gridPos.x
				select i)
			{
				GameObject dropPrefab = item.m_dropPrefab;
				WriteItem(val, (dropPrefab != null) ? StringExtensionMethods.GetStableHashCode(((Object)dropPrefab).name) : 0, item, includeSlot: true);
			}
			val.Write(0);
			return val.GetArray();
		}

		internal static byte[] ForPayload(byte[] bytes)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			ZPackage val = new ZPackage(bytes);
			int num = val.ReadInt();
			if (num != 109)
			{
				return (byte[])bytes.Clone();
			}
			int num2 = val.ReadUShort();
			List<(int, ItemData)> list = new List<(int, ItemData)>();
			for (int i = 0; i < num2; i++)
			{
				list.Add(ItemData.Load(val, (Item)num));
			}
			ZPackage val2 = new ZPackage();
			val2.Write(num);
			val2.Write(num2);
			foreach (var item in from tuple in list
				orderby tuple.itemData.m_gridPos.y, tuple.itemData.m_gridPos.x
				select tuple)
			{
				WriteItem(val2, item.Item1, item.Item2, includeSlot: true);
			}
			val2.Write(bytes.Length - val.GetPos());
			for (int num3 = val.GetPos(); num3 < bytes.Length; num3++)
			{
				val2.Write(bytes[num3]);
			}
			return val2.GetArray();
		}

		private static void WriteItem(ZPackage key, int prefab, ItemData item, bool includeSlot)
		{
			key.Write(prefab);
			if (includeSlot)
			{
				key.Write(item.m_gridPos.x);
				key.Write(item.m_gridPos.y);
				key.Write(item.m_stack);
			}
			key.Write(item.m_quality);
			key.Write(item.m_variant);
			key.Write(item.m_worldLevel);
			key.Write(item.m_crafterID);
			key.Write((item.m_crafterID == 0L) ? "" : (item.m_crafterName ?? ""));
			key.Write(item.m_cheated);
			key.Write(item.m_customData?.Count ?? 0);
			if (item.m_customData == null)
			{
				return;
			}
			foreach (KeyValuePair<string, string> item2 in item.m_customData.OrderBy<KeyValuePair<string, string>, string>((KeyValuePair<string, string> p) => p.Key, StringComparer.Ordinal))
			{
				key.Write(item2.Key);
				key.Write(item2.Value);
			}
		}
	}
	internal static class InventoryOperationNetwork
	{
		private sealed class Pending
		{
			internal string World;

			internal string Id;

			internal string Kind;

			internal byte[] Payload;

			internal float Retry;

			internal float Created;

			internal float NextBusyWarning;

			internal Action<int, string, byte[]> Completed;
		}

		private sealed class Network
		{
			private readonly Dictionary<string, string> modules = new Dictionary<string, string>();

			private readonly HashSet<long> compatible = new HashSet<long>();

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

			private readonly Dictionary<long, Tuple<float, int>> rates = new Dictionary<long, Tuple<float, int>>();

			private readonly Dictionary<long, Tuple<float, int>> queryRates = new Dictionary<long, Tuple<float, int>>();

			private readonly Dictionary<long, float> actorWarnings = new Dictionary<long, float>();

			private ZRoutedRpc registered;

			private bool ready;

			private float nextHello;

			private float nextCleanup;

			private int frame = -1;

			private string mismatch = "";

			private bool Complete => modules.Count > 0;

			private string BuildStamp => OperationRequest.Hash(string.Join(";", from x in modules.OrderBy<KeyValuePair<string, string>, string>((KeyValuePair<string, string> x) => x.Key, StringComparer.Ordinal)
				select x.Key + "=" + x.Value));

			private string World => ZNet.instance.GetWorldUID().ToString(CultureInfo.InvariantCulture);

			internal Dictionary<string, Delegate> Routes()
			{
				return new Dictionary<string, Delegate>
				{
					["module"] = (Action<string, string>)delegate(string module, string build)
					{
						modules[module] = build;
					},
					["tick"] = new Action(Tick),
					["clear"] = new Action(Clear),
					["ready"] = (Func<bool>)(() => ready && Complete),
					["installed"] = (Func<bool>)(() => modules.Count > 0),
					["compatible"] = (Func<long, bool>)((long peer) => peer == ZDOMan.GetSessionID() || compatible.Contains(peer)),
					["send"] = new Func<string, string, string, byte[], Action<int, string, byte[]>, bool>(Send),
					["cancel-query"] = new Func<string, bool>(CancelQuery)
				};
			}

			private void Clear()
			{
				ready = false;
				frame = -1;
				nextHello = 0f;
				nextCleanup = 0f;
				mismatch = "";
				compatible.Clear();
				rates.Clear();
				queryRates.Clear();
				actorWarnings.Clear();
				pending.Clear();
				SharedOperationHost.Stop();
			}

			private void Tick()
			{
				//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
				//IL_02f3: Expected O, but got Unknown
				//IL_0401: Unknown result type (might be due to invalid IL or missing references)
				//IL_0408: Expected O, but got Unknown
				if (!Complete || (Object)(object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null)
				{
					ready = false;
					return;
				}
				World world = ZNet.instance.GetWorld();
				if (world == null || world.m_uid == 0L)
				{
					ready = false;
				}
				else
				{
					if (Time.frameCount == frame)
					{
						return;
					}
					frame = Time.frameCount;
					if (registered != ZRoutedRpc.instance)
					{
						registered = ZRoutedRpc.instance;
						ready = false;
						registered.Register<ZPackage>("RunicOperations_Hello_v2", (Action<long, ZPackage>)Hello);
						registered.Register<ZPackage>("RunicOperations_Ready_v2", (Action<long, ZPackage>)ReadyReply);
						registered.Register<ZPackage>("RunicOperations_Request_v2", (Action<long, ZPackage>)Request);
						registered.Register<ZPackage>("RunicOperations_Result_v2", (Action<long, ZPackage>)Result);
					}
					if (ZNet.instance.IsServer())
					{
						if (!SharedOperationHost.Active)
						{
							SharedOperationHost.Start(World, Path.Combine(Utils.GetSaveDataPath((FileSource)2), "RunicOperations", World));
						}
						ready = true;
						SharedOperationHost.Tick(Time.frameCount);
						if (Time.realtimeSinceStartup > nextCleanup)
						{
							nextCleanup = Time.realtimeSinceStartup + 10f;
							HashSet<long> live = new HashSet<long>(from x in ZNet.instance.GetPeers()
								select x.m_uid);
							compatible.RemoveWhere((long x) => !live.Contains(x));
							long[] array = rates.Keys.Where((long x) => !live.Contains(x)).ToArray();
							foreach (long key in array)
							{
								rates.Remove(key);
							}
							array = queryRates.Keys.Where((long x) => !live.Contains(x)).ToArray();
							foreach (long key2 in array)
							{
								queryRates.Remove(key2);
							}
							array = actorWarnings.Keys.Where((long x) => !live.Contains(x)).ToArray();
							foreach (long key3 in array)
							{
								actorWarnings.Remove(key3);
							}
						}
					}
					else if (!ready && Time.realtimeSinceStartup >= nextHello && ZNet.instance.GetServerPeer() != null)
					{
						nextHello = Time.realtimeSinceStartup + 2f;
						ZPackage val = new ZPackage();
						val.Write(2);
						val.Write(World);
						val.Write(BuildStamp);
						registered.InvokeRoutedRPC(ZNet.instance.GetServerPeer().m_uid, "RunicOperations_Hello_v2", new object[1] { val });
					}
					if (!ready)
					{
						return;
					}
					Pending[] array2 = this.pending.Values.OrderBy((Pending x) => Priority(x.Kind)).ToArray();
					foreach (Pending pending in array2)
					{
						if (this.pending.TryGetValue(pending.Id, out var value) && value == pending && !(pending.World != World) && !(Time.realtimeSinceStartup < pending.Retry))
						{
							pending.Retry = Time.realtimeSinceStartup + ((pending.Kind == "storage.query.v2") ? 0.25f : 2f);
							ZPackage val2 = new ZPackage();
							val2.Write(2);
							val2.Write(pending.World);
							val2.Write(pending.Id);
							val2.Write(pending.Kind);
							val2.Write(pending.Payload);
							long num2 = (ZNet.instance.IsServer() ? ZDOMan.GetSessionID() : (ZNet.instance.GetServerPeer()?.m_uid ?? 0));
							if (num2 != 0L)
							{
								registered.InvokeRoutedRPC(num2, "RunicOperations_Request_v2", new object[1] { val2 });
							}
						}
					}
				}
			}

			private bool Send(string world, string id, string kind, byte[] payload, Action<int, string, byte[]> completed)
			{
				if (!Guid.TryParseExact(id, "N", out var _) || kind == null || kind.Length > 128 || payload == null || payload.Length > 65536)
				{
					return false;
				}
				if (pending.TryGetValue(id, out var value))
				{
					if (value.World != world || value.Kind != kind || !OwnerOperationQueue.Same(value.Payload, payload))
					{
						return false;
					}
					value.Completed = completed;
					return true;
				}
				if (kind == "storage.query.v2" || kind == "storage.viewer.v2")
				{
					if (pending.Values.Count((Pending x) => x.Kind == kind) >= ((kind == "storage.query.v2") ? 4 : 2))
					{
						return false;
					}
				}
				else
				{
					int num = pending.Values.Count((Pending x) => !ReadOnlyKind(x.Kind));
					if (num >= 16 || (BackgroundKind(kind) && num >= 12))
					{
						return false;
					}
				}
				pending.Add(id, new Pending
				{
					World = world,
					Id = id,
					Kind = kind,
					Payload = (byte[])payload.Clone(),
					Completed = completed,
					Created = Time.realtimeSinceStartup
				});
				return true;
			}

			private bool CancelQuery(string id)
			{
				if (id == null || !pending.TryGetValue(id, out var value) || !ReadOnlyKind(value.Kind))
				{
					return false;
				}
				return pending.Remove(id);
			}

			private void Hello(long sender, ZPackage packet)
			{
				//IL_009a: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a0: Expected O, but got Unknown
				try
				{
					if (Complete && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZNet.instance.GetPeer(sender) != null && packet.Size() <= 256)
					{
						int num = packet.ReadInt();
						string text = packet.ReadString();
						string text2 = packet.ReadString();
						OperationPacket.End(packet);
						bool flag = num == 2 && text == World && text2 == BuildStamp;
						if (flag)
						{
							compatible.Add(sender);
						}
						else
						{
							compatible.Remove(sender);
						}
						ZPackage val = new ZPackage();
						val.Write(2);
						val.Write(World);
						val.Write(flag);
						val.Write(BuildStamp);
						registered.InvokeRoutedRPC(sender, "RunicOperations_Ready_v2", new object[1] { val });
						if (!flag)
						{
							Debug.Log((object)"Runic peer uses native inventory handling; optional Runic server transactions were not negotiated.");
						}
					}
				}
				catch (Exception ex)
				{
					MutationGate.Report("operation hello rejected: " + ex.GetType().Name);
				}
			}

			private void ReadyReply(long sender, ZPackage packet)
			{
				try
				{
					ZNet instance = ZNet.instance;
					if (instance != null && instance.GetServerPeer()?.m_uid == sender && packet.Size() <= 256)
					{
						int num = packet.ReadInt();
						string text = packet.ReadString();
						bool flag = packet.ReadBool();
						string text2 = packet.ReadString();
						OperationPacket.End(packet);
						ready = num == 2 && text == World && flag && text2 == BuildStamp;
						if (!ready && mismatch != text2)
						{
							mismatch = text2;
							Debug.Log((object)"Runic uses native inventory handling for this server; optional Runic server transactions were not negotiated.");
						}
					}
				}
				catch
				{
					ready = false;
				}
			}

			private void Request(long sender, ZPackage packet)
			{
				try
				{
					if (!ready || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (sender != ZDOMan.GetSessionID() && !compatible.Contains(sender)) || packet == null || packet.Size() > 66048 || packet.ReadInt() != 2)
					{
						return;
					}
					string world = packet.ReadString();
					string id = packet.ReadString();
					string text = packet.ReadString();
					byte[] array = OperationPacket.Bytes(packet);
					OperationPacket.End(packet);
					if (world != World || !Guid.TryParseExact(id, "N", out var _) || text.Length > 128 || array.Length > 65536)
					{
						return;
					}
					float realtimeSinceStartup = Time.realtimeSinceStartup;
					Dictionary<long, Tuple<float, int>> dictionary = (ReadOnlyKind(text) ? queryRates : rates);
					if (!dictionary.TryGetValue(sender, out var value) || realtimeSinceStartup - value.Item1 >= 1f)
					{
						value = Tuple.Create(realtimeSinceStartup, 0);
					}
					if (value.Item2 >= 32)
					{
						return;
					}
					dictionary[sender] = Tuple.Create(value.Item1, value.Item2 + 1);
					if (!NetworkInventoryAccess.TryActor(sender, out var actor))
					{
						Reply(sender, world, id, 1, "actor-unavailable", Array.Empty<byte>());
						if (!actorWarnings.TryGetValue(sender, out var value2) || realtimeSinceStartup >= value2)
						{
							actorWarnings[sender] = realtimeSinceStartup + 30f;
							Debug.LogWarning((object)$"Runic inventory request deferred: character identity unavailable for peer {sender}; request={id}. The request will retry.");
						}
					}
					else
					{
						Tuple<int, string, byte[]> tuple = SharedOperationHost.Submit(world, actor, id, text, array, delegate(int status, string reason, byte[] response)
						{
							Reply(sender, world, id, status, reason, response);
						});
						if (tuple.Item1 != 0)
						{
							Reply(sender, world, id, tuple.Item1, tuple.Item2, tuple.Item3);
						}
					}
				}
				catch (Exception ex)
				{
					MutationGate.Report("operation request rejected: " + ex.GetType().Name + ": " + ex.Message);
				}
			}

			private void Reply(long receiver, string world, string id, int status, string reason, byte[] payload)
			{
				//IL_0022: Unknown result type (might be due to invalid IL or missing references)
				//IL_0028: Expected O, but got Unknown
				if (registered != null && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
				{
					ZPackage val = new ZPackage();
					val.Write(2);
					val.Write(world);
					val.Write(id);
					val.Write(status);
					val.Write(reason);
					val.Write(payload);
					registered.InvokeRoutedRPC(receiver, "RunicOperations_Result_v2", new object[1] { val });
				}
			}

			private void Result(long sender, ZPackage packet)
			{
				try
				{
					if ((Object)(object)ZNet.instance == (Object)null || packet == null || packet.Size() > 70500)
					{
						return;
					}
					long num = (ZNet.instance.IsServer() ? ZDOMan.GetSessionID() : (ZNet.instance.GetServerPeer()?.m_uid ?? 0));
					if (num == 0L || sender != num || packet.ReadInt() != 2)
					{
						return;
					}
					string text = packet.ReadString();
					string key = packet.ReadString();
					int num2 = packet.ReadInt();
					string text2 = packet.ReadString();
					byte[] array = OperationPacket.Bytes(packet);
					OperationPacket.End(packet);
					if (text != World || !pending.TryGetValue(key, out var value) || value.World != text || array.Length > 65536 || text2.Length > 4096 || !Enum.IsDefined(typeof(OperationStatus), num2))
					{
						return;
					}
					switch (num2)
					{
					case 0:
						break;
					case 1:
					case 4:
						if (num2 == 1 && text2 == "container-live-state-requested" && !BackgroundKind(value.Kind))
						{
							value.Retry = Math.Min(value.Retry, Time.realtimeSinceStartup + 0.05f);
						}
						else if (num2 == 1 && !ReadOnlyKind(value.Kind) && !BackgroundKind(value.Kind))
						{
							value.Retry = Math.Min(value.Retry, Time.realtimeSinceStartup + 0.25f);
						}
						WarnBusy(value, text2);
						break;
					default:
						pending.Remove(key);
						value.Completed?.Invoke(num2, text2, array);
						break;
					}
				}
				catch (Exception ex)
				{
					MutationGate.Report("operation reply deferred: " + ex.GetType().Name + ": " + ex.Message);
				}
			}

			private void WarnBusy(Pending request, string reason)
			{
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				float num = realtimeSinceStartup - request.Created;
				if (!(num < 3f) && !(realtimeSinceStartup < request.NextBusyWarning))
				{
					request.NextBusyWarning = realtimeSinceStartup + 30f;
					int num2 = pending.Values.Count((Pending x) => x.Kind == "storage.query.v2");
					int num3 = pending.Values.Count((Pending x) => x.Kind == "storage.viewer.v2");
					Debug.LogWarning((object)("Runic inventory request is still waiting: kind=" + request.Kind + "; request=" + request.Id + "; age=" + num.ToString("F1", CultureInfo.InvariantCulture) + "s; reason=" + reason + "; " + $"pending mutations={pending.Count - num2 - num3}/16, chest queries={num2}/4, viewers={num3}/2. The same request will retry."));
				}
			}
		}

		private const string HelloRpc = "RunicOperations_Hello_v2";

		private const string ReadyRpc = "RunicOperations_Ready_v2";

		private const string RequestRpc = "RunicOperations_Request_v2";

		private const string ResultRpc = "RunicOperations_Result_v2";

		private const string QueryKind = "storage.query.v2";

		private const string ViewerKind = "storage.viewer.v2";

		private static Dictionary<string, Delegate> Routes => SharedState.Get("operations.network.v2", () => new Network().Routes());

		internal static bool Ready => ((Func<bool>)Routes["ready"])();

		internal static bool Installed => ((Func<bool>)Routes["installed"])();

		internal static void RegisterModule(string module)
		{
			((Action<string, string>)Routes["module"])(module, Assembly.GetExecutingAssembly().ManifestModule.ModuleVersionId.ToString("N"));
		}

		internal static void Tick()
		{
			((Action)Routes["tick"])();
		}

		internal static void Clear()
		{
			((Action)Routes["clear"])();
		}

		internal static bool Compatible(long peer)
		{
			return ((Func<long, bool>)Routes["compatible"])(peer);
		}

		internal static bool Send(string world, string id, string kind, byte[] payload, Action<int, string, byte[]> completed)
		{
			return ((Func<string, string, string, byte[], Action<int, string, byte[]>, bool>)Routes["send"])(world, id, kind, payload, completed);
		}

		internal static bool CancelQuery(string id)
		{
			return ((Func<string, bool>)Routes["cancel-query"])(id);
		}

		private static bool ReadOnlyKind(string kind)
		{
			if (!(kind == "storage.query.v2"))
			{
				return kind == "storage.viewer.v2";
			}
			return true;
		}