Decompiled source of TooManyLags Standalone v1.0.1

TooManyLagsStandalone.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Steamworks;
using UnityEngine;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("1.0.1.0")]
namespace TooManyLagsStandalone;

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

		internal readonly ZDO Data;

		internal readonly Kinds Kind;

		internal Cell Sector;

		internal long SeenOwner;

		internal ushort SeenRevision;

		internal bool SeenUse;

		internal long RejectedOwner;

		internal ushort RejectedRevision;

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

		internal Node(AuthorityController owner, ZDO data, Kinds kind, Cell cell, Cell sector)
		{
			this.owner = owner;
			Data = data;
			Kind = kind;
			Cell = cell;
			Sector = sector;
			SeenOwner = data.GetOwner();
			SeenRevision = data.OwnerRevision;
		}

		internal override ApplyResult Apply(long target)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: 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)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			if (!Data.IsValid() || !object.ReferenceEquals(ZDOMan.instance.GetZDO(Data.m_uid), Data))
			{
				return ApplyResult.Dropped;
			}
			if (target == 0)
			{
				return ApplyResult.Done;
			}
			if (Data.OwnerRevision == ushort.MaxValue || (RejectedOwner == Data.GetOwner() && RejectedRevision == Data.OwnerRevision))
			{
				return ApplyResult.Dropped;
			}
			if (!owner.Available(target, Data.GetPosition()))
			{
				return ApplyResult.Done;
			}
			if (Data.GetOwner() == target && !owner.Handoffs.Pending(Data.m_uid))
			{
				Managed = true;
				ManagedOwner = target;
				return ApplyResult.Done;
			}
			if (!owner.Available(Data.GetOwner(), Data.GetPosition()))
			{
				return ApplyResult.Done;
			}
			if (Data.GetLong(ZDOVars.s_user, 0L) != 0 || Data.GetBool(ZDOVars.s_attachJointHash, false) || (Kind == Kinds.TamedAnimals && (!Data.GetBool(ZDOVars.s_tamed, false) || !string.IsNullOrEmpty(Data.GetString(ZDOVars.s_follow, "")))))
			{
				return ApplyResult.Done;
			}
			if ((!Plugin.Network.Capable(Data.GetOwner()) || !Plugin.Network.Capable(target)) && !owner.NativeTransferSafe(Data))
			{
				return ApplyResult.Done;
			}
			owner.Handoffs.Request(Data, target, Kind, ZDOID.None);
			return ApplyResult.Pending;
		}
	}

	private readonly Settings settings;

	private readonly OwnershipCadence cadence;

	private readonly ObjectCatalog catalog;

	private readonly AuthorityWorld index;

	private readonly RegionContentionGate regions = new RegionContentionGate();

	private readonly List<GridPeer> gridPeers = new List<GridPeer>(12);

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

	internal readonly List<PlayerPeer> Players = new List<PlayerPeer>(12);

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

	private readonly Dictionary<Cell, HashSet<Node>> sectors = new Dictionary<Cell, HashSet<Node>>();

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

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

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

	private readonly HashSet<SectorIndex> visited = new HashSet<SectorIndex>();

	private readonly Action<ZDOMan, Vector2s, List<ZDO>, HashSet<SectorIndex>> find;

	internal readonly Handoffs Handoffs;

	internal readonly MobPolicy Mobs;

	private int ranks;

	private int mobPeers;

	private bool failed;

	private bool interestDirty;

	internal bool Active
	{
		get
		{
			if (!failed && Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer())
			{
				return ZDOMan.instance != null;
			}
			return false;
		}
	}

	internal AuthorityController(Settings settings)
	{
		this.settings = settings;
		cadence = new OwnershipCadence(settings.Interval);
		catalog = new ObjectCatalog(settings.Categories);
		index = new AuthorityWorld(0L, settings.Full, settings.Pattern, settings.Keep, settings.Directional, settings.Inner, settings.Outer, settings.CellSize == 4, settings.Forward, Plugin.Network.Latency, settings.Margin);
		find = AccessTools.MethodDelegate<Action<ZDOMan, Vector2s, List<ZDO>, HashSet<SectorIndex>>>(AccessTools.Method(typeof(ZDOMan), "FindObjects", (Type[])null, (Type[])null), (object)null, true);
		Handoffs = new Handoffs(this);
		Mobs = new MobPolicy(this);
		index.AfterGridUpdate = SyncInterest;
	}

	internal bool Shared(long uid)
	{
		if (Active)
		{
			return regions.Shared(uid);
		}
		return false;
	}

	internal bool WatchMobs(long uid)
	{
		if (mobPeers > 1)
		{
			return Shared(uid);
		}
		return false;
	}

	internal bool NativeTransferSafe(ZDO data)
	{
		return catalog.NativeTransferSafe(data.GetPrefab());
	}

	internal bool Available(long uid, Vector3 point)
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		if (!Active || uid == 0 || !Plugin.Network.Present(uid) || !Finite(point))
		{
			return false;
		}
		foreach (PlayerPeer player in Players)
		{
			if (player.Uid == uid && player.Character != null && player.Character.IsValid() && player.Character.GetFloat(ZDOVars.s_health, 1f) > 0f && Finite(player.Reference) && ZNetScene.InActiveArea(point, player.Reference))
			{
				return true;
			}
		}
		return false;
	}

	internal void Poll(float now)
	{
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00db: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: 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_01b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		if (!Active || !cadence.Due(now) || !Object.op_Implicit((Object)(object)ZoneSystem.instance) || !Object.op_Implicit((Object)(object)ZNetScene.instance))
		{
			return;
		}
		try
		{
			Plugin.Network.Players(Players);
			Players.Sort((PlayerPeer a, PlayerPeer b) => a.Uid.CompareTo(b.Uid));
			regionPeers.Clear();
			gridPeers.Clear();
			mobPeers = 0;
			foreach (PlayerPeer player in Players)
			{
				if (settings.Mobs && Plugin.Network.MobCapable(player.Uid))
				{
					mobPeers++;
				}
				Vector3 position = player.Character.GetPosition();
				Vector2s zone = ZoneSystem.GetZone(player.Reference);
				Vector2s zone2 = ZoneSystem.GetZone(position);
				regionPeers.Add(new RegionPeer(player.Uid, new Cell(zone.x, zone.y), Finite(player.Reference), new Cell(zone2.x, zone2.y), Finite(position)));
			}
			if (regions.Update(regionPeers))
			{
				interestDirty = true;
				Mobs.RanksChanged();
			}
			foreach (PlayerPeer player2 in Players)
			{
				if (regions.Shared(player2.Uid))
				{
					Vector3 position2 = player2.Character.GetPosition();
					if (Cell.At(position2.x, position2.z, settings.CellSize, out var cell))
					{
						gridPeers.Add(new GridPeer(player2.Uid, cell, settings.Directional ? CameraHeading.Read(player2.Character, player2.Uid, Plugin.Network.Capable(player2.Uid)) : 0f));
					}
				}
			}
			if (ranks != Plugin.Network.RankVersion)
			{
				ranks = Plugin.Network.RankVersion;
				index.Grid.RanksChanged();
				Mobs.RanksChanged();
			}
			index.Tick(gridPeers, now);
			Mobs.Tick(now);
			Handoffs.Tick(now);
			RequestRouter.Flush(now);
			if (Plugin.Local != null && !ZNet.instance.IsDedicated())
			{
				Plugin.Local.SetMobWatch(settings.Mobs && WatchMobs(ZDOMan.GetSessionID()));
			}
			Plugin.Network.FlushAll();
		}
		catch (Exception error)
		{
			Stop();
			Plugin.Fault(error);
		}
	}

	private void SyncInterest()
	{
		//IL_0155: Unknown result type (might be due to invalid IL or missing references)
		if (!interestDirty)
		{
			return;
		}
		interestDirty = false;
		retiring.Clear();
		foreach (Cell item in interest)
		{
			if (!regions.ZoneKeys.Contains(item))
			{
				retiring.Add(item);
			}
		}
		foreach (Cell item2 in retiring)
		{
			interest.Remove(item2);
			if (!sectors.TryGetValue(item2, out var value))
			{
				continue;
			}
			List<Node> list = new List<Node>(value);
			foreach (Node item3 in list)
			{
				Remove(item3);
			}
		}
		foreach (Cell zoneKey in regions.ZoneKeys)
		{
			if (!interest.Add(zoneKey))
			{
				continue;
			}
			scan.Clear();
			visited.Clear();
			find(ZDOMan.instance, new Vector2s((short)zoneKey.X, (short)zoneKey.Z), scan, visited);
			foreach (ZDO item4 in scan)
			{
				Changed(item4);
			}
		}
		scan.Clear();
		visited.Clear();
		retiring.Clear();
	}

	internal void CapabilityChanged(long uid)
	{
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		if (!Active || !Object.op_Implicit((Object)(object)ZNetScene.instance) || !Object.op_Implicit((Object)(object)ZoneSystem.instance))
		{
			return;
		}
		foreach (Cell item in interest)
		{
			scan.Clear();
			visited.Clear();
			find(ZDOMan.instance, new Vector2s((short)item.X, (short)item.Z), scan, visited);
			foreach (ZDO item2 in scan)
			{
				if (item2.GetOwner() == uid)
				{
					Changed(item2);
				}
			}
		}
		scan.Clear();
		visited.Clear();
		foreach (Node value in nodes.Values)
		{
			if (value.Data.GetOwner() == uid || index.Grid.Owner(value.Cell) == uid)
			{
				index.Received(value, Time.unscaledTime);
			}
		}
	}

	internal void Changed(ZDO data)
	{
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_0113: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_014d: Unknown result type (might be due to invalid IL or missing references)
		//IL_016e: Unknown result type (might be due to invalid IL or missing references)
		if (!Active || data == null || !data.IsValid())
		{
			return;
		}
		Handoffs.Observe(data);
		Mobs.Changed(data);
		Vector2s sector = data.GetSector();
		Cell cell = new Cell(sector.x, sector.y);
		Node value;
		if (!interest.Contains(cell))
		{
			if (nodes.TryGetValue(data.m_uid, out value))
			{
				Remove(value);
			}
			return;
		}
		Kinds kinds = catalog.Prefab(data.GetPrefab());
		if (kinds == Kinds.None || kinds == Kinds.Incinerators || (kinds == Kinds.TamedAnimals && (!data.GetBool(ZDOVars.s_tamed, false) || MobPolicy.FriendlyPrefab(data.GetPrefab()))))
		{
			if (nodes.TryGetValue(data.m_uid, out value))
			{
				Remove(value);
			}
			return;
		}
		if (!Plugin.Network.Capable(data.GetOwner()) && !catalog.NativeTransferSafe(data.GetPrefab()))
		{
			if (nodes.TryGetValue(data.m_uid, out value))
			{
				Remove(value);
			}
			return;
		}
		Vector3 position = data.GetPosition();
		if (!Finite(position) || !Cell.At(position.x, position.z, settings.CellSize, out var cell2))
		{
			return;
		}
		if (!nodes.TryGetValue(data.m_uid, out value))
		{
			value = new Node(this, data, kinds, cell2, cell);
			nodes.Add(data.m_uid, value);
			AddSector(value);
			index.Add(value, Time.unscaledTime);
			return;
		}
		if (!object.ReferenceEquals(value.Data, data) || value.Kind != kinds)
		{
			Remove(value);
			Changed(data);
			return;
		}
		if (!value.Sector.Equals(cell))
		{
			RemoveSector(value);
			value.Sector = cell;
			AddSector(value);
		}
		if (!value.Cell.Equals(cell2))
		{
			index.Move(value, cell2, Time.unscaledTime);
		}
		long owner = data.GetOwner();
		ushort ownerRevision = data.OwnerRevision;
		if (owner != value.SeenOwner || ownerRevision != value.SeenRevision)
		{
			value.SeenOwner = owner;
			value.SeenRevision = ownerRevision;
			index.Received(value, Time.unscaledTime);
		}
		bool flag = data.GetLong(ZDOVars.s_user, 0L) != 0 || data.GetBool(ZDOVars.s_attachJointHash, false) || (kinds == Kinds.TamedAnimals && !string.IsNullOrEmpty(data.GetString(ZDOVars.s_follow, "")));
		if (flag != value.SeenUse)
		{
			value.SeenUse = flag;
			index.Received(value, Time.unscaledTime);
		}
	}

	internal void Destroyed(ZDOID id)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		if (nodes.TryGetValue(id, out var value))
		{
			Remove(value);
		}
		Handoffs.Forget(id);
		Mobs.Forget(id);
	}

	private void AddSector(Node node)
	{
		if (!sectors.TryGetValue(node.Sector, out var value))
		{
			value = new HashSet<Node>();
			sectors.Add(node.Sector, value);
		}
		value.Add(node);
	}

	private void RemoveSector(Node node)
	{
		if (sectors.TryGetValue(node.Sector, out var value))
		{
			value.Remove(node);
			if (value.Count == 0)
			{
				sectors.Remove(node.Sector);
			}
		}
	}

	private void Remove(Node node)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		index.Remove(node);
		nodes.Remove(node.Data.m_uid);
		RemoveSector(node);
		Handoffs.Cancel(node.Data.m_uid);
	}

	internal long Desired(ZDO data, Kinds kind, ZDOID target)
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		if (!Active || data == null || !data.IsValid())
		{
			return 0L;
		}
		if (kind == Kinds.None)
		{
			return Mobs.Desired(data, target);
		}
		if (!nodes.TryGetValue(data.m_uid, out var value) || !object.ReferenceEquals(value.Data, data))
		{
			return 0L;
		}
		return index.Grid.Owner(value.Cell);
	}

	internal bool IsManaged(ZDO data, int method)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		if (!Active || data == null)
		{
			return false;
		}
		if (!nodes.TryGetValue(data.m_uid, out var value) || !ObjectCatalog.OwnerRequest(value.Kind, method))
		{
			return Mobs.Managed(data.m_uid, method);
		}
		return true;
	}

	internal void Wake(ZDOID id)
	{
		//IL_0006: 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)
		if (nodes.TryGetValue(id, out var value))
		{
			index.Received(value, Time.unscaledTime);
		}
		Mobs.Wake(id);
	}

	internal void Rejected(ZDOID id)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		if (nodes.TryGetValue(id, out var value))
		{
			value.RejectedOwner = value.Data.GetOwner();
			value.RejectedRevision = value.Data.OwnerRevision;
		}
		Mobs.Forget(id);
	}

	internal void Stop()
	{
		failed = true;
		Handoffs.Clear();
		Mobs.Clear();
		nodes.Clear();
		sectors.Clear();
		interest.Clear();
		regions.Clear();
		Players.Clear();
		gridPeers.Clear();
		regionPeers.Clear();
	}

	internal static bool Finite(Vector3 value)
	{
		if (!float.IsNaN(value.x) && !float.IsNaN(value.y) && !float.IsNaN(value.z) && !float.IsInfinity(value.x) && !float.IsInfinity(value.y))
		{
			return !float.IsInfinity(value.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 GridPeer(long uid, Cell cell, float yaw = 0f)
	{
		Uid = uid;
		Cell = cell;
		Yaw = yaw;
	}
}
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 bool Shared;

		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[8][];

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

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

	private readonly bool directional;

	private readonly bool discardVacant;

	private readonly float innerDelay;

	private readonly float outerDelay;

	private readonly float forwardDelay;

	internal readonly float Delay;

	private float lastTime;

	private float nextDue = float.PositiveInfinity;

	private int deferredCells;

	private readonly Func<long, float> latency;

	private readonly float margin;

	private bool ranksDirty;

	internal int Rebuilds;

	internal int NeighborReads;

	internal int PlayerUpdates;

	internal int TimerVisits;

	internal int PolicyAllocations;

	internal int RememberedCells => remembered.Count;

	internal IEnumerable<Cell> CoveredCells
	{
		get
		{
			foreach (KeyValuePair<Cell, Policy> item in policies)
			{
				KeyValuePair<Cell, Policy> keyValuePair = item;
				if (keyValuePair.Value.Count != 0)
				{
					KeyValuePair<Cell, Policy> keyValuePair2 = item;
					yield return keyValuePair2.Key;
				}
			}
		}
	}

	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, Func<long, float> latency = null, float margin = 30f)
	{
		this.latency = latency;
		this.margin = margin;
		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;
		}
	}

	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;
	}

	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.Shared) && 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 = Resolve(policy);
		policy.Shared = policy.Count > 1;
		if (policy.Candidate != num)
		{
			if (policy.Owner == policy.Candidate || policy.Candidate == 0 || policy.Count <= 1)
			{
				policy.Since = now;
			}
			policy.Candidate = num;
		}
		float num2 = FieldWait(policy);
		if (num2 < policy.Wait && policy.Candidate != 0)
		{
			Accelerations.Add(cell);
		}
		policy.Wait = num2;
		if (policy.Count > 1 && 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));
		}
	}

	private long Resolve(Policy policy)
	{
		if (policy.Count == 0)
		{
			return 0L;
		}
		if (policy.Count == 1)
		{
			return policy.Uids;
		}
		if (latency == null)
		{
			return server;
		}
		long uid = policy.First.Uid;
		float num = latency(uid);
		bool flag = policy.First.Uid == policy.Candidate;
		if (policy.Others != null)
		{
			foreach (Request other in policy.Others)
			{
				float num2 = latency(other.Uid);
				if (num2 < num || (num2 == num && other.Uid < uid))
				{
					uid = other.Uid;
					num = num2;
				}
				flag |= other.Uid == policy.Candidate;
			}
		}
		if (flag && latency(policy.Candidate) <= num + margin)
		{
			return policy.Candidate;
		}
		return uid;
	}

	internal void RanksChanged()
	{
		ranksDirty = true;
	}

	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 ? ((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 (ranksDirty)
		{
			foreach (KeyValuePair<Cell, Policy> policy2 in policies)
			{
				if (policy2.Value.Count > 1)
				{
					dirty.Add(policy2.Key);
				}
			}
			ranksDirty = false;
		}
		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 Action AfterGridUpdate;

	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, Func<long, float> latency = null, float margin = 30f)
	{
		Grid = new AuthorityGrid(server, delay, pattern, directional, innerDelay, outerDelay, keepOnExit, forward, forwardDelay, latency, margin);
		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);
		if (AfterGridUpdate != null)
		{
			AfterGridUpdate();
		}
		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 CameraHeading
{
	internal static readonly int Key = StringExtensionMethods.GetStableHashCode("botanic.toomanylags.standalone.cameraSector");

	internal static int Sector(float yaw)
	{
		if (float.IsNaN(yaw) || float.IsInfinity(yaw))
		{
			return -1;
		}
		yaw %= 360f;
		if (yaw < 0f)
		{
			yaw += 360f;
		}
		return (int)((yaw + 22.5f) / 45f) & 7;
	}

	internal static int Sector(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 Sector((float)(Math.Atan2(x, z) * (180.0 / Math.PI)));
	}

	internal static float Read(ZDO player, long peer, bool cameraAvailable)
	{
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		int num = ((cameraAvailable && player.GetOwner() == peer) ? player.GetInt(Key, -1) : (-1));
		if (num < 0 || num >= 8)
		{
			Quaternion rotation = player.GetRotation();
			return ((Quaternion)(ref rotation)).eulerAngles.y;
		}
		return (float)num * 45f;
	}
}
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, 0.05f);
	}

	internal void Sample(float now)
	{
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		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.IsServer() && 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;
		}
		GameCamera instance2 = GameCamera.instance;
		int num = -1;
		if (Object.op_Implicit((Object)(object)instance2) && !GameCamera.InFreeFly())
		{
			Vector3 forward = ((Component)instance2).transform.forward;
			num = CameraHeading.Sector(forward.x, forward.z);
		}
		bool flag = !object.ReferenceEquals(previous, zDO) || !object.ReferenceEquals(network, instance) || !object.ReferenceEquals(world, ZDOMan.instance);
		if (flag)
		{
			ZDOExtraData.AddSessionHash(CameraHeading.Key);
		}
		if (flag || zDO.GetInt(CameraHeading.Key, -1) != num)
		{
			zDO.Set(CameraHeading.Key, num, false);
		}
		previous = zDO;
		network = instance;
		world = ZDOMan.instance;
	}

	internal void Reset(bool invalidate = false)
	{
		//IL_005d: 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.Set(CameraHeading.Key, -1, false);
		}
		previous = null;
		network = null;
		world = null;
	}
}
[HarmonyPatch(typeof(GameCamera), "LateUpdate")]
internal static class CameraSamplePatch
{
	[HarmonyPostfix]
	[HarmonyPriority(0)]
	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 static class FieldReader
{
	internal static Func<TSource, TValue> Create<TSource, TValue>(FieldInfo field)
	{
		if (field == null || field.IsStatic || !typeof(TValue).IsAssignableFrom(field.FieldType) || typeof(TSource).IsValueType || field.DeclaringType.IsValueType)
		{
			throw new NotSupportedException("Unsupported read-only field getter.");
		}
		DynamicMethod dynamicMethod = new DynamicMethod("TooManyLagsRead_" + field.Name, typeof(TValue), new Type[1] { typeof(TSource) }, typeof(FieldReader).Module, skipVisibility: true);
		ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
		iLGenerator.Emit(OpCodes.Ldarg_0);
		if (typeof(TSource) != field.DeclaringType)
		{
			iLGenerator.Emit(OpCodes.Castclass, field.DeclaringType);
		}
		iLGenerator.Emit(OpCodes.Ldfld, field);
		iLGenerator.Emit(OpCodes.Ret);
		return (Func<TSource, TValue>)dynamicMethod.CreateDelegate(typeof(Func<TSource, TValue>));
	}
}
internal static class FriendlyCreatures
{
	internal static bool IsFriendly(Character actor)
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Invalid comparison between Unknown and I4
		if (Object.op_Implicit((Object)(object)actor) && !actor.IsPlayer())
		{
			return (int)actor.GetFaction() == 0;
		}
		return false;
	}
}
internal sealed class Handoffs
{
	private sealed class Job
	{
		internal ZDO Data;

