Decompiled source of TooManyLags Server Side v0.13.2

TooManyLagsServerSide.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Steamworks;
using UnityEngine;

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

internal enum GridCoveragePattern
{
	Square3x3,
	Rounded5x5,
	Rounded7x7
}
internal enum FieldTier : byte
{
	Outer,
	Inner,
	Forward,
	ImmediateForward
}
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, 8f, out cell);
	}

	internal static bool At(float x, float z, float size, out Cell cell)
	{
		cell = default(Cell);
		int num = Subdivisions(size);
		if (num == 0)
		{
			return false;
		}
		if (float.IsNaN(x) || float.IsNaN(z) || float.IsInfinity(x) || float.IsInfinity(z))
		{
			return false;
		}
		double num2 = (double)num / 64.0;
		double num3 = Math.Floor(((double)x + 32.0) * num2);
		double num4 = Math.Floor(((double)z + 32.0) * num2);
		int num5 = 256 * num;
		if (num3 < (double)(-num5) || num3 >= (double)num5 || num4 < (double)(-num5) || num4 >= (double)num5)
		{
			return false;
		}
		cell = new Cell((int)num3, (int)num4);
		return true;
	}

	internal static int Subdivisions(float size)
	{
		if (size != 4f)
		{
			if (size != 6.4f)
			{
				if (size != 8f)
				{
					if (size != 16f)
					{
						return 0;
					}
					return 4;
				}
				return 8;
			}
			return 10;
		}
		return 16;
	}

	internal static double Edge(int coordinate, int subdivisions)
	{
		return (double)coordinate * 64.0 / (double)subdivisions - 32.0;
	}

	internal static Cell Zone(Cell fine, float size)
	{
		if (size == 6.4f)
		{
			return new Cell(Floor10(fine.X), Floor10(fine.Z));
		}
		int num = ((size == 4f) ? 4 : ((size == 16f) ? 2 : 3));
		return new Cell(fine.X >> num, fine.Z >> num);
	}

	private static int Floor10(int value)
	{
		if (value < 0)
		{
			return (value + 1) / 10 - 1;
		}
		return value / 10;
	}

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

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

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

	internal Cell Cell;

	internal float Yaw;

	internal int Coverage;

	internal bool FastForward;

	internal bool Large;

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

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

		internal long Owner;

		internal long Uids;

		internal int Count;

		internal int FastCount;

		internal int ForwardCount;

		internal int ImmediateCount;

		internal bool Shared;

		internal float Since;

		internal float Wait;

		internal float Deadline;

		internal float OverlapUntil;

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

	internal const int CoverageCount = 56;

	private const int LargeStart = 112;

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

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

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

	internal Action<Cell> Renewed;

	private readonly bool directional;

	private readonly bool discardVacant;

	private float innerDelay;

	private float outerDelay;

	private float forwardDelay;

	private float overlapCooldown;

	internal float Delay;

	private float lastTime;

	private float nextDue = float.PositiveInfinity;

	private int deferredCells;

	private int coolingCells;

	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 int CoolingCells => coolingCells;

	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, float overlapCooldown = 0f, bool fullRear = false, bool includeOuter = true)
	{
		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);
		this.overlapCooldown = SafeDelay(overlapCooldown, 1f);
		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 || fullRear || num2 >= 0 || (num2 >= -1 && Math.Abs(value) > 1)))
					{
						bool flag3 = false;
						if (directional)
						{
							flag3 = (fullRear ? (Math.Abs(j) <= 1 && Math.Abs(k) <= 1) : ((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)));
						}
						if (!directional || includeOuter || flag3 || flag2)
						{
							list.Add(new Offset(j, k, flag3 ? FieldTier.Inner : (flag2 ? FieldTier.Forward : FieldTier.Outer)));
						}
					}
				}
			}
			footprints[i] = list.ToArray();
			Dictionary<Cell, FieldTier> dictionary = new Dictionary<Cell, FieldTier>(list.Count);
			foreach (Offset item in list)
			{
				dictionary.Add(item.Cell, item.Tier);
			}
			maskTypes[i] = dictionary;
		}
		for (int l = 2; l <= 3; l++)
		{
			for (int m = 0; m < 8; m++)
			{
				int num3 = (l - 1) * 8 + m;
				int num4 = (l - 2) * 8 + m;
				if (!directional)
				{
					footprints[num3] = footprints[m];
					maskTypes[num3] = maskTypes[m];
					continue;
				}
				Dictionary<Cell, FieldTier> dictionary2 = new Dictionary<Cell, FieldTier>(maskTypes[num4]);
				Offset[] array3 = footprints[(m + l - 1) & 7];
				for (int n = 0; n < array3.Length; n++)
				{
					Offset offset = array3[n];
					if (!dictionary2.TryGetValue(offset.Cell, out var value2) || offset.Tier == FieldTier.Inner || (value2 == FieldTier.Outer && offset.Tier == FieldTier.Forward))
					{
						dictionary2[offset.Cell] = offset.Tier;
					}
				}
				List<Offset> list2 = new List<Offset>(dictionary2.Count);
				foreach (KeyValuePair<Cell, FieldTier> item2 in dictionary2)
				{
					list2.Add(new Offset(item2.Key.X, item2.Key.Z, item2.Value));
				}
				footprints[num3] = list2.ToArray();
				maskTypes[num3] = dictionary2;
			}
		}
		for (int num5 = 0; num5 < 16; num5++)
		{
			for (int num6 = 0; num6 < 2; num6++)
			{
				int num7 = 24 + num6 * 16 + num5;
				if (!directional)
				{
					footprints[num7] = footprints[num5];
					maskTypes[num7] = maskTypes[num5];
					continue;
				}
				int num8 = num5 & 7;
				int num9 = ((num6 == 0) ? ((num8 + ((num5 < 8) ? 1 : 2)) & 7) : ((num8 + 7) & 7));
				Dictionary<Cell, FieldTier> dictionary3 = new Dictionary<Cell, FieldTier>(maskTypes[num5]);
				Offset[] array4 = footprints[num9];
				for (int num10 = 0; num10 < array4.Length; num10++)
				{
					Offset offset2 = array4[num10];
					if (!dictionary3.ContainsKey(offset2.Cell))
					{
						dictionary3.Add(offset2.Cell, FieldTier.Inner);
					}
				}
				List<Offset> list3 = new List<Offset>(dictionary3.Count);
				foreach (KeyValuePair<Cell, FieldTier> item3 in dictionary3)
				{
					list3.Add(new Offset(item3.Key.X, item3.Key.Z, item3.Value));
				}
				footprints[num7] = list3.ToArray();
				maskTypes[num7] = dictionary3;
			}
		}
		for (int num11 = 0; num11 < 56; num11++)
		{
			bool flag4 = false;
			Offset[] array5 = footprints[num11];
			for (int num12 = 0; num12 < array5.Length; num12++)
			{
				Offset offset3 = array5[num12];
				if (offset3.Tier == FieldTier.Forward)
				{
					flag4 = true;
					break;
				}
			}
			if (!flag4)
			{
				footprints[num11 + 56] = footprints[num11];
				maskTypes[num11 + 56] = maskTypes[num11];
				continue;
			}
			Offset[] array6 = new Offset[footprints[num11].Length];
			Dictionary<Cell, FieldTier> dictionary4 = new Dictionary<Cell, FieldTier>(array6.Length);
			for (int num13 = 0; num13 < array6.Length; num13++)
			{
				Offset offset4 = footprints[num11][num13];
				if (offset4.Tier == FieldTier.Forward)
				{
					offset4.Tier = FieldTier.ImmediateForward;
				}
				array6[num13] = offset4;
				dictionary4.Add(offset4.Cell, offset4.Tier);
			}
			footprints[num11 + 56] = array6;
			maskTypes[num11 + 56] = dictionary4;
		}
		for (int num14 = 0; num14 < 8; num14++)
		{
			Dictionary<Cell, FieldTier> dictionary5 = (includeOuter ? new Dictionary<Cell, FieldTier>(maskTypes[num14]) : new Dictionary<Cell, FieldTier>());
			for (int num15 = -2; num15 <= 2; num15++)
			{
				for (int num16 = -1; num16 <= 2; num16++)
				{
					if ((num16 != -1 && num16 != 2) || Math.Abs(num15) != 2)
					{
						LargeCell(dictionary5, num15, num16, num14 / 2, FieldTier.Inner);
						if ((num14 & 1) != 0)
						{
							LargeCell(dictionary5, num16, -num15, num14 / 2, FieldTier.Inner);
						}
					}
				}
			}
			if ((num14 & 1) != 0)
			{
				LargeCell(dictionary5, 2, 2, num14 / 2, FieldTier.Inner);
			}
			for (int num17 = -1; num17 <= 1; num17++)
			{
				if ((num14 & 1) == 0)
				{
					LargeCell(dictionary5, num17, 3, num14 / 2, FieldTier.Forward);
				}
				else
				{
					LargeCell(dictionary5, (num17 == -1) ? 2 : 3, (num17 == 1) ? 2 : 3, num14 / 2, FieldTier.Forward);
				}
			}
			for (int num18 = 0; num18 < 2; num18++)
			{
				int num19 = 112 + num18 * 8 + num14;
				Dictionary<Cell, FieldTier> dictionary6 = new Dictionary<Cell, FieldTier>(dictionary5.Count);
				List<Offset> list4 = new List<Offset>(dictionary5.Count);
				foreach (KeyValuePair<Cell, FieldTier> item4 in dictionary5)
				{
					FieldTier fieldTier = ((num18 != 0 && item4.Value == FieldTier.Forward) ? FieldTier.ImmediateForward : item4.Value);
					dictionary6.Add(item4.Key, fieldTier);
					list4.Add(new Offset(item4.Key.X, item4.Key.Z, fieldTier));
				}
				footprints[num19] = list4.ToArray();
				maskTypes[num19] = dictionary6;
			}
		}
	}

	private static void LargeCell(Dictionary<Cell, FieldTier> types, int x, int z, int turns, FieldTier tier)
	{
		for (int i = 0; i < turns; i++)
		{
			int num = x;
			x = z;
			z = -num;
		}
		Cell key = new Cell(x, z);
		if (!types.TryGetValue(key, out var value) || value != FieldTier.Inner)
		{
			types[key] = tier;
		}
	}

	internal static int HeadingCoverage(float yaw, float overlapDegrees = 30f)
	{
		if (float.IsNaN(yaw) || float.IsInfinity(yaw))
		{
			return -1;
		}
		yaw %= 360f;
		if (yaw < 0f)
		{
			yaw += 360f;
		}
		int num = (int)(yaw / 45f);
		float num2 = yaw - (float)num * 45f;
		if (float.IsNaN(overlapDegrees) || float.IsInfinity(overlapDegrees))
		{
			overlapDegrees = 30f;
		}
		overlapDegrees = Math.Max(0f, Math.Min(45f, overlapDegrees));
		if (overlapDegrees == 0f)
		{
			if (!(num2 < 22.5f))
			{
				return (num + 1) & 7;
			}
			return num;
		}
		float num3 = (45f - overlapDegrees) * 0.5f;
		float num4 = (45f + overlapDegrees) * 0.5f;
		if (!(num2 < num3))
		{
			if (!(num2 <= num4))
			{
				return (num + 1) & 7;
			}
			return 8 + num;
		}
		return num;
	}

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

	internal void ConfigureOverlapCooldown(float seconds)
	{
		overlapCooldown = SafeDelay(seconds, 1f);
	}

	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 int Participants(Cell cell)
	{
		if (!policies.TryGetValue(cell, out var value))
		{
			return 0;
		}
		return value.Count;
	}

	internal bool Covers(long uid, Cell cell)
	{
		if (previous.TryGetValue(uid, out var value))
		{
			return maskTypes[value.Heading].ContainsKey(new Cell(cell.X - value.Cell.X, cell.Z - value.Cell.Z));
		}
		return false;
	}

	internal bool CoverageStart(long uid, Cell cell, out float since)
	{
		since = 0f;
		if (!policies.TryGetValue(cell, out var value) || value.Count == 0)
		{
			return false;
		}
		if (value.First.Uid == uid)
		{
			since = value.First.Since;
			return true;
		}
		if (value.Others != null)
		{
			foreach (Request other in value.Others)
			{
				if (other.Uid == uid)
				{
					since = other.Since;
					return true;
				}
			}
		}
		return false;
	}

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

	internal int WorkPriority(Cell cell)
	{
		if (!policies.TryGetValue(cell, out var value) || value.Count == 0)
		{
			return 3;
		}
		if (directional)
		{
			if (value.FastCount == 0 && value.ImmediateCount == 0)
			{
				if (value.ForwardCount == 0)
				{
					return 2;
				}
				return 1;
			}
			return 0;
		}
		return 2;
	}

	private float FieldWait(Policy policy)
	{
		if (!directional)
		{
			return Delay;
		}
		if (policy.ImmediateCount != 0)
		{
			return 0f;
		}
		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);
	}

	internal bool FastForward(long uid)
	{
		if (previous.TryGetValue(uid, out var value))
		{
			if (value.Heading < 112)
			{
				return value.Heading >= 56;
			}
			return value.Heading >= 120;
		}
		return false;
	}

	internal bool LargeCoverage(long uid)
	{
		if (previous.TryGetValue(uid, out var value))
		{
			return value.Heading >= 112;
		}
		return false;
	}

	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;
		case FieldTier.ImmediateForward:
			value.ImmediateCount += (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);
		if (add && Renewed != null)
		{
			Renewed(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;
		case FieldTier.ImmediateForward:
			policy.ImmediateCount--;
			break;
		}
		switch (after)
		{
		case FieldTier.Inner:
			policy.FastCount++;
			break;
		case FieldTier.Forward:
			policy.ForwardCount++;
			break;
		case FieldTier.ImmediateForward:
			policy.ImmediateCount++;
			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 (Renewed != null)
				{
					Renewed(cell);
				}
				if (value != offset.Tier)
				{
					Reclassify(uid, cell, offset.Tier, value);
				}
				else if ((deferredCells != 0 && policies[cell].Deferred) || (coolingCells != 0 && policies[cell].OverlapUntil != 0f))
				{
					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 > 1 && policy.Owner != 0 && overlapCooldown > 0f)
			{
				HoldOverlap(policy, lastTime + overlapCooldown);
			}
		}
		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 = (policy.ImmediateCount = 0)));
			policy.Since = (policy.Wait = (policy.Deadline = (policy.OverlapUntil = 0f)));
			policy.Shared = false;
			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);
			HoldOverlap(policy, 0f);
			policy.Candidate = 0L;
			Commit(cell, policy);
			return;
		}
		if (policy.OverlapUntil != 0f)
		{
			if ((policy.Owner == server || seen.Contains(policy.Owner)) && policy.Count != 0 && now < policy.OverlapUntil)
			{
				policy.Candidate = policy.Owner;
				policy.Shared = policy.Count > 1;
				pending.Remove(cell);
				return;
			}
			HoldOverlap(policy, 0f);
		}
		if (directional && discardVacant && policy.Count == 1 && policy.First.Tier != FieldTier.Inner && policy.First.Tier != FieldTier.ImmediateForward && (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);
		bool flag = policy.Count > 1 && !policy.Shared;
		long num = Resolve(policy);
		policy.Shared = policy.Count > 1;
		if (flag && policy.Owner != 0 && policy.Owner == num && overlapCooldown > 0f)
		{
			HoldOverlap(policy, now + overlapCooldown);
		}
		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.ImmediateForward) ? 0f : ((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 void HoldOverlap(Policy policy, float until)
	{
		if (policy.OverlapUntil == 0f != (until == 0f))
		{
			coolingCells += ((until != 0f) ? 1 : (-1));
		}
		policy.OverlapUntil = until;
	}

	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)
		{
			if (coolingCells != 0)
			{
				foreach (Policy value4 in policies.Values)
				{
					if (value4.OverlapUntil != 0f)
					{
						value4.OverlapUntil = now + overlapCooldown;
					}
				}
			}
			nextDue = float.PositiveInfinity;
			foreach (Cell item in pending)
			{
				Policy policy = policies[item];
				policy.Since = now;
				policy.Deadline = now + policy.Wait;
				policy.First.Since = now;
				if (policy.Others != null)
				{
					for (int i = 0; i < policy.Others.Count; i++)
					{
						Request value = policy.Others[i];
						value.Since = now;
						policy.Others[i] = value;
					}
				}
				nextDue = Math.Min(nextDue, policy.Deadline);
			}
		}
		bool flag2 = false;
		foreach (GridPeer peer in peers)
		{
			if (peer.Uid == 0 || peer.Uid == server || !seen.Add(peer.Uid))
			{
				continue;
			}
			PeerState value2;
			bool flag3 = previous.TryGetValue(peer.Uid, out value2);
			if (flag3)
			{
				value2.Refreshed = false;
			}
			float num = Yaw(peer.Yaw, flag3 ? value2.Yaw : 0f);
			int num2 = (directional ? ((peer.Coverage >= 0 && peer.Coverage < 56) ? peer.Coverage : ((flag3 && (float.IsNaN(peer.Yaw) || float.IsInfinity(peer.Yaw))) ? ((value2.Heading >= 112) ? ((value2.Heading - 112) & 7) : (value2.Heading % 56)) : ((int)((num + 22.5f) / 45f) & 7))) : 0);
			if (directional && peer.Large)
			{
				num2 = 112 + ((int)((num + 22.5f) / 45f) & 7) + (peer.FastForward ? 8 : 0);
			}
			else if (directional && peer.FastForward)
			{
				num2 += 56;
			}
			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 abstract class AuthorityNode
{
	internal Cell Cell;

	internal bool Managed;

	internal bool Removed;

	internal long ManagedOwner;

	internal long ConfirmedFieldOwner;

	internal AuthorityWorld.ObjectBucket IndexedBucket;

	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 ApplyResult
{
	Done,
	Pending,
	Dropped
}
internal sealed class AuthorityWorld
{
	internal sealed class ObjectBucket
	{
		internal readonly HashSet<AuthorityNode> Nodes = new HashSet<AuthorityNode>();

		internal long Target;

		internal int Unconfirmed;

		internal bool PriorityCached;

		internal int Priority;
	}

	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<ObjectBucket> priorityBuckets = new List<ObjectBucket>();

	private readonly List<AuthorityNode>[] work = new List<AuthorityNode>[4]
	{
		new List<AuthorityNode>(),
		new List<AuthorityNode>(),
		new List<AuthorityNode>(),
		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, float overlapCooldown = 0f, bool fullRear = false, bool includeOuter = true)
	{
		Grid = new AuthorityGrid(server, delay, pattern, directional, innerDelay, outerDelay, keepOnExit, forward, forwardDelay, latency, margin, overlapCooldown, fullRear || !keepOnExit, includeOuter);
		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.IndexedBucket = objectBucket;
			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.IndexedBucket = objectBucket;
			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);
			bool flag = false;
			float num2 = 0f;
			foreach (AuthorityNode node in value.Nodes)
			{
				node.ConfirmedFieldOwner = 0L;
				if (pending.Add(node))
				{
					node.NotBefore = now;
				}
				else if (Grid.Directional)
				{
					if (!flag)
					{
						num2 = Grid.Wait(change);
						flag = true;
					}
					node.NotBefore = Math.Min(node.NotBefore, now + num2);
				}
			}
		}
		if (pending.Count == 0)
		{
			return;
		}
		ClearPriorityBatch();
		try
		{
			foreach (AuthorityNode item2 in pending)
			{
				work[BatchPriority(item2)].Add(item2);
			}
		}
		finally
		{
			ClearPriorityBatch();
		}
		try
		{
			List<AuthorityNode>[] array = work;
			foreach (List<AuthorityNode> list in array)
			{
				foreach (AuthorityNode item3 in list)
				{
					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;
					}
				}
			}
		}
		finally
		{
			List<AuthorityNode>[] array2 = work;
			foreach (List<AuthorityNode> list2 in array2)
			{
				list2.Clear();
			}
		}
	}

	internal void Dismiss(AuthorityNode node)
	{
		Invalidate(node);
		pending.Remove(node);
		node.Managed = false;
	}

	internal int BatchPriority(AuthorityNode node)
	{
		ObjectBucket indexedBucket = node.IndexedBucket;
		if (indexedBucket == null || indexedBucket.Nodes.Count == 1)
		{
			return Grid.WorkPriority(node.Cell);
		}
		if (!indexedBucket.PriorityCached)
		{
			indexedBucket.Priority = Grid.WorkPriority(node.Cell);
			indexedBucket.PriorityCached = true;
			priorityBuckets.Add(indexedBucket);
		}
		return indexedBucket.Priority;
	}

	internal void ClearPriorityBatch()
	{
		foreach (ObjectBucket priorityBucket in priorityBuckets)
		{
			priorityBucket.PriorityCached = false;
		}
		priorityBuckets.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))
		{
			node.IndexedBucket = null;
			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 sealed class Controller
{
	private sealed class Node : AuthorityNode
	{
		internal readonly Controller Controller;

		internal readonly ZDO Data;

		internal readonly ZNetView View;

		internal readonly Groups PrefabGroups;

		internal Groups Group;

		internal MobState Mob;

		internal long GraceSource;

		internal ushort GraceRevision;

		internal long LastOwner;

		internal bool InGrid;

		internal bool Released;

		internal bool Pinned;

		internal bool Writing;

		internal bool ControlPinned;

		internal bool RiderSeen;

		internal long AcceptedRider;

		internal HashSet<long> ControlRequests;

		internal Dictionary<long, float> NativeUseRequests;

		internal byte ActorState;

		internal RefillState Refill;

		internal Container Container;

		internal Ship Ship;

		internal Vagon Cart;

		internal Vector3 ShipAnchor;

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

		internal Node(Controller owner, ZDO data, ZNetView view, Groups group, Cell cell)
		{
			Controller = owner;
			Data = data;
			View = view;
			PrefabGroups = group;
			Group = CreatureGroup(data, group);
			Cell = cell;
		}

		internal override ApplyResult Apply(long target)
		{
			return Controller.Apply(this, target);
		}
	}

	private struct Latency
	{
		internal float Until;

		internal int Ping;
	}

	private sealed class MobState
	{
		internal Humanoid Actor;

		internal MonsterAI Ai;

		internal MobAttackProfile Profile;

		internal long Source;

		internal ushort Revision;

		internal bool Pending;

		internal bool Armed;

		internal bool SawAttack;

		internal bool Looping;

		internal bool Combat;

		internal bool IdleHandoffObserved;

		internal float TriggerAt;

		internal float IdleAt = float.PositiveInfinity;

		internal Vector3 Position;

		internal Quaternion Rotation;

		internal bool TargetValid;

		internal int SelectionEpoch;

		internal long Target;

		internal float[] Cooldowns;

		internal float CooldownClock = float.NegativeInfinity;

		internal bool ForecastDirty = true;
	}

	private struct MobPlayer
	{
		internal long Uid;

		internal ZDO Data;

		internal Vector3 Position;

		internal MobPlayer(long uid, ZDO data, Vector3 position)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			Uid = uid;
			Data = data;
			Position = position;
		}
	}

	private sealed class RefillState
	{
		private readonly ZDO data;

		private readonly bool fuel;

		private readonly bool queue;

		private readonly bool ammo;

		private readonly int[] slots;

		private readonly bool[] filled;

		private float lastFuel;

		private int lastQueue;

		private int lastAmmo;

		internal float Until;

		internal RefillState(ZDO data, ZNetView view)
		{
			this.data = data;
			int num = 0;
			MonoBehaviour[] componentsInChildren = ((Component)view).GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val in componentsInChildren)
			{
				if (!Object.op_Implicit((Object)(object)val) || (Object)(object)((Component)val).GetComponentInParent<ZNetView>(true) != (Object)(object)view)
				{
					continue;
				}
				if (val is Smelter)
				{
					fuel = true;
					queue = true;
				}
				if (val is Fireplace || val is ShieldGenerator)
				{
					fuel = true;
				}
				if (val is Turret)
				{
					ammo = true;
				}
				CookingStation val2 = (CookingStation)(object)((val is CookingStation) ? val : null);
				if (Object.op_Implicit((Object)(object)val2))
				{
					fuel = true;
					if (val2.m_slots != null)
					{
						num = Math.Max(num, val2.m_slots.Length);
					}
				}
			}
			if (num != 0)
			{
				slots = new int[num];
				filled = new bool[num];
				for (int j = 0; j < num; j++)
				{
					slots[j] = StringExtensionMethods.GetStableHashCode("slot" + j);
				}
			}
			Read();
		}

		internal bool Read()
		{
			bool flag = false;
			if (fuel)
			{
				float num = data.GetFloat(ZDOVars.s_fuel, 0f);
				flag |= num > lastFuel;
				lastFuel = num;
			}
			if (queue)
			{
				int num2 = data.GetInt(ZDOVars.s_queued, 0);
				flag |= num2 > lastQueue;
				lastQueue = num2;
			}
			if (ammo)
			{
				int num3 = data.GetInt(ZDOVars.s_ammo, 0);
				flag |= num3 > lastAmmo;
				lastAmmo = num3;
			}
			if (slots != null)
			{
				for (int i = 0; i < slots.Length; i++)
				{
					bool flag2 = !string.IsNullOrEmpty(data.GetString(slots[i], ""));
					flag |= flag2 && !filled[i];
					filled[i] = flag2;
				}
			}
			return flag;
		}
	}

	private readonly Settings options;

	private readonly long server;

	private readonly Catalog catalog;

	private readonly AuthorityWorld world;

	private readonly NativeInventoryHandoff inventory;

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

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

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

	private bool hadPeers;

	private int shipCount;

	private readonly Dictionary<ZDO, Node> mobile = new Dictionary<ZDO, Node>();

	private readonly HashSet<Node> changed = new HashSet<Node>();

	private readonly HashSet<Node> defaults = new HashSet<Node>();

	private readonly List<Node> changedWork = new List<Node>();

	private readonly List<Node> defaultWork = new List<Node>();

	private readonly Dictionary<Cell, bool> readyAreas = new Dictionary<Cell, bool>();

	private float readyAt = float.NegativeInfinity;

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

	private readonly List<KeyValuePair<long, float>> boundUseRequests = new List<KeyValuePair<long, float>>();

	private readonly Dictionary<long, Latency> latency = new Dictionary<long, Latency>();

	private readonly DueQueue<Node> grace = new DueQueue<Node>();

	private float nextLatencyPrune;

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

	private readonly HashSet<Node> forecastWaiting = new HashSet<Node>();

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

	private Collider[] forecastColliders;

	private readonly HashSet<Character> forecastCharacters = new HashSet<Character>();

	private readonly HashSet<StaticTarget> forecastBuildings = new HashSet<StaticTarget>();

	private bool forecastDirty;

	internal int MobForecastChecks;

	internal int MobForecastQueries;

	internal int MobAbilityObservations;

	internal int MobForecastPhysicsQueries;

	internal int MobForecastCharacterSweeps;

	private readonly Dictionary<ZDO, Node> mobs = new Dictionary<ZDO, Node>();

	private readonly Dictionary<int, MobAttackProfile> mobProfiles = new Dictionary<int, MobAttackProfile>();

	private readonly HashSet<Node> sharedMobs = new HashSet<Node>();

	private readonly HashSet<Node> mobDirty = new HashSet<Node>();

	private readonly HashSet<Node> pendingMobs = new HashSet<Node>();

	private readonly DueQueue<Node> mobDue = new DueQueue<Node>();

	private List<MobPlayer> mobPlayers = new List<MobPlayer>();

	private List<MobPlayer> previousMobPlayers = new List<MobPlayer>();

	private bool mobPlayersChanged;

	private int mobPlayerEpoch;

	internal int MobGuardChecks;

	internal int MobConeChecks;

	private Node mobTestNode;

	private long mobTestPlayer;

	private long mobTestTarget;

	private float mobTestStarted;

	private float mobTestNext;

	private float mobTestReport;

	private bool mobTestStopped;

	private bool mobTestPending;

	private int mobTestRequests;

	private int mobTestTransfers;

	internal Action<string> MobTestLog;

	private int passes;

	private int registrationCalls;

	private int validRegistrations;

	private int assignCalls;

	internal int TrackedObjects => nodes.Count;

	internal int WaitingCells => waiting.Count;

	internal int RememberedCells => world.Grid.RememberedCells;

	internal int PendingTransfers => grace.Count;

	internal bool HasWaitingForecast => forecastWaiting.Count != 0;

	internal int PendingMobTransfers => pendingMobs.Count;

	internal int SharedMobCount => sharedMobs.Count;

	internal bool HasMobNodes => mobs.Count != 0;

	internal bool MobTestActive => mobTestNode != null;

	internal int MobTestTransfers => mobTestTransfers;

	internal bool ProtectRefills => options.RefillHold > 0f;

	private static bool CartIdle(ZDO data)
	{
		if (!data.GetBool(ZDOVars.s_attachJointHash, false))
		{
			return data.GetInt(ZDOVars.s_inUse, 0) == 0;
		}
		return false;
	}

	private ApplyResult ApplyCart(Node node, long target, long owner)
	{
		PruneUseRequests(node);
		if (!Object.op_Implicit((Object)(object)node.Cart) || !Object.op_Implicit((Object)(object)node.Container) || !CartIdle(node.Data) || node.Cart.InUse() || (node.NativeUseRequests != null && node.NativeUseRequests.Count != 0))
		{
			Wait(node);
			return ApplyResult.Dropped;
		}
		if (owner != target && (target == server || owner != server) && !MobileAreaReady(node))
		{
			Wait(node);
			return ApplyResult.Dropped;
		}
		return ApplyContainer(node, target, owner);
	}

	internal void CartDenied(ZDO data, long requester)
	{
		if (data != null && nodes.TryGetValue(data, out var value) && value.Group == Groups.Carts && value.NativeUseRequests != null && value.NativeUseRequests.Remove(requester))
		{
			changed.Add(value);
		}
	}

	private void CartAttached(Node node, long owner)
	{
		if (node.NativeUseRequests != null && node.Data.GetBool(ZDOVars.s_attachJointHash, false))
		{
			node.NativeUseRequests.Remove(owner);
		}
	}

	internal Controller(Settings options, long server, Func<int, bool> reserved = null)
	{
		this.options = options;
		this.server = server;
		catalog = new Catalog(options.Groups, reserved);
		inventory = new NativeInventoryHandoff(server);
		world = new AuthorityWorld(server, options.FullDelay, options.Pattern, options.Keep, options.Directional, options.InnerDelay, options.OuterDelay, forward: true, options.ForwardDelay, null, 30f, options.OverlapHold, fullRear: true, options.IncludeOuter);
		world.Grid.Renewed = Renew;
		world.AfterGridUpdate = RefreshMobCones;
	}

	internal void Tick(float now)
	{
		//IL_007c: 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_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cb: 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)
		//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0140: Unknown result type (might be due to invalid IL or missing references)
		//IL_0145: Unknown result type (might be due to invalid IL or missing references)
		passes++;
		peers.Clear();
		BeginMobPlayers();
		foreach (ZNetPeer peer in ZNet.instance.GetPeers())
		{
			if (!peer.IsReady() || peer.m_uid == 0 || peer.m_uid == server || ((ZDOID)(ref peer.m_characterID)).IsNone())
			{
				continue;
			}
			ZDO zDO = ZDOMan.instance.GetZDO(peer.m_characterID);
			if (zDO == null || !zDO.IsValid() || zDO.GetOwner() != peer.m_uid)
			{
				continue;
			}
			Vector3 position = zDO.GetPosition();
			if (Cell.At(position.x, position.z, options.CellSize, out var cell))
			{
				bool flag = LargeGrid(peer.m_uid, now);
				bool fastForward = false;
				if (options.Directional && (options.Pattern != GridCoveragePattern.Square3x3 || flag) && options.ForwardDelay > 0f && options.FastMovementSpeed > 0f)
				{
					Vector3 vec = zDO.GetVec3(ZDOVars.s_velHash, Vector3.zero);
					float num = vec.x * vec.x + vec.z * vec.z;
					float num2 = options.FastMovementSpeed * (world.Grid.FastForward(peer.m_uid) ? 0.75f : 1f);
					fastForward = !float.IsNaN(num) && !float.IsInfinity(num) && num >= num2 * num2;
				}
				List<GridPeer> list = peers;
				long uid = peer.m_uid;
				Cell cell2 = cell;
				Quaternion rotation = zDO.GetRotation();
				list.Add(new GridPeer(uid, cell2, ((Quaternion)(ref rotation)).eulerAngles.y, -1, fastForward, flag));
				AddMobPlayer(peer.m_uid, zDO, position);
			}
		}
		if (peers.Count == 0)
		{
			ClearTransfers();
		}
		if (peers.Count != 0 || hadPeers || world.Grid.CachedCells != 0)
		{
			hadPeers = peers.Count != 0;
			if (mobTestNode != null && peers.Count != 1)
			{
				StopMobTest("requires exactly one online player");
			}
			RefreshChanges(now);
			world.Tick(peers, now);
			if (options.MobOwnershipTest)
			{
				TickMobTest(now);
			}
			ServiceDefaults(now);
		}
	}

	internal void Add(ZDO data, ZNetView view)
	{
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_0226: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0203: Unknown result type (might be due to invalid IL or missing references)
		ForecastTargetChanged(data, null, worldEvent: true);
		registrationCalls++;
		if (data == null || !data.IsValid() || !Object.op_Implicit((Object)(object)view) || !view.IsValid() || nodes.ContainsKey(data))
		{
			return;
		}
		validRegistrations++;
		Groups groups = catalog.Get(data.GetPrefab());
		if (groups == Groups.None)
		{
			return;
		}
		Vector3 position = data.GetPosition();
		if (!Cell.At(position.x, position.z, options.CellSize, out var cell))
		{
			return;
		}
		Node node = new Node(this, data, view, groups, cell);
		node.LastOwner = data.GetOwner();
		Node node2 = node;
		nodes.Add(data, node2);
		if (groups == Groups.Containers || groups == Groups.Ships || groups == Groups.Carts)
		{
			Container[] componentsInChildren = ((Component)view).GetComponentsInChildren<Container>(true);
			foreach (Container val in componentsInChildren)
			{
				if (((groups == Groups.Ships || groups == Groups.Carts) && (Object)(object)val.m_rootObjectOverride == (Object)(object)view) || (Object)(object)((Component)val).GetComponentInParent<ZNetView>(true) == (Object)(object)view)
				{
					node2.Container = val;
					break;
				}
			}
		}
		if (groups == Groups.Carts)
		{
			node2.Cart = ((Component)view).GetComponent<Vagon>();
			if (!Object.op_Implicit((Object)(object)node2.Cart) || !Object.op_Implicit((Object)(object)node2.Container) || (Object)(object)node2.Cart.m_container != (Object)(object)node2.Container || (Object)(object)node2.Container.m_wagon != (Object)(object)node2.Cart)
			{
				nodes.Remove(data);
				return;
			}
		}
		if (groups == Groups.Ships)
		{
			node2.Ship = ((Component)view).GetComponent<Ship>();
			if (!Object.op_Implicit((Object)(object)node2.Ship) || !Object.op_Implicit((Object)(object)node2.Ship.m_shipControlls))
			{
				nodes.Remove(data);
				return;
			}
			node2.ShipAnchor = ((Component)view).transform.InverseTransformPoint(((Component)node2.Ship.m_shipControlls).transform.position);
			shipCount++;
		}
		if (Catalog.LocalOnly(groups))
		{
			if (Catalog.Idle(data) && ActiveArea(position))
			{
				Assign(node2, server);
			}
			return;
		}
		if ((groups & Groups.RefillStations) != Groups.None && options.RefillHold > 0f)
		{
			node2.Refill = new RefillState(data, view);
		}
		if (Catalog.Mobile(groups))
		{
			mobile.Add(data, node2);
			node2.ActorState = State(node2);
			if (groups == Groups.Fish && node2.LastOwner != 0 && node2.LastOwner != server)
			{
				node2.Pinned = true;
			}
		}
		if ((groups & Groups.HostileMobs) != Groups.None)
		{
			AddMob(node2);
		}
		Refresh(node2, Time.unscaledTime);
	}

	internal void Remove(ZDO data)
	{
		ForecastTargetChanged(data, null, worldEvent: true);
		if (data != null && nodes.TryGetValue(data, out var value))
		{
			if (value.Group == Groups.Ships)
			{
				shipCount--;
			}
			ForgetMob(value);
			grace.Remove(value);
			Unwait(value);
			inventory.Forget(data);
			if (value.InGrid)
			{
				world.Remove(value);
			}
			value.Removed = true;
			changed.Remove(value);
			defaults.Remove(value);
			mobile.Remove(data);
			nodes.Remove(data);
		}
	}

	internal long NativeDestination(ZDO data, long proposed)
	{
		//IL_015e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0163: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
		if (proposed != server || data == null || !data.Persistent || !Catalog.Idle(data))
		{
			return proposed;
		}
		Groups groups = catalog.Get(data.GetPrefab());
		if (groups == Groups.None)
		{
			return proposed;
		}
		if ((groups & Groups.HostileMobs) != Groups.None)
		{
			return proposed;
		}
		if ((groups == Groups.Containers || groups == Groups.Ships || groups == Groups.Carts) && data.GetOwner() != 0)
		{
			return proposed;
		}
		if (groups == Groups.Ships || groups == Groups.Carts)
		{
			return proposed;
		}
		if (Catalog.LocalOnly(groups) || (groups == Groups.StaticBuildings && !options.StaticGrid))
		{
			return server;
		}
		if (nodes.TryGetValue(data, out var value) && (value.Pinned || value.Released || value.ControlPinned))
		{
			return proposed;
		}
		if (value != null && value.Refill != null && Time.unscaledTime < value.Refill.Until)
		{
			return proposed;
		}
		if (groups == Groups.TamedAnimals && (ActorState(data) != 1 || data.GetFloat(ZDOVars.s_health, 1f) <= 0f))
		{
			return proposed;
		}
		if ((groups == Groups.Fish && data.GetInt(ZDOVars.s_hooked, 0) != 0) || (groups == Groups.LooseItems && data.GetBool(ZDOVars.s_piece, false)))
		{
			return proposed;
		}
		if (Catalog.Pickup(groups) && data.GetOwner() != 0 && data.GetOwner() != server)
		{
			return proposed;
		}
		Vector3 position = data.GetPosition();
		if (!Cell.At(position.x, position.z, options.CellSize, out var cell))
		{
			return proposed;
		}
		long num = world.Grid.Owner(cell);
		if (num == 0 || num == server || !Available(num, position))
		{
			return proposed;
		}
		return num;
	}

	private ApplyResult Apply(Node node, long target)
	{
		return Apply(node, target, due: false);
	}

	private ApplyResult Apply(Node node, long target, bool due)
	{
		//IL_0269: Unknown result type (might be due to invalid IL or missing references)
		//IL_0225: Unknown result type (might be due to invalid IL or missing references)
		//IL_0301: Unknown result type (might be due to invalid IL or missing references)
		Unwait(node);
		if (peers.Count == 0)
		{
			return ApplyResult.Dropped;
		}
		if (!node.Data.IsValid() || !Object.op_Implicit((Object)(object)node.View) || !node.View.IsValid() || !object.ReferenceEquals(node.View.GetZDO(), node.Data))
		{
			return ApplyResult.Dropped;
		}
		if (node.Released || node.Pinned || node.ControlPinned || (node.Group == Groups.Fish && node.Data.GetOwner() != 0 && node.Data.GetOwner() != server))
		{
			return ApplyResult.Dropped;
		}
		if (node.Group == Groups.TamedAnimals && (ActorState(node.Data) != 1 || node.Data.GetFloat(ZDOVars.s_health, 1f) <= 0f))
		{
			grace.Remove(node);
			Wait(node);
			return ApplyResult.Dropped;
		}
		if (target == 0 && options.Keep)
		{
			grace.Remove(node);
			CancelMob(node);
			sharedMobs.Remove(node);
			return ApplyResult.Done;
		}
		if (target == 0)
		{
			target = server;
		}
		long owner = node.Data.GetOwner();
		if (node.Group == Groups.HostileMobs)
		{
			return ApplyMob(node, target, owner);
		}
		if (node.Group == Groups.Ships)
		{
			return ApplyShip(node, target, owner);
		}
		if (node.Group == Groups.Carts)
		{
			return ApplyCart(node, target, owner);
		}
		if (node.Group == Groups.Containers)
		{
			return ApplyContainer(node, target, owner);
		}
		if (node.Group == Groups.LooseItems)
		{
			PruneUseRequests(node);
		}
		if (owner == target)
		{
			grace.Remove(node);
			node.Managed = true;
			node.ManagedOwner = target;
			return ApplyResult.Done;
		}
		if (node.Group == Groups.LooseItems && ItemPickupProtected(node, owner))
		{
			grace.Remove(node);
			Wait(node);
			return ApplyResult.Dropped;
		}
		if (node.Refill != null && Time.unscaledTime < node.Refill.Until && (owner == server || Available(owner, node.Data.GetPosition())))
		{
			grace.Remove(node);
			Wait(node);
			return ApplyResult.Dropped;
		}
		if (Catalog.Terminal(node.View) || !Catalog.Idle(node.Data) || !Available(target, node.Data.GetPosition()) || (Catalog.Mobile(node.Group) && target == server && !MobileAreaReady(node)) || (target == server && !((Component)node.View).gameObject.activeInHierarchy))
		{
			grace.Remove(node);
			Wait(node);
			return ApplyResult.Dropped;
		}
		if (target == server && owner != 0 && Catalog.NeedsGrace(node.Group) && !due && (node.Group != Groups.LooseItems || Available(owner, node.Data.GetPosition())))
		{
			if (grace.Contains(node))
			{
				return ApplyResult.Dropped;
			}
			if (!Grace(owner, Time.unscaledTime, out var value))
			{
				Wait(node);
				return ApplyResult.Dropped;
			}
			if (value > 0f)
			{
				node.GraceSource = owner;
				node.GraceRevision = node.Data.OwnerRevision;
				grace.Add(node, Time.unscaledTime + value);
				return ApplyResult.Dropped;
			}
		}
		grace.Remove(node);
		Assign(node, target);
		if (node.Data.GetOwner() != target)
		{
			Wait(node);
			return ApplyResult.Dropped;
		}
		node.Managed = true;
		node.ManagedOwner = target;
		return ApplyResult.Done;
	}

	internal bool Available(long uid, Vector3 position)
	{
		//IL_0022: 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)
		if (uid == server)
		{
			return true;
		}
		ZNetPeer peer = ZNet.instance.GetPeer(uid);
		if (peer != null && peer.IsReady())
		{
			return ZNetScene.InActiveArea(position, ZoneSystem.GetZone(peer.GetRefPos()));
		}
		return false;
	}

	private static bool ActiveArea(Vector3 position)
	{
		//IL_0022: 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)
		foreach (ZNetPeer peer in ZNet.instance.GetPeers())
		{
			if (peer.IsReady() && ZNetScene.InActiveArea(position, ZoneSystem.GetZone(peer.GetRefPos())))
			{
				return true;
			}
		}
		return false;
	}

	internal bool Request(ZDO data, int method)
	{
		if (data == null || !data.IsValid())
		{
			return false;
		}
		Groups groups = CreatureGroup(data, catalog.Get(data.GetPrefab()));
		if (groups != Groups.None)
		{
			return Catalog.OwnerRequest(groups, method);
		}
		return false;
	}

	internal bool GridManagesFire(int prefab)
	{
		if (!options.RespectFireOwnership)
		{
			return (catalog.Get(prefab) & Groups.RefillStations) != 0;
		}
		return false;
	}

	private void Wait(Node node)
	{
		if (node.InGrid)
		{
			if (!waiting.TryGetValue(node.Cell, out var value))
			{
				value = new HashSet<Node>();
				waiting.Add(node.Cell, value);
			}
			value.Add(node);
		}
	}

	private void Unwait(Node node)
	{
		if (waiting.TryGetValue(node.Cell, out var value) && value.Remove(node) && value.Count == 0)
		{
			waiting.Remove(node.Cell);
		}
	}

	private void Renew(Cell cell)
	{
		if (!waiting.TryGetValue(cell, out var value))
		{
			return;
		}
		foreach (Node item in value)
		{
			world.Received(item, Time.unscaledTime);
		}
		waiting.Remove(cell);
	}

	private bool MobileAreaReady(Node node)
	{
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		if (readyAt != Time.unscaledTime)
		{
			readyAt = Time.unscaledTime;
			readyAreas.Clear();
		}
		Cell cell = node.Cell;
		if (Object.op_Implicit((Object)(object)node.Ship))
		{
			Vector3 position = node.Data.GetPosition();
			if (!Cell.At(position.x, position.z, options.CellSize, out cell))
			{
				return false;
			}
		}
		Cell key = Cell.Zone(cell, options.CellSize);
		if (!readyAreas.TryGetValue(key, out var value))
		{
			value = ZNetScene.instance.IsAreaReady(node.Data.GetPosition());
			readyAreas.Add(key, value);
		}
		return value;
	}

	private static byte ActorState(ZDO data)
	{
		//IL_0054: 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)
		byte b = (byte)(data.GetBool(ZDOVars.s_tamed, false) ? 1 : 0);
		if (data.GetLong(ZDOVars.s_user, 0L) == 0 && string.IsNullOrEmpty(data.GetString(ZDOVars.s_follow, "")) && string.IsNullOrEmpty(data.GetString(ZDOVars.s_attachJointHash, "")))
		{
			ZDOID connectionZDOID = data.GetConnectionZDOID((ConnectionType)2);
			if (((ZDOID)(ref connectionZDOID)).IsNone())
			{
				goto IL_0068;
			}
		}
		b |= 2;
		goto IL_0068;
		IL_0068:
		if (data.GetBool(ZDOVars.s_alert, false) || data.GetBool(ZDOVars.s_haveTargetHash, false))
		{
			b |= 4;
		}
		return b;
	}

	private static byte State(Node node)
	{
		if (node.Group == Groups.None)
		{
			return 0;
		}
		if (node.Group == Groups.HostileMobs)
		{
			if ((ActorState(node.Data) & 3) != 0)
			{
				return 2;
			}
			return 1;
		}
		if (node.Group == Groups.Ships)
		{
			return 1;
		}
		if (node.Group == Groups.Carts)
		{
			if (!node.Data.GetBool(ZDOVars.s_attachJointHash, false))
			{
				return 1;
			}
			return 2;
		}
		if (node.Group == Groups.TamedAnimals)
		{
			return ActorState(node.Data);
		}
		if (node.Group == Groups.LooseItems)
		{
			if (!node.Data.GetBool(ZDOVars.s_piece, false))
			{
				return 1;
			}
			return 2;
		}
		if (node.Data.GetInt(ZDOVars.s_hooked, 0) == 0)
		{
			return 1;
		}
		return 2;
	}

	private bool UsesGrid(Node node)
	{
		if (node.Group == Groups.None || node.Released || node.Pinned || node.ControlPinned || Catalog.LocalOnly(node.Group))
		{
			return false;
		}
		if (node.Group == Groups.StaticBuildings)
		{
			return options.StaticGrid;
		}
		if (Catalog.Mobile(node.Group))
		{
			return node.ActorState == 1;
		}
		return true;
	}

	private void Assign(Node node, long target)
	{
		assignCalls++;
		if (Object.op_Implicit((Object)(object)node.Ship) && target == server && node.Data.GetOwner() != server)
		{
			ShipOwnership.Prepare(node.Ship);
		}
		node.Writing = true;
		try
		{
			node.Data.SetOwner(target);
		}
		finally
		{
			node.Writing = false;
			node.LastOwner = node.Data.GetOwner();
		}
		if (node.Group == Groups.Fish && target != 0 && target != server)
		{
			node.Pinned = true;
			changed.Add(node);
		}
	}

	internal void OwnerAssigned(ZDO data, long owner)
	{
		if (nodes.TryGetValue(data, out var value) && !value.Writing && value.LastOwner != owner)
		{
			value.LastOwner = owner;
			value.Released = owner == 0;
			MobOwnerChanged(value);
			if (Object.op_Implicit((Object)(object)value.Ship) && owner == server)
			{
				ShipOwnership.Prepare(value.Ship);
			}
			if (value.Group == Groups.Carts)
			{
				CartAttached(value, owner);
			}
			if (value.Group == Groups.Fish && owner != 0 && owner != server)
			{
				value.Pinned = true;
			}
			if (!Catalog.LocalOnly(value.Group))
			{
				changed.Add(value);
			}
		}
	}

	internal void PositionChanged(ZDO data)
	{
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		if (mobile.Count != 0 && mobile.TryGetValue(data, out var value) && !value.Pinned && !value.Released && value.ActorState == 1)
		{
			MobPoseChanged(value);
			Vector3 val = GridPosition(value);
			if (Cell.At(val.x, val.z, options.CellSize, out var cell) && !value.Cell.Equals(cell))
			{
				changed.Add(value);
			}
		}
	}

	internal void DataReceived(ZDO data, long source)
	{
		SourceData(data, source);
		RefillChanged(data);
		StateChanged(data);
		if (mobs.Count != 0 && mobs.TryGetValue(data, out var value))
		{
			MobPoseChanged(value);
		}
	}

	internal void StateChanged(ZDO data)
	{
		if (mobile.Count == 0 || !mobile.TryGetValue(data, out var value))
		{
			return;
		}
		Groups groups = CreatureGroup(data, value.PrefabGroups);
		if (groups != value.Group)
		{
			CancelMob(value);
			sharedMobs.Remove(value);
			value.Group = groups;
			changed.Add(value);
		}
		if (value.Mob != null)
		{
			bool flag = data.GetBool(ZDOVars.s_alert, false) || data.GetBool(ZDOVars.s_haveTargetHash, false);
			if (flag != value.Mob.Combat)
			{
				value.Mob.Combat = flag;
				WakeMob(value);
			}
		}
		if (value.Group == Groups.Ships)
		{
			PositionChanged(data);
			return;
		}
		if (value.Group == Groups.Carts && value.NativeUseRequests != null && value.NativeUseRequests.Count != 0)
		{
			CartAttached(value, data.GetOwner());
		}
		if (value.ControlPinned && value.Group == Groups.TamedAnimals)
		{
			if (data.GetLong(ZDOVars.s_user, 0L) != 0)
			{
				value.RiderSeen = true;
			}
			else if (value.RiderSeen)
			{
				value.RiderSeen = false;
				value.AcceptedRider = 0L;
				value.ControlPinned = value.ControlRequests != null && value.ControlRequests.Count != 0;
				changed.Add(value);
			}
		}
		byte b = State(value);
		if (b != value.ActorState)
		{
			value.ActorState = b;
			if (value.Group == Groups.Fish && b != 1)
			{
				value.Pinned = true;
			}
			changed.Add(value);
		}
	}

	private bool ItemPickupProtected(Node node, long owner)
	{
		if (owner == 0 || owner == server || !world.Grid.Covers(owner, node.Cell))
		{
			if (node.NativeUseRequests != null)
			{
				return node.NativeUseRequests.Count != 0;
			}
			return false;
		}
		return true;
	}

	private void PruneUseRequests(Node node, bool moved = false)
	{
		if (node.NativeUseRequests == null || node.NativeUseRequests.Count == 0)
		{
			return;
		}
		expiredUseRequests.Clear();
		boundUseRequests.Clear();
		foreach (KeyValuePair<long, float> nativeUseRequest in node.NativeUseRequests)
		{
			if (!world.Grid.CoverageStart(nativeUseRequest.Key, node.Cell, out var since) || (!moved && !float.IsNaN(nativeUseRequest.Value) && nativeUseRequest.Value != since))
			{
				expiredUseRequests.Add(nativeUseRequest.Key);
			}
			else if (moved || float.IsNaN(nativeUseRequest.Value))
			{
				boundUseRequests.Add(new KeyValuePair<long, float>(nativeUseRequest.Key, since));
			}
		}
		foreach (long expiredUseRequest in expiredUseRequests)
		{
			node.NativeUseRequests.Remove(expiredUseRequest);
		}
		foreach (KeyValuePair<long, float> boundUseRequest in boundUseRequests)
		{
			node.NativeUseRequests[boundUseRequest.Key] = boundUseRequest.Value;
		}
		expiredUseRequests.Clear();
		boundUseRequests.Clear();
	}

	internal void PinPickup(ZDO data, long requester = 0L)
	{
		if (data == null || !nodes.TryGetValue(data, out var value) || (!Catalog.Pickup(value.Group) && value.Group != Groups.Carts))
		{
			return;
		}
		if ((value.Group == Groups.LooseItems || value.Group == Groups.Carts) && requester != 0)
		{
			if (value.NativeUseRequests == null)
			{
				value.NativeUseRequests = new Dictionary<long, float>();
			}
			PruneUseRequests(value);
			value.NativeUseRequests[requester] = (world.Grid.CoverageStart(requester, value.Cell, out var since) ? since : float.NaN);
			grace.Remove(value);
			Unwait(value);
			if (value.InGrid)
			{
				world.Dismiss(value);
				Wait(value);
			}
		}
		else
		{
			value.Pinned = true;
			grace.Remove(value);
			Unwait(value);
			if (value.InGrid)
			{
				world.Remove(value);
				value.InGrid = false;
			}
		}
	}

	internal void PinControl(ZDO data, long requester = 0L)
	{
		if (data != null && mobile.TryGetValue(data, out var value) && value.Group == Groups.TamedAnimals)
		{
			if (value.ControlRequests == null)
			{
				value.ControlRequests = new HashSet<long>();
			}
			value.ControlRequests.Add(requester);
			value.ControlPinned = true;
			value.RiderSeen |= data.GetLong(ZDOVars.s_user, 0L) != 0;
			grace.Remove(value);
			Unwait(value);
			if (value.InGrid)
			{
				world.Remove(value);
				value.InGrid = false;
			}
		}
	}

	internal void ControlReply(ZDO data, bool allowed, long recipient = 0L)
	{
		if (data != null && mobile.TryGetValue(data, out var value) && value.ControlPinned && value.ControlRequests != null && value.ControlRequests.Remove(recipient))
		{
			if (allowed)
			{
				value.AcceptedRider = recipient;
			}
			if (!allowed && value.AcceptedRider == 0 && !value.RiderSeen && value.ControlRequests.Count == 0)
			{
				value.ControlPinned = false;
				changed.Add(value);
			}
		}
	}

	internal bool ExpectsControlReply(ZDO data, long recipient)
	{
		if (data != null && mobile.TryGetValue(data, out var value) && value.ControlPinned && value.ControlRequests != null)
		{
			return value.ControlRequests.Contains(recipient);
		}
		return false;
	}

	private void Refresh(Node node, float now)
	{
		//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)
		if (node.Removed && !nodes.ContainsKey(node.Data))
		{
			return;
		}
		bool flag = UsesGrid(node);
		Vector3 val = GridPosition(node);
		if (node.Group == Groups.Fish)
		{
			Fish component = ((Component)node.View).GetComponent<Fish>();
			if (Object.op_Implicit((Object)(object)component) && component.IsHooked())
			{
				node.Pinned = true;
				flag = false;
			}
		}
		if (node.Group == Groups.LooseItems)
		{
			ItemDrop component2 = ((Component)node.View).GetComponent<ItemDrop>();
			if (Object.op_Implicit((Object)(object)component2) && component2.IsPiece())
			{
				flag = false;
			}
		}
		if (!Cell.At(val.x, val.z, options.CellSize, out var cell))
		{
			flag = false;
		}
		if (!flag)
		{
			CancelMob(node);
			sharedMobs.Remove(node);
			mobDirty.Remove(node);
			if (node.InGrid)
			{
				world.Remove(node);
				node.InGrid = false;
			}
			grace.Remove(node);
			Unwait(node);
			if (node.Group == Groups.StaticBuildings && !node.Released)
			{
				node.Removed = false;
				defaults.Add(node);
			}
		}
		else if (!node.InGrid)
		{
			bool flag2 = node.Group == Groups.Carts && !node.Cell.Equals(cell);
			node.Cell = cell;
			node.InGrid = true;
			world.Add(node, now);
			if (flag2)
			{
				PruneUseRequests(node, moved: true);
			}
		}
		else if (!node.Cell.Equals(cell))
		{
			grace.Remove(node);
			Unwait(node);
			if (node.Group == Groups.LooseItems || node.Group == Groups.Carts)
			{
				PruneUseRequests(node);
			}
			world.Move(node, cell, now);
			if (node.Group == Groups.LooseItems || node.Group == Groups.Carts)
			{
				PruneUseRequests(node, moved: true);
			}
		}
		else
		{
			grace.Remove(node);
			world.Received(node, now);
		}
	}

	private void RefreshChanges(float now)
	{
		if (changed.Count == 0)
		{
			return;
		}
		changedWork.Clear();
		changedWork.AddRange(changed);
		changed.Clear();
		foreach (Node item in changedWork)
		{
			Refresh(item, now);
		}
		changedWork.Clear();
	}

	private void ServiceDefaults(float now)
	{
		if (peers.Count == 0 || defaults.Count == 0)
		{
			return;
		}
		defaultWork.Clear();
		defaultWork.AddRange(defaults);
		defaults.Clear();
		foreach (Node item in defaultWork)
		{
			if (!item.Removed && !item.Released)
			{
				Apply(item, server);
			}
		}
		defaultWork.Clear();
	}

	private long Desired(Node node)
	{
		if (node.Group != Groups.StaticBuildings || options.StaticGrid)
		{
			return world.Grid.Owner(node.Cell);
		}
		return server;
	}

	private bool Grace(long uid, float now, out float value)
	{
		value = options.MinimumGrace;
		if (options.GraceMultiplier == 0f)
		{
			return true;
		}
		int num = Ping(uid, now);
		if (num <= 0)
		{
			return false;
		}
		value = Math.Max(value, (float)num * 0.001f * options.GraceMultiplier);
		return true;
	}

	private int Ping(long uid, float now)
	{
		if (latency.TryGetValue(uid, out var value) && now < value.Until)
		{
			return value.Ping;
		}
		ZNetPeer peer = ZNet.instance.GetPeer(uid);
		if (peer == null || !peer.IsReady() || peer.m_socket == null)
		{
			return 0;
		}
		int num;
		try
		{
			num = SocketLatency.Read(peer.m_socket);
		}
		catch (Exception)
		{
			num = 0;
		}
		latency[uid] = new Latency
		{
			Until = now + 1f,
			Ping = num
		};
		return num;
	}

	private bool LargeGrid(long uid, float now)
	{
		if (!options.Directional || options.AdaptivePing <= 0)
		{
			return false;
		}
		int num = Ping(uid, now);
		bool flag = world.Grid.LargeCoverage(uid);
		if (num > 0)
		{
			return (float)num >= (float)options.AdaptivePing * (flag ? 0.75f : 1f);
		}
		return flag;
	}

	internal void SourceData(ZDO data, long source)
	{
		if (grace.Count != 0 && source != 0 && nodes.TryGetValue(data, out var value) && value.GraceSource == source && grace.Remove(value))
		{
			value.Managed = false;
			if (value.InGrid)
			{
				world.Dismiss(value);
			}
			Wait(value);
		}
	}

	internal void SourceRequest(ZDO data, long source)
	{
		SourceData(data, source);
	}

	internal void Service(float now)
	{
		ServiceMobs(now);
		Node value;
		while (grace.Take(now, out value))
		{
			if (value.Removed || !nodes.ContainsKey(value.Data) || !value.Data.IsValid())
			{
				continue;
			}
			if (value.Data.GetOwner() != value.GraceSource || value.Data.OwnerRevision != value.GraceRevision)
			{
				Wait(value);
				continue;
			}
			long num = Desired(value);
			if (num == 0 && !options.Keep)
			{
				num = server;
			}
			if (num == server)
			{
				Apply(value, server, due: true);
			}
		}
		if (!(now >= nextLatencyPrune) || latency.Count == 0)
		{
			return;
		}
		nextLatencyPrune = now + 1f;
		stalePeers.Clear();
		foreach (KeyValuePair<long, Latency> item in latency)
		{
			if (ZNet.instance.GetPeer(item.Key) == null)
			{
				stalePeers.Add(item.Key);
			}
		}
		foreach (long stalePeer in stalePeers)
		{
			latency.Remove(stalePeer);
		}
	}

	private void ClearTransfers()
	{
		grace.Clear();
		latency.Clear();
		inventory.Clear();
		ClearMobs();
	}

	internal bool FinishedRequest(ZDO data, int method)
	{
		if (!Catalog.PickRequest(method))
		{
			return false;
		}
		if (nodes.TryGetValue(data, out var value) && Object.op_Implicit((Object)(object)value.View))
		{
			return Catalog.FinishedPick(value.View);
		}
		return false;
	}

	private ApplyResult ApplyContainer(Node node, long target, long owner)
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Invalid comparison between Unknown and I4
		//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0183: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)node.Container) || (int)node.Container.m_privacy != 2)
		{
			return ApplyResult.Dropped;
		}
		if (inventory.Transitioning(node.Data) || inventory.Pending(node.Data))
		{
			Wait(node);
			return ApplyResult.Dropped;
		}
		if (owner == target)
		{
			inventory.Forget(node.Data);
			node.Managed = true;
			node.ManagedOwner = target;
			return ApplyResult.Done;
		}
		int num = world.Grid.Participants(node.Cell);
		if (target == server && num == 1)
		{
			return ApplyResult.Dropped;
		}
		if (!((Component)node.View).gameObject.activeInHierarchy || !(Object.op_Implicit((Object)(object)node.Cart) ? CartIdle(node.Data) : (Object.op_Implicit((Object)(object)node.Ship) ? ShipIdle(node.Data) : Catalog.Idle(node.Data))) || !Available(target, node.Data.GetPosition()) || node.Data.OwnerRevision == ushort.MaxValue)
		{
			Wait(node);
			return ApplyResult.Dropped;
		}
		if (owner == server)
		{
			if (node.Container.IsInUse() || inventory.Acquire(node.Data, node.View, node.Container) != 2)
			{
				Wait(node);
				return ApplyResult.Dropped;
			}
		}
		else if ((num > 0 || Object.op_Implicit((Object)(object)node.Ship) || Object.op_Implicit((Object)(object)node.Cart)) && owner != 0 && Available(owner, node.Data.GetPosition()))
		{
			int num2 = inventory.Acquire(node.Data, node.View, node.Container);
			if (num2 != 2)
			{
				Wait(node);
				return ApplyResult.Dropped;
			}
		}
		else
		{
			inventory.Forget(node.Data);
		}
		if (node.Data.GetOwner() != target)
		{
			if (target != server)
			{
				ZDOMan.instance.ForceSendZDO(target, node.Data.m_uid);
			}
			Assign(node, target);
		}
		if (node.Data.GetOwner() != target)
		{
			Wait(node);
			return ApplyResult.Dropped;
		}
		inventory.Forget(node.Data);
		node.Managed = true;
		node.ManagedOwner = target;
		return ApplyResult.Done;
	}

	internal bool ExpectsInventoryReply(ZDO data)
	{
		return inventory.Pending(data);
	}

	internal void InventoryReply(ZDO data, long sender, bool granted)
	{
		if (data == null || !nodes.TryGetValue(data, out var value) || !Object.op_Implicit((Object)(object)value.Container))
		{
			return;
		}
		if (inventory.Response(value.Container, sender, granted))
		{
			if (value.InGrid)
			{
				world.Dismiss(value);
				Wait(value);
			}
		}
		else if (granted && inventory.Received(data))
		{
			changed.Add(value);
		}
	}

	internal bool WatchesForecastTurn(ZDO data)
	{
		if (forecastWaiting.Count != 0 && data != null && mobs.TryGetValue(data, out var value))
		{
			return forecastWaiting.Contains(value);
		}
		return false;
	}

	internal void ForecastMobTurned(ZDO data)
	{
		if (forecastWaiting.Count != 0 && data != null && mobs.TryGetValue(data, out var value) && forecastWaiting.Contains(value))
		{
			value.Mob.ForecastDirty = true;
			WakeMob(value);
		}
	}

	private void ObserveMobAbility(Node node, string trigger, long source)
	{
		MobForecastProfile forecast = node.Mob.Profile.Forecast;
		if (forecast != null && forecast.Triggers.TryGetValue(trigger, out var value) && value >= 0)
		{
			MobAbilityObservations++;
			node.Mob.ForecastDirty = true;
			if (node.Mob.Cooldowns == null)
			{
				node.Mob.Cooldowns = new float[forecast.Abilities.Count];
				ResetMobCooldowns(node.Mob);
			}
			CheckForecastClock(node.Mob);
			int num = ((source != server) ? Ping(source, Time.unscaledTime) : 0);
			float num2 = ((source != server && num <= 0) ? 0f : Math.Max(0f, forecast.Abilities[value].Cooldown - 0.1f - (float)num * 0.001f));
			node.Mob.Cooldowns[value] = Time.time + num2;
		}
	}

	private static void CheckForecastClock(MobState mob)
	{
		if (Time.time < mob.CooldownClock)
		{
			ResetMobCooldowns(mob);
		}
		mob.CooldownClock = Time.time;
	}

	private static void ResetMobCooldowns(MobState mob)
	{
		if (mob.Cooldowns != null)
		{
			for (int i = 0; i < mob.Cooldowns.Length; i++)
			{
				mob.Cooldowns[i] = float.NegativeInfinity;
			}
		}
	}

	internal void ForecastTargetChanged(ZDO data, Vector3? previous = null, bool worldEvent = false)
	{
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: 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_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		if (forecastWaiting.Count == 0 || data == null || !ForecastTarget(data))
		{
			return;
		}
		ZNetView val = (worldEvent ? null : ZNetScene.instance.FindInstance(data));
		Character val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<Character>() : null);
		if (!Object.op_Implicit((Object)(object)val2))
		{
			forecastDirty = true;
			return;
		}
		float radius;
		try
		{
			radius = val2.GetRadius();
		}
		catch (NullReferenceException)
		{
			forecastDirty = true;
			return;
		}
		Vector3 position = data.GetPosition();
		if (float.IsNaN(radius) || float.IsInfinity(radius) || radius < 0f || !Finite(position))
		{
			forecastDirty = true;
			return;
		}
		foreach (Node item in forecastWaiting)
		{
			float range = item.Mob.Profile.Forecast.MaximumRange + radius;
			Vector3 position2 = item.Data.GetPosition();
			if (object.ReferenceEquals(item.Data, data) || Near(position2, position, range) || (previous.HasValue && Near(position2, previous.Value, range)))
			{
				item.Mob.ForecastDirty = true;
				WakeMob(item);
			}
		}
	}

	private static bool Near(Vector3 a, Vector3 b, float range)
	{
		float num = a.x - b.x;
		float num2 = a.y - b.y;
		float num3 = a.z - b.z;
		return num * num + num2 * num2 + num3 * num3 <= range * range;
	}

	private bool ForecastTarget(ZDO data)
	{
		int prefab = data.GetPrefab();
		if (!forecastKinds.TryGetValue(prefab, out var value))
		{
			GameObject prefab2 = ZNetScene.instance.GetPrefab(prefab);
			value = Object.op_Implicit((Object)(object)prefab2) && (Object.op_Implicit((Object)(object)prefab2.GetComponent<Character>()) || Object.op_Implicit((Object)(object)prefab2.GetComponentInChildren<StaticTarget>(true)));
			forecastKinds[prefab] = value;
		}
		return value;
	}

	internal ForecastReceiveState CaptureForecastReceive(ZDO data)
	{
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: 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 (forecastWaiting.Count == 0 || data == null || !ForecastTarget(data))
		{
			return default(ForecastReceiveState);
		}
		bool flag = WatchesForecastTurn(data);
		return new ForecastReceiveState
		{
			Tracked = true,
			TrackRotation = flag,
			Rotation = (Quaternion)(flag ? data.GetRotation() : default(Quaternion)),
			Living = (data.GetFloat(ZDOVars.s_health, 1f) > 0f),
			Tamed = data.GetBool(ZDOVars.s_tamed, false),
			Aggravated = data.GetBool(ZDOVars.s_aggravated, false)
		};
	}

	internal void ForecastReceived(ZDO data, ForecastReceiveState previous)
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		if (previous.Tracked)
		{
			if (previous.TrackRotation && !((Quaternion)(ref previous.Rotation)).Equals(data.GetRotation()))
			{
				ForecastMobTurned(data);
			}
			if (previous.Living != data.GetFloat(ZDOVars.s_health, 1f) > 0f || previous.Tamed != data.GetBool(ZDOVars.s_tamed, false) || previous.Aggravated != data.GetBool(ZDOVars.s_aggravated, false))
			{
				ForecastTargetChanged(data);
			}
		}
	}

	private bool ForecastMobAttack(Node node)
	{
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0154: Unknown result type (might be due to invalid IL or missing references)
		//IL_0156: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_021f: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_036d: Unknown result type (might be due to invalid IL or missing references)
		//IL_036f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0371: Unknown result type (might be due to invalid IL or missing references)
		MobForecastProfile forecast = node.Mob.Profile.Forecast;
		if (forecast == null)
		{
			return false;
		}
		if (forecastWaiting.Contains(node) && !node.Mob.ForecastDirty)
		{
			return true;
		}
		node.Mob.ForecastDirty = false;
		MobForecastChecks++;
		if (forecast.Unknown)
		{
			return true;
		}
		CheckForecastClock(node.Mob);
		float time = Time.time;
		float num = 0f;
		for (int i = 0; i < forecast.Abilities.Count; i++)
		{
			if (node.Mob.Cooldowns == null || time >= node.Mob.Cooldowns[i])
			{
				num = Math.Max(num, forecast.Abilities[i].Range);
			}
		}
		if (num == 0f)
		{
			return false;
		}
		Vector3 position = node.Data.GetPosition();
		Vector3 val = node.Data.GetRotation() * new Vector3(0f, 0f, 1f);
		if (!Finite(position) || !Finite(val))
		{
			return true;
		}
		MobForecastQueries++;
		int num2 = 0;
		try
		{
			foreach (MobPlayer mobPlayer in mobPlayers)
			{
				ZNetView val2 = ZNetScene.instance.FindInstance(mobPlayer.Data);
				if (Object.op_Implicit((Object)(object)val2) && ForecastCharacter(node, forecast, time, num, position, val, ((Component)val2).GetComponent<Character>()))
				{
					return true;
				}
			}
			MobForecastCharacterSweeps++;
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if (ForecastCharacter(node, forecast, time, num, position, val, allCharacter))
				{
					return true;
				}
			}
			if (!node.Mob.Ai.m_attackPlayerObjects)
			{
				return false;
			}
			if (forecastColliders == null)
			{
				forecastColliders = (Collider[])(object)new Collider[128];
			}
			MobForecastPhysicsQueries++;
			num2 = Physics.OverlapSphereNonAlloc(position, num + 0.5f, forecastColliders, -1, (QueryTriggerInteraction)1);
			if (num2 >= forecastColliders.Length)
			{
				return true;
			}
			for (int j = 0; j < num2; j++)
			{
				Collider val3 = forecastColliders[j];
				if (!Object.op_Implicit((Object)(object)val3))
				{
					continue;
				}
				StaticTarget componentInParent = ((Component)val3).GetComponentInParent<StaticTarget>(true);
				if (!Object.op_Implicit((Object)(object)componentInParent) || !forecastBuildings.Add(componentInParent) || (!componentInParent.IsPriorityTarget() && !componentInParent.IsRandomTarget()))
				{
					continue;
				}
				Vector3 value = componentInParent.FindClosestPoint(position);
				Vector3 center = componentInParent.GetCenter();
				if (!Finite(value) || !Finite(center))
				{
					return true;
				}
				for (int k = 0; k < forecast.Abilities.Count; k++)
				{
					MobForecastProfile.Ability ability = forecast.Abilities[k];
					if (ability.Enemy && (node.Mob.Cooldowns == null || !(time < node.Mob.Cooldowns[k])))
					{
						float num3 = value.x - position.x;
						float num4 = value.y - position.y;
						float num5 = value.z - position.z;
						if (num3 * num3 + num4 * num4 + num5 * num5 <= ability.Range * ability.Range && FacingBuilding(ability, position, val, center))
						{
							return true;
						}
					}
				}
			}
			return false;
		}
		catch (NullReferenceException)
		{
			return true;
		}
		finally
		{
			if (forecastColliders != null)
			{
				Array.Clear(forecastColliders, 0, Math.Min(num2, forecastColliders.Length));
			}
			forecastCharacters.Clear();
			forecastBuildings.Clear();
		}
	}

	private bool ForecastCharacter(Node node, MobForecastProfile profile, float now, float maximum, Vector3 origin, Vector3 forward, Character target)
	{
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: 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_0093: 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_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)target) || (Object)(object)target == (Object)(object)node.Mob.Actor || !forecastCharacters.Add(target) || target.IsDead())
		{
			return false;
		}
		ZNetView component = ((Component)target).GetComponent<ZNetView>();
		Vector3 val = ((Object.op_Implicit((Object)(object)component) && component.IsValid()) ? component.GetZDO().GetPosition() : ((Component)target).transform.position);
		float radius = target.GetRadius();
		if (!Finite(val) || float.IsNaN(radius) || float.IsInfinity(radius) || radius < 0f)
		{
			return true;
		}
		if (!Near(origin, val, maximum + radius))
		{
			return false;
		}
		bool flag = BaseAI.IsEnemy((Character)(object)node.Mob.Actor, target);
		for (int i = 0; i < profile.Abilities.Count; i++)
		{
			if ((node.Mob.Cooldowns == null || now >= node.Mob.Cooldowns[i]) && profile.Abilities[i].Enemy == flag && MobForecastProfile.Contains(profile.Abilities[i], origin, forward, val, radius))
			{
				return true;
			}
		}
		return false;
	}

	private static bool FacingBuilding(MobForecastProfile.Ability ability, Vector3 origin, Vector3 forward, Vector3 center)
	{
		float num = center.x - origin.x;
		float num2 = center.z - origin.z;
		float num3 = (num * num + num2 * num2) * (forward.x * forward.x + forward.z * forward.z);
		float num4 = ((num3 <= 1E-06f) ? 1f : ((num * forward.x + num2 * forward.z) / (float)Math.Sqrt(num3)));
		if (!ability.Inverted)
		{
			return num4 + 1E-06f >= ability.Cosine;
		}
		return num4 <= ability.Cosine + 1E-06f;
	}

	private static Groups CreatureGroup(ZDO data, Groups groups)
	{
		if ((groups & (Groups.TamedAnimals | Groups.HostileMobs)) == 0)
		{
			return groups;
		}
		return (Groups)((int)groups & (data.GetBool(ZDOVars.s_tamed, false) ? 4096 : 524288));
	}

	private void AddMob(Node node)
	{
		//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)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		Humanoid component = ((Component)node.View).GetComponent<Humanoid>();
		MonsterAI component2 = ((Component)node.View).GetComponent<MonsterAI>();
		if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component2))
		{
			if (!mobProfiles.TryGetValue(node.Data.GetPrefab(), out var value))
			{
				GameObject prefab = ZNetScene.instance.GetPrefab(node.Data.GetPrefab());
				value = new MobAttackProfile(Object.op_Implicit((Object)(object)prefab) ? prefab.GetComponent<Humanoid>() : component, options.MobAttackForecast);
				mobProfiles.Add(node.Data.GetPrefab(), value);
			}
			node.Mob = new MobState
			{
				Actor = component,
				Ai = component2,
				Profile = value,
				Source = node.Data.GetOwner(),
				Revision = node.Data.OwnerRevision,
				Position = node.Data.GetPosition(),
				Rotation = node.Data.GetRotation(),
				Combat = (node.Data.GetBool(ZDOVars.s_alert, false) || node.Data.GetBool(ZDOVars.s_haveTargetHash, false))
			};
			mobs.Add(node.Data, node);
		}
	}

	private void ForgetMob(Node node)
	{
		CancelMob(node);
		mobs.Remove(node.Data);
		sharedMobs.Remove(node);
		mobDirty.Remove(node);
	}

	private void CancelMob(Node node)
	{
		mobDue.Remove(node);
		pendingMobs.Remove(node);
		forecastWaiting.Remove(node);
		if (node.Mob != null)
		{
			node.Mob.Pending = false;
		}
	}

	private void MobOwnerChanged(Node node)
	{
		if (node.Mob != null)
		{
			CancelMob(node);
			MobState mob = node.Mob;
			mob.Source = node.Data.GetOwner();
			mob.Revision = node.Data.OwnerRevision;
			mob.Armed = (mob.SawAttack = (mob.Looping = false));
			mob.IdleAt = float.PositiveInfinity;
			mob.IdleHandoffObserved = false;
			mob.TargetValid = false;
			ResetMobCooldowns(mob);
			mob.ForecastDirty = true;
		}
	}

	private void BeginMobPlayers()
	{
		List<MobPlayer> list = previousMobPlayers;
		previousMobPlayers = mobPlayers;
		mobPlayers = list;
		mobPlayers.Clear();
		mobPlayersChanged = false;
	}

	private void AddMobPlayer(long uid, ZDO data, Vector3 position)
	{
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		if ((options.Groups & Groups.HostileMobs) != Groups.None)
		{
			int count = mobPlayers.Count;
			if (count >= previousMobPlayers.Count || previousMobPlayers[count].Uid != uid || !Same(position, previousMobPlayers[count].Position))
			{
				mobPlayersChanged = true;
			}
			mobPlayers.Add(new MobPlayer(uid, data, position));
		}
	}

	private static bool Same(Vector3 a, Vector3 b)
	{
		if (a.x == b.x && a.y == b.y)
		{
			return a.z == b.z;
		}
		return false;
	}

	private void MobPoseChanged(Node node)
	{
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: 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_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		if (node.Mob != null && sharedMobs.Contains(node))
		{
			Vector3 position = node.Data.GetPosition();
			Quaternion rotation = node.Data.GetRotation();
			if (!Same(position, node.Mob.Position) || !((Quaternion)(ref rotation)).Equals(node.Mob.Rotation))
			{
				node.Mob.Position = position;
				node.Mob.Rotation = rotation;
				node.Mob.TargetValid = false;
				mobDirty.Add(node);
			}
		}
	}

	private void RefreshMobCones()
	{
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		if (mobs.Count == 0)
		{
			return;
		}
		if (mobPlayersChanged || mobPlayers.Count != previousMobPlayers.Count)
		{
			mobPlayerEpoch++;
			if (forecastWaiting.Count != 0)
			{
				if (mobPlayers.Count != previousMobPlayers.Count)
				{
					forecastDirty = true;
				}
				else
				{
					for (int i = 0; i < mobPlayers.Count; i++)
					{
						if (mobPlayers[i].Uid != previousMobPlayers[i].Uid)
						{
							forecastDirty = true;
							break;
						}
						if (!Same(mobPlayers[i].Position, previousMobPlayers[i].Position))
						{
							ForecastTargetChanged(mobPlayers[i].Data, previousMobPlayers[i].Position);
						}
					}
				}
			}
			foreach (Node sharedMob in sharedMobs)
			{
				if (sharedMob.InGrid && !sharedMob.Removed)
				{
					world.Received(sharedMob, Time.unscaledTime);
				}
			}
		}
		foreach (Node item in mobDirty)
		{
			if (item.InGrid && !item.Removed)
			{
				world.Received(item, Time.unscaledTime);
			}
		}
		mobDirty.Clear();
	}

	private long MobTarget(Node node, long field, bool force = false)
	{
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: 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_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0199: Unknown result type (might be due to invalid IL or missing references)
		//IL_019e: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_025e: Unknown result type (might be due to invalid IL or missing references)
		if (object.ReferenceEquals(node, mobTestNode))
		{
			return mobTestTarget;
		}
		if (field != server || world.Grid.Participants(node.Cell) < 2)
		{
			sharedMobs.Remove(node);
			node.Mob.TargetValid = false;
			return field;
		}
		sharedMobs.Add(node);
		if (!force && node.Mob.TargetValid && node.Mob.SelectionEpoch == mobPlayerEpoch)
		{
			return node.Mob.Target;
		}
		MobConeChecks++;
		Vector3 position = node.Data.GetPosition();
		Vector3 value = node.Data.GetRotation() * new Vector3(0f, 0f, 1f);
		if (!Finite(position) || !Finite(value))
		{
			return server;
		}
		float num = (((Character)node.Mob.Actor).IsBoss() ? options.BossMobConeRange : options.MobConeRange);
		float num2 = num * num;
		float num3 = num2;
		int num4 = 0;
		long uid = server;
		long owner = node.Data.GetOwner();
		foreach (MobPlayer mobPlayer in mobPlayers)
		{
			if (!mobPlayer.Data.IsValid() || mobPlayer.Data.GetOwner() != mobPlayer.Uid || mobPlayer.Data.GetFloat(ZDOVars.s_health, 1f) <= 0f)
			{
				continue;
			}
			Vector3 position2 = mobPlayer.Data.GetPosition();
			if (!Finite(position2))
			{
				continue;
			}
			float num5 = position2.x - position.x;
			float num6 = position2.y - position.y;
			float num7 = position2.z - position.z;
			float num8 = num5 * n