Decompiled source of TooManyLags FGN Addon v1.0.12

TooManyLags.dll

Decompiled 4 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.ExceptionServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using TooManyLags.PlayerSimulation;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("1.0.12.0")]
namespace TooManyLags
{
	internal sealed class AuthorityController
	{
		private struct Receipt
		{
			internal int Epoch;

			internal bool Ready;
		}

		private struct OwnerCell : IEquatable<OwnerCell>
		{
			internal long Owner;

			internal Cell Cell;

			internal Vector2s Center;

			public bool Equals(OwnerCell other)
			{
				//IL_002b: Unknown result type (might be due to invalid IL or missing references)
				if (Owner == other.Owner && Cell.Equals(other.Cell))
				{
					return ((Vector2s)(ref Center)).Equals(other.Center);
				}
				return false;
			}

			public override bool Equals(object other)
			{
				if (other is OwnerCell)
				{
					return Equals((OwnerCell)other);
				}
				return false;
			}

			public override int GetHashCode()
			{
				return Owner.GetHashCode() ^ Cell.GetHashCode() ^ ((object)Unsafe.As<Vector2s, Vector2s>(ref Center)/*cast due to .constrained prefix*/).GetHashCode();
			}
		}

		internal sealed class NativeNode : AuthorityNode
		{
			private readonly AuthorityController owner;

			internal readonly ZDO Data;

			internal readonly ZDOID Id;

			internal readonly ZNetView View;

			internal readonly Kinds Kind;

			private readonly WearNTear wear;

			private readonly Character actor;

			private readonly Tameable tameable;

			private readonly bool ship;

			private readonly bool serverStation;

			private readonly bool multipleInventories;

			private readonly Vagon cart;

			private readonly Container container;

			internal long ObservedOwner;

			internal uint Revision;

			internal int Requests;

			private float refillAt;

			private float refillUntil;

			private float inventoryAt;

			private float inventoryUntil;

			private long refillOwner;

			private long inventoryOwner;

			private uint refillRevision;

			private bool coveredBefore;

			internal bool ServerStation => serverStation;

			internal bool Summoned
			{
				get
				{
					if (Kind == Kinds.TamedAnimals)
					{
						if (!ObjectCatalog.IsSummoned(tameable))
						{
							return FriendlyCreatures.IsFriendly(actor);
						}
						return true;
					}
					return false;
				}
			}

			internal override long CurrentOwner => Data.GetOwner();

			internal NativeNode(AuthorityController owner, ZDO data, ZNetView view, Kinds kind, Cell cell, Tameable animal, Character character)
			{
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				this.owner = owner;
				Data = data;
				Id = data.m_uid;
				View = view;
				Kind = kind;
				Cell = cell;
				coveredBefore = owner.indexed.Grid.Candidate(cell) != 0;
				wear = ((Component)view).GetComponent<WearNTear>();
				ship = kind == Kinds.Vehicles && Object.op_Implicit((Object)(object)((Component)view).GetComponent<Ship>());
				if (kind == Kinds.Vehicles)
				{
					cart = ((Component)view).GetComponent<Vagon>();
				}
				if (kind == Kinds.TamedAnimals)
				{
					actor = character;
					tameable = animal;
				}
				Container[] componentsInChildren = ((Component)view).GetComponentsInChildren<Container>(true);
				foreach (Container val in componentsInChildren)
				{
					ZNetView val2 = (Object.op_Implicit((Object)(object)val.m_rootObjectOverride) ? val.m_rootObjectOverride : ((Component)val).GetComponentInParent<ZNetView>());
					if (!((Object)(object)val2 != (Object)(object)view))
					{
						if (Object.op_Implicit((Object)(object)container))
						{
							multipleInventories = true;
						}
						else
						{
							container = val;
						}
					}
				}
				serverStation = Object.op_Implicit((Object)(object)((Component)view).GetComponentInChildren<Smelter>(true)) || Object.op_Implicit((Object)(object)((Component)view).GetComponentInChildren<CookingStation>(true)) || Object.op_Implicit((Object)(object)((Component)view).GetComponentInChildren<Fireplace>(true)) || Object.op_Implicit((Object)(object)((Component)view).GetComponentInChildren<Fermenter>(true)) || Object.op_Implicit((Object)(object)((Component)view).GetComponentInChildren<ShieldGenerator>(true)) || Object.op_Implicit((Object)(object)((Component)view).GetComponentInChildren<Turret>(true)) || kind == Kinds.Incinerators;
				ObservedOwner = data.GetOwner();
				Revision = data.OwnerRevision;
			}

			internal override bool CanRemember(long fieldOwner)
			{
				long num = ((owner.refillStations && serverStation) ? owner.server : fieldOwner);
				if (Managed && ManagedOwner == num)
				{
					return Data.GetOwner() == num;
				}
				return false;
			}

			internal void Refilled(int method)
			{
				if (ObjectCatalog.RefillRequest(Kind, method))
				{
					refillOwner = Data.GetOwner();
					refillRevision = Data.OwnerRevision;
					refillAt = Time.unscaledTime;
					refillUntil = refillAt + 10f;
				}
				if (method == StringExtensionMethods.GetStableHashCode("RPC_RequestOpen") || method == StringExtensionMethods.GetStableHashCode("RPC_RequestStack") || method == StringExtensionMethods.GetStableHashCode("RPC_RequestTakeAll"))
				{
					inventoryOwner = Data.GetOwner();
					inventoryAt = Time.unscaledTime;
					inventoryUntil = inventoryAt + 5f;
				}
			}

			internal bool UseProtected()
			{
				float unscaledTime = Time.unscaledTime;
				if (refillOwner == 0 || !(unscaledTime >= refillAt) || !(unscaledTime < refillUntil) || Data.GetOwner() != refillOwner || Data.OwnerRevision != refillRevision)
				{
					if (Object.op_Implicit((Object)(object)container))
					{
						if (!(unscaledTime >= inventoryAt) || !(unscaledTime < inventoryUntil) || inventoryOwner == 0)
						{
							return Data.GetInt(ZDOVars.s_inUse, 0) != 0;
						}
						return true;
					}
					return false;
				}
				return true;
			}

			internal void ObserveUse()
			{
				if (Object.op_Implicit((Object)(object)container) && Data.GetInt(ZDOVars.s_inUse, 0) != 0)
				{
					inventoryOwner = 0L;
					inventoryUntil = 0f;
				}
			}

			internal bool Identity()
			{
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_0027: Unknown result type (might be due to invalid IL or missing references)
				//IL_0042: Unknown result type (might be due to invalid IL or missing references)
				if (!Removed && Data.IsValid() && Data.m_uid == Id && object.ReferenceEquals(owner.world.GetZDO(Id), Data) && Object.op_Implicit((Object)(object)View) && View.IsValid() && object.ReferenceEquals(View.GetZDO(), Data) && ((Component)View).gameObject.activeInHierarchy && !View.m_distant && (Object)(object)((Component)View).transform.parent == (Object)null)
				{
					return !BakedObjects.IsBaked(Data);
				}
				return false;
			}

			internal bool Controllable()
			{
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Invalid comparison between Unknown and I4
				if (Kind == Kinds.Incinerators)
				{
					return false;
				}
				if (multipleInventories || (Object.op_Implicit((Object)(object)container) && (int)container.m_privacy != 2))
				{
					return false;
				}
				if (Kind != Kinds.TamedAnimals)
				{
					if (View.m_persistent && (!Object.op_Implicit((Object)(object)wear) || ((Behaviour)wear).isActiveAndEnabled) && (!ship || !owner.compatibility.ShipsOwnedExternally) && Data.GetLong(ZDOVars.s_user, 0L) == 0)
					{
						if (Object.op_Implicit((Object)(object)cart))
						{
							return !cart.InUse();
						}
						return true;
					}
					return false;
				}
				if (!Summoned && Object.op_Implicit((Object)(object)actor) && !(actor is Player) && ((Behaviour)actor).isActiveAndEnabled && Data.GetBool(ZDOVars.s_tamed, false) && Data.GetLong(ZDOVars.s_user, 0L) == 0 && !actor.IsDead() && !actor.IsAttached() && !actor.IsRiding() && (!Object.op_Implicit((Object)(object)tameable) || !tameable.HaveRider()))
				{
					if (Object.op_Implicit((Object)(object)tameable) && tameable.m_commandable)
					{
						return string.IsNullOrEmpty(Data.GetString(ZDOVars.s_follow, ""));
					}
					return true;
				}
				return false;
			}

			private long DesiredOwner(long fieldOwner)
			{
				if (owner.refillStations && serverStation)
				{
					return owner.server;
				}
				if (!owner.NeedsGrid(Cell))
				{
					return 0L;
				}
				if (owner.indexed.Grid.Candidate(Cell) == 0)
				{
					if (owner.keepOnExit && coveredBefore)
					{
						return 0L;
					}
					if (owner.directional && !coveredBefore)
					{
						fieldOwner = 0L;
					}
				}
				if (fieldOwner == 0 && owner.defaultInteractives)
				{
					return owner.server;
				}
				return fieldOwner;
			}

			internal override ApplyResult Apply(long target)
			{
				//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
				//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
				//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
				//IL_0181: Unknown result type (might be due to invalid IL or missing references)
				//IL_0187: Invalid comparison between Unknown and I4
				//IL_024d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0335: Unknown result type (might be due to invalid IL or missing references)
				//IL_0325: Unknown result type (might be due to invalid IL or missing references)
				//IL_0389: Unknown result type (might be due to invalid IL or missing references)
				//IL_038e: Unknown result type (might be due to invalid IL or missing references)
				//IL_038f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0394: Unknown result type (might be due to invalid IL or missing references)
				//IL_03d8: Unknown result type (might be due to invalid IL or missing references)
				//IL_03dd: Unknown result type (might be due to invalid IL or missing references)
				//IL_03ec: Unknown result type (might be due to invalid IL or missing references)
				//IL_0420: Unknown result type (might be due to invalid IL or missing references)
				//IL_04b7: Unknown result type (might be due to invalid IL or missing references)
				//IL_05c0: Unknown result type (might be due to invalid IL or missing references)
				//IL_0557: Unknown result type (might be due to invalid IL or missing references)
				//IL_06c8: Unknown result type (might be due to invalid IL or missing references)
				//IL_0617: Unknown result type (might be due to invalid IL or missing references)
				if (!Identity() || Summoned)
				{
					owner.Remove(this);
					return ApplyResult.Dropped;
				}
				if ((!owner.refillStations || !serverStation) && !owner.NeedsGrid(Cell))
				{
					if (owner.Inventory.Transitioning(Data) || owner.Cooperative.Transitioning(Data) || owner.TransferPending(Data))
					{
						return ApplyResult.Pending;
					}
					owner.Inventory.Forget(Data);
					owner.Cooperative.Forget(Data);
					Managed = false;
					return ApplyResult.Done;
				}
				if (!coveredBefore && owner.indexed.Grid.Candidate(Cell) != 0)
				{
					coveredBefore = true;
				}
				if (!Controllable())
				{
					Managed = false;
					if (Kind == Kinds.TamedAnimals && Object.op_Implicit((Object)(object)actor) && !actor.IsDead() && Data.GetBool(ZDOVars.s_tamed, false))
					{
						return ApplyResult.Pending;
					}
					if (Kind == Kinds.Vehicles && !multipleInventories && (!ship || !owner.compatibility.ShipsOwnedExternally) && (!Object.op_Implicit((Object)(object)container) || (int)container.m_privacy == 2))
					{
						return ApplyResult.Pending;
					}
					return ApplyResult.Dropped;
				}
				if ((!owner.refillStations || !serverStation) && target == 0 && owner.defaultInteractives && owner.indexed.Grid.Candidate(Cell) != 0)
				{
					return ApplyResult.Pending;
				}
				target = DesiredOwner(target);
				if (target == 0)
				{
					return ApplyResult.Done;
				}
				Vector3 position = Data.GetPosition();
				if (!Finite(position) || !Cell.At(position.x, position.z, owner.cellSize, out var cell))
				{
					return ApplyResult.Dropped;
				}
				if (!cell.Equals(Cell))
				{
					owner.indexed.Move(this, cell, Time.unscaledTime);
					return ApplyResult.Pending;
				}
				if (!owner.AvailableOwner(target, position))
				{
					return ApplyResult.Pending;
				}
				long num = Data.GetOwner();
				uint ownerRevision = Data.OwnerRevision;
				bool flag = owner.TransferPending(Data);
				if (num == target && !flag && (!Object.op_Implicit((Object)(object)container) || !owner.Inventory.Received(Data) || owner.Inventory.Acquire(Data, View, container) == 2))
				{
					Managed = true;
					ManagedOwner = target;
					owner.Inventory.Forget(Data);
					owner.Cooperative.Forget(Data);
					return ApplyResult.Done;
				}
				if (UseProtected() && owner.AvailableOwner(num, position))
				{
					return ApplyResult.Pending;
				}
				if (!owner.AreaReady(position))
				{
					return ApplyResult.Pending;
				}
				if (Kind != Kinds.TamedAnimals)
				{
					if (ObjectCatalog.Classify(((Component)View).gameObject) != Kind)
					{
						goto IL_03a4;
					}
					if (Object.op_Implicit((Object)(object)wear) && wear.m_staticPosition)
					{
						Vector3 val = ((Component)View).transform.position - position;
						if (((Vector3)(ref val)).sqrMagnitude > 0.0025f)
						{
							goto IL_03a4;
						}
					}
				}
				if (!Identity() || !Controllable() || Data.GetOwner() != num || Data.OwnerRevision != ownerRevision || Data.GetPosition() != position || !owner.AvailableOwner(target, position))
				{
					return ApplyResult.Pending;
				}
				if (ownerRevision == 65535)
				{
					Managed = false;
					return ApplyResult.Dropped;
				}
				if (Object.op_Implicit((Object)(object)container))
				{
					if (num != 0 && owner.AvailableOwner(num, position))
					{
						if (owner.Inventory.Acquire(Data, View, container) != 2)
						{
							return ApplyResult.Pending;
						}
						num = Data.GetOwner();
						ownerRevision = Data.OwnerRevision;
					}
					else
					{
						owner.Inventory.Forget(Data);
					}
				}
				else if (Kind != Kinds.TamedAnimals && num != owner.server && num != 0 && owner.AvailableOwner(num, position))
				{
					switch (owner.Cooperative.Request(Data, target))
					{
					case 1:
						return ApplyResult.Pending;
					case 2:
						Managed = true;
						ManagedOwner = Data.GetOwner();
						ObservedOwner = ManagedOwner;
						Revision = Data.OwnerRevision;
						owner.Cooperative.Forget(Data);
						if (ManagedOwner != owner.server)
						{
							owner.world.ForceSendZDO(ManagedOwner, Id);
						}
						return ApplyResult.Done;
					}
				}
				if (!Identity() || !Controllable() || Data.GetOwner() != num || Data.OwnerRevision != ownerRevision || DesiredOwner(owner.indexed.Grid.Owner(Cell)) != target || !owner.AvailableOwner(target, Data.GetPosition()))
				{
					return ApplyResult.Pending;
				}
				if (Data.GetOwner() != target)
				{
					if (Data.OwnerRevision == ushort.MaxValue)
					{
						return ApplyResult.Dropped;
					}
					owner.writing = true;
					try
					{
						if (target != owner.server)
						{
							owner.world.ForceSendZDO(target, Id);
						}
						Data.SetOwner(target);
					}
					finally
					{
						owner.writing = false;
					}
				}
				ObservedOwner = Data.GetOwner();
				Revision = Data.OwnerRevision;
				Managed = ObservedOwner == target;
				ManagedOwner = ObservedOwner;
				if (Managed)
				{
					owner.Inventory.Forget(Data);
					owner.Cooperative.Forget(Data);
					if (target != owner.server)
					{
						owner.world.ForceSendZDO(target, Id);
					}
				}
				if (!Managed)
				{
					return ApplyResult.Pending;
				}
				return ApplyResult.Done;
				IL_03a4:
				return ApplyResult.Dropped;
			}
		}