		internal Kinds Kind;

		internal ZDOID MobTarget;

		internal HandoffTransaction State;

		internal float LastSend;

		internal float CommitAt;

		internal bool Activated;

		internal bool NativeSource;

		internal bool NativeTarget;
	}

	private sealed class Receipt
	{
		internal long Source;

		internal long Target;

		internal ushort Revision;

		internal Kinds Kind;

		internal float Until;

		internal float Next;

		internal Job Job;

		internal bool Activated;
	}

	private readonly AuthorityController controller;

	private readonly Dictionary<ZDOID, Job> jobs = new Dictionary<ZDOID, Job>();

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

	private readonly Dictionary<ZDOID, Receipt> receipts = new Dictionary<ZDOID, Receipt>();

	private readonly List<ZDOID> expired = new List<ZDOID>();

	private float nextReceipt = float.PositiveInfinity;

	private long sequence;

	internal Handoffs(AuthorityController controller)
	{
		this.controller = controller;
	}

	internal bool Pending(ZDOID id)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		return jobs.ContainsKey(id);
	}

	internal bool Committing(ZDOID id)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		if (!jobs.TryGetValue(id, out var value) || value.State.Phase != HandoffPhase.Commit)
		{
			if (receipts.TryGetValue(id, out var value2) && !value2.Activated)
			{
				return Time.unscaledTime < value2.Until;
			}
			return false;
		}
		return true;
	}

	internal bool AcceptsForward(ZDOID id, long source, long target, ushort revision, int method)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		Kinds kind;
		if (jobs.TryGetValue(id, out var value) && value.State.Source == source && value.State.Target == target && value.State.Revision + 1 == revision)
		{
			kind = value.Kind;
		}
		else
		{
			if (!receipts.TryGetValue(id, out var value2) || !(value2.Until >= Time.unscaledTime) || value2.Source != source || value2.Target != target || value2.Revision != revision)
			{
				return false;
			}
			kind = value2.Kind;
		}
		if (kind != Kinds.None)
		{
			return ObjectCatalog.OwnerRequest(kind, method);
		}
		return method == StringExtensionMethods.GetStableHashCode("RPC_Damage");
	}

	internal bool Request(ZDO data, long target, Kinds kind, ZDOID mobTarget)
	{
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0144: Unknown result type (might be due to invalid IL or missing references)
		//IL_0113: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0158: Unknown result type (might be due to invalid IL or missing references)
		//IL_017f: 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_01da: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
		if (!controller.Active || data == null || !data.IsValid() || target == 0 || target == data.GetOwner() || data.GetOwner() == 0 || data.OwnerRevision == ushort.MaxValue || Committing(data.m_uid))
		{
			return false;
		}
		if (kind == Kinds.None && (!Plugin.Network.MobCapable(target) || !Plugin.Network.MobCapable(data.GetOwner())))
		{
			return false;
		}
		bool flag = !Plugin.Network.Capable(data.GetOwner());
		bool flag2 = !Plugin.Network.Capable(target);
		if ((flag || flag2) && (kind == Kinds.None || !controller.NativeTransferSafe(data)))
		{
			return false;
		}
		if (jobs.TryGetValue(data.m_uid, out var value))
		{
			if (value.State.Phase == HandoffPhase.Commit)
			{
				return false;
			}
			if (value.State.Target == target && value.MobTarget == mobTarget)
			{
				if (value.NativeSource && NativeOwnership.Idle(data))
				{
					value.State.Wake(Time.unscaledTime);
				}
				return true;
			}
			Cancel(data.m_uid);
		}
		if (jobs.Count >= 4096 || RequestRouter.Saturated || !controller.Available(data.GetOwner(), data.GetPosition()) || !controller.Available(target, data.GetPosition()))
		{
			return false;
		}
		Job job = new Job();
		job.Data = data;
		job.Kind = kind;
		job.MobTarget = mobTarget;
		job.NativeSource = flag;
		job.NativeTarget = flag2;
		job.Activated = flag2;
		job.State = new HandoffTransaction(++sequence, data.GetOwner(), target, data.OwnerRevision, Time.unscaledTime);
		Job value2 = job;
		jobs.Add(data.m_uid, value2);
		ZDOMan.instance.ForceSendZDO(target, data.m_uid);
		return true;
	}

	internal void Tick(float now)
	{
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		//IL_0136: Unknown result type (might be due to invalid IL or missing references)
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0200: Unknown result type (might be due to invalid IL or missing references)
		//IL_026b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0241: Unknown result type (might be due to invalid IL or missing references)
		//IL_0383: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0303: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_033e: Unknown result type (might be due to invalid IL or missing references)
		//IL_035b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0400: 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_04ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_054e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0570: Unknown result type (might be due to invalid IL or missing references)
		if (now >= nextReceipt)
		{
			nextReceipt = float.PositiveInfinity;
			expired.Clear();
			foreach (KeyValuePair<ZDOID, Receipt> receipt in receipts)
			{
				Receipt value = receipt.Value;
				if (now >= value.Until)
				{
					expired.Add(receipt.Key);
					continue;
				}
				if (!value.Activated && now >= value.Next)
				{
					value.Next = now + 2f;
					Job receiptJob = value.Job;
					ZDOMan.instance.ForceSendZDO(value.Target, receiptJob.Data.m_uid);
					Plugin.Network.Send(value.Target, Message.ActivationCheck, delegate(ZPackage p)
					{
						Write(p, receiptJob);
					});
				}
				nextReceipt = Math.Min(nextReceipt, value.Activated ? value.Until : Math.Min(value.Until, value.Next));
			}
			foreach (ZDOID item in expired)
			{
				receipts.Remove(item);
			}
			expired.Clear();
		}
		if (jobs.Count == 0)
		{
			return;
		}
		scratch.Clear();
		foreach (Job value2 in jobs.Values)
		{
			scratch.Add(value2);
		}
		foreach (Job job in scratch)
		{
			if (!jobs.ContainsKey(job.Data.m_uid))
			{
				continue;
			}
			HandoffTransaction state = job.State;
			if (!job.Data.IsValid() || !object.ReferenceEquals(ZDOMan.instance.GetZDO(job.Data.m_uid), job.Data))
			{
				Cancel(job.Data.m_uid);
			}
			else if (state.Observe(job.Data.GetOwner(), job.Data.OwnerRevision))
			{
				Finish(job);
			}
			else if (job.NativeSource && Plugin.Network.Capable(state.Source))
			{
				Cancel(job.Data.m_uid, force: true);
				controller.Wake(job.Data.m_uid);
			}
			else if (state.Phase == HandoffPhase.Commit && now - job.CommitAt > 12f)
			{
				Cancel(job.Data.m_uid, force: true);
				controller.Wake(job.Data.m_uid);
			}
			else if (!controller.Available(state.Source, job.Data.GetPosition()) || !controller.Available(state.Target, job.Data.GetPosition()))
			{
				Cancel(job.Data.m_uid);
			}
			else if (state.Phase != HandoffPhase.Commit && controller.Desired(job.Data, job.Kind, job.MobTarget) != state.Target)
			{
				Cancel(job.Data.m_uid);
			}
			else
			{
				if (now < state.Next)
				{
					continue;
				}
				if (state.Phase == HandoffPhase.Blocked)
				{
					state.Wake(now);
				}
				if (state.Phase == HandoffPhase.Prepare && job.NativeTarget)
				{
					state.Prepared(ready: true, now);
					job.CommitAt = now;
				}
				if (state.Phase == HandoffPhase.Commit && job.NativeSource)
				{
					if (state.MatchesSource(job.Data.GetOwner(), job.Data.OwnerRevision))
					{
						if (!controller.NativeTransferSafe(job.Data))
						{
							Cancel(job.Data.m_uid, force: true);
							continue;
						}
						if (!NativeOwnership.Idle(job.Data))
						{
							state.Block(now);
							continue;
						}
						job.Data.SetOwner(state.Target);
						ZDOMan.instance.ForceSendZDO(state.Source, job.Data.m_uid);
						ZDOMan.instance.ForceSendZDO(state.Target, job.Data.m_uid);
						Observe(job.Data);
					}
				}
				else if (state.Phase == HandoffPhase.Prepare || state.Phase == HandoffPhase.Commit)
				{
					Message message = ((state.Phase == HandoffPhase.Prepare) ? Message.Prepare : Message.Commit);
					long uid = ((state.Phase == HandoffPhase.Prepare) ? state.Target : state.Source);
					state.Next = now + ((message == Message.Prepare) ? 0.5f : 2f);
					job.LastSend = now;
					Plugin.Network.Send(uid, message, delegate(ZPackage p)
					{
						Write(p, job);
					});
				}
			}
		}
		scratch.Clear();
	}

	private static void Write(ZPackage p, Job job)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		p.Write(job.State.Token);
		p.Write(job.Data.m_uid);
		p.Write(job.State.Source);
		p.Write(job.State.Target);
		p.Write(job.State.Revision);
		p.Write((int)job.Kind);
		p.Write(job.MobTarget);
	}

	internal void Receive(long sender, Message message, ZPackage packet)
	{
		//IL_0008: 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_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Unknown result type (might be due to invalid IL or missing references)
		//IL_0192: Unknown result type (might be due to invalid IL or missing references)
		//IL_0186: Unknown result type (might be due to invalid IL or missing references)
		long num = packet.ReadLong();
		ZDOID val = packet.ReadZDOID();
		Job value2;
		if (message == Message.Activated)
		{
			ushort num2 = packet.ReadUShort();
			if (receipts.TryGetValue(val, out var value) && value.Job.State.Token == num && value.Target == sender && value.Revision == num2)
			{
				value.Activated = true;
				controller.Wake(val);
			}
			else if (jobs.TryGetValue(val, out value2) && value2.State.Token == num && value2.State.Target == sender && value2.State.Revision + 1 == num2)
			{
				value2.Activated = true;
			}
		}
		else
		{
			if (!jobs.TryGetValue(val, out value2) || value2.State.Token != num)
			{
				return;
			}
			HandoffTransaction state = value2.State;
			float unscaledTime = Time.unscaledTime;
			if (message == Message.Prepared && sender == state.Target)
			{
				bool flag = packet.ReadBool();
				state.Prepared(flag, unscaledTime);
				if (flag)
				{
					value2.CommitAt = unscaledTime;
				}
			}
			else if (message == Message.Wake && sender == state.Source)
			{
				state.Wake(unscaledTime);
			}
			else
			{
				if (message != Message.Result || sender != state.Source || state.Phase != HandoffPhase.Commit)
				{
					return;
				}
				int num3 = packet.ReadInt();
				switch (num3)
				{
				case 1:
					state.Next = unscaledTime + 2f;
					Observe(value2.Data);
					return;
				case 2:
					state.Block(unscaledTime);
					return;
				}
				Cancel(val, force: true);
				if (num3 == 3)
				{
					controller.Rejected(val);
				}
				controller.Wake(val);
			}
		}
	}

	internal void Observe(ZDO data)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		if (data != null && jobs.TryGetValue(data.m_uid, out var value) && value.State.Observe(data.GetOwner(), data.OwnerRevision))
		{
			Finish(value);
		}
	}

	private void Finish(Job job)
	{
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		if (job.State.Phase == HandoffPhase.Complete && (!job.NativeSource || !job.NativeTarget))
		{
			receipts[job.Data.m_uid] = new Receipt
			{
				Source = job.State.Source,
				Target = job.State.Target,
				Revision = job.Data.OwnerRevision,
				Kind = job.Kind,
				Until = Time.unscaledTime + 12f,
				Job = job,
				Activated = job.Activated
			};
			nextReceipt = Time.unscaledTime;
		}
		jobs.Remove(job.Data.m_uid);
		controller.Wake(job.Data.m_uid);
	}

	internal void Forget(ZDOID id)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		jobs.Remove(id);
		receipts.Remove(id);
	}

	internal void Cancel(ZDOID id, bool force = false)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: 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)
		if (jobs.TryGetValue(id, out var job) && (force || job.State.Phase != HandoffPhase.Commit || !(Time.unscaledTime - job.State.Started < 12f)))
		{
			jobs.Remove(id);
			Plugin.Network.Send(job.State.Target, Message.Cancel, delegate(ZPackage p)
			{
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				p.Write(job.State.Token);
				p.Write(id);
			});
			Plugin.Network.Send(job.State.Source, Message.Cancel, delegate(ZPackage p)
			{
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				p.Write(job.State.Token);
				p.Write(id);
			});
		}
	}

	internal void Clear()
	{
		jobs.Clear();
		scratch.Clear();
		receipts.Clear();
		expired.Clear();
		nextReceipt = float.PositiveInfinity;
	}
}
internal enum HandoffPhase
{
	Prepare,
	Commit,
	Blocked,
	Complete,
	Obsolete
}
internal sealed class HandoffTransaction
{
	internal readonly long Token;

	internal readonly long Source;

	internal readonly long Target;

	internal readonly ushort Revision;

	internal HandoffPhase Phase;

	internal float Next;

	internal float Started;

	internal HandoffTransaction(long token, long source, long target, ushort revision, float now)
	{
		if (token <= 0 || source == 0 || target == 0 || source == target || revision == ushort.MaxValue)
		{
			throw new ArgumentException("Invalid handoff identity.");
		}
		Token = token;
		Source = source;
		Target = target;
		Revision = revision;
		Started = (Next = now);
	}

	internal bool Observe(long owner, ushort revision)
	{
		if (owner == Target && revision == Revision + 1)
		{
			Phase = HandoffPhase.Complete;
			return true;
		}
		if ((owner == Source && revision == Revision) || (owner == Target && revision == Revision) || (owner == Source && revision == Revision + 1))
		{
			return false;
		}
		Phase = HandoffPhase.Obsolete;
		return true;
	}

	internal bool MatchesSource(long owner, ushort revision)
	{
		if (owner == Source)
		{
			return revision == Revision;
		}
		return false;
	}

	internal void Prepared(bool ready, float now)
	{
		if (Phase == HandoffPhase.Prepare)
		{
			if (ready)
			{
				Phase = HandoffPhase.Commit;
			}
			Next = now + (ready ? 0f : 0.5f);
		}
	}