		private readonly Kinds kinds;

		private float delay;

		private readonly OwnershipCadence cadence;

		private readonly CompatibilityBridge compatibility;

		private readonly Action<Exception> fault;

		private readonly Action<Exception> requestFault;

		private readonly bool staticEnabled;

		private readonly bool defaultInteractives;

		private readonly bool refillStations;

		private readonly bool keepOnExit;

		private readonly int cellSize;

		private readonly GridCoveragePattern pattern;

		private readonly bool directional;

		private float innerDelay;

		private float outerDelay;

		private float forwardDelay;

		private StaticBuildings statics;

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

		private ConditionalWeakTable<ZNetView, NativeNode> views = new ConditionalWeakTable<ZNetView, NativeNode>();

		private readonly Dictionary<Vector2s, Receipt> readiness = new Dictionary<Vector2s, Receipt>();

		private readonly Dictionary<OwnerCell, bool> available = new Dictionary<OwnerCell, bool>();

		private readonly List<GridPeer> peers = new List<GridPeer>();

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

		private readonly CameraWake cameras = new CameraWake();

		private readonly RegionContentionGate regions = new RegionContentionGate();

		private readonly List<RegionPeer> regionPeers = new List<RegionPeer>();

		private readonly List<ZDO> characters = new List<ZDO>();

		private static readonly Comparison<GridPeer> peerOrder = (GridPeer a, GridPeer b) => a.Uid.CompareTo(b.Uid);

		private readonly Func<ZNetScene, Dictionary<ZDO, ZNetView>> instances;

		private ObjectCatalog catalog;

		private AuthorityWorld indexed;

		private ZNet network;

		private ZDOMan world;

		private ZNetScene scene;

		private long server;

		private int epoch;

		private bool primed;

		private bool active;

		private bool failed;

		private bool writing;

		private bool coarseEnabled;

		internal readonly RequestRouter Router;

		internal NativeInventoryHandoff Inventory { get; private set; }

		internal FgnOwnerHandoff Cooperative { get; private set; }

		internal bool SendingInventory
		{
			get
			{
				if (Inventory != null)
				{
					return Inventory.Sending;
				}
				return false;
			}
		}

		internal bool IsActive
		{
			get
			{
				if (active)
				{
					return !failed;
				}
				return false;
			}
		}

		internal RegionContentionGate RegionGate => regions;

		internal int ScheduledAttempts
		{
			get
			{
				if (indexed != null)
				{
					return indexed.Attempts;
				}
				return 0;
			}
		}

		internal int FieldUpdates
		{
			get
			{
				if (indexed != null)
				{
					return indexed.Grid.PlayerUpdates;
				}
				return 0;
			}
		}

		internal bool DiagnosticsEnabled => false;

		internal long Server => server;

		internal AuthorityController(Kinds kinds, float delay, CompatibilityBridge compatibility, Action<Exception> fault, bool staticEnabled, Action<Exception> requestFault, float checkIntervalSeconds, bool defaultInteractives, bool refillStations, int cellSize, GridCoveragePattern pattern, bool keepOnExit, bool directional = false, float innerDelay = 0f, float outerDelay = 3f, float forwardDelay = 0.2f)
		{
			this.kinds = kinds;
			this.delay = delay;
			cadence = new OwnershipCadence(checkIntervalSeconds);
			this.compatibility = compatibility;
			this.fault = fault;
			this.requestFault = requestFault;
			this.staticEnabled = staticEnabled;
			this.defaultInteractives = defaultInteractives;
			this.refillStations = refillStations;
			this.keepOnExit = keepOnExit;
			this.cellSize = ((cellSize == 4 || cellSize == 16) ? cellSize : 8);
			this.directional = directional;
			this.innerDelay = innerDelay;
			this.outerDelay = outerDelay;
			this.forwardDelay = forwardDelay;
			this.pattern = ((pattern == GridCoveragePattern.Rounded5x5 || pattern == GridCoveragePattern.Rounded7x7) ? pattern : GridCoveragePattern.Square3x3);
			if (this.cellSize == 4 && this.pattern == GridCoveragePattern.Square3x3)
			{
				throw new ArgumentException("4-metre cells require Rounded5x5 or Rounded7x7.");
			}
			FieldInfo field = typeof(ZNetScene).GetField("m_instances", BindingFlags.Instance | BindingFlags.NonPublic);
			if (field == null || field.FieldType != typeof(Dictionary<ZDO, ZNetView>))
			{
				throw new NotSupportedException("Native instance registry changed.");
			}
			DynamicMethod dynamicMethod = new DynamicMethod("BotanicInstances", field.FieldType, new Type[1] { typeof(ZNetScene) }, typeof(AuthorityController).Module, skipVisibility: true);
			ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
			iLGenerator.Emit(OpCodes.Ldarg_0);
			iLGenerator.Emit(OpCodes.Ldfld, field);
			iLGenerator.Emit(OpCodes.Ret);
			instances = (Func<ZNetScene, Dictionary<ZDO, ZNetView>>)dynamicMethod.CreateDelegate(typeof(Func<ZNetScene, Dictionary<ZDO, ZNetView>>));
			Router = new RequestRouter(this);
		}

		private bool Context()
		{
			if (failed || !Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsDedicated() || ZNet.IsSinglePlayer || ZDOMan.instance == null || !Object.op_Implicit((Object)(object)ZNetScene.instance) || !Object.op_Implicit((Object)(object)ZoneSystem.instance))
			{
				return false;
			}
			if (!object.ReferenceEquals(world, ZDOMan.instance) || !object.ReferenceEquals(network, ZNet.instance) || !object.ReferenceEquals(scene, ZNetScene.instance))
			{
				Reset();
				world = ZDOMan.instance;
				network = ZNet.instance;
				scene = ZNetScene.instance;
				server = ZDOMan.GetSessionID();
				if (server == 0)
				{
					return false;
				}
				coarseEnabled = !defaultInteractives && ZoneSystem.instance.m_zoneSize == 64f;
				catalog = new ObjectCatalog(kinds);
				indexed = new AuthorityWorld(server, delay, pattern, keepOnExit, directional, innerDelay, outerDelay, cellSize == 4, forwardDelay);
				statics = (staticEnabled ? new StaticBuildings(() => active) : null);
				Inventory = new NativeInventoryHandoff(server);
				Cooperative = new FgnOwnerHandoff(compatibility.Assembly, server);
			}
			return indexed != null;
		}

		internal void Poll(float now)
		{
			if (cadence.Due(now))
			{
				Tick();
			}
		}

		internal void ConfigureInterval(float interval)
		{
			cadence.Configure(interval);
		}

		internal void ConfigureDelays(float full, float inner, float forward, float outer)
		{
			delay = full;
			innerDelay = inner;
			forwardDelay = forward;
			outerDelay = outer;
			if (indexed != null)
			{
				indexed.Grid.ConfigureDelays(full, inner, forward, outer);
			}
		}

		internal void Tick()
		{
			//IL_026f: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_032d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0332: Unknown result type (might be due to invalid IL or missing references)
			//IL_0361: Unknown result type (might be due to invalid IL or missing references)
			//IL_0363: Unknown result type (might be due to invalid IL or missing references)
			//IL_0357: Unknown result type (might be due to invalid IL or missing references)
			//IL_035d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0368: Unknown result type (might be due to invalid IL or missing references)
			//IL_037a: Unknown result type (might be due to invalid IL or missing references)
			//IL_037c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0370: Unknown result type (might be due to invalid IL or missing references)
			//IL_0376: Unknown result type (might be due to invalid IL or missing references)
			//IL_0381: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				cameras.Clear();
				active = Context() && compatibility.Enabled();
				if (!active)
				{
					Router.Flush();
					return;
				}
				bool flag = !defaultInteractives && ZoneSystem.instance.m_zoneSize == 64f;
				if (coarseEnabled != flag)
				{
					coarseEnabled = flag;
					regions.Clear();
				}
				readiness.Clear();
				available.Clear();
				if (statics != null)
				{
					statics.Begin(network, scene, server, 0, takeovers: true);
				}
				if (!primed)
				{
					foreach (KeyValuePair<ZDO, ZNetView> item in instances(scene))
					{
						Register(item.Key, item.Value);
					}
					primed = true;
				}
				peers.Clear();
				peerIds.Clear();
				regionPeers.Clear();
				characters.Clear();
				List<ZNetPeer> list = network.GetPeers();
				if (coarseEnabled && list.Count <= 1)
				{
					if (list.Count == 1)
					{
						ZNetPeer val = list[0];
						if (val != null && val.IsReady() && val.m_uid != 0 && val.m_uid != server)
						{
							regionPeers.Add(new RegionPeer(val.m_uid, default(Cell), referenceValid: true, default(Cell), characterValid: false));
						}
					}
					regions.Update(regionPeers);
					indexed.Tick(peers, Time.unscaledTime);
					Router.Flush();
					return;
				}
				foreach (ZNetPeer item2 in list)
				{
					if (item2 == null || !item2.IsReady() || item2.m_uid == 0 || item2.m_uid == server || !peerIds.Add(item2.m_uid))
					{
						continue;
					}
					ZDO val2 = (((ZDOID)(ref item2.m_characterID)).IsNone() ? null : world.GetZDO(item2.m_characterID));
					Vector3 val3 = (Vector3)((val2 != null && val2.IsValid()) ? val2.GetPosition() : new Vector3(float.NaN, 0f, float.NaN));
					Cell cell;
					bool flag2 = Cell.At(val3.x, val3.z, cellSize, out cell);
					if (flag2)
					{
						peers.Add(GridPeer.Covered(item2.m_uid, cell, (!coarseEnabled && directional) ? CameraHeading.Read(val2, item2.m_uid, cameras) : 0));
						if (coarseEnabled)
						{
							characters.Add(val2);
						}
					}
					if (coarseEnabled)
					{
						Vector3 refPos = item2.GetRefPos();
						Cell cell2;
						bool flag3 = Cell.At(refPos.x, refPos.z, cellSize, out cell2);
						Vector2s val4 = (Vector2s)(flag3 ? ZoneSystem.GetZone(refPos) : default(Vector2s));
						Vector2s val5 = (Vector2s)(flag2 ? ZoneSystem.GetZone(val3) : default(Vector2s));
						regionPeers.Add(new RegionPeer(item2.m_uid, new Cell(val4.x, val4.y), flag3, new Cell(val5.x, val5.y), flag2));
					}
				}
				if (coarseEnabled)
				{
					regions.Update(regionPeers);
					int num = 0;
					for (int i = 0; i < peers.Count; i++)
					{
						GridPeer value = peers[i];
						if (regions.Shared(value.Uid))
						{
							if (directional)
							{
								value.Coverage = CameraHeading.Read(characters[i], value.Uid, cameras);
							}
							peers[num++] = value;
						}
					}
					if (num != peers.Count)
					{
						peers.RemoveRange(num, peers.Count - num);
					}
					characters.Clear();
				}
				peers.Sort(peerOrder);
				indexed.Tick(peers, Time.unscaledTime);
				Router.Flush();
			}
			catch (Exception error)
			{
				Fail(error);
			}
		}

		internal void Register(ZDO data, ZNetView view)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!Context())
				{
					return;
				}
				epoch++;
				if (statics != null)
				{
					statics.InvalidateReadiness();
				}
				if (data == null || !Object.op_Implicit((Object)(object)view) || !view.IsValid() || !object.ReferenceEquals(view.GetZDO(), data) || ((ZDOID)(ref data.m_uid)).IsNone() || BakedObjects.IsBaked(data))
				{
					return;
				}
				if (nodes.TryGetValue(data.m_uid, out var value))
				{
					if (object.ReferenceEquals(value.Data, data) && object.ReferenceEquals(value.View, view))
					{
						return;
					}
					Remove(value);
				}
				Kinds kinds = catalog.Instance(data.GetPrefab(), view);
				switch (kinds)
				{
				case Kinds.TamedAnimals:
					if (!data.GetBool(ZDOVars.s_tamed, false))
					{
						return;
					}
					break;
				case Kinds.None:
					return;
				}
				Tameable animal = ((kinds == Kinds.TamedAnimals) ? ((Component)view).GetComponent<Tameable>() : null);
				Character val = ((kinds == Kinds.TamedAnimals) ? ((Component)view).GetComponent<Character>() : null);
				if (kinds != Kinds.TamedAnimals || (!ObjectCatalog.IsSummoned(animal) && !FriendlyCreatures.IsFriendly(val)))
				{
					Vector3 position = data.GetPosition();
					if (Finite(position) && Cell.At(position.x, position.z, cellSize, out var cell))
					{
						NativeNode nativeNode = new NativeNode(this, data, view, kinds, cell, animal, val);
						nodes.Add(nativeNode.Id, nativeNode);
						views.Remove(view);
						views.Add(view, nativeNode);
						indexed.Add(nativeNode, Time.unscaledTime, NeedsPolicy(nativeNode));
					}
				}
			}
			catch (Exception error)
			{
				Fail(error);
			}
		}

		internal void Unregister(ZNetView view)
		{
			if (object.ReferenceEquals(view, null))
			{
				return;
			}
			try
			{
				epoch++;
				if (statics != null)
				{
					statics.InvalidateReadiness();
					statics.Remove(view);
				}
				if (views.TryGetValue(view, out var value))
				{
					Remove(value);
				}
			}
			catch (Exception error)
			{
				Fail(error);
			}
		}

		private void Remove(NativeNode node)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			Inventory.Forget(node.Data);
			Cooperative.Forget(node.Data);
			indexed.Remove(node);
			nodes.Remove(node.Id);
			views.Remove(node.View);
			Router.Remove(node);
		}

		internal void Position(ZDO data)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			if (failed || indexed == null)
			{
				return;
			}
			try
			{
				if (!nodes.TryGetValue(data.m_uid, out var value) || !object.ReferenceEquals(value.Data, data))
				{
					return;
				}
				Vector3 position = data.GetPosition();
				if (!Finite(position))
				{
					Remove(value);
					return;
				}
				float num = (float)(value.Cell.X * cellSize) - 32f;
				float num2 = (float)(value.Cell.Z * cellSize) - 32f;
				if (!(position.x >= num) || !(position.x < num + (float)cellSize) || !(position.z >= num2) || !(position.z < num2 + (float)cellSize))
				{
					if (!Cell.At(position.x, position.z, cellSize, out var cell))
					{
						Remove(value);
						return;
					}
					bool schedule = NeedsGrid(cell) || (refillStations && value.ServerStation) || ProtectedTransfer(data);
					indexed.Move(value, cell, Time.unscaledTime, schedule);
				}
			}
			catch (Exception error)
			{
				Fail(error);
			}
		}

		internal void DataChanged(ZDO data)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if (failed || indexed == null)
			{
				return;
			}
			try
			{
				if (active && object.ReferenceEquals(world, ZDOMan.instance) && cameras.Changed(data))
				{
					cadence.Request();
				}
				if (nodes.TryGetValue(data.m_uid, out var value) && object.ReferenceEquals(value.Data, data))
				{
					value.ObserveUse();
					if (value.Kind == Kinds.TamedAnimals && (!data.GetBool(ZDOVars.s_tamed, false) || value.Summoned))
					{
						Remove(value);
					}
				}
				else if ((kinds & Kinds.TamedAnimals) != Kinds.None && data.GetBool(ZDOVars.s_tamed, false))
				{
					ZNetView val = scene.FindInstance(data);
					if (Object.op_Implicit((Object)(object)val))
					{
						Register(data, val);
					}
				}
			}
			catch (Exception error)
			{
				Fail(error);
			}
		}

		internal void OwnerChanged(ZDO data, bool ownershipField = true)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: 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)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			if (failed || writing || indexed == null)
			{
				return;
			}
			try
			{
				if (!nodes.TryGetValue(data.m_uid, out var value) || !object.ReferenceEquals(value.Data, data))
				{
					long owner = data.GetOwner();
					if (active && ownershipField && catalog.IsLooseDrop(data.GetPrefab()) && owner != 0 && owner != server && data.IsValid() && object.ReferenceEquals(world.GetZDO(data.m_uid), data))
					{
						ZNetPeer peer = network.GetPeer(owner);
						ZNetView val = scene.FindInstance(data);
						if (peer != null && peer.IsReady() && Object.op_Implicit((Object)(object)val) && val.IsValid() && object.ReferenceEquals(val.GetZDO(), data) && Object.op_Implicit((Object)(object)((Component)val).GetComponent<ItemDrop>()))
						{
							world.ForceSendZDO(owner, data.m_uid);
						}
					}
				}
				else
				{
					if (Inventory.Transitioning(data) || Cooperative.Transitioning(data))
					{
						return;
					}
					long owner2 = data.GetOwner();
					uint ownerRevision = data.OwnerRevision;
					if (value.ObservedOwner == owner2 && value.Revision == ownerRevision)
					{
						return;
					}
					value.ObservedOwner = owner2;
					value.Revision = ownerRevision;
					if (active && (Inventory.Received(data) || Cooperative.Received(data)) && value.Identity() && value.Controllable() && AvailableOwner(owner2, data.GetPosition()))
					{
						value.Managed = true;
						value.ManagedOwner = owner2;
						if (owner2 != server)
						{
							world.ForceSendZDO(owner2, data.m_uid);
						}
						indexed.Received(value, Time.unscaledTime);
					}
					else
					{
						indexed.Changed(value, Time.unscaledTime, NeedsPolicy(value));
					}
				}
			}
			catch (Exception error)
			{
				Fail(error);
			}
		}

		internal bool Persistent(ZDO data)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			bool persistent = data.Persistent;
			try
			{
				if (!active || failed || !persistent || !object.ReferenceEquals(world, ZDOMan.instance) || BakedObjects.IsBaked(data))
				{
					return persistent;
				}
				if (nodes.TryGetValue(data.m_uid, out var value) && object.ReferenceEquals(value.Data, data) && (value.UseProtected() || ProtectedTransfer(data) || (value.Managed && value.ManagedOwner == data.GetOwner() && (NeedsGrid(value.Cell) || (refillStations && value.ServerStation)))) && value.Identity() && value.Controllable() && AvailableOwner(data.GetOwner(), data.GetPosition()))
				{
					return false;
				}
				return (statics == null || !statics.Manage(data)) && persistent;
			}
			catch (Exception error)
			{
				Fail(error);
				return persistent;
			}
		}

		internal bool TransferPending(ZDO data)
		{
			if (Inventory == null || !Inventory.Pending(data))
			{
				if (Cooperative != null)
				{
					return Cooperative.Pending(data);
				}
				return false;
			}
			return true;
		}

		internal void InventoryResponse(Container container, long sender, bool granted)
		{
			if (!active || Inventory == null)
			{
				return;
			}
			try
			{
				Inventory.Response(container, sender, granted);
			}
			catch (Exception error)
			{
				Fail(error);
			}
		}

		internal bool AvailableOwner(long owner, Vector3 position)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: 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_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: 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)
			if (owner == server)
			{
				return true;
			}
			ZNetPeer peer = network.GetPeer(owner);
			if (peer == null || !peer.IsReady() || !Cell.At(position.x, position.z, cellSize, out var cell))
			{
				return false;
			}
			OwnerCell key = new OwnerCell
			{
				Owner = owner,
				Cell = cell,
				Center = ZoneSystem.GetZone(peer.GetRefPos())
			};
			if (available.TryGetValue(key, out var value))
			{
				return value;
			}
			float num = (float)(cell.X * cellSize) - 32f;
			float num2 = (float)(cell.Z * cellSize) - 32f;
			value = ZNetScene.InActiveArea(new Vector3(num, 0f, num2), key.Center) && ZNetScene.InActiveArea(new Vector3(num + (float)cellSize, 0f, num2), key.Center) && ZNetScene.InActiveArea(new Vector3(num, 0f, num2 + (float)cellSize), key.Center) && ZNetScene.InActiveArea(new Vector3(num + (float)cellSize, 0f, num2 + (float)cellSize), key.Center);
			available[key] = value;
			return value;
		}

		private bool AreaReady(Vector3 position)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//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_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			Vector2s zone = ZoneSystem.GetZone(position);
			if (readiness.TryGetValue(zone, out var value) && value.Epoch == epoch)
			{
				return value.Ready;
			}
			int num = epoch;
			bool flag = scene.IsAreaReady(position);
			if (num != epoch)
			{
				return false;
			}
			readiness[zone] = new Receipt
			{
				Epoch = epoch,
				Ready = flag
			};
			return flag;
		}

		internal NativeNode RequestNode(ZDOID id, int method)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if (!active || failed || !nodes.TryGetValue(id, out var value) || !ObjectCatalog.OwnerRequest(value.Kind, method) || !value.Identity())
			{
				return null;
			}
			return value;
		}

		private bool NeedsGrid(Cell cell)
		{
			if (coarseEnabled)
			{
				return regions.Contains(cell, cellSize);
			}
			return true;
		}

		private bool ProtectedTransfer(ZDO data)
		{
			if (!Inventory.Transitioning(data) && !Cooperative.Transitioning(data) && !TransferPending(data))
			{
				return Cooperative.Reserved(data);
			}
			return true;
		}

		private bool NeedsPolicy(NativeNode node)
		{
			if (!NeedsGrid(node.Cell) && (!refillStations || !node.ServerStation))
			{
				return ProtectedTransfer(node.Data);
			}
			return true;
		}

		internal void RequestFinished(NativeNode node)
		{
			if (indexed != null && !node.Removed)
			{
				indexed.Received(node, Time.unscaledTime);
			}
		}

		internal void SceneDestroyed(ZNetScene destroyed)
		{
			if (object.ReferenceEquals(scene, destroyed))
			{
				Reset();
			}
		}

		internal void RouterFault(Exception error)
		{
			Fail(error);
		}

		internal void RequestFault(Exception error)
		{
			try
			{
				if (requestFault != null)
				{
					requestFault(error);
				}
			}
			catch
			{
			}
		}

		internal void Reset()
		{
			active = false;
			primed = false;
			coarseEnabled = false;
			if (Inventory != null)
			{
				Inventory.Clear();
			}
			if (Cooperative != null)
			{
				Cooperative.Clear();
			}
			Inventory = null;
			Cooperative = null;
			nodes.Clear();
			views = new ConditionalWeakTable<ZNetView, NativeNode>();
			readiness.Clear();
			available.Clear();
			peers.Clear();
			peerIds.Clear();
			cameras.Clear();
			regionPeers.Clear();
			characters.Clear();
			regions.Clear();
			Router.Clear();
			indexed = null;
			catalog = null;
			statics = null;
			scene = null;
			network = null;
			world = null;
			epoch = 0;
		}

		private void Fail(Exception error)
		{
			failed = true;
			Reset();
			try
			{
				fault(error);
			}
			catch
			{
			}
		}

		private static bool Finite(Vector3 v)
		{
			if (!float.IsNaN(v.x) && !float.IsNaN(v.y) && !float.IsNaN(v.z) && !float.IsInfinity(v.x) && !float.IsInfinity(v.y))
			{
				return !float.IsInfinity(v.z);
			}
			return false;
		}
	}
	internal abstract class AuthorityNode
	{
		internal Cell Cell;

		internal bool Managed;

		internal bool Removed;

		internal long ManagedOwner;

		internal long ConfirmedFieldOwner;

		internal float NotBefore;

		internal abstract long CurrentOwner { get; }

		internal abstract ApplyResult Apply(long target);

		internal virtual bool CanRemember(long fieldOwner)
		{
			if (Managed && ManagedOwner == fieldOwner)
			{
				return CurrentOwner == fieldOwner;
			}
			return false;
		}
	}
	internal enum GridCoveragePattern
	{
		Square3x3,
		Rounded5x5,
		Rounded7x7
	}
	internal enum FieldTier : byte
	{
		Outer,
		Inner,
		Forward
	}
	internal struct Cell : IEquatable<Cell>
	{
		internal int X;

		internal int Z;

		internal Cell(int x, int z)
		{
			X = x;
			Z = z;
		}

		internal static bool At(float x, float z, out Cell cell)
		{
			return At(x, z, 8, out cell);
		}

		internal static bool At(float x, float z, int size, out Cell cell)
		{
			cell = default(Cell);
			if (size != 4 && size != 8 && size != 16)
			{
				return false;
			}
			if (float.IsNaN(x) || float.IsNaN(z) || float.IsInfinity(x) || float.IsInfinity(z))
			{
				return false;
			}
			double num = Math.Floor(((double)x + 32.0) / (double)size);
			double num2 = Math.Floor(((double)z + 32.0) / (double)size);
			int num3 = 16384 / size;
			if (num < (double)(-num3) || num >= (double)num3 || num2 < (double)(-num3) || num2 >= (double)num3)
			{
				return false;
			}
			cell = new Cell((int)num, (int)num2);
			return true;
		}

		public bool Equals(Cell other)
		{
			if (X == other.X)
			{
				return Z == other.Z;
			}
			return false;
		}

		public override bool Equals(object other)
		{
			if (other is Cell)
			{
				return Equals((Cell)other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return (X * 397) ^ Z;
		}
	}
	internal struct GridPeer
	{
		internal long Uid;

		internal Cell Cell;

		internal float Yaw;

		internal int Coverage;

		internal GridPeer(long uid, Cell cell, float yaw = 0f, int coverage = -1)
		{
			Uid = uid;
			Cell = cell;
			Yaw = yaw;
			Coverage = coverage;
		}

		internal static GridPeer Covered(long uid, Cell cell, int coverage)
		{
			return new GridPeer(uid, cell, float.NaN, coverage);
		}
	}
	internal sealed class AuthorityGrid
	{
		private sealed class Policy
		{
			internal long Candidate;

			internal long Owner;

			internal long Uids;

			internal int Count;

			internal int FastCount;

			internal int ForwardCount;

			internal float Since;

			internal float Wait;

			internal float Deadline;

			internal bool Deferred;

			internal Request First;

			internal List<Request> Others;
		}

		private struct Request
		{
			internal long Uid;

			internal float Since;

			internal FieldTier Tier;

			internal Request(long uid, float since, FieldTier tier)
			{
				Uid = uid;
				Since = since;
				Tier = tier;
			}
		}

		private sealed class PeerState
		{
			internal Cell Cell;

			internal float Yaw;

			internal int Heading;

			internal bool Refreshed;
		}

		private struct Offset
		{
			internal Cell Cell;

			internal FieldTier Tier;

			internal Offset(int x, int z, FieldTier tier)
			{
				Cell = new Cell(x, z);
				Tier = tier;
			}
		}

		private readonly Dictionary<Cell, Policy> policies = new Dictionary<Cell, Policy>();

		private readonly Stack<Policy> reusable = new Stack<Policy>();

		private readonly Dictionary<long, PeerState> previous = new Dictionary<long, PeerState>();

		private readonly Dictionary<Cell, long> remembered = new Dictionary<Cell, long>();

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

		private readonly List<long> departed = new List<long>();

		private readonly HashSet<Cell> dirty = new HashSet<Cell>();

		private readonly HashSet<Cell> pending = new HashSet<Cell>();

		private readonly List<Cell> due = new List<Cell>();

		internal readonly List<Cell> Changes = new List<Cell>();

		private readonly long server;

		private readonly Offset[][] footprints = new Offset[16][];

		private readonly Dictionary<Cell, FieldTier>[] maskTypes = new Dictionary<Cell, FieldTier>[16];

		internal readonly HashSet<Cell> Accelerations = new HashSet<Cell>();

		private readonly bool directional;

		private readonly bool discardVacant;

		private float innerDelay;

		private float outerDelay;

		private float forwardDelay;

		internal float Delay;

		private float lastTime;

		private float nextDue = float.PositiveInfinity;

		private int deferredCells;

		internal int Rebuilds;

		internal int NeighborReads;

		internal int PlayerUpdates;

		internal int TimerVisits;

		internal int PolicyAllocations;

		internal int RememberedCells => remembered.Count;

		internal bool Directional => directional;

		internal bool HasPeers => previous.Count != 0;

		internal int CachedCells => policies.Count;

		internal int ReusablePolicies => reusable.Count;

		internal int DeferredCells => deferredCells;

		internal AuthorityGrid(long server, float delay, GridCoveragePattern pattern = GridCoveragePattern.Square3x3, bool directional = false, float innerDelay = 0f, float outerDelay = 3f, bool discardVacant = false, bool forward = false, float forwardDelay = 0.2f)
		{
			this.server = server;
			Delay = delay;
			this.directional = directional;
			this.discardVacant = discardVacant;
			this.innerDelay = SafeDelay(innerDelay, 0f);
			this.outerDelay = SafeDelay(outerDelay, 3f);
			this.forwardDelay = SafeDelay(forwardDelay, 0.2f);
			int num = pattern switch
			{
				GridCoveragePattern.Rounded5x5 => 2, 
				GridCoveragePattern.Rounded7x7 => 3, 
				_ => 1, 
			};
			forward = directional && forward && num > 1;
			int[] array = new int[8] { 0, 1, 1, 1, 0, -1, -1, -1 };
			int[] array2 = new int[8] { 1, 1, 0, -1, -1, -1, 0, 1 };
			for (int i = 0; i < 8; i++)
			{
				List<Offset> list = new List<Offset>(37);
				for (int j = -num; j <= num; j++)
				{
					for (int k = -num; k <= num; k++)
					{
						int num2 = j * array[i] + k * array2[i];
						int value = -j * array2[i] + k * array[i];
						bool flag = array[i] != 0 && array2[i] != 0;
						bool flag2 = forward && ((!flag) ? (num2 == 2 && Math.Abs(value) <= 1) : ((j == 2 * array[i] && (k == array2[i] || k == 2 * array2[i])) || (j == array[i] && k == 2 * array2[i])));
						if ((num == 1 || j * j + k * k <= num * (num + 1) || flag2) && (!directional || num2 >= 0 || (num2 >= -1 && Math.Abs(value) > 1)))
						{
							bool flag3 = false;
							if (directional)
							{
								flag3 = ((array[i] != 0 && array2[i] != 0) ? ((j == 0 && k == 0) || (num2 == 0 && Math.Abs(j) == 1 && Math.Abs(k) == 1) || (j == array[i] && k == array2[i]) || (j == array[i] && k == 0) || (j == 0 && k == array2[i])) : (num2 >= 0 && num2 <= 1 && Math.Abs(value) <= 1));
							}
							list.Add(new Offset(j, k, flag3 ? FieldTier.Inner : (flag2 ? FieldTier.Forward : FieldTier.Outer)));
						}
					}
				}
				footprints[i] = list.ToArray();
				Dictionary<Cell, FieldTier> dictionary = new Dictionary<Cell, FieldTier>(list.Count);
				foreach (Offset item in list)
				{
					dictionary.Add(item.Cell, item.Tier);
				}
				maskTypes[i] = dictionary;
			}
			for (int l = 0; l < 8; l++)
			{
				if (!directional)
				{
					footprints[8 + l] = footprints[l];
					maskTypes[8 + l] = maskTypes[l];
					continue;
				}
				Dictionary<Cell, FieldTier> dictionary2 = new Dictionary<Cell, FieldTier>(maskTypes[l]);
				Offset[] array3 = footprints[(l + 1) & 7];
				for (int m = 0; m < array3.Length; m++)
				{
					Offset offset = array3[m];
					if (!dictionary2.TryGetValue(offset.Cell, out var value2) || offset.Tier == FieldTier.Inner || (value2 == FieldTier.Outer && offset.Tier == FieldTier.Forward))
					{
						dictionary2[offset.Cell] = offset.Tier;
					}
				}
				List<Offset> list2 = new List<Offset>(dictionary2.Count);
				foreach (KeyValuePair<Cell, FieldTier> item2 in dictionary2)
				{
					list2.Add(new Offset(item2.Key.X, item2.Key.Z, item2.Value));
				}
				footprints[8 + l] = list2.ToArray();
				maskTypes[8 + l] = dictionary2;
			}
		}

		internal static int HeadingCoverage(float yaw)
		{
			if (float.IsNaN(yaw) || float.IsInfinity(yaw))
			{
				return -1;
			}
			yaw %= 360f;
			if (yaw < 0f)
			{
				yaw += 360f;
			}
			int num = (int)(yaw / 45f);
			float num2 = yaw - (float)num * 45f;
			if (!(num2 < 7.5f))
			{
				if (!(num2 <= 37.5f))
				{
					return (num + 1) & 7;
				}
				return 8 + num;
			}
			return num;
		}

		private static float SafeDelay(float value, float fallback)
		{
			if (!float.IsNaN(value) && !float.IsInfinity(value))
			{
				return Math.Max(0f, Math.Min(30f, value));
			}
			return fallback;
		}

		internal void ConfigureDelays(float full, float inner, float forward, float outer)
		{
			Delay = SafeDelay(full, 2f);
			innerDelay = SafeDelay(inner, 0f);
			forwardDelay = SafeDelay(forward, 0.2f);
			outerDelay = SafeDelay(outer, 3f);
		}

		private static float Yaw(float value, float fallback)
		{
			if (float.IsNaN(value) || float.IsInfinity(value))
			{
				return fallback;
			}
			value %= 360f;
			if (!(value < 0f))
			{
				return value;
			}
			return value + 360f;
		}

		internal long Owner(Cell cell)
		{
			if (!policies.TryGetValue(cell, out var value))
			{
				return 0L;
			}
			return value.Owner;
		}

		internal long Candidate(Cell cell)
		{
			if (!policies.TryGetValue(cell, out var value))
			{
				return 0L;
			}
			return value.Candidate;
		}

		private float FieldWait(Policy policy)
		{
			if (!directional)
			{
				return Delay;
			}
			if (policy.ForwardCount != 0)
			{
				float num = forwardDelay;
				if (policy.FastCount != 0)
				{
					num = Math.Min(num, innerDelay);
				}
				if (policy.Count > policy.FastCount + policy.ForwardCount)
				{
					num = Math.Min(num, outerDelay);
				}
				return num;
			}
			if (policy.FastCount == 0)
			{
				return outerDelay;
			}
			if (policy.FastCount != policy.Count)
			{
				return Math.Min(innerDelay, outerDelay);
			}
			return innerDelay;
		}

		internal float Wait(Cell cell)
		{
			if (!policies.TryGetValue(cell, out var value))
			{
				if (!directional)
				{
					return Delay;
				}
				return outerDelay;
			}
			return FieldWait(value);
		}

		internal float Remaining(Cell cell, float now)
		{
			if (!policies.TryGetValue(cell, out var value) || value.Owner == value.Candidate)
			{
				return 0f;
			}
			return Math.Max(0f, value.Deadline - now);
		}

		internal bool IsRemembered(Cell cell, long uid)
		{
			if (uid != 0 && uid != server && remembered.TryGetValue(cell, out var value))
			{
				return value == uid;
			}
			return false;
		}

		internal void Remember(Cell cell, long uid)
		{
			if (uid != 0 && uid != server && Owner(cell) == uid && Candidate(cell) == uid)
			{
				remembered[cell] = uid;
			}
		}

		internal void Forget(Cell cell)
		{
			remembered.Remove(cell);
		}

		private void Contribute(long uid, PeerState peer, bool add, float now)
		{
			Offset[] array = footprints[peer.Heading];
			for (int i = 0; i < array.Length; i++)
			{
				Offset offset = array[i];
				Contribution(uid, new Cell(peer.Cell.X + offset.Cell.X, peer.Cell.Z + offset.Cell.Z), offset.Tier, add, now);
			}
		}

		private void Contribution(long uid, Cell cell, FieldTier tier, bool add, float now)
		{
			NeighborReads++;
			if (!policies.TryGetValue(cell, out var value))
			{
				if (reusable.Count != 0)
				{
					value = reusable.Pop();
				}
				else
				{
					value = new Policy();
					PolicyAllocations++;
				}
				policies.Add(cell, value);
			}
			value.Count += (add ? 1 : (-1));
			value.Uids ^= uid;
			switch (tier)
			{
			case FieldTier.Inner:
				value.FastCount += (add ? 1 : (-1));
				break;
			case FieldTier.Forward:
				value.ForwardCount += (add ? 1 : (-1));
				break;
			}
			if (add)
			{
				Request request = new Request(uid, now, tier);
				if (value.Count == 1)
				{
					value.First = request;
				}
				else
				{
					if (value.Others == null)
					{
						value.Others = new List<Request>(1);
					}
					value.Others.Add(request);
				}
				if (remembered.Count != 0 && remembered.TryGetValue(cell, out var value2) && value2 != uid)
				{
					remembered.Remove(cell);
				}
			}
			else if (value.First.Uid == uid)
			{
				if (value.Others != null && value.Others.Count != 0)
				{
					int index = value.Others.Count - 1;
					value.First = value.Others[index];
					value.Others.RemoveAt(index);
				}
				else
				{
					value.First = default(Request);
				}
			}
			else if (value.Others != null)
			{
				for (int i = 0; i < value.Others.Count; i++)
				{
					if (value.Others[i].Uid == uid)
					{
						value.Others.RemoveAt(i);
						break;
					}
				}
			}
			dirty.Add(cell);
		}

		private void Reclassify(long uid, Cell cell, FieldTier before, FieldTier after)
		{
			Policy policy = policies[cell];
			NeighborReads += 2;
			switch (before)
			{
			case FieldTier.Inner:
				policy.FastCount--;
				break;
			case FieldTier.Forward:
				policy.ForwardCount--;
				break;
			}
			switch (after)
			{
			case FieldTier.Inner:
				policy.FastCount++;
				break;
			case FieldTier.Forward:
				policy.ForwardCount++;
				break;
			}
			if (policy.First.Uid == uid)
			{
				policy.First.Tier = after;
			}
			else if (policy.Others != null)
			{
				for (int i = 0; i < policy.Others.Count; i++)
				{
					if (policy.Others[i].Uid == uid)
					{
						Request value = policy.Others[i];
						value.Tier = after;
						policy.Others[i] = value;
						break;
					}
				}
			}
			dirty.Add(cell);
		}

		private void MoveContribution(long uid, PeerState old, Cell next, int heading, float now)
		{
			Offset[] array = footprints[old.Heading];
			for (int i = 0; i < array.Length; i++)
			{
				Offset offset = array[i];
				Cell cell = new Cell(old.Cell.X + offset.Cell.X, old.Cell.Z + offset.Cell.Z);
				if (maskTypes[heading].TryGetValue(new Cell(cell.X - next.X, cell.Z - next.Z), out var value))
				{
					if (value != offset.Tier)
					{
						Reclassify(uid, cell, offset.Tier, value);
					}
					else if (deferredCells != 0 && policies[cell].Deferred)
					{
						dirty.Add(cell);
					}
				}
				else
				{
					Contribution(uid, cell, offset.Tier, add: false, now);
				}
			}
			Offset[] array2 = footprints[heading];
			for (int j = 0; j < array2.Length; j++)
			{
				Offset offset2 = array2[j];
				Cell cell2 = new Cell(next.X + offset2.Cell.X, next.Z + offset2.Cell.Z);
				if (!maskTypes[old.Heading].ContainsKey(new Cell(cell2.X - old.Cell.X, cell2.Z - old.Cell.Z)))
				{
					Contribution(uid, cell2, offset2.Tier, add: true, now);
				}
			}
		}

		private void Commit(Cell cell, Policy policy)
		{
			pending.Remove(cell);
			if (policy.Owner != policy.Candidate)
			{
				policy.Owner = policy.Candidate;
				Changes.Add(cell);
			}
			if (policy.Count == 0 && policy.Owner == 0 && policies.Remove(cell) && discardVacant && reusable.Count < 1024)
			{
				policy.Candidate = (policy.Owner = (policy.Uids = 0L));
				policy.Count = (policy.FastCount = (policy.ForwardCount = 0));
				policy.Since = (policy.Wait = (policy.Deadline = 0f));
				policy.First = default(Request);
				if (policy.Others != null)
				{
					policy.Others.Clear();
				}
				reusable.Push(policy);
			}
		}

		private void Evaluate(Cell cell, float now)
		{
			Policy policy = policies[cell];
			if (discardVacant && policy.Count == 0)
			{
				SetDeferred(policy, value: false);
				policy.Candidate = 0L;
				Commit(cell, policy);
				return;
			}
			if (directional && discardVacant && policy.Count == 1 && policy.First.Tier != FieldTier.Inner && (policy.Deferred || policy.Candidate == server) && previous.TryGetValue(policy.Uids, out var value) && !value.Refreshed)
			{
				SetDeferred(policy, value: true);
				policy.Candidate = policy.Owner;
				policy.Wait = FieldWait(policy);
				pending.Remove(cell);
				return;
			}
			SetDeferred(policy, value: false);
			long num = ((policy.Count > 1) ? server : ((policy.Count == 1) ? policy.Uids : 0));
			if (policy.Candidate != num)
			{
				if (policy.Owner == policy.Candidate || policy.Candidate == 0 || num != server)
				{
					policy.Since = now;
				}
				policy.Candidate = num;
			}
			float num2 = FieldWait(policy);
			if (num2 < policy.Wait && policy.Candidate != 0)
			{
				Accelerations.Add(cell);
			}
			policy.Wait = num2;
			if (num == server && remembered.Count != 0)
			{
				Forget(cell);
			}
			if (policy.Owner == num || (remembered.Count != 0 && IsRemembered(cell, num)))
			{
				Commit(cell, policy);
				return;
			}
			policy.Deadline = policy.Since + num2;
			if (policy.Count != 0)
			{
				policy.Deadline = RequestDeadline(policy.First, policy.Since, now);
				if (policy.Others != null)
				{
					foreach (Request other in policy.Others)
					{
						policy.Deadline = Math.Min(policy.Deadline, RequestDeadline(other, policy.Since, now));
					}
				}
			}
			if (now >= policy.Deadline)
			{
				Commit(cell, policy);
				return;
			}
			pending.Add(cell);
			nextDue = Math.Min(nextDue, policy.Deadline);
		}

		private float RequestDeadline(Request request, float since, float now)
		{
			return Math.Max(Math.Min(request.Since, now), since) + ((!directional) ? Delay : ((request.Tier == FieldTier.Inner) ? innerDelay : ((request.Tier == FieldTier.Forward) ? forwardDelay : outerDelay)));
		}

		private void SetDeferred(Policy policy, bool value)
		{
			if (policy.Deferred != value)
			{
				policy.Deferred = value;
				deferredCells += (value ? 1 : (-1));
			}
		}

		internal void Update(List<GridPeer> peers, float now)
		{
			Changes.Clear();
			Accelerations.Clear();
			dirty.Clear();
			seen.Clear();
			departed.Clear();
			bool flag = now < lastTime;
			lastTime = now;
			if (flag)
			{
				nextDue = float.PositiveInfinity;
				foreach (Cell item in pending)
				{
					Policy policy = policies[item];
					policy.Since = now;
					policy.Deadline = now + policy.Wait;
					policy.First.Since = now;
					if (policy.Others != null)
					{
						for (int i = 0; i < policy.Others.Count; i++)
						{
							Request value = policy.Others[i];
							value.Since = now;
							policy.Others[i] = value;
						}
					}
					nextDue = Math.Min(nextDue, policy.Deadline);
				}
			}
			bool flag2 = false;
			foreach (GridPeer peer in peers)
			{
				if (peer.Uid == 0 || peer.Uid == server || !seen.Add(peer.Uid))
				{
					continue;
				}
				PeerState value2;
				bool flag3 = previous.TryGetValue(peer.Uid, out value2);
				if (flag3)
				{
					value2.Refreshed = false;
				}
				float num = Yaw(peer.Yaw, flag3 ? value2.Yaw : 0f);
				int num2 = (directional ? ((peer.Coverage >= 0 && peer.Coverage < 16) ? peer.Coverage : ((flag3 && (float.IsNaN(peer.Yaw) || float.IsInfinity(peer.Yaw))) ? value2.Heading : ((int)((num + 22.5f) / 45f) & 7))) : 0);
				if (!flag3 || !value2.Cell.Equals(peer.Cell) || value2.Heading != num2)
				{
					if (flag3)
					{
						MoveContribution(peer.Uid, value2, peer.Cell, num2, now);
					}
					else
					{
						value2 = new PeerState();
						previous.Add(peer.Uid, value2);
					}
					value2.Cell = peer.Cell;
					value2.Yaw = num;
					value2.Heading = num2;
					value2.Refreshed = true;
					if (!flag3)
					{
						Contribute(peer.Uid, value2, add: true, now);
					}
					flag2 = true;
					PlayerUpdates++;
				}
			}
			foreach (KeyValuePair<long, PeerState> previou in previous)
			{
				if (!seen.Contains(previou.Key))
				{
					departed.Add(previou.Key);
				}
			}
			foreach (long item2 in departed)
			{
				Contribute(item2, previous[item2], add: false, now);
				previous.Remove(item2);
				flag2 = true;
			}
			if (flag2)
			{
				Rebuilds++;
				foreach (Cell item3 in dirty)
				{
					Evaluate(item3, now);
				}
			}
			if (pending.Count == 0)
			{
				nextDue = float.PositiveInfinity;
			}
			else
			{
				if (now < nextDue)
				{
					return;
				}
				due.Clear();
				foreach (Cell item4 in pending)
				{
					due.Add(item4);
				}
				nextDue = float.PositiveInfinity;
				foreach (Cell item5 in due)
				{
					TimerVisits++;
					if (policies.TryGetValue(item5, out var value3))
					{
						if (now >= value3.Deadline)
						{
							Commit(item5, value3);
						}
						else
						{
							nextDue = Math.Min(nextDue, value3.Deadline);
						}
					}
				}
				due.Clear();
			}
		}
	}
	internal enum ApplyResult
	{
		Done,
		Pending,
		Dropped
	}
	internal sealed class AuthorityWorld
	{
		private sealed class ObjectBucket
		{
			internal readonly HashSet<AuthorityNode> Nodes = new HashSet<AuthorityNode>();

			internal long Target;

			internal int Unconfirmed;
		}

		internal readonly AuthorityGrid Grid;

		private readonly Dictionary<Cell, ObjectBucket> buckets = new Dictionary<Cell, ObjectBucket>();

		private readonly HashSet<AuthorityNode> pending = new HashSet<AuthorityNode>();

		private readonly List<AuthorityNode> scratch = new List<AuthorityNode>();

		private readonly bool keepOnExit;

		private readonly long server;

		private float lastTime;

		internal int Attempts;

		internal int SkippedCells;

		internal int BucketCount => buckets.Count;

		internal int PendingCount => pending.Count;

		internal AuthorityWorld(long server, float delay, GridCoveragePattern pattern = GridCoveragePattern.Square3x3, bool keepOnExit = true, bool directional = false, float innerDelay = 0f, float outerDelay = 3f, bool forward = false, float forwardDelay = 0.2f)
		{
			Grid = new AuthorityGrid(server, delay, pattern, directional, innerDelay, outerDelay, keepOnExit, forward, forwardDelay);
			this.keepOnExit = keepOnExit;
			this.server = server;
		}

		internal void Add(AuthorityNode node, float now, bool schedule = true)
		{
			node.Removed = false;
			ObjectBucket objectBucket = Bucket(node.Cell);
			if (objectBucket.Nodes.Add(node))
			{
				node.ConfirmedFieldOwner = 0L;
				if (objectBucket.Target != 0)
				{
					objectBucket.Unconfirmed++;
				}
				if (keepOnExit)
				{
					Grid.Forget(node.Cell);
				}
				if (schedule)
				{
					Queue(node, now);
				}
				else
				{
					node.Managed = false;
				}
			}
		}

		internal void Remove(AuthorityNode node)
		{
			Detach(node);
			node.Removed = true;
			node.Managed = false;
			pending.Remove(node);
		}

		internal void Move(AuthorityNode node, Cell next, float now, bool schedule = true)
		{
			if (!node.Removed && !node.Cell.Equals(next))
			{
				Detach(node);
				node.Cell = next;
				ObjectBucket objectBucket = Bucket(next);
				objectBucket.Nodes.Add(node);
				node.ConfirmedFieldOwner = 0L;
				if (objectBucket.Target != 0)
				{
					objectBucket.Unconfirmed++;
				}
				if (keepOnExit)
				{
					Grid.Forget(next);
				}
				if (schedule)
				{
					Queue(node, now + Grid.Wait(next));
					return;
				}
				pending.Remove(node);
				node.Managed = false;
			}
		}

		internal void Changed(AuthorityNode node, float now, bool schedule = true)
		{
			if (!node.Removed)
			{
				Invalidate(node);
				if (schedule)
				{
					Queue(node, now + Grid.Wait(node.Cell));
					return;
				}
				pending.Remove(node);
				node.Managed = false;
			}
		}

		internal void Received(AuthorityNode node, float now)
		{
			if (!node.Removed)
			{
				Invalidate(node);
				Queue(node, Math.Max(now, node.NotBefore));
			}
		}

		internal void Tick(List<GridPeer> peers, float now)
		{
			if (peers.Count == 0 && !Grid.HasPeers && Grid.CachedCells == 0 && pending.Count == 0)
			{
				Grid.Changes.Clear();
				Grid.Accelerations.Clear();
				return;
			}
			if (now < lastTime)
			{
				foreach (AuthorityNode item in pending)
				{
					item.NotBefore = now + Grid.Wait(item.Cell);
				}
			}
			lastTime = now;
			Grid.Update(peers, now);
			foreach (Cell change in Grid.Changes)
			{
				if ((keepOnExit && Grid.Owner(change) == 0) || !buckets.TryGetValue(change, out var value))
				{
					continue;
				}
				long num = Grid.Owner(change);
				if (keepOnExit && Grid.IsRemembered(change, num) && value.Target == num && value.Unconfirmed == 0)
				{
					SkippedCells++;
					continue;
				}
				value.Target = ((keepOnExit && num != server) ? num : 0);
				value.Unconfirmed = ((value.Target != 0) ? value.Nodes.Count : 0);
				foreach (AuthorityNode node in value.Nodes)
				{
					node.ConfirmedFieldOwner = 0L;
					if (!pending.Contains(node))
					{
						Queue(node, now);
					}
					else if (Grid.Directional)
					{
						node.NotBefore = Math.Min(node.NotBefore, now + Grid.Wait(change));
					}
				}
			}
			scratch.Clear();
			foreach (AuthorityNode item2 in pending)
			{
				scratch.Add(item2);
			}
			foreach (AuthorityNode item3 in scratch)
			{
				if (item3.Removed || !pending.Contains(item3))
				{
					continue;
				}
				if (Grid.Accelerations.Count != 0 && Grid.Accelerations.Contains(item3.Cell))
				{
					item3.NotBefore = Math.Min(item3.NotBefore, now + Grid.Wait(item3.Cell));
				}
				if (now < item3.NotBefore)
				{
					continue;
				}
				Attempts++;
				ApplyResult applyResult = item3.Apply(Grid.Owner(item3.Cell));
				if (applyResult != ApplyResult.Pending)
				{
					pending.Remove(item3);
				}
				switch (applyResult)
				{
				case ApplyResult.Dropped:
					item3.Managed = false;
					break;
				case ApplyResult.Done:
					if (keepOnExit && !item3.Removed)
					{
						Confirm(item3);
					}
					break;
				}
			}
			scratch.Clear();
		}

		private void Queue(AuthorityNode node, float at)
		{
			node.NotBefore = at;
			pending.Add(node);
		}

		private void Invalidate(AuthorityNode node)
		{
			if (keepOnExit)
			{
				Grid.Forget(node.Cell);
				if (buckets.TryGetValue(node.Cell, out var value) && value.Target != 0 && node.ConfirmedFieldOwner == value.Target)
				{
					node.ConfirmedFieldOwner = 0L;
					value.Unconfirmed++;
				}
			}
		}

		private void Confirm(AuthorityNode node)
		{
			if (buckets.TryGetValue(node.Cell, out var value) && value.Target != 0 && node.ConfirmedFieldOwner != value.Target && node.CanRemember(value.Target))
			{
				node.ConfirmedFieldOwner = value.Target;
				value.Unconfirmed--;
				Certify(node.Cell, value);
			}
		}

		private void Certify(Cell cell, ObjectBucket bucket)
		{
			if (keepOnExit && bucket.Target != 0 && bucket.Unconfirmed == 0)
			{
				Grid.Remember(cell, bucket.Target);
			}
		}

		private void Detach(AuthorityNode node)
		{
			if (buckets.TryGetValue(node.Cell, out var value) && value.Nodes.Remove(node))
			{
				bool flag = value.Target != 0 && node.ConfirmedFieldOwner != value.Target;
				if (flag)
				{
					value.Unconfirmed--;
				}
				node.ConfirmedFieldOwner = 0L;
				if (value.Nodes.Count == 0)
				{
					buckets.Remove(node.Cell);
					Grid.Forget(node.Cell);
				}
				else if (flag)
				{
					Certify(node.Cell, value);
				}
			}
		}

		private ObjectBucket Bucket(Cell cell)
		{
			if (!buckets.TryGetValue(cell, out var value))
			{
				long num = Grid.Owner(cell);
				ObjectBucket objectBucket = new ObjectBucket();
				objectBucket.Target = ((keepOnExit && num != server) ? num : 0);
				value = objectBucket;
				buckets.Add(cell, value);
			}
			return value;
		}
	}
	internal static class BakedObjects
	{
		private static readonly int multiplayer = StringExtensionMethods.GetStableHashCode("PUP_SAFE_Baked_multiplayer");

		private static readonly int solo = StringExtensionMethods.GetStableHashCode("PUP_SAFE_Baked_solo");

		internal static bool IsBaked(ZDO data)
		{
			if (!data.GetBool(multiplayer, false))
			{
				return data.GetBool(solo, false);
			}
			return true;
		}
	}
	internal static class CameraHeading
	{
		internal static readonly int Key = StringExtensionMethods.GetStableHashCode("botanic.toomanylags.fgnaddon.cameraCoverage75");

		internal static int Coverage(float yaw)
		{
			return AuthorityGrid.HeadingCoverage(yaw);
		}

		internal static int Coverage(float x, float z)
		{
			if (float.IsNaN(x) || float.IsNaN(z) || float.IsInfinity(x) || float.IsInfinity(z) || (double)x * (double)x + (double)z * (double)z < 1E-06)
			{
				return -1;
			}
			return Coverage((float)(Math.Atan2(x, z) * (180.0 / Math.PI)));
		}

		internal static int Read(ZDO player, long peer, CameraWake wake = null)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			bool flag = player.GetOwner() == peer;
			int num = (flag ? player.GetInt(Key, -1) : (-1));
			if (wake != null && flag)
			{
				wake.Track(player.m_uid, peer, num);
			}
			if (num < 0 || num >= 16)
			{
				Quaternion rotation = player.GetRotation();
				return Coverage(((Quaternion)(ref rotation)).eulerAngles.y);
			}
			return num;
		}
	}
	internal sealed class CameraWake
	{
		private struct Stamp
		{
			internal long Peer;

			internal int Coverage;
		}

		private readonly Dictionary<ZDOID, Stamp> players = new Dictionary<ZDOID, Stamp>();

		internal int Count => players.Count;

		private static int Valid(int value)
		{
			if (value < 0 || value >= 16)
			{
				return -1;
			}
			return value;
		}

		internal void Clear()
		{
			players.Clear();
		}

		internal void Track(ZDOID id, long peer, int coverage)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			players[id] = new Stamp
			{
				Peer = peer,
				Coverage = Valid(coverage)
			};
		}

		internal bool Changed(ZDO data)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			if (data == null || !players.TryGetValue(data.m_uid, out var value) || data.GetOwner() != value.Peer || !data.IsValid() || ZDOMan.instance == null || !object.ReferenceEquals(ZDOMan.instance.GetZDO(data.m_uid), data))
			{
				return false;
			}
			int num = Valid(data.GetInt(CameraHeading.Key, -1));
			if (num == value.Coverage)
			{
				return false;
			}
			value.Coverage = num;
			players[data.m_uid] = value;
			return true;
		}
	}
	internal sealed class CameraPublisher
	{
		private readonly OwnershipCadence cadence;

		private ZDO previous;

		private ZNet network;

		private ZDOMan world;

		internal CameraPublisher(float interval)
		{
			cadence = new OwnershipCadence(interval);
		}

		internal void Configure(float interval)
		{
			cadence.Configure(interval);
		}

		internal void Sample(float now)
		{
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: 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)
			if (!cadence.Due(now))
			{
				return;
			}
			ZNet instance = ZNet.instance;
			if (!Object.op_Implicit((Object)(object)instance) || instance.IsDedicated() || ZNet.IsSinglePlayer || ZDOMan.instance == null)
			{
				Reset();
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer) || instance.GetServerPeer() == null)
			{
				Reset();
				return;
			}
			ZNetView component = ((Component)localPlayer).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || !component.IsOwner())
			{
				Reset();
				return;
			}
			ZDO zDO = component.GetZDO();
			if (zDO == null || !zDO.IsValid() || ((ZDOID)(ref zDO.m_uid)).IsNone() || !object.ReferenceEquals(ZDOMan.instance.GetZDO(zDO.m_uid), zDO))
			{
				Reset();
				return;
			}
			bool flag = !object.ReferenceEquals(previous, zDO) || !object.ReferenceEquals(network, instance) || !object.ReferenceEquals(world, ZDOMan.instance);
			GameCamera instance2 = GameCamera.instance;
			int num = -1;
			if (Object.op_Implicit((Object)(object)instance2) && !GameCamera.InFreeFly())
			{
				Vector3 forward = ((Component)instance2).transform.forward;
				num = CameraHeading.Coverage(forward.x, forward.z);
			}
			if (flag)
			{
				ZDOExtraData.AddSessionHash(CameraHeading.Key);
			}
			if (flag || zDO.GetInt(CameraHeading.Key, -1) != num)
			{
				zDO.Set(CameraHeading.Key, num, false);
				ZDOMan.instance.ForceSendZDO(zDO.m_uid);
			}
			previous = zDO;
			network = instance;
			world = ZDOMan.instance;
		}

		internal void Reset(bool invalidate = false)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			if (invalidate && previous != null && object.ReferenceEquals(network, ZNet.instance) && object.ReferenceEquals(world, ZDOMan.instance) && previous.IsValid() && previous.GetOwner() == ZDOMan.GetSessionID() && object.ReferenceEquals(world.GetZDO(previous.m_uid), previous) && previous.GetInt(CameraHeading.Key, -1) != -1)
			{
				previous.Set(CameraHeading.Key, -1, false);
				world.ForceSendZDO(previous.m_uid);
			}
			previous = null;
			network = null;
			world = null;
		}
	}
	[HarmonyPatch(typeof(GameCamera), "LateUpdate")]
	internal static class CameraSamplePatch
	{
		[HarmonyPriority(0)]
		[HarmonyPostfix]
		private static void Postfix()
		{
			Plugin.SampleCamera();
		}
	}
	[HarmonyPatch(typeof(GameCamera), "OnDestroy")]
	internal static class CameraLossPatch
	{
		[HarmonyPrefix]
		private static void Prefix(GameCamera __instance)
		{
			if ((Object)(object)GameCamera.instance == (Object)(object)__instance)
			{
				Plugin.InvalidateCamera();
			}
		}
	}
	internal abstract class CellReadiness
	{
		internal abstract bool Ready(Vector3 point, float radius);

		internal static CellReadiness Create(MonoBehaviour pup)
		{
			Type type = ((object)pup).GetType();
			Type type2 = type.GetField("multiplayerRemoteCells", BindingFlags.Instance | BindingFlags.NonPublic).FieldType.GetGenericArguments()[1];
			Type type3 = type.GetField("fastColliderCells", BindingFlags.Instance | BindingFlags.NonPublic).FieldType.GetGenericArguments()[1];
			Type type4 = type.GetField("records", BindingFlags.Instance | BindingFlags.NonPublic).FieldType.GetGenericArguments()[1].GetGenericArguments()[0];
			return (CellReadiness)Activator.CreateInstance(typeof(TypedCellReadiness<, , >).MakeGenericType(type2, type3, type4), pup);
		}
	}
	internal sealed class TypedCellReadiness<R, F, B> : CellReadiness where R : class where F : class
	{
		private readonly MonoBehaviour pup;

		private readonly Func<object, Dictionary<Vector2Int, List<B>>> records;

		private readonly Func<object, Dictionary<Vector2Int, R>> remotes;

		private readonly Func<object, Dictionary<Vector2Int, F>> fasts;

		private readonly Func<object, Dictionary<Vector2Int, GameObject>> roots;

		private readonly Func<object, Dictionary<Vector2Int, string>> ids;

		private readonly Func<object, Dictionary<GameObject, string>> validated;

		private readonly Func<object, Dictionary<Vector2Int, bool>> active;

		private readonly Func<object, Dictionary<Vector2Int, HashSet<string>>> loaded;

		private readonly Func<object, HashSet<Vector2Int>> physics;

		private readonly Func<object, HashSet<Vector2Int>> verified;

		private readonly Func<object, HashSet<Vector2Int>> worldRestores;

		private readonly Func<object, HashSet<Vector2Int>> vanillaRestores;

		private readonly Func<object, HashSet<Vector2Int>> loading;

		private readonly Func<object, int> queued;

		private readonly Func<R, string> remoteId;

		private readonly Func<F, string> fastId;

		private readonly Func<F, GameObject> fastRoot;

		private readonly Func<F, HashSet<string>> required;

		private readonly Func<Vector2Int, bool> pending;

		public TypedCellReadiness(MonoBehaviour value)
		{
			pup = value;
			Type type = ((object)pup).GetType();
			records = Read<Dictionary<Vector2Int, List<B>>>(type, "records");
			remotes = Read<Dictionary<Vector2Int, R>>(type, "multiplayerRemoteCells");
			fasts = Read<Dictionary<Vector2Int, F>>(type, "fastColliderCells");
			roots = Read<Dictionary<Vector2Int, GameObject>>(type, "clusters");
			ids = Read<Dictionary<Vector2Int, string>>(type, "cacheIds");
			validated = Read<Dictionary<GameObject, string>>(type, "validatedReplacementCacheIds");
			active = Read<Dictionary<Vector2Int, bool>>(type, "colliderCellActive");
			loaded = Read<Dictionary<Vector2Int, HashSet<string>>>(type, "fastLoadedSubclusters");
			physics = Read<HashSet<Vector2Int>>(type, "multiplayerRemotePhysicsReady");
			verified = Read<HashSet<Vector2Int>>(type, "verifiedRemoteReplacements");
			worldRestores = Read<HashSet<Vector2Int>>(type, "pendingWorldSaveRestores");
			vanillaRestores = Read<HashSet<Vector2Int>>(type, "multiplayerPendingVanillaRestore");
			loading = Read<HashSet<Vector2Int>>(type, "loadingCells");
			queued = Read<int>(type, "queuedColliderCount");
			remoteId = FieldReader.Create<R, string>(Field(typeof(R), "cacheId"));
			fastId = FieldReader.Create<F, string>(Field(typeof(F), "cacheId"));
			fastRoot = FieldReader.Create<F, GameObject>(Field(typeof(F), "root"));
			required = FieldReader.Create<F, HashSet<string>>(Field(typeof(F), "required"));
			pending = (Func<Vector2Int, bool>)Delegate.CreateDelegate(typeof(Func<Vector2Int, bool>), pup, type.GetMethod("HasPendingCollidersForCell", BindingFlags.Instance | BindingFlags.NonPublic));
		}

		private static FieldInfo Field(Type type, string name)
		{
			return type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}

		private static Func<object, T> Read<T>(Type type, string name)
		{
			return FieldReader.Create<object, T>(Field(type, name));
		}

		internal override bool Ready(Vector3 point, float radius)
		{
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_029e: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0392: Unknown result type (might be due to invalid IL or missing references)
			//IL_0306: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0338: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e3: Unknown result type (might be due to invalid IL or missing references)
			int num = Mathf.FloorToInt((point.x - radius) / 64f);
			int num2 = Mathf.FloorToInt((point.x + radius) / 64f);
			int num3 = Mathf.FloorToInt((point.z - radius) / 64f);
			int num4 = Mathf.FloorToInt((point.z + radius) / 64f);
			Dictionary<Vector2Int, R> dictionary = remotes(pup);
			Dictionary<Vector2Int, GameObject> dictionary2 = roots(pup);
			Dictionary<Vector2Int, bool> dictionary3 = active(pup);
			Vector2Int val = default(Vector2Int);
			for (int i = num; i <= num2; i++)
			{
				for (int j = num3; j <= num4; j++)
				{
					float num5 = Mathf.Max(0f, Mathf.Max((float)i * 64f - point.x, point.x - (float)(i + 1) * 64f));
					float num6 = Mathf.Max(0f, Mathf.Max((float)j * 64f - point.z, point.z - (float)(j + 1) * 64f));
					if (num5 * num5 + num6 * num6 > radius * radius)
					{
						continue;
					}
					((Vector2Int)(ref val))..ctor(i, j);
					R value;
					bool flag = dictionary.TryGetValue(val, out value);
					List<B> value2;
					bool flag2 = records(pup).TryGetValue(val, out value2);
					if (!flag && !flag2)
					{
						continue;
					}
					if (worldRestores(pup).Contains(val) || vanillaRestores(pup).Contains(val) || !flag2 || value2 == null || value2.Count == 0 || !ids(pup).TryGetValue(val, out var value3) || string.IsNullOrEmpty(value3) || value3 == "RESTORE_PENDING" || (flag && value3 != remoteId(value)))
					{
						return false;
					}
					GameObject value4;
					if (flag)
					{
						if (!physics(pup).Contains(val) || !verified(pup).Contains(val) || !dictionary2.TryGetValue(val, out value4) || !Object.op_Implicit((Object)(object)value4) || !value4.activeInHierarchy || !validated(pup).TryGetValue(value4, out var value5) || value5 != remoteId(value))
						{
							return false;
						}
					}
					else
					{
						if (ZNet.instance.IsDedicated() || (!ids(pup).ContainsKey(val) && !dictionary2.ContainsKey(val)))
						{
							continue;
						}
						if (fasts(pup).TryGetValue(val, out var value6))
						{
							value4 = fastRoot(value6);
							if (!Object.op_Implicit((Object)(object)value4) || !value4.activeInHierarchy || !ids(pup).TryGetValue(val, out var value7) || value7 != fastId(value6) || !loaded(pup).TryGetValue(val, out var value8))
							{
								return false;
							}
							foreach (string item in required(value6))
							{
								if (!value8.Contains(item))
								{
									return false;
								}
							}
						}
						else if (!dictionary2.TryGetValue(val, out value4) || !Object.op_Implicit((Object)(object)value4) || !value4.activeInHierarchy || loading(pup).Contains(val) || (queued(pup) > 0 && pending(val)))
						{
							return false;
						}
					}
					if (dictionary3.TryGetValue(val, out var value9) && !value9)
					{
						return false;
					}
				}
			}
			return true;
		}
	}
	internal sealed class CompatibilityBridge
	{
		private const BindingFlags Flags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private readonly BaseUnityPlugin fgn;

		private readonly ConfigEntry<bool> sss;

		private readonly ConfigEntry<bool> broad;

		private readonly ConfigEntry<bool> selective;

		private readonly ConfigEntry<bool> inserts;

		private readonly Func<bool> simulation;

		private readonly Func<bool> serverShips;

		private readonly Func<ConfigEntry<bool>> helm;

		internal readonly Assembly Assembly;

		internal bool ShipsOwnedExternally { get; private set; }

		internal CompatibilityBridge(BaseUnityPlugin fgn)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			this.fgn = fgn;
			Assembly = ((object)fgn).GetType().Assembly;
			BepInPlugin val = (BepInPlugin)Attribute.GetCustomAttribute(((object)fgn).GetType(), typeof(BepInPlugin));
			if (val == null || val.GUID != "com.Fire.FiresGhettoNetworkMod")
			{
				throw new NotSupportedException("FGN plugin identity changed.");
			}
			sss = Config(((object)fgn).GetType(), "ConfigEnableServerAuthority");
			broad = Config(((object)fgn).GetType(), "ConfigEnableServerOwnership");
			selective = Config(((object)fgn).GetType(), "ConfigEnableServerOwnershipSelective");
			simulation = Read<bool>(Assembly.GetType("FiresGhettoNetworkMod.ServerAuthorityPatches", throwOnError: true), "SimulationActive");
			inserts = Config(Assembly.GetType("FiresGhettoNetworkMod.StationRouter", throwOnError: true), "ConfigEnabled");
			Type type = Assembly.GetType("FiresGhettoNetworkMod.HelmOwnership", throwOnError: false);
			if (type != null)
			{
				serverShips = Read<bool>(type, "ServerOwnsShips");
				helm = Read<ConfigEntry<bool>>(type, "ConfigEnabled");
			}
		}

		internal bool Enabled()
		{
			ConfigEntry<bool> val = ((helm == null) ? null : helm());
			ShipsOwnedExternally = serverShips != null && serverShips() && val != null && val.Value;
			if (Object.op_Implicit((Object)(object)fgn) && ((Behaviour)fgn).isActiveAndEnabled && sss.Value && simulation() && !broad.Value && !selective.Value)
			{
				return inserts.Value;
			}
			return false;
		}

		internal bool Configured()
		{
			if (sss.Value && !broad.Value && !selective.Value)
			{
				return inserts.Value;
			}
			return false;
		}

		internal string State()
		{
			return "SSS=" + sss.Value + "; simulation=" + simulation() + "; broad=" + broad.Value + "; selective=" + selective.Value + "; stationRouter=" + inserts.Value;
		}

		private static ConfigEntry<bool> Config(Type type, string name)
		{
			FieldInfo field = type.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null || field.FieldType != typeof(ConfigEntry<bool>))
			{
				throw new NotSupportedException("FGN configuration contract changed: " + name);
			}
			ConfigEntry<bool> val = (ConfigEntry<bool>)field.GetValue(null);
			if (val == null)
			{
				throw new NotSupportedException("FGN configuration is not initialized: " + name);
			}
			return val;
		}

		private static Func<T> Read<T>(Type type, string name)
		{
			FieldInfo field = type.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field == null || field.FieldType != typeof(T))
			{
				throw new NotSupportedException("FGN state contract changed: " + name);
			}
			DynamicMethod dynamicMethod = new DynamicMethod("BotanicFgn_" + name, typeof(T), Type.EmptyTypes, typeof(CompatibilityBridge).Module, skipVisibility: true);
			ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
			iLGenerator.Emit(OpCodes.Ldsfld, field);
			iLGenerator.Emit(OpCodes.Ret);
			return (Func<T>)dynamicMethod.CreateDelegate(typeof(Func<T>));
		}
	}
	internal sealed class ConfigCommands
	{
		private readonly ConfigFile config;

		private readonly Action<string> apply;

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

		internal ConfigCommands(ConfigFile config, Action<string> apply)
		{
			this.config = config;
			this.apply = apply;
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config)
			{
				if (!Live(item.Key.Key))
				{
					active.Add(item.Key.Key, item.Value.BoxedValue);
				}
			}
		}

		internal static bool Local(string key)
		{
			return string.Equals(key, "EnableLogs", StringComparison.OrdinalIgnoreCase);
		}

		internal static bool Live(string key)
		{
			switch (key)
			{
			case "EnableLogs":
			case "CameraHeadingCheckIntervalSeconds":
			case "MovementRotationCheckIntervalSeconds":
			case "InnerTransitionDelaySeconds":
			case "ForwardTransitionDelaySeconds":
			case "OuterTransitionDelaySeconds":
			case "TransitionDelaySeconds":
			case "ServerFallbackPingMs":
			case "ServerFallbackRadius":
				return true;
			default:
				return false;
			}
		}

		internal string Execute(string command, bool server)
		{
			if (command == null || command.Length > 256)
			{
				return "Invalid config command.";
			}
			string[] array = command.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
			if (array.Length == 1 && string.Equals(array[0], "list", StringComparison.OrdinalIgnoreCase))
			{
				StringBuilder stringBuilder = new StringBuilder(server ? "SERVER settings (live/restart):" : "LOCAL settings (live):");
				foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config)
				{
					if (server != Local(item.Key.Key))
					{
						stringBuilder.Append('\n').Append(Display(item.Value));
					}
				}
				return stringBuilder.ToString();
			}
			if (array.Length < 1 || array.Length > 2)
			{
				return "Usage: tml_config list | <Key> [value].";
			}
			ConfigEntryBase val = null;
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item2 in config)
			{
				if (string.Equals(item2.Key.Key, array[0], StringComparison.OrdinalIgnoreCase))
				{
					val = item2.Value;
					break;
				}
			}
			if (val == null)
			{
				return "Unknown setting. Use tml_config list.";
			}
			string key = val.Definition.Key;
			if (server == Local(key))
			{
				return "Wrong setting scope. Logging is local; camera timing and ownership settings belong on the server.";
			}
			if (array.Length == 1)
			{
				return Display(val) + "\n" + val.Description.Description;
			}
			if (!Parse(val.SettingType, array[1], out var value) || (val.Description.AcceptableValues != null && !val.Description.AcceptableValues.IsValid(value)))
			{
				return "Invalid value for " + key + ".\n" + val.Description.Description + "\n" + ((val.Description.AcceptableValues == null) ? "" : val.Description.AcceptableValues.ToDescriptionString());
			}
			if (InvalidGrid(key, value))
			{
				return "Invalid grid combination: 4-metre cells require Rounded5x5 or Rounded7x7. Change the other setting first.";
			}
			bool flag = Live(key);
			if (object.Equals(val.BoxedValue, value))
			{
				return key + " is already " + Value(value) + "." + (Pending(val) ? RestartNotice(key) : "");
			}
			object boxedValue = val.BoxedValue;
			bool saveOnConfigSet = config.SaveOnConfigSet;
			config.SaveOnConfigSet = false;
			try
			{
				val.BoxedValue = value;
				config.Save();
				if (flag)
				{
					apply(key);
				}
				string text = (server ? "SERVER: " : "LOCAL: ") + key + " = " + Value(value);
				if (flag)
				{
					return text + " (saved and applied; existing handoffs remain protected).";
				}
				return text + (Pending(val) ? (" (saved)." + RestartNotice(key)) : " (saved; matches the current session, no restart pending).");
			}
			catch (Exception ex)
			{
				val.BoxedValue = boxedValue;
				try
				{
					if (flag)
					{
						apply(key);
					}
					config.Save();
				}
				catch (Exception)
				{
				}
				return "Change failed: " + ex.GetBaseException().Message;
			}
			finally
			{
				config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private bool Pending(ConfigEntryBase entry)
		{
			if (active.TryGetValue(entry.Definition.Key, out var value))
			{
				return !object.Equals(value, entry.BoxedValue);
			}
			return false;
		}

		private string Display(ConfigEntryBase entry)
		{
			string key = entry.Definition.Key;
			string text = key + " = " + Value(entry.BoxedValue);
			if (Pending(entry))
			{
				return text + " [restart pending; active: " + Value(active[key]) + "]";
			}
			return text + (Live(key) ? " [live]" : " [restart]");
		}

		private static string RestartNotice(string key)
		{
			return " Takes effect after server restart; the current session is unchanged." + ((key == "Enabled" || key == "EnableAIOwnership") ? " Set the same value on every client and restart those clients too." : "");
		}

		private bool InvalidGrid(string key, object value)
		{
			if (key != "GridCellSizeMeters" && key != "GridCoveragePattern")
			{
				return false;
			}
			int num = 0;
			string text = null;
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config)
			{
				if (item.Key.Key == "GridCellSizeMeters")
				{
					num = (int)((key == item.Key.Key) ? value : item.Value.BoxedValue);
				}
				else if (item.Key.Key == "GridCoveragePattern")
				{
					text = (string)((key == item.Key.Key) ? value : item.Value.BoxedValue);
				}
			}
			if (num == 4)
			{
				return text == "Square3x3";
			}
			return false;
		}

		private static string Value(object value)
		{
			return Convert.ToString(value, CultureInfo.InvariantCulture);
		}

		private static bool Parse(Type type, string text, out object value)
		{
			value = null;
			int result2;
			float result3;
			if (type == typeof(bool) && bool.TryParse(text, out var result))
			{
				value = result;
			}
			else if (type == typeof(string))
			{
				value = text;
			}
			else if (type == typeof(int) && int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out result2))
			{
				value = result2;
			}
			else if (type == typeof(float) && float.TryParse(text.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out result3) && !float.IsNaN(result3) && !float.IsInfinity(result3))
			{
				value = result3;
			}
			return value != null;
		}
	}
	internal static class ConfigConsole
	{
		private const string RequestRpc = "TooManyLags_Config_1";

		private const string ReplyRpc = "TooManyLags_ConfigReply_1";

		private const string CameraRpc = "TooManyLags_CameraInterval_1";

		private static bool registered;

		private static WeakReference terminal;

		private static ZNet network;

		private static int ticket;

		private static double until;

		private static ZNet cameraNetwork;

		internal static bool CameraConfigured
		{
			get
			{
				if (Object.op_Implicit((Object)(object)ZNet.instance))
				{
					return object.ReferenceEquals(cameraNetwork, ZNet.instance);
				}
				return false;
			}
		}

		internal static void Register()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			if (!registered)
			{
				new ConsoleCommand("tml_config", "tml_config list | <Key> [value]. Server settings require admin; logging is local. Restart settings are saved for the next restart.", new ConsoleEvent(Run), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				registered = true;
			}
		}

		private static void Run(ConsoleEventArgs args)
		{
			if (args == null || !Object.op_Implicit((Object)(object)args.Context))
			{
				return;
			}
			if (args.Args.Length < 2)
			{
				args.Context.AddString("tml_config list | <Key> [value]. EnableLogs applies locally. Camera timing and ownership settings require server admin rights.");
				return;
			}
			string text = string.Join(" ", args.Args, 1, args.Args.Length - 1);
			if (ConfigCommands.Local(args.Args[1]))
			{
				Print(args.Context, Plugin.ConfigCommand(text, server: false));
				return;
			}
			if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsDedicated())
			{
				Print(args.Context, Plugin.ConfigCommand(text, server: true));
				return;
			}
			ZNetPeer val = (Object.op_Implicit((Object)(object)ZNet.instance) ? ZNet.instance.GetServerPeer() : null);
			if (val == null || !val.IsReady() || val.m_rpc == null)
			{
				args.Context.AddString("Connect to a dedicated server running this TooManyLags version first.");
				return;
			}
			if (text.Length > 256)
			{
				args.Context.AddString("Config command too long.");
				return;
			}
			ticket = ((ticket == int.MaxValue) ? 1 : (ticket + 1));
			terminal = new WeakReference(args.Context);
			network = ZNet.instance;
			until = Time.realtimeSinceStartupAsDouble + 10.0;
			try
			{
				val.m_rpc.Invoke("TooManyLags_Config_1", new object[2] { text, ticket });
			}
			catch (Exception ex)
			{
				args.Context.AddString("Config request failed: " + ex.GetBaseException().Message);
			}
		}

		internal static void Connect(ZNetPeer peer)
		{
			if (peer == null || peer.m_rpc == null)
			{
				return;
			}
			if (peer.m_server && Object.op_Implicit((Object)(object)ZNet.instance) && !ZNet.instance.IsDedicated())
			{
				cameraNetwork = null;
			}
			double window = -100.0;
			int requests = 0;
			peer.m_rpc.Register<string, int>("TooManyLags_Config_1", (Action<ZRpc, string, int>)delegate(ZRpc rpc, string command, int id)
			{
				ZNet instance = ZNet.instance;
				if (Object.op_Implicit((Object)(object)instance) && instance.IsDedicated() && peer.IsReady() && object.ReferenceEquals(instance.GetPeer(peer.m_uid), peer) && object.ReferenceEquals(rpc, peer.m_rpc))
				{
					double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
					if (realtimeSinceStartupAsDouble - window >= 1.0)
					{
						window = realtimeSinceStartupAsDouble;
						requests = 0;
					}
					if (++requests <= 4 && id > 0 && command != null && command.Length <= 256)
					{
						string text = ((peer.m_socket != null && instance.IsAdmin(peer.m_socket.GetHostName())) ? Plugin.ConfigCommand(command, server: true) : "Server configuration requires admin rights (adminlist.txt).");
						try
						{
							rpc.Invoke("TooManyLags_ConfigReply_1", new object[2] { text, id });
						}
						catch (Exception)
						{
						}
					}
				}
			});
			peer.m_rpc.Register<string, int>("TooManyLags_ConfigReply_1", (Action<ZRpc, string, int>)delegate(ZRpc rpc, string reply, int id)
			{
				if (Object.op_Implicit((Object)(object)ZNet.instance) && !ZNet.instance.IsDedicated() && peer.m_server && object.ReferenceEquals(network, ZNet.instance) && object.ReferenceEquals(ZNet.instance.GetServerPeer(), peer) && object.ReferenceEquals(rpc, peer.m_rpc) && id == ticket && terminal != null && !(Time.realtimeSinceStartupAsDouble > until) && reply != null && reply.Length <= 6000)
				{
					object? target = terminal.Target;
					Terminal val = (Terminal)((target is Terminal) ? target : null);
					terminal = null;
					network = null;
					if (Object.op_Implicit((Object)(object)val))
					{
						Print(val, reply);
					}
				}
			});
			peer.m_rpc.Register<float>("TooManyLags_CameraInterval_1", (Action<ZRpc, float>)delegate(ZRpc rpc, float interval)
			{
				if (Object.op_Implicit((Object)(object)ZNet.instance) && !ZNet.instance.IsDedicated() && peer.m_server && peer.IsReady() && object.ReferenceEquals(ZNet.instance.GetServerPeer(), peer) && object.ReferenceEquals(rpc, peer.m_rpc) && !float.IsNaN(interval) && !float.IsInfinity(interval) && !(interval < 0.05f) && !(interval > 5f))
				{
					Plugin.ApplyCameraInterval(interval);
					cameraNetwork = ZNet.instance;
				}
			});
		}

		internal static void Ready(ZRpc rpc)
		{
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsDedicated())
			{
				return;
			}
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer != null && peer.IsReady() && object.ReferenceEquals(peer.m_rpc, rpc))
				{
					SendCamera(peer, Plugin.CameraInterval());
					break;
				}
			}
		}

		internal static void BroadcastCamera(float interval)
		{
			if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsDedicated())
			{
				return;
			}
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer != null && peer.IsReady())
				{
					SendCamera(peer, interval);
				}
			}
		}

		private static void SendCamera(ZNetPeer peer, float interval)
		{
			try
			{
				if (peer.m_rpc != null)
				{
					peer.m_rpc.Invoke("TooManyLags_CameraInterval_1", new object[1] { interval });
				}
			}
			catch (Exception)
			{
			}
		}

		private static void Print(Terminal context, string lines)
		{
			string[] array = lines.Split('\n');
			foreach (string text in array)
			{
				context.AddString(text.TrimEnd('\r'));
			}
		}

		internal static void Reset()
		{
			terminal = null;
			network = null;
			cameraNetwork = null;
			until = 0.0;
		}
	}
	[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
	internal static class ConfigConnectionPatch
	{
		[HarmonyPostfix]
		private static void Postfix(ZNetPeer peer)
		{
			ConfigConsole.Connect(peer);
		}
	}
	[HarmonyPatch(typeof(ZNet), "Shutdown")]
	internal static class ConfigShutdownPatch
	{
		[HarmonyPrefix]
		private static void Prefix()
		{
			ConfigConsole.Reset();
		}
	}
	[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
	internal static class ConfigReadyPatch
	{
		[HarmonyPostfix]
		private static void Postfix(ZRpc rpc)
		{
			ConfigConsole.Ready(rpc);
		}
	}
	internal static class FgnAttackRetry
	{
		private sealed class Ticket
		{
			internal Humanoid Mob;

			internal ZDO Data;

			internal ZDOMan World;

			internal ZNet Network;

			internal long Owner;

			internal long Epoch;

			internal uint Revision;
		}

		private static ConditionalWeakTable<Attack, Ticket> waiting = new ConditionalWeakTable<Attack, Ticket>();

		private static Func<Humanoid, Attack> currentAttack;

		private static SynchronizationContext context;

		private static Action<Humanoid, ZDO> retry;

		private static Action<Exception> fail;

		private static readonly SendOrPostCallback dispatch = Dispatch;

		private static long epoch;

		private static int armedCount;

		internal static void Install(Harmony harmony, Action<Humanoid, ZDO> onEnd, Action<Exception> onFailure)
		{
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Expected O, but got Unknown
			SynchronizationContext current = SynchronizationContext.Current;
			FieldInfo fieldInfo = AccessTools.Field(typeof(Humanoid), "m_currentAttack");
			MethodInfo methodInfo = AccessTools.Method(typeof(Attack), "Stop", Type.EmptyTypes, (Type[])null);
			if (current == null || current.GetType().FullName != "UnityEngine.UnitySynchronizationContext" || fieldInfo == null || fieldInfo.IsStatic || fieldInfo.FieldType != typeof(Attack) || methodInfo == null || methodInfo.IsStatic || methodInfo.ReturnType != typeof(void))
			{
				throw new NotSupportedException("Native attack-end re