	internal void Block(float now)
	{
		if (Phase == HandoffPhase.Commit)
		{
			Phase = HandoffPhase.Blocked;
			Next = now + 5f;
		}
	}

	internal void Wake(float now)
	{
		if (Phase == HandoffPhase.Blocked)
		{
			Phase = HandoffPhase.Prepare;
			Next = now;
		}
	}
}
internal static class IntervalConfig
{
	internal const string Key = "MovementRotationCheckIntervalSeconds";

	internal const float Default = 0.1f;
}
internal sealed class LocalAgent
{
	private sealed class Ticket
	{
		internal long Token;

		internal long Source;

		internal long Target;

		internal ZDOID Id;

		internal ZDOID MobTarget;

		internal ushort Revision;

		internal Kinds Kind;

		internal float Until;

		internal bool Adopted;
	}

	private sealed class Lease
	{
		internal ushort Revision;

		internal float Until;
	}

	private sealed class Busy
	{
		internal Ticket Ticket;

		internal float Check;
	}

	private sealed class Forward
	{
		internal long Owner;

		internal ushort Revision;

		internal float Until;

		internal Kinds Kind;
	}

	private sealed class Replay
	{
		internal int Result;

		internal float Until;
	}

	private sealed class Published
	{
		internal long Owner;

		internal ushort Revision;
	}

	private sealed class ViewState
	{
		internal Kinds Kind;

		internal Character Actor;

		internal MonsterAI Ai;

		internal Tameable Animal;

		internal Vagon Cart;

		internal Container Container;

		internal int Inventories;
	}

	private readonly Dictionary<ZDOID, Ticket> prepared = new Dictionary<ZDOID, Ticket>();

	private readonly Dictionary<ZDOID, Busy> blocked = new Dictionary<ZDOID, Busy>();

	private readonly Dictionary<ZDOID, Lease> refill = new Dictionary<ZDOID, Lease>();

	private readonly Dictionary<ZDOID, float> opening = new Dictionary<ZDOID, float>();

	private readonly Dictionary<ZDOID, Forward> forward = new Dictionary<ZDOID, Forward>();

	private readonly Dictionary<long, Replay> replay = new Dictionary<long, Replay>();

	private readonly List<ZDOID> ids = new List<ZDOID>();

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

	private readonly Dictionary<Vector2s, bool> areaReady = new Dictionary<Vector2s, bool>();

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

	private static readonly ConditionalWeakTable<ZNetView, Published> published = new ConditionalWeakTable<ZNetView, Published>();

	private readonly OwnershipCadence cadence = new OwnershipCadence(0.1f);

	private int readinessFrame = -1;

	private float nextSweep;

	private bool mobWatch;

	internal bool MobsEnabled = true;

	private static readonly Action<MonsterAI, float> sensed = FloatSetter();

	private static readonly Func<BaseAI, ZNetView> aiView = FieldReader.Create<BaseAI, ZNetView>(AccessTools.Field(typeof(BaseAI), "m_nview"));

	private static readonly Func<MonsterAI, Character> aiTarget = FieldReader.Create<MonsterAI, Character>(AccessTools.Field(typeof(MonsterAI), "m_targetCreature"));

	private static readonly Action<BaseAI, bool> alerted = AccessTools.MethodDelegate<Action<BaseAI, bool>>(AccessTools.Method(typeof(BaseAI), "SetAlerted", (Type[])null, (Type[])null), (object)null, true);

	private static readonly Func<InventoryGui, Container> currentInventory = FieldReader.Create<InventoryGui, Container>(AccessTools.Field(typeof(InventoryGui), "m_currentContainer"));

	internal bool HasWork => prepared.Count + blocked.Count + refill.Count + opening.Count + forward.Count + replay.Count != 0;

	private static Action<MonsterAI, float> FloatSetter()
	{
		FieldInfo fieldInfo = AccessTools.Field(typeof(MonsterAI), "m_timeSinceSensedTargetCreature");
		if (fieldInfo == null || fieldInfo.FieldType != typeof(float))
		{
			throw new NotSupportedException("Native monster target timer unavailable.");
		}
		DynamicMethod dynamicMethod = new DynamicMethod("TMLS_ResetSensedTarget", typeof(void), new Type[2]
		{
			typeof(MonsterAI),
			typeof(float)
		}, typeof(LocalAgent).Module, skipVisibility: true);
		ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
		iLGenerator.Emit(OpCodes.Ldarg_0);
		iLGenerator.Emit(OpCodes.Ldarg_1);
		iLGenerator.Emit(OpCodes.Stfld, fieldInfo);
		iLGenerator.Emit(OpCodes.Ret);
		return (Action<MonsterAI, float>)dynamicMethod.CreateDelegate(typeof(Action<MonsterAI, float>));
	}

	private ViewState State(ZNetView view)
	{
		return views.GetValue(view, delegate(ZNetView value)
		{
			Container[] componentsInChildren = ((Component)value).GetComponentsInChildren<Container>(true);
			return new ViewState
			{
				Kind = ObjectCatalog.Classify(((Component)value).gameObject),
				Actor = ((Component)value).GetComponent<Character>(),
				Ai = ((Component)value).GetComponent<MonsterAI>(),
				Animal = ((Component)value).GetComponent<Tameable>(),
				Cart = ((Component)value).GetComponent<Vagon>(),
				Container = ((componentsInChildren.Length == 1) ? componentsInChildren[0] : null),
				Inventories = componentsInChildren.Length
			};
		});
	}

	private static Ticket Read(ZPackage p)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		Ticket ticket = new Ticket();
		ticket.Token = p.ReadLong();
		ticket.Id = p.ReadZDOID();
		ticket.Source = p.ReadLong();
		ticket.Target = p.ReadLong();
		ticket.Revision = p.ReadUShort();
		ticket.Kind = (Kinds)p.ReadInt();
		ticket.MobTarget = p.ReadZDOID();
		ticket.Until = Time.unscaledTime + 8f;
		return ticket;
	}

	internal void Receive(Message message, ZPackage packet)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_013e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0259: Unknown result type (might be due to invalid IL or missing references)
		//IL_03cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_040b: Unknown result type (might be due to invalid IL or missing references)
		//IL_042b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0482: Unknown result type (might be due to invalid IL or missing references)
		//IL_0186: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_04e3: 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_02cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_022b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
		if (ZDOMan.instance == null || !Object.op_Implicit((Object)(object)ZNetScene.instance))
		{
			return;
		}
		if (message == Message.Cancel)
		{
			long num = packet.ReadLong();
			ZDOID key = packet.ReadZDOID();
			if (prepared.TryGetValue(key, out var value) && value.Token == num)
			{
				value.Until = Math.Min(value.Until, Time.unscaledTime + 2f);
			}
			if (!replay.TryGetValue(num, out var value2) || value2.Result != 1)
			{
				replay[num] = new Replay
				{
					Result = 0,
					Until = Time.unscaledTime + 15f
				};
			}
			if (blocked.TryGetValue(key, out var value3) && value3.Ticket.Token == num)
			{
				blocked.Remove(key);
			}
			return;
		}
		Ticket ticket = Read(packet);
		long sessionID = ZDOMan.GetSessionID();
		if (ticket.Token <= 0 || ticket.Source == 0 || ticket.Target == 0 || ticket.Source == ticket.Target || ticket.Revision == ushort.MaxValue)
		{
			return;
		}
		switch (message)
		{
		case Message.ActivationCheck:
		{
			if (ticket.Target != sessionID)
			{
				break;
			}
			ZDO zDO3 = ZDOMan.instance.GetZDO(ticket.Id);
			ZNetView val3 = ((zDO3 != null) ? ZNetScene.instance.FindInstance(zDO3) : null);
			if (zDO3 == null || !Object.op_Implicit((Object)(object)val3) || !val3.IsValid() || !((Component)val3).gameObject.activeInHierarchy || !AreaReady(zDO3.GetPosition()))
			{
				break;
			}
			if (zDO3.GetOwner() == sessionID && zDO3.OwnerRevision == ticket.Revision + 1)
			{
				if (ticket.Kind == Kinds.None && zDO3.GetLong(MobPolicy.WriterKey, 0L) != sessionID)
				{
					Adopt(zDO3, ticket.MobTarget);
				}
				PublishOwned(zDO3);
				Activation(ticket, zDO3.OwnerRevision);
			}
			else if (zDO3.GetOwner() == ticket.Source && zDO3.OwnerRevision == ticket.Revision && prepared.Count < 4096)
			{
				prepared[ticket.Id] = ticket;
			}
			break;
		}
		case Message.Prepare:
			if (ticket.Target == sessionID)
			{
				ZDO zDO2 = ZDOMan.instance.GetZDO(ticket.Id);
				ZNetView val = ((zDO2 != null) ? ZNetScene.instance.FindInstance(zDO2) : null);
				bool ready = zDO2 != null && zDO2.IsValid() && zDO2.GetOwner() == ticket.Source && zDO2.OwnerRevision == ticket.Revision && Object.op_Implicit((Object)(object)val) && val.IsValid() && ((Component)val).gameObject.activeInHierarchy && AreaReady(zDO2.GetPosition());
				if (ready && ticket.Kind == Kinds.None)
				{
					Character val2 = TargetInstance(ticket.MobTarget);
					ready = MobsEnabled && Object.op_Implicit((Object)(object)State(val).Ai) && MobPolicy.Eligible(zDO2) && val2 is Player && !val2.IsDead();
				}
				if (ready && prepared.Count < 4096)
				{
					prepared[ticket.Id] = ticket;
				}
				else
				{
					ready = false;
				}
				Reply(Message.Prepared, ticket, delegate(ZPackage p)
				{
					p.Write(ready);
				});
			}
			break;
		case Message.Commit:
		{
			if (ticket.Source != sessionID)
			{
				break;
			}
			if (replay.TryGetValue(ticket.Token, out var value4))
			{
				ReplyResult(ticket, value4.Result);
				break;
			}
			ZDO zDO = ZDOMan.instance.GetZDO(ticket.Id);
			ZNetView view = ((zDO != null) ? ZNetScene.instance.FindInstance(zDO) : null);
			float until;
			int num2 = Check(ticket, zDO, view, out until);
			switch (num2)
			{
			case 1:
				ZDOMan.instance.ForceSendZDO(ticket.Id);
				zDO.SetOwner(ticket.Target);
				forward[ticket.Id] = new Forward
				{
					Owner = ticket.Target,
					Revision = zDO.OwnerRevision,
					Until = Time.unscaledTime + 5f,
					Kind = ticket.Kind
				};
				blocked.Remove(ticket.Id);
				replay[ticket.Token] = new Replay
				{
					Result = 1,
					Until = Time.unscaledTime + 10f
				};
				break;
			case 2:
				if (blocked.Count < 4096)
				{
					blocked[ticket.Id] = new Busy
					{
						Ticket = ticket,
						Check = until
					};
				}
				break;
			}
			ReplyResult(ticket, num2);
			break;
		}
		}
	}

	private int Check(Ticket ticket, ZDO data, ZNetView view, out float until)
	{
		//IL_028d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02fc: Unknown result type (might be due to invalid IL or missing references)
		until = float.PositiveInfinity;
		if (data == null || !data.IsValid() || data.GetOwner() != ticket.Source || data.OwnerRevision != ticket.Revision || !Object.op_Implicit((Object)(object)view) || !view.IsValid() || !object.ReferenceEquals(view.GetZDO(), data) || !((Component)view).gameObject.activeInHierarchy || data.OwnerRevision == ushort.MaxValue)
		{
			return 0;
		}
		ViewState viewState = State(view);
		if (ticket.Kind == Kinds.None)
		{
			Character val = (Object.op_Implicit((Object)(object)viewState.Ai) ? TargetEvents.Read(viewState.Ai) : null);
			if (!MobsEnabled || !Object.op_Implicit((Object)(object)viewState.Ai) || !Object.op_Implicit((Object)(object)viewState.Actor) || !MobPolicy.Eligible(data) || viewState.Actor.IsDead() || viewState.Actor.IsBoss() || !(val is Player) || val.IsDead() || val.GetZDOID() != ticket.MobTarget)
			{
				return 0;
			}
		}
		else if (viewState.Kind != ticket.Kind || viewState.Kind == Kinds.Incinerators || viewState.Inventories > 1)
		{
			return 3;
		}
		if (Object.op_Implicit((Object)(object)viewState.Actor) && (viewState.Actor.InAttack() || viewState.Actor.IsStaggering() || (ticket.Kind == Kinds.TamedAnimals && Object.op_Implicit((Object)(object)viewState.Ai) && Object.op_Implicit((Object)(object)aiTarget(viewState.Ai)))))
		{
			until = Time.unscaledTime + 0.1f;
			return 2;
		}
		if (data.GetLong(ZDOVars.s_user, 0L) != 0 || (Object.op_Implicit((Object)(object)viewState.Cart) && viewState.Cart.InUse()))
		{
			return 2;
		}
		if (Object.op_Implicit((Object)(object)viewState.Animal) && ObjectCatalog.IsSummoned(viewState.Animal))
		{
			return 3;
		}
		if (Object.op_Implicit((Object)(object)viewState.Animal) && (viewState.Animal.HaveRider() || (viewState.Animal.m_commandable && !string.IsNullOrEmpty(data.GetString(ZDOVars.s_follow, "")))))
		{
			until = Time.unscaledTime + 0.1f;
			return 2;
		}
		if (Object.op_Implicit((Object)(object)viewState.Actor) && (FriendlyCreatures.IsFriendly(viewState.Actor) || viewState.Actor.IsDead()))
		{
			return 3;
		}
		if (Object.op_Implicit((Object)(object)viewState.Actor) && (viewState.Actor.IsAttached() || viewState.Actor.IsRiding()))
		{
			until = Time.unscaledTime + 0.1f;
			return 2;
		}
		if ((opening.TryGetValue(ticket.Id, out var value) && value > Time.unscaledTime) || (Object.op_Implicit((Object)(object)viewState.Container) && (viewState.Container.IsInUse() || data.GetInt(ZDOVars.s_inUse, 0) != 0 || (Object.op_Implicit((Object)(object)InventoryGui.instance) && (Object)(object)currentInventory(InventoryGui.instance) == (Object)(object)viewState.Container))))
		{
			return 2;
		}
		if (refill.TryGetValue(ticket.Id, out var value2) && value2.Revision == data.OwnerRevision && value2.Until > Time.unscaledTime)
		{
			until = value2.Until;
			return 2;
		}
		return 1;
	}

	private bool AreaReady(Vector3 point)
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		if (readinessFrame != Time.frameCount)
		{
			readinessFrame = Time.frameCount;
			areaReady.Clear();
		}
		Vector2s zone = ZoneSystem.GetZone(point);
		if (!areaReady.TryGetValue(zone, out var value))
		{
			value = ZNetScene.instance.IsAreaReady(point);
			areaReady.Add(zone, value);
		}
		return value;
	}

	private static Character TargetInstance(ZDOID id)
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = ((!((ZDOID)(ref id)).IsNone() && Object.op_Implicit((Object)(object)ZNetScene.instance)) ? ZNetScene.instance.FindInstance(id) : null);
		if (!Object.op_Implicit((Object)(object)val))
		{
			return null;
		}
		return val.GetComponent<Character>();
	}

	internal void Observe(ZDO data, bool final = true)
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_0161: Unknown result type (might be due to invalid IL or missing references)
		if (data == null || !data.IsValid() || ZDOMan.instance == null)
		{
			return;
		}
		if (prepared.TryGetValue(data.m_uid, out var value) && data.GetOwner() == ZDOMan.GetSessionID() && (data.OwnerRevision == value.Revision || data.OwnerRevision == value.Revision + 1))
		{
			if (!value.Adopted && value.Kind == Kinds.None)
			{
				Adopt(data, value.MobTarget);
				value.Adopted = true;
			}
			if (final && data.OwnerRevision == value.Revision + 1)
			{
				prepared.Remove(data.m_uid);
				PublishOwned(data);
				Activation(value, data.OwnerRevision);
			}
		}
		if (blocked.ContainsKey(data.m_uid))
		{
			UseEnded(data.m_uid);
		}
		if (!final || data.GetOwner() != ZDOMan.GetSessionID() || !MobsEnabled || !mobWatch || !MobPolicy.HostilePrefab(data.GetPrefab()))
		{
			return;
		}
		ZNetView val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(data) : null);
		MonsterAI val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<MonsterAI>() : null);
		if (Object.op_Implicit((Object)(object)val2) && Watched((BaseAI)(object)val2))
		{
			if (data.GetLong(MobPolicy.WriterKey, 0L) != ZDOMan.GetSessionID())
			{
				Adopt(data, data.GetZDOID(MobPolicy.TargetKey));
			}
			PublishOwned(data);
		}
	}

	private void Adopt(ZDO data, ZDOID targetId)
	{
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		ZNetView val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(data) : null);
		MonsterAI val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<MonsterAI>() : null);
		Character val3 = TargetInstance(targetId);
		Character val4 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<Character>() : null);
		if (MobsEnabled && Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val4) && !val4.IsTamed() && Object.op_Implicit((Object)(object)val3) && !val3.IsDead() && BaseAI.IsEnemy(val4, val3))
		{
			Vector3 val5 = ((Component)val3).transform.position - ((Component)val2).transform.position;
			if (!(((Vector3)(ref val5)).sqrMagnitude > 3600f))
			{
				TargetEvents.Adopt(val2, val3);
				sensed(val2, 0f);
				alerted((BaseAI)(object)val2, arg2: true);
			}
		}
	}

	internal bool Watched(BaseAI ai)
	{
		if (!MobsEnabled || !mobWatch || !(ai is MonsterAI))
		{
			return false;
		}
		ZNetView val = aiView(ai);
		if (!Object.op_Implicit((Object)(object)val) || !val.IsValid() || !val.IsOwner())
		{
			return false;
		}
		Character component = ((Component)ai).GetComponent<Character>();
		if (Object.op_Implicit((Object)(object)component) && !component.IsBoss() && !component.IsDead() && !FriendlyCreatures.IsFriendly(component))
		{
			return !component.IsTamed();
		}
		return false;
	}

	internal void TargetChanged(BaseAI ai)
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Expected O, but got Unknown
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
		if (Watched(ai))
		{
			ZDO data = aiView(ai).GetZDO();
			Character val = TargetEvents.Read((MonsterAI)ai);
			ZDOID id = ((val is Player && !val.IsDead()) ? val.GetZDOID() : ZDOID.None);
			long owner = data.GetOwner();
			ushort revision = data.OwnerRevision;
			if (data.GetZDOID(MobPolicy.TargetKey) != id || data.GetLong(MobPolicy.WriterKey, 0L) != owner || data.GetLong(MobPolicy.RevisionKey, -1L) != revision)
			{
				data.Set(MobPolicy.TargetKey, id);
				data.Set(MobPolicy.WriterKey, owner);
				data.Set(MobPolicy.RevisionKey, (long)revision);
			}
			Published orCreateValue = published.GetOrCreateValue(aiView(ai));
			orCreateValue.Owner = owner;
			orCreateValue.Revision = revision;
			Plugin.Network.ToServer(Message.Target, delegate(ZPackage p)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				p.Write(data.m_uid);
				p.Write(revision);
				p.Write(id);
			});
		}
	}

	private void PublishOwned(ZDO data)
	{
		if (data.GetOwner() != ZDOMan.GetSessionID() || !Object.op_Implicit((Object)(object)ZNetScene.instance))
		{
			return;
		}
		ZNetView val = ZNetScene.instance.FindInstance(data);
		MonsterAI val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<MonsterAI>() : null);
		if (Object.op_Implicit((Object)(object)val2) && Watched((BaseAI)(object)val2))
		{
			Published orCreateValue = published.GetOrCreateValue(val);
			if (orCreateValue.Owner != data.GetOwner() || orCreateValue.Revision != data.OwnerRevision)
			{
				TargetChanged((BaseAI)(object)val2);
			}
		}
	}

	internal void SetMobWatch(bool value)
	{
		value &= MobsEnabled;
		if (mobWatch == value)
		{
			return;
		}
		mobWatch = value;
		if (!value)
		{
			return;
		}
		foreach (BaseAI allInstance in BaseAI.GetAllInstances())
		{
			if (Watched(allInstance))
			{
				TargetChanged(allInstance);
			}
		}
	}

	internal void Instance(ZNetView view)
	{
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)view) || !view.IsValid())
		{
			return;
		}
		ZDO zDO = view.GetZDO();
		Observe(zDO);
		if (MobsEnabled && MobPolicy.HostilePrefab(zDO.GetPrefab()))
		{
			if (zDO.IsOwner() && zDO.GetLong(MobPolicy.WriterKey, 0L) != ZDOMan.GetSessionID())
			{
				Adopt(zDO, zDO.GetZDOID(MobPolicy.TargetKey));
			}
			PublishOwned(zDO);
		}
	}

	internal void Outgoing(ZDOID id, int method)
	{
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		if (!ZNet.IsSinglePlayer && (method == StringExtensionMethods.GetStableHashCode("RPC_RequestOpen") || method == StringExtensionMethods.GetStableHashCode("RPC_RequestStack") || method == StringExtensionMethods.GetStableHashCode("RPC_RequestTakeAll")))
		{
			opening[id] = Time.unscaledTime + 5f;
		}
	}

	internal void Response(Container container)
	{
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		if (Object.op_Implicit((Object)(object)container))
		{
			ZNetView val = (Object.op_Implicit((Object)(object)container.m_rootObjectOverride) ? ((Component)container.m_rootObjectOverride).GetComponent<ZNetView>() : ((Component)container).GetComponent<ZNetView>());
			if (Object.op_Implicit((Object)(object)val) && val.IsValid())
			{
				opening.Remove(val.GetZDO().m_uid);
				UseEnded(val.GetZDO().m_uid);
			}
		}
	}

	internal void Refilling(ZDO data, int method)
	{
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		if (!ZNet.IsSinglePlayer && ObjectCatalog.IsRefillMethod(method) && data != null && data.IsOwner() && Object.op_Implicit((Object)(object)ZNetScene.instance))
		{
			ZNetView val = ZNetScene.instance.FindInstance(data);
			if (Object.op_Implicit((Object)(object)val) && ObjectCatalog.RefillRequest(State(val).Kind, method))
			{
				refill[data.m_uid] = new Lease
				{
					Revision = data.OwnerRevision,
					Until = Time.unscaledTime + 10f
				};
			}
		}
	}

	internal bool MayForward(ZDO data, int method)
	{
		//IL_000a: Unknown result type (might be due to invalid IL or missing references)
		if (data == null || !forward.TryGetValue(data.m_uid, out var value) || Time.unscaledTime > value.Until || data.GetOwner() != value.Owner || data.OwnerRevision != value.Revision)
		{
			return false;
		}
		if (value.Kind != Kinds.None)
		{
			return ObjectCatalog.OwnerRequest(value.Kind, method);
		}
		return method == StringExtensionMethods.GetStableHashCode("RPC_Damage");
	}

	internal bool Prepared(ZDOID id)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		return prepared.ContainsKey(id);
	}

	internal bool NeedsFinalImport(ZDO data)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		if (prepared.ContainsKey(data.m_uid))
		{
			return true;
		}
		if (!MobsEnabled || !mobWatch || data.GetOwner() != ZDOMan.GetSessionID() || !MobPolicy.HostilePrefab(data.GetPrefab()))
		{
			return false;
		}
		ZNetView val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(data) : null);
		if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)((Component)val).GetComponent<MonsterAI>()))
		{
			return false;
		}
		Published orCreateValue = published.GetOrCreateValue(val);
		if (orCreateValue.Owner == data.GetOwner() && orCreateValue.Revision == data.OwnerRevision)
		{
			return data.GetLong(MobPolicy.WriterKey, 0L) != data.GetOwner();
		}
		return true;
	}

	internal bool ForwardDestination(ZDOID id, ZDO data, int method, out long owner, out ushort revision)
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		owner = 0L;
		revision = 0;
		if (!forward.TryGetValue(id, out var value) || Time.unscaledTime > value.Until || (data != null && (data.GetOwner() != value.Owner || data.OwnerRevision != value.Revision)))
		{
			return false;
		}
		if (!((value.Kind == Kinds.None) ? (method == StringExtensionMethods.GetStableHashCode("RPC_Damage")) : ObjectCatalog.OwnerRequest(value.Kind, method)))
		{
			return false;
		}
		owner = value.Owner;
		revision = value.Revision;
		return true;
	}

	internal void UseEnded(ZDOID id)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		if (blocked.TryGetValue(id, out var value))
		{
			ZDO val = ((ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(id) : null);
			ZNetView view = ((val != null && Object.op_Implicit((Object)(object)ZNetScene.instance)) ? ZNetScene.instance.FindInstance(val) : null);
			float until;
			int num = Check(value.Ticket, val, view, out until);
			if (num == 2)
			{
				value.Check = until;
				return;
			}
			blocked.Remove(id);
			Reply(Message.Wake, value.Ticket, delegate
			{
			});
		}
	}

	internal void Tick(float now)
	{
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_025e: 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_0292: Unknown result type (might be due to invalid IL or missing references)
		//IL_0297: Unknown result type (might be due to invalid IL or missing references)
		//IL_029f: Unknown result type (might be due to invalid IL or missing references)
		if (!cadence.Due(now))
		{
			return;
		}
		ids.Clear();
		foreach (KeyValuePair<ZDOID, Busy> item in blocked)
		{
			if (now >= item.Value.Check)
			{
				ids.Add(item.Key);
			}
		}
		foreach (ZDOID id in ids)
		{
			UseEnded(id);
		}
		ids.Clear();
		if (now < nextSweep)
		{
			return;
		}
		nextSweep = now + 2f;
		Sweep(prepared, (Ticket t) => now >= t.Until);
		Sweep(refill, (Lease t) => now >= t.Until);
		Sweep(opening, (float t) => now >= t);
		Sweep(forward, (Forward t) => now >= t.Until);
		tokens.Clear();
		foreach (KeyValuePair<long, Replay> item2 in replay)
		{
			if (now >= item2.Value.Until)
			{
				tokens.Add(item2.Key);
			}
		}
		foreach (long token in tokens)
		{
			replay.Remove(token);
		}
		tokens.Clear();
		ids.Clear();
		foreach (KeyValuePair<ZDOID, Busy> item3 in blocked)
		{
			if (ZDOMan.instance == null || ZDOMan.instance.GetZDO(item3.Key) == null || now - item3.Value.Ticket.Until > 10f)
			{
				ids.Add(item3.Key);
			}
		}
		foreach (ZDOID id2 in ids)
		{
			blocked.Remove(id2);
		}
		ids.Clear();
	}

	private void Sweep<T>(Dictionary<ZDOID, T> source, Predicate<T> expired)
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		ids.Clear();
		foreach (KeyValuePair<ZDOID, T> item in source)
		{
			if (expired(item.Value))
			{
				ids.Add(item.Key);
			}
		}
		foreach (ZDOID id in ids)
		{
			source.Remove(id);
		}
		ids.Clear();
	}

	private static void Reply(Message message, Ticket ticket, Action<ZPackage> write)
	{
		Plugin.Network.ToServer(message, delegate(ZPackage p)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			p.Write(ticket.Token);
			p.Write(ticket.Id);
			write(p);
		});
	}

	private static void ReplyResult(Ticket ticket, int result)
	{
		Reply(Message.Result, ticket, delegate(ZPackage p)
		{
			p.Write(result);
		});
	}

	private static void Activation(Ticket ticket, ushort revision)
	{
		Reply(Message.Activated, ticket, delegate(ZPackage p)
		{
			p.Write(revision);
		});
	}

	internal void Reset()
	{
		prepared.Clear();
		blocked.Clear();
		refill.Clear();
		opening.Clear();
		forward.Clear();
		replay.Clear();
		areaReady.Clear();
		mobWatch = false;
	}
}
internal sealed class MobPolicy
{
	private sealed class Record
	{
		internal ZDO Data;

		internal ZDOID Target;

		internal long Writer;

		internal ushort Revision;

		internal float Next;
	}

	internal static readonly KeyValuePair<int, int> TargetKey = ZDO.GetHashZDOID("botanic.toomanylags.standalone.target");

	internal static readonly int WriterKey = StringExtensionMethods.GetStableHashCode("botanic.toomanylags.standalone.targetWriter");

	internal static readonly int RevisionKey = StringExtensionMethods.GetStableHashCode("botanic.toomanylags.standalone.targetRevision");

	private readonly Dictionary<ZDOID, Record> records = new Dictionary<ZDOID, Record>();

	private readonly List<ZDOID> removing = new List<ZDOID>();

	private static readonly Dictionary<int, int> types = new Dictionary<int, int>();

	private readonly AuthorityController controller;

	private float next = float.PositiveInfinity;

	internal MobPolicy(AuthorityController controller)
	{
		this.controller = controller;
	}

	private static int PrefabType(int hash)
	{
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		if (types.TryGetValue(hash, out var value))
		{
			return value;
		}
		GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab(hash) : null);
		if (!Object.op_Implicit((Object)(object)val))
		{
			return 0;
		}
		Character component = val.GetComponent<Character>();
		value = ((Object.op_Implicit((Object)(object)component) && !(component is Player) && (int)component.GetFaction() == 0) ? 1 : ((Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)val.GetComponent<MonsterAI>()) && !component.IsBoss()) ? 2 : 0));
		types.Add(hash, value);
		return value;
	}

	internal static bool FriendlyPrefab(int hash)
	{
		return PrefabType(hash) == 1;
	}

	internal static bool HostilePrefab(int hash)
	{
		if (hash != 0)
		{
			return PrefabType(hash) == 2;
		}
		return false;
	}

	internal static bool Eligible(ZDO data)
	{
		if (data != null && data.IsValid() && data.GetOwner() != 0 && data.GetFloat(ZDOVars.s_health, 1f) > 0f && !data.GetBool(ZDOVars.s_tamed, false) && data.GetLong(ZDOVars.s_user, 0L) == 0)
		{
			return PrefabType(data.GetPrefab()) == 2;
		}
		return false;
	}

	internal void Report(long sender, ZDOID id, ushort revision, ZDOID target)
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: 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_00de: 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)
		if (!Plugin.Options.Mobs || ZDOMan.instance == null)
		{
			return;
		}
		ZDO zDO = ZDOMan.instance.GetZDO(id);
		if (!Eligible(zDO) || zDO.GetOwner() != sender || zDO.OwnerRevision != revision || !Plugin.Network.MobCapable(sender))
		{
			return;
		}
		if (((ZDOID)(ref target)).IsNone())
		{
			Forget(id);
			controller.Handoffs.Cancel(id);
			return;
		}
		bool flag = false;
		foreach (PlayerPeer player in controller.Players)
		{
			if (player.Character.m_uid == target)
			{
				flag = true;
				break;
			}
		}
		if (flag && (!records.TryGetValue(id, out var value) || !object.ReferenceEquals(value.Data, zDO) || !(value.Target == target) || value.Writer != sender || value.Revision != revision))
		{
			records[id] = new Record
			{
				Data = zDO,
				Target = target,
				Writer = sender,
				Revision = revision,
				Next = Time.unscaledTime
			};
			next = Time.unscaledTime;
		}
	}

	internal void Changed(ZDO data)
	{
		//IL_0047: 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_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: 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)
		if (!Plugin.Options.Mobs || data == null || data.GetPrefab() == 0)
		{
			return;
		}
		if (!Eligible(data))
		{
			if (records.ContainsKey(data.m_uid))
			{
				Forget(data.m_uid);
			}
			return;
		}
		if (records.TryGetValue(data.m_uid, out var value) && (value.Writer != data.GetOwner() || value.Revision != data.OwnerRevision))
		{
			Wake(data.m_uid);
		}
		if (data.GetLong(WriterKey, 0L) == data.GetOwner() && data.GetLong(RevisionKey, -1L) == data.OwnerRevision)
		{
			Report(data.GetOwner(), data.m_uid, data.OwnerRevision, data.GetZDOID(TargetKey));
		}
	}

	internal long Desired(ZDO data, ZDOID target)
	{
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
		if (!Plugin.Options.Mobs || !Eligible(data) || ((ZDOID)(ref target)).IsNone() || !Plugin.Network.MobCapable(data.GetOwner()))
		{
			return 0L;
		}
		PlayerPeer playerPeer = default(PlayerPeer);
		int num = 0;
		foreach (PlayerPeer player in controller.Players)
		{
			if (player.Character.m_uid == target)
			{
				playerPeer = player;
			}
			if (Plugin.Network.MobCapable(player.Uid) && controller.Available(player.Uid, data.GetPosition()))
			{
				num++;
			}
		}
		if (num < 2 || playerPeer.Uid == 0 || !Plugin.Network.MobCapable(playerPeer.Uid) || !controller.Available(playerPeer.Uid, data.GetPosition()))
		{
			return 0L;
		}
		float num2 = Plugin.Network.Latency(playerPeer.Uid);
		if (float.IsInfinity(num2) || num2 <= (float)Plugin