Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of TooManyLags Standalone v1.0.1
TooManyLagsStandalone.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Threading; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Steamworks; using UnityEngine; [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: CompilationRelaxations(8)] [assembly: AssemblyVersion("1.0.1.0")] namespace TooManyLagsStandalone; internal sealed class AuthorityController { private sealed class Node : AuthorityNode { private readonly AuthorityController owner; internal readonly ZDO Data; internal readonly Kinds Kind; internal Cell Sector; internal long SeenOwner; internal ushort SeenRevision; internal bool SeenUse; internal long RejectedOwner; internal ushort RejectedRevision; internal override long CurrentOwner => Data.GetOwner(); internal Node(AuthorityController owner, ZDO data, Kinds kind, Cell cell, Cell sector) { this.owner = owner; Data = data; Kind = kind; Cell = cell; Sector = sector; SeenOwner = data.GetOwner(); SeenRevision = data.OwnerRevision; } internal override ApplyResult Apply(long target) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) if (!Data.IsValid() || !object.ReferenceEquals(ZDOMan.instance.GetZDO(Data.m_uid), Data)) { return ApplyResult.Dropped; } if (target == 0) { return ApplyResult.Done; } if (Data.OwnerRevision == ushort.MaxValue || (RejectedOwner == Data.GetOwner() && RejectedRevision == Data.OwnerRevision)) { return ApplyResult.Dropped; } if (!owner.Available(target, Data.GetPosition())) { return ApplyResult.Done; } if (Data.GetOwner() == target && !owner.Handoffs.Pending(Data.m_uid)) { Managed = true; ManagedOwner = target; return ApplyResult.Done; } if (!owner.Available(Data.GetOwner(), Data.GetPosition())) { return ApplyResult.Done; } if (Data.GetLong(ZDOVars.s_user, 0L) != 0 || Data.GetBool(ZDOVars.s_attachJointHash, false) || (Kind == Kinds.TamedAnimals && (!Data.GetBool(ZDOVars.s_tamed, false) || !string.IsNullOrEmpty(Data.GetString(ZDOVars.s_follow, ""))))) { return ApplyResult.Done; } if ((!Plugin.Network.Capable(Data.GetOwner()) || !Plugin.Network.Capable(target)) && !owner.NativeTransferSafe(Data)) { return ApplyResult.Done; } owner.Handoffs.Request(Data, target, Kind, ZDOID.None); return ApplyResult.Pending; } } private readonly Settings settings; private readonly OwnershipCadence cadence; private readonly ObjectCatalog catalog; private readonly AuthorityWorld index; private readonly RegionContentionGate regions = new RegionContentionGate(); private readonly List<GridPeer> gridPeers = new List<GridPeer>(12); private readonly List<RegionPeer> regionPeers = new List<RegionPeer>(12); internal readonly List<PlayerPeer> Players = new List<PlayerPeer>(12); private readonly Dictionary<ZDOID, Node> nodes = new Dictionary<ZDOID, Node>(); private readonly Dictionary<Cell, HashSet<Node>> sectors = new Dictionary<Cell, HashSet<Node>>(); private readonly HashSet<Cell> interest = new HashSet<Cell>(); private readonly List<Cell> retiring = new List<Cell>(); private readonly List<ZDO> scan = new List<ZDO>(); private readonly HashSet<SectorIndex> visited = new HashSet<SectorIndex>(); private readonly Action<ZDOMan, Vector2s, List<ZDO>, HashSet<SectorIndex>> find; internal readonly Handoffs Handoffs; internal readonly MobPolicy Mobs; private int ranks; private int mobPeers; private bool failed; private bool interestDirty; internal bool Active { get { if (!failed && Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer()) { return ZDOMan.instance != null; } return false; } } internal AuthorityController(Settings settings) { this.settings = settings; cadence = new OwnershipCadence(settings.Interval); catalog = new ObjectCatalog(settings.Categories); index = new AuthorityWorld(0L, settings.Full, settings.Pattern, settings.Keep, settings.Directional, settings.Inner, settings.Outer, settings.CellSize == 4, settings.Forward, Plugin.Network.Latency, settings.Margin); find = AccessTools.MethodDelegate<Action<ZDOMan, Vector2s, List<ZDO>, HashSet<SectorIndex>>>(AccessTools.Method(typeof(ZDOMan), "FindObjects", (Type[])null, (Type[])null), (object)null, true); Handoffs = new Handoffs(this); Mobs = new MobPolicy(this); index.AfterGridUpdate = SyncInterest; } internal bool Shared(long uid) { if (Active) { return regions.Shared(uid); } return false; } internal bool WatchMobs(long uid) { if (mobPeers > 1) { return Shared(uid); } return false; } internal bool NativeTransferSafe(ZDO data) { return catalog.NativeTransferSafe(data.GetPrefab()); } internal bool Available(long uid, Vector3 point) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) if (!Active || uid == 0 || !Plugin.Network.Present(uid) || !Finite(point)) { return false; } foreach (PlayerPeer player in Players) { if (player.Uid == uid && player.Character != null && player.Character.IsValid() && player.Character.GetFloat(ZDOVars.s_health, 1f) > 0f && Finite(player.Reference) && ZNetScene.InActiveArea(point, player.Reference)) { return true; } } return false; } internal void Poll(float now) { //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) if (!Active || !cadence.Due(now) || !Object.op_Implicit((Object)(object)ZoneSystem.instance) || !Object.op_Implicit((Object)(object)ZNetScene.instance)) { return; } try { Plugin.Network.Players(Players); Players.Sort((PlayerPeer a, PlayerPeer b) => a.Uid.CompareTo(b.Uid)); regionPeers.Clear(); gridPeers.Clear(); mobPeers = 0; foreach (PlayerPeer player in Players) { if (settings.Mobs && Plugin.Network.MobCapable(player.Uid)) { mobPeers++; } Vector3 position = player.Character.GetPosition(); Vector2s zone = ZoneSystem.GetZone(player.Reference); Vector2s zone2 = ZoneSystem.GetZone(position); regionPeers.Add(new RegionPeer(player.Uid, new Cell(zone.x, zone.y), Finite(player.Reference), new Cell(zone2.x, zone2.y), Finite(position))); } if (regions.Update(regionPeers)) { interestDirty = true; Mobs.RanksChanged(); } foreach (PlayerPeer player2 in Players) { if (regions.Shared(player2.Uid)) { Vector3 position2 = player2.Character.GetPosition(); if (Cell.At(position2.x, position2.z, settings.CellSize, out var cell)) { gridPeers.Add(new GridPeer(player2.Uid, cell, settings.Directional ? CameraHeading.Read(player2.Character, player2.Uid, Plugin.Network.Capable(player2.Uid)) : 0f)); } } } if (ranks != Plugin.Network.RankVersion) { ranks = Plugin.Network.RankVersion; index.Grid.RanksChanged(); Mobs.RanksChanged(); } index.Tick(gridPeers, now); Mobs.Tick(now); Handoffs.Tick(now); RequestRouter.Flush(now); if (Plugin.Local != null && !ZNet.instance.IsDedicated()) { Plugin.Local.SetMobWatch(settings.Mobs && WatchMobs(ZDOMan.GetSessionID())); } Plugin.Network.FlushAll(); } catch (Exception error) { Stop(); Plugin.Fault(error); } } private void SyncInterest() { //IL_0155: Unknown result type (might be due to invalid IL or missing references) if (!interestDirty) { return; } interestDirty = false; retiring.Clear(); foreach (Cell item in interest) { if (!regions.ZoneKeys.Contains(item)) { retiring.Add(item); } } foreach (Cell item2 in retiring) { interest.Remove(item2); if (!sectors.TryGetValue(item2, out var value)) { continue; } List<Node> list = new List<Node>(value); foreach (Node item3 in list) { Remove(item3); } } foreach (Cell zoneKey in regions.ZoneKeys) { if (!interest.Add(zoneKey)) { continue; } scan.Clear(); visited.Clear(); find(ZDOMan.instance, new Vector2s((short)zoneKey.X, (short)zoneKey.Z), scan, visited); foreach (ZDO item4 in scan) { Changed(item4); } } scan.Clear(); visited.Clear(); retiring.Clear(); } internal void CapabilityChanged(long uid) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) if (!Active || !Object.op_Implicit((Object)(object)ZNetScene.instance) || !Object.op_Implicit((Object)(object)ZoneSystem.instance)) { return; } foreach (Cell item in interest) { scan.Clear(); visited.Clear(); find(ZDOMan.instance, new Vector2s((short)item.X, (short)item.Z), scan, visited); foreach (ZDO item2 in scan) { if (item2.GetOwner() == uid) { Changed(item2); } } } scan.Clear(); visited.Clear(); foreach (Node value in nodes.Values) { if (value.Data.GetOwner() == uid || index.Grid.Owner(value.Cell) == uid) { index.Received(value, Time.unscaledTime); } } } internal void Changed(ZDO data) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) if (!Active || data == null || !data.IsValid()) { return; } Handoffs.Observe(data); Mobs.Changed(data); Vector2s sector = data.GetSector(); Cell cell = new Cell(sector.x, sector.y); Node value; if (!interest.Contains(cell)) { if (nodes.TryGetValue(data.m_uid, out value)) { Remove(value); } return; } Kinds kinds = catalog.Prefab(data.GetPrefab()); if (kinds == Kinds.None || kinds == Kinds.Incinerators || (kinds == Kinds.TamedAnimals && (!data.GetBool(ZDOVars.s_tamed, false) || MobPolicy.FriendlyPrefab(data.GetPrefab())))) { if (nodes.TryGetValue(data.m_uid, out value)) { Remove(value); } return; } if (!Plugin.Network.Capable(data.GetOwner()) && !catalog.NativeTransferSafe(data.GetPrefab())) { if (nodes.TryGetValue(data.m_uid, out value)) { Remove(value); } return; } Vector3 position = data.GetPosition(); if (!Finite(position) || !Cell.At(position.x, position.z, settings.CellSize, out var cell2)) { return; } if (!nodes.TryGetValue(data.m_uid, out value)) { value = new Node(this, data, kinds, cell2, cell); nodes.Add(data.m_uid, value); AddSector(value); index.Add(value, Time.unscaledTime); return; } if (!object.ReferenceEquals(value.Data, data) || value.Kind != kinds) { Remove(value); Changed(data); return; } if (!value.Sector.Equals(cell)) { RemoveSector(value); value.Sector = cell; AddSector(value); } if (!value.Cell.Equals(cell2)) { index.Move(value, cell2, Time.unscaledTime); } long owner = data.GetOwner(); ushort ownerRevision = data.OwnerRevision; if (owner != value.SeenOwner || ownerRevision != value.SeenRevision) { value.SeenOwner = owner; value.SeenRevision = ownerRevision; index.Received(value, Time.unscaledTime); } bool flag = data.GetLong(ZDOVars.s_user, 0L) != 0 || data.GetBool(ZDOVars.s_attachJointHash, false) || (kinds == Kinds.TamedAnimals && !string.IsNullOrEmpty(data.GetString(ZDOVars.s_follow, ""))); if (flag != value.SeenUse) { value.SeenUse = flag; index.Received(value, Time.unscaledTime); } } internal void Destroyed(ZDOID id) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (nodes.TryGetValue(id, out var value)) { Remove(value); } Handoffs.Forget(id); Mobs.Forget(id); } private void AddSector(Node node) { if (!sectors.TryGetValue(node.Sector, out var value)) { value = new HashSet<Node>(); sectors.Add(node.Sector, value); } value.Add(node); } private void RemoveSector(Node node) { if (sectors.TryGetValue(node.Sector, out var value)) { value.Remove(node); if (value.Count == 0) { sectors.Remove(node.Sector); } } } private void Remove(Node node) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) index.Remove(node); nodes.Remove(node.Data.m_uid); RemoveSector(node); Handoffs.Cancel(node.Data.m_uid); } internal long Desired(ZDO data, Kinds kind, ZDOID target) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (!Active || data == null || !data.IsValid()) { return 0L; } if (kind == Kinds.None) { return Mobs.Desired(data, target); } if (!nodes.TryGetValue(data.m_uid, out var value) || !object.ReferenceEquals(value.Data, data)) { return 0L; } return index.Grid.Owner(value.Cell); } internal bool IsManaged(ZDO data, int method) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (!Active || data == null) { return false; } if (!nodes.TryGetValue(data.m_uid, out var value) || !ObjectCatalog.OwnerRequest(value.Kind, method)) { return Mobs.Managed(data.m_uid, method); } return true; } internal void Wake(ZDOID id) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (nodes.TryGetValue(id, out var value)) { index.Received(value, Time.unscaledTime); } Mobs.Wake(id); } internal void Rejected(ZDOID id) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (nodes.TryGetValue(id, out var value)) { value.RejectedOwner = value.Data.GetOwner(); value.RejectedRevision = value.Data.OwnerRevision; } Mobs.Forget(id); } internal void Stop() { failed = true; Handoffs.Clear(); Mobs.Clear(); nodes.Clear(); sectors.Clear(); interest.Clear(); regions.Clear(); Players.Clear(); gridPeers.Clear(); regionPeers.Clear(); } internal static bool Finite(Vector3 value) { if (!float.IsNaN(value.x) && !float.IsNaN(value.y) && !float.IsNaN(value.z) && !float.IsInfinity(value.x) && !float.IsInfinity(value.y)) { return !float.IsInfinity(value.z); } return false; } } internal abstract class AuthorityNode { internal Cell Cell; internal bool Managed; internal bool Removed; internal long ManagedOwner; internal long ConfirmedFieldOwner; internal float NotBefore; internal abstract long CurrentOwner { get; } internal abstract ApplyResult Apply(long target); internal virtual bool CanRemember(long fieldOwner) { if (Managed && ManagedOwner == fieldOwner) { return CurrentOwner == fieldOwner; } return false; } } internal enum GridCoveragePattern { Square3x3, Rounded5x5, Rounded7x7 } internal enum FieldTier : byte { Outer, Inner, Forward } internal struct Cell : IEquatable<Cell> { internal int X; internal int Z; internal Cell(int x, int z) { X = x; Z = z; } internal static bool At(float x, float z, out Cell cell) { return At(x, z, 8, out cell); } internal static bool At(float x, float z, int size, out Cell cell) { cell = default(Cell); if (size != 4 && size != 8 && size != 16) { return false; } if (float.IsNaN(x) || float.IsNaN(z) || float.IsInfinity(x) || float.IsInfinity(z)) { return false; } double num = Math.Floor(((double)x + 32.0) / (double)size); double num2 = Math.Floor(((double)z + 32.0) / (double)size); int num3 = 16384 / size; if (num < (double)(-num3) || num >= (double)num3 || num2 < (double)(-num3) || num2 >= (double)num3) { return false; } cell = new Cell((int)num, (int)num2); return true; } public bool Equals(Cell other) { if (X == other.X) { return Z == other.Z; } return false; } public override bool Equals(object other) { if (other is Cell) { return Equals((Cell)other); } return false; } public override int GetHashCode() { return (X * 397) ^ Z; } } internal struct GridPeer { internal long Uid; internal Cell Cell; internal float Yaw; internal GridPeer(long uid, Cell cell, float yaw = 0f) { Uid = uid; Cell = cell; Yaw = yaw; } } internal sealed class AuthorityGrid { private sealed class Policy { internal long Candidate; internal long Owner; internal long Uids; internal int Count; internal int FastCount; internal int ForwardCount; internal bool Shared; internal float Since; internal float Wait; internal float Deadline; internal bool Deferred; internal Request First; internal List<Request> Others; } private struct Request { internal long Uid; internal float Since; internal FieldTier Tier; internal Request(long uid, float since, FieldTier tier) { Uid = uid; Since = since; Tier = tier; } } private sealed class PeerState { internal Cell Cell; internal float Yaw; internal int Heading; internal bool Refreshed; } private struct Offset { internal Cell Cell; internal FieldTier Tier; internal Offset(int x, int z, FieldTier tier) { Cell = new Cell(x, z); Tier = tier; } } private readonly Dictionary<Cell, Policy> policies = new Dictionary<Cell, Policy>(); private readonly Stack<Policy> reusable = new Stack<Policy>(); private readonly Dictionary<long, PeerState> previous = new Dictionary<long, PeerState>(); private readonly Dictionary<Cell, long> remembered = new Dictionary<Cell, long>(); private readonly HashSet<long> seen = new HashSet<long>(); private readonly List<long> departed = new List<long>(); private readonly HashSet<Cell> dirty = new HashSet<Cell>(); private readonly HashSet<Cell> pending = new HashSet<Cell>(); private readonly List<Cell> due = new List<Cell>(); internal readonly List<Cell> Changes = new List<Cell>(); private readonly long server; private readonly Offset[][] footprints = new Offset[8][]; private readonly Dictionary<Cell, FieldTier>[] maskTypes = new Dictionary<Cell, FieldTier>[8]; internal readonly HashSet<Cell> Accelerations = new HashSet<Cell>(); private readonly bool directional; private readonly bool discardVacant; private readonly float innerDelay; private readonly float outerDelay; private readonly float forwardDelay; internal readonly float Delay; private float lastTime; private float nextDue = float.PositiveInfinity; private int deferredCells; private readonly Func<long, float> latency; private readonly float margin; private bool ranksDirty; internal int Rebuilds; internal int NeighborReads; internal int PlayerUpdates; internal int TimerVisits; internal int PolicyAllocations; internal int RememberedCells => remembered.Count; internal IEnumerable<Cell> CoveredCells { get { foreach (KeyValuePair<Cell, Policy> item in policies) { KeyValuePair<Cell, Policy> keyValuePair = item; if (keyValuePair.Value.Count != 0) { KeyValuePair<Cell, Policy> keyValuePair2 = item; yield return keyValuePair2.Key; } } } } internal bool Directional => directional; internal bool HasPeers => previous.Count != 0; internal int CachedCells => policies.Count; internal int ReusablePolicies => reusable.Count; internal int DeferredCells => deferredCells; internal AuthorityGrid(long server, float delay, GridCoveragePattern pattern = GridCoveragePattern.Square3x3, bool directional = false, float innerDelay = 0f, float outerDelay = 3f, bool discardVacant = false, bool forward = false, float forwardDelay = 0.2f, Func<long, float> latency = null, float margin = 30f) { this.latency = latency; this.margin = margin; this.server = server; Delay = delay; this.directional = directional; this.discardVacant = discardVacant; this.innerDelay = SafeDelay(innerDelay, 0f); this.outerDelay = SafeDelay(outerDelay, 3f); this.forwardDelay = SafeDelay(forwardDelay, 0.2f); int num = pattern switch { GridCoveragePattern.Rounded5x5 => 2, GridCoveragePattern.Rounded7x7 => 3, _ => 1, }; forward = directional && forward && num > 1; int[] array = new int[8] { 0, 1, 1, 1, 0, -1, -1, -1 }; int[] array2 = new int[8] { 1, 1, 0, -1, -1, -1, 0, 1 }; for (int i = 0; i < 8; i++) { List<Offset> list = new List<Offset>(37); for (int j = -num; j <= num; j++) { for (int k = -num; k <= num; k++) { int num2 = j * array[i] + k * array2[i]; int value = -j * array2[i] + k * array[i]; bool flag = array[i] != 0 && array2[i] != 0; bool flag2 = forward && ((!flag) ? (num2 == 2 && Math.Abs(value) <= 1) : ((j == 2 * array[i] && (k == array2[i] || k == 2 * array2[i])) || (j == array[i] && k == 2 * array2[i]))); if ((num == 1 || j * j + k * k <= num * (num + 1) || flag2) && (!directional || num2 >= 0 || (num2 >= -1 && Math.Abs(value) > 1))) { bool flag3 = false; if (directional) { flag3 = ((array[i] != 0 && array2[i] != 0) ? ((j == 0 && k == 0) || (num2 == 0 && Math.Abs(j) == 1 && Math.Abs(k) == 1) || (j == array[i] && k == array2[i]) || (j == array[i] && k == 0) || (j == 0 && k == array2[i])) : (num2 >= 0 && num2 <= 1 && Math.Abs(value) <= 1)); } list.Add(new Offset(j, k, flag3 ? FieldTier.Inner : (flag2 ? FieldTier.Forward : FieldTier.Outer))); } } } footprints[i] = list.ToArray(); Dictionary<Cell, FieldTier> dictionary = new Dictionary<Cell, FieldTier>(list.Count); foreach (Offset item in list) { dictionary.Add(item.Cell, item.Tier); } maskTypes[i] = dictionary; } } private static float SafeDelay(float value, float fallback) { if (!float.IsNaN(value) && !float.IsInfinity(value)) { return Math.Max(0f, Math.Min(30f, value)); } return fallback; } private static float Yaw(float value, float fallback) { if (float.IsNaN(value) || float.IsInfinity(value)) { return fallback; } value %= 360f; if (!(value < 0f)) { return value; } return value + 360f; } internal long Owner(Cell cell) { if (!policies.TryGetValue(cell, out var value)) { return 0L; } return value.Owner; } internal long Candidate(Cell cell) { if (!policies.TryGetValue(cell, out var value)) { return 0L; } return value.Candidate; } private float FieldWait(Policy policy) { if (!directional) { return Delay; } if (policy.ForwardCount != 0) { float num = forwardDelay; if (policy.FastCount != 0) { num = Math.Min(num, innerDelay); } if (policy.Count > policy.FastCount + policy.ForwardCount) { num = Math.Min(num, outerDelay); } return num; } if (policy.FastCount == 0) { return outerDelay; } if (policy.FastCount != policy.Count) { return Math.Min(innerDelay, outerDelay); } return innerDelay; } internal float Wait(Cell cell) { if (!policies.TryGetValue(cell, out var value)) { if (!directional) { return Delay; } return outerDelay; } return FieldWait(value); } internal float Remaining(Cell cell, float now) { if (!policies.TryGetValue(cell, out var value) || value.Owner == value.Candidate) { return 0f; } return Math.Max(0f, value.Deadline - now); } internal bool IsRemembered(Cell cell, long uid) { if (uid != 0 && uid != server && remembered.TryGetValue(cell, out var value)) { return value == uid; } return false; } internal void Remember(Cell cell, long uid) { if (uid != 0 && uid != server && Owner(cell) == uid && Candidate(cell) == uid) { remembered[cell] = uid; } } internal void Forget(Cell cell) { remembered.Remove(cell); } private void Contribute(long uid, PeerState peer, bool add, float now) { Offset[] array = footprints[peer.Heading]; for (int i = 0; i < array.Length; i++) { Offset offset = array[i]; Contribution(uid, new Cell(peer.Cell.X + offset.Cell.X, peer.Cell.Z + offset.Cell.Z), offset.Tier, add, now); } } private void Contribution(long uid, Cell cell, FieldTier tier, bool add, float now) { NeighborReads++; if (!policies.TryGetValue(cell, out var value)) { if (reusable.Count != 0) { value = reusable.Pop(); } else { value = new Policy(); PolicyAllocations++; } policies.Add(cell, value); } value.Count += (add ? 1 : (-1)); value.Uids ^= uid; switch (tier) { case FieldTier.Inner: value.FastCount += (add ? 1 : (-1)); break; case FieldTier.Forward: value.ForwardCount += (add ? 1 : (-1)); break; } if (add) { Request request = new Request(uid, now, tier); if (value.Count == 1) { value.First = request; } else { if (value.Others == null) { value.Others = new List<Request>(1); } value.Others.Add(request); } if (remembered.Count != 0 && remembered.TryGetValue(cell, out var value2) && value2 != uid) { remembered.Remove(cell); } } else if (value.First.Uid == uid) { if (value.Others != null && value.Others.Count != 0) { int index = value.Others.Count - 1; value.First = value.Others[index]; value.Others.RemoveAt(index); } else { value.First = default(Request); } } else if (value.Others != null) { for (int i = 0; i < value.Others.Count; i++) { if (value.Others[i].Uid == uid) { value.Others.RemoveAt(i); break; } } } dirty.Add(cell); } private void Reclassify(long uid, Cell cell, FieldTier before, FieldTier after) { Policy policy = policies[cell]; NeighborReads += 2; switch (before) { case FieldTier.Inner: policy.FastCount--; break; case FieldTier.Forward: policy.ForwardCount--; break; } switch (after) { case FieldTier.Inner: policy.FastCount++; break; case FieldTier.Forward: policy.ForwardCount++; break; } if (policy.First.Uid == uid) { policy.First.Tier = after; } else if (policy.Others != null) { for (int i = 0; i < policy.Others.Count; i++) { if (policy.Others[i].Uid == uid) { Request value = policy.Others[i]; value.Tier = after; policy.Others[i] = value; break; } } } dirty.Add(cell); } private void MoveContribution(long uid, PeerState old, Cell next, int heading, float now) { Offset[] array = footprints[old.Heading]; for (int i = 0; i < array.Length; i++) { Offset offset = array[i]; Cell cell = new Cell(old.Cell.X + offset.Cell.X, old.Cell.Z + offset.Cell.Z); if (maskTypes[heading].TryGetValue(new Cell(cell.X - next.X, cell.Z - next.Z), out var value)) { if (value != offset.Tier) { Reclassify(uid, cell, offset.Tier, value); } else if (deferredCells != 0 && policies[cell].Deferred) { dirty.Add(cell); } } else { Contribution(uid, cell, offset.Tier, add: false, now); } } Offset[] array2 = footprints[heading]; for (int j = 0; j < array2.Length; j++) { Offset offset2 = array2[j]; Cell cell2 = new Cell(next.X + offset2.Cell.X, next.Z + offset2.Cell.Z); if (!maskTypes[old.Heading].ContainsKey(new Cell(cell2.X - old.Cell.X, cell2.Z - old.Cell.Z))) { Contribution(uid, cell2, offset2.Tier, add: true, now); } } } private void Commit(Cell cell, Policy policy) { pending.Remove(cell); if (policy.Owner != policy.Candidate) { policy.Owner = policy.Candidate; Changes.Add(cell); } if (policy.Count == 0 && policy.Owner == 0 && policies.Remove(cell) && discardVacant && reusable.Count < 1024) { policy.Candidate = (policy.Owner = (policy.Uids = 0L)); policy.Count = (policy.FastCount = (policy.ForwardCount = 0)); policy.Since = (policy.Wait = (policy.Deadline = 0f)); policy.First = default(Request); if (policy.Others != null) { policy.Others.Clear(); } reusable.Push(policy); } } private void Evaluate(Cell cell, float now) { Policy policy = policies[cell]; if (discardVacant && policy.Count == 0) { SetDeferred(policy, value: false); policy.Candidate = 0L; Commit(cell, policy); return; } if (directional && discardVacant && policy.Count == 1 && policy.First.Tier != FieldTier.Inner && (policy.Deferred || policy.Shared) && previous.TryGetValue(policy.Uids, out var value) && !value.Refreshed) { SetDeferred(policy, value: true); policy.Candidate = policy.Owner; policy.Wait = FieldWait(policy); pending.Remove(cell); return; } SetDeferred(policy, value: false); long num = Resolve(policy); policy.Shared = policy.Count > 1; if (policy.Candidate != num) { if (policy.Owner == policy.Candidate || policy.Candidate == 0 || policy.Count <= 1) { policy.Since = now; } policy.Candidate = num; } float num2 = FieldWait(policy); if (num2 < policy.Wait && policy.Candidate != 0) { Accelerations.Add(cell); } policy.Wait = num2; if (policy.Count > 1 && remembered.Count != 0) { Forget(cell); } if (policy.Owner == num || (remembered.Count != 0 && IsRemembered(cell, num))) { Commit(cell, policy); return; } policy.Deadline = policy.Since + num2; if (policy.Count != 0) { policy.Deadline = RequestDeadline(policy.First, policy.Since, now); if (policy.Others != null) { foreach (Request other in policy.Others) { policy.Deadline = Math.Min(policy.Deadline, RequestDeadline(other, policy.Since, now)); } } } if (now >= policy.Deadline) { Commit(cell, policy); return; } pending.Add(cell); nextDue = Math.Min(nextDue, policy.Deadline); } private float RequestDeadline(Request request, float since, float now) { return Math.Max(Math.Min(request.Since, now), since) + ((!directional) ? Delay : ((request.Tier == FieldTier.Inner) ? innerDelay : ((request.Tier == FieldTier.Forward) ? forwardDelay : outerDelay))); } private void SetDeferred(Policy policy, bool value) { if (policy.Deferred != value) { policy.Deferred = value; deferredCells += (value ? 1 : (-1)); } } private long Resolve(Policy policy) { if (policy.Count == 0) { return 0L; } if (policy.Count == 1) { return policy.Uids; } if (latency == null) { return server; } long uid = policy.First.Uid; float num = latency(uid); bool flag = policy.First.Uid == policy.Candidate; if (policy.Others != null) { foreach (Request other in policy.Others) { float num2 = latency(other.Uid); if (num2 < num || (num2 == num && other.Uid < uid)) { uid = other.Uid; num = num2; } flag |= other.Uid == policy.Candidate; } } if (flag && latency(policy.Candidate) <= num + margin) { return policy.Candidate; } return uid; } internal void RanksChanged() { ranksDirty = true; } internal void Update(List<GridPeer> peers, float now) { Changes.Clear(); Accelerations.Clear(); dirty.Clear(); seen.Clear(); departed.Clear(); bool flag = now < lastTime; lastTime = now; if (flag) { nextDue = float.PositiveInfinity; foreach (Cell item in pending) { Policy policy = policies[item]; policy.Since = now; policy.Deadline = now + policy.Wait; policy.First.Since = now; if (policy.Others != null) { for (int i = 0; i < policy.Others.Count; i++) { Request value = policy.Others[i]; value.Since = now; policy.Others[i] = value; } } nextDue = Math.Min(nextDue, policy.Deadline); } } bool flag2 = false; foreach (GridPeer peer in peers) { if (peer.Uid == 0 || peer.Uid == server || !seen.Add(peer.Uid)) { continue; } PeerState value2; bool flag3 = previous.TryGetValue(peer.Uid, out value2); if (flag3) { value2.Refreshed = false; } float num = Yaw(peer.Yaw, flag3 ? value2.Yaw : 0f); int num2 = (directional ? ((int)((num + 22.5f) / 45f) & 7) : 0); if (!flag3 || !value2.Cell.Equals(peer.Cell) || value2.Heading != num2) { if (flag3) { MoveContribution(peer.Uid, value2, peer.Cell, num2, now); } else { value2 = new PeerState(); previous.Add(peer.Uid, value2); } value2.Cell = peer.Cell; value2.Yaw = num; value2.Heading = num2; value2.Refreshed = true; if (!flag3) { Contribute(peer.Uid, value2, add: true, now); } flag2 = true; PlayerUpdates++; } } foreach (KeyValuePair<long, PeerState> previou in previous) { if (!seen.Contains(previou.Key)) { departed.Add(previou.Key); } } foreach (long item2 in departed) { Contribute(item2, previous[item2], add: false, now); previous.Remove(item2); flag2 = true; } if (ranksDirty) { foreach (KeyValuePair<Cell, Policy> policy2 in policies) { if (policy2.Value.Count > 1) { dirty.Add(policy2.Key); } } ranksDirty = false; } if (flag2) { Rebuilds++; } foreach (Cell item3 in dirty) { Evaluate(item3, now); } if (pending.Count == 0) { nextDue = float.PositiveInfinity; } else { if (now < nextDue) { return; } due.Clear(); foreach (Cell item4 in pending) { due.Add(item4); } nextDue = float.PositiveInfinity; foreach (Cell item5 in due) { TimerVisits++; if (policies.TryGetValue(item5, out var value3)) { if (now >= value3.Deadline) { Commit(item5, value3); } else { nextDue = Math.Min(nextDue, value3.Deadline); } } } due.Clear(); } } } internal enum ApplyResult { Done, Pending, Dropped } internal sealed class AuthorityWorld { private sealed class ObjectBucket { internal readonly HashSet<AuthorityNode> Nodes = new HashSet<AuthorityNode>(); internal long Target; internal int Unconfirmed; } internal readonly AuthorityGrid Grid; private readonly Dictionary<Cell, ObjectBucket> buckets = new Dictionary<Cell, ObjectBucket>(); private readonly HashSet<AuthorityNode> pending = new HashSet<AuthorityNode>(); private readonly List<AuthorityNode> scratch = new List<AuthorityNode>(); private readonly bool keepOnExit; private readonly long server; private float lastTime; internal Action AfterGridUpdate; internal int Attempts; internal int SkippedCells; internal int BucketCount => buckets.Count; internal int PendingCount => pending.Count; internal AuthorityWorld(long server, float delay, GridCoveragePattern pattern = GridCoveragePattern.Square3x3, bool keepOnExit = true, bool directional = false, float innerDelay = 0f, float outerDelay = 3f, bool forward = false, float forwardDelay = 0.2f, Func<long, float> latency = null, float margin = 30f) { Grid = new AuthorityGrid(server, delay, pattern, directional, innerDelay, outerDelay, keepOnExit, forward, forwardDelay, latency, margin); this.keepOnExit = keepOnExit; this.server = server; } internal void Add(AuthorityNode node, float now, bool schedule = true) { node.Removed = false; ObjectBucket objectBucket = Bucket(node.Cell); if (objectBucket.Nodes.Add(node)) { node.ConfirmedFieldOwner = 0L; if (objectBucket.Target != 0) { objectBucket.Unconfirmed++; } if (keepOnExit) { Grid.Forget(node.Cell); } if (schedule) { Queue(node, now); } else { node.Managed = false; } } } internal void Remove(AuthorityNode node) { Detach(node); node.Removed = true; node.Managed = false; pending.Remove(node); } internal void Move(AuthorityNode node, Cell next, float now, bool schedule = true) { if (!node.Removed && !node.Cell.Equals(next)) { Detach(node); node.Cell = next; ObjectBucket objectBucket = Bucket(next); objectBucket.Nodes.Add(node); node.ConfirmedFieldOwner = 0L; if (objectBucket.Target != 0) { objectBucket.Unconfirmed++; } if (keepOnExit) { Grid.Forget(next); } if (schedule) { Queue(node, now + Grid.Wait(next)); return; } pending.Remove(node); node.Managed = false; } } internal void Changed(AuthorityNode node, float now, bool schedule = true) { if (!node.Removed) { Invalidate(node); if (schedule) { Queue(node, now + Grid.Wait(node.Cell)); return; } pending.Remove(node); node.Managed = false; } } internal void Received(AuthorityNode node, float now) { if (!node.Removed) { Invalidate(node); Queue(node, Math.Max(now, node.NotBefore)); } } internal void Tick(List<GridPeer> peers, float now) { if (peers.Count == 0 && !Grid.HasPeers && Grid.CachedCells == 0 && pending.Count == 0) { Grid.Changes.Clear(); Grid.Accelerations.Clear(); return; } if (now < lastTime) { foreach (AuthorityNode item in pending) { item.NotBefore = now + Grid.Wait(item.Cell); } } lastTime = now; Grid.Update(peers, now); if (AfterGridUpdate != null) { AfterGridUpdate(); } foreach (Cell change in Grid.Changes) { if ((keepOnExit && Grid.Owner(change) == 0) || !buckets.TryGetValue(change, out var value)) { continue; } long num = Grid.Owner(change); if (keepOnExit && Grid.IsRemembered(change, num) && value.Target == num && value.Unconfirmed == 0) { SkippedCells++; continue; } value.Target = ((keepOnExit && num != server) ? num : 0); value.Unconfirmed = ((value.Target != 0) ? value.Nodes.Count : 0); foreach (AuthorityNode node in value.Nodes) { node.ConfirmedFieldOwner = 0L; if (!pending.Contains(node)) { Queue(node, now); } else if (Grid.Directional) { node.NotBefore = Math.Min(node.NotBefore, now + Grid.Wait(change)); } } } scratch.Clear(); foreach (AuthorityNode item2 in pending) { scratch.Add(item2); } foreach (AuthorityNode item3 in scratch) { if (item3.Removed || !pending.Contains(item3)) { continue; } if (Grid.Accelerations.Count != 0 && Grid.Accelerations.Contains(item3.Cell)) { item3.NotBefore = Math.Min(item3.NotBefore, now + Grid.Wait(item3.Cell)); } if (now < item3.NotBefore) { continue; } Attempts++; ApplyResult applyResult = item3.Apply(Grid.Owner(item3.Cell)); if (applyResult != ApplyResult.Pending) { pending.Remove(item3); } switch (applyResult) { case ApplyResult.Dropped: item3.Managed = false; break; case ApplyResult.Done: if (keepOnExit && !item3.Removed) { Confirm(item3); } break; } } scratch.Clear(); } private void Queue(AuthorityNode node, float at) { node.NotBefore = at; pending.Add(node); } private void Invalidate(AuthorityNode node) { if (keepOnExit) { Grid.Forget(node.Cell); if (buckets.TryGetValue(node.Cell, out var value) && value.Target != 0 && node.ConfirmedFieldOwner == value.Target) { node.ConfirmedFieldOwner = 0L; value.Unconfirmed++; } } } private void Confirm(AuthorityNode node) { if (buckets.TryGetValue(node.Cell, out var value) && value.Target != 0 && node.ConfirmedFieldOwner != value.Target && node.CanRemember(value.Target)) { node.ConfirmedFieldOwner = value.Target; value.Unconfirmed--; Certify(node.Cell, value); } } private void Certify(Cell cell, ObjectBucket bucket) { if (keepOnExit && bucket.Target != 0 && bucket.Unconfirmed == 0) { Grid.Remember(cell, bucket.Target); } } private void Detach(AuthorityNode node) { if (buckets.TryGetValue(node.Cell, out var value) && value.Nodes.Remove(node)) { bool flag = value.Target != 0 && node.ConfirmedFieldOwner != value.Target; if (flag) { value.Unconfirmed--; } node.ConfirmedFieldOwner = 0L; if (value.Nodes.Count == 0) { buckets.Remove(node.Cell); Grid.Forget(node.Cell); } else if (flag) { Certify(node.Cell, value); } } } private ObjectBucket Bucket(Cell cell) { if (!buckets.TryGetValue(cell, out var value)) { long num = Grid.Owner(cell); ObjectBucket objectBucket = new ObjectBucket(); objectBucket.Target = ((keepOnExit && num != server) ? num : 0); value = objectBucket; buckets.Add(cell, value); } return value; } } internal static class CameraHeading { internal static readonly int Key = StringExtensionMethods.GetStableHashCode("botanic.toomanylags.standalone.cameraSector"); internal static int Sector(float yaw) { if (float.IsNaN(yaw) || float.IsInfinity(yaw)) { return -1; } yaw %= 360f; if (yaw < 0f) { yaw += 360f; } return (int)((yaw + 22.5f) / 45f) & 7; } internal static int Sector(float x, float z) { if (float.IsNaN(x) || float.IsNaN(z) || float.IsInfinity(x) || float.IsInfinity(z) || (double)x * (double)x + (double)z * (double)z < 1E-06) { return -1; } return Sector((float)(Math.Atan2(x, z) * (180.0 / Math.PI))); } internal static float Read(ZDO player, long peer, bool cameraAvailable) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) int num = ((cameraAvailable && player.GetOwner() == peer) ? player.GetInt(Key, -1) : (-1)); if (num < 0 || num >= 8) { Quaternion rotation = player.GetRotation(); return ((Quaternion)(ref rotation)).eulerAngles.y; } return (float)num * 45f; } } internal sealed class CameraPublisher { private readonly OwnershipCadence cadence; private ZDO previous; private ZNet network; private ZDOMan world; internal CameraPublisher(float interval) { cadence = new OwnershipCadence(interval, 0.05f); } internal void Sample(float now) { //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) if (!cadence.Due(now)) { return; } ZNet instance = ZNet.instance; if (!Object.op_Implicit((Object)(object)instance) || instance.IsDedicated() || ZNet.IsSinglePlayer || ZDOMan.instance == null) { Reset(); return; } Player localPlayer = Player.m_localPlayer; if (!Object.op_Implicit((Object)(object)localPlayer) || (!instance.IsServer() && instance.GetServerPeer() == null)) { Reset(); return; } ZNetView component = ((Component)localPlayer).GetComponent<ZNetView>(); if (!Object.op_Implicit((Object)(object)component) || !component.IsValid() || !component.IsOwner()) { Reset(); return; } ZDO zDO = component.GetZDO(); if (zDO == null || !zDO.IsValid() || ((ZDOID)(ref zDO.m_uid)).IsNone() || !object.ReferenceEquals(ZDOMan.instance.GetZDO(zDO.m_uid), zDO)) { Reset(); return; } GameCamera instance2 = GameCamera.instance; int num = -1; if (Object.op_Implicit((Object)(object)instance2) && !GameCamera.InFreeFly()) { Vector3 forward = ((Component)instance2).transform.forward; num = CameraHeading.Sector(forward.x, forward.z); } bool flag = !object.ReferenceEquals(previous, zDO) || !object.ReferenceEquals(network, instance) || !object.ReferenceEquals(world, ZDOMan.instance); if (flag) { ZDOExtraData.AddSessionHash(CameraHeading.Key); } if (flag || zDO.GetInt(CameraHeading.Key, -1) != num) { zDO.Set(CameraHeading.Key, num, false); } previous = zDO; network = instance; world = ZDOMan.instance; } internal void Reset(bool invalidate = false) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) if (invalidate && previous != null && object.ReferenceEquals(network, ZNet.instance) && object.ReferenceEquals(world, ZDOMan.instance) && previous.IsValid() && previous.GetOwner() == ZDOMan.GetSessionID() && object.ReferenceEquals(world.GetZDO(previous.m_uid), previous)) { previous.Set(CameraHeading.Key, -1, false); } previous = null; network = null; world = null; } } [HarmonyPatch(typeof(GameCamera), "LateUpdate")] internal static class CameraSamplePatch { [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix() { Plugin.SampleCamera(); } } [HarmonyPatch(typeof(GameCamera), "OnDestroy")] internal static class CameraLossPatch { [HarmonyPrefix] private static void Prefix(GameCamera __instance) { if ((Object)(object)GameCamera.instance == (Object)(object)__instance) { Plugin.InvalidateCamera(); } } } internal static class FieldReader { internal static Func<TSource, TValue> Create<TSource, TValue>(FieldInfo field) { if (field == null || field.IsStatic || !typeof(TValue).IsAssignableFrom(field.FieldType) || typeof(TSource).IsValueType || field.DeclaringType.IsValueType) { throw new NotSupportedException("Unsupported read-only field getter."); } DynamicMethod dynamicMethod = new DynamicMethod("TooManyLagsRead_" + field.Name, typeof(TValue), new Type[1] { typeof(TSource) }, typeof(FieldReader).Module, skipVisibility: true); ILGenerator iLGenerator = dynamicMethod.GetILGenerator(); iLGenerator.Emit(OpCodes.Ldarg_0); if (typeof(TSource) != field.DeclaringType) { iLGenerator.Emit(OpCodes.Castclass, field.DeclaringType); } iLGenerator.Emit(OpCodes.Ldfld, field); iLGenerator.Emit(OpCodes.Ret); return (Func<TSource, TValue>)dynamicMethod.CreateDelegate(typeof(Func<TSource, TValue>)); } } internal static class FriendlyCreatures { internal static bool IsFriendly(Character actor) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 if (Object.op_Implicit((Object)(object)actor) && !actor.IsPlayer()) { return (int)actor.GetFaction() == 0; } return false; } } internal sealed class Handoffs { private sealed class Job { internal ZDO Data; internal Kinds Kind; internal ZDOID MobTarget; internal HandoffTransaction State; internal float LastSend; internal float CommitAt; internal bool Activated; internal bool NativeSource; internal bool NativeTarget; } private sealed class Receipt { internal long Source; internal long Target; internal ushort Revision; internal Kinds Kind; internal float Until; internal float Next; internal Job Job; internal bool Activated; } private readonly AuthorityController controller; private readonly Dictionary<ZDOID, Job> jobs = new Dictionary<ZDOID, Job>(); private readonly List<Job> scratch = new List<Job>(); private readonly Dictionary<ZDOID, Receipt> receipts = new Dictionary<ZDOID, Receipt>(); private readonly List<ZDOID> expired = new List<ZDOID>(); private float nextReceipt = float.PositiveInfinity; private long sequence; internal Handoffs(AuthorityController controller) { this.controller = controller; } internal bool Pending(ZDOID id) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return jobs.ContainsKey(id); } internal bool Committing(ZDOID id) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (!jobs.TryGetValue(id, out var value) || value.State.Phase != HandoffPhase.Commit) { if (receipts.TryGetValue(id, out var value2) && !value2.Activated) { return Time.unscaledTime < value2.Until; } return false; } return true; } internal bool AcceptsForward(ZDOID id, long source, long target, ushort revision, int method) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) Kinds kind; if (jobs.TryGetValue(id, out var value) && value.State.Source == source && value.State.Target == target && value.State.Revision + 1 == revision) { kind = value.Kind; } else { if (!receipts.TryGetValue(id, out var value2) || !(value2.Until >= Time.unscaledTime) || value2.Source != source || value2.Target != target || value2.Revision != revision) { return false; } kind = value2.Kind; } if (kind != Kinds.None) { return ObjectCatalog.OwnerRequest(kind, method); } return method == StringExtensionMethods.GetStableHashCode("RPC_Damage"); } internal bool Request(ZDO data, long target, Kinds kind, ZDOID mobTarget) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) if (!controller.Active || data == null || !data.IsValid() || target == 0 || target == data.GetOwner() || data.GetOwner() == 0 || data.OwnerRevision == ushort.MaxValue || Committing(data.m_uid)) { return false; } if (kind == Kinds.None && (!Plugin.Network.MobCapable(target) || !Plugin.Network.MobCapable(data.GetOwner()))) { return false; } bool flag = !Plugin.Network.Capable(data.GetOwner()); bool flag2 = !Plugin.Network.Capable(target); if ((flag || flag2) && (kind == Kinds.None || !controller.NativeTransferSafe(data))) { return false; } if (jobs.TryGetValue(data.m_uid, out var value)) { if (value.State.Phase == HandoffPhase.Commit) { return false; } if (value.State.Target == target && value.MobTarget == mobTarget) { if (value.NativeSource && NativeOwnership.Idle(data)) { value.State.Wake(Time.unscaledTime); } return true; } Cancel(data.m_uid); } if (jobs.Count >= 4096 || RequestRouter.Saturated || !controller.Available(data.GetOwner(), data.GetPosition()) || !controller.Available(target, data.GetPosition())) { return false; } Job job = new Job(); job.Data = data; job.Kind = kind; job.MobTarget = mobTarget; job.NativeSource = flag; job.NativeTarget = flag2; job.Activated = flag2; job.State = new HandoffTransaction(++sequence, data.GetOwner(), target, data.OwnerRevision, Time.unscaledTime); Job value2 = job; jobs.Add(data.m_uid, value2); ZDOMan.instance.ForceSendZDO(target, data.m_uid); return true; } internal void Tick(float now) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0383: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_03c1: Unknown result type (might be due to invalid IL or missing references) //IL_03a8: Unknown result type (might be due to invalid IL or missing references) //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_035b: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Unknown result type (might be due to invalid IL or missing references) //IL_0420: Unknown result type (might be due to invalid IL or missing references) //IL_04ee: Unknown result type (might be due to invalid IL or missing references) //IL_054e: Unknown result type (might be due to invalid IL or missing references) //IL_0570: Unknown result type (might be due to invalid IL or missing references) if (now >= nextReceipt) { nextReceipt = float.PositiveInfinity; expired.Clear(); foreach (KeyValuePair<ZDOID, Receipt> receipt in receipts) { Receipt value = receipt.Value; if (now >= value.Until) { expired.Add(receipt.Key); continue; } if (!value.Activated && now >= value.Next) { value.Next = now + 2f; Job receiptJob = value.Job; ZDOMan.instance.ForceSendZDO(value.Target, receiptJob.Data.m_uid); Plugin.Network.Send(value.Target, Message.ActivationCheck, delegate(ZPackage p) { Write(p, receiptJob); }); } nextReceipt = Math.Min(nextReceipt, value.Activated ? value.Until : Math.Min(value.Until, value.Next)); } foreach (ZDOID item in expired) { receipts.Remove(item); } expired.Clear(); } if (jobs.Count == 0) { return; } scratch.Clear(); foreach (Job value2 in jobs.Values) { scratch.Add(value2); } foreach (Job job in scratch) { if (!jobs.ContainsKey(job.Data.m_uid)) { continue; } HandoffTransaction state = job.State; if (!job.Data.IsValid() || !object.ReferenceEquals(ZDOMan.instance.GetZDO(job.Data.m_uid), job.Data)) { Cancel(job.Data.m_uid); } else if (state.Observe(job.Data.GetOwner(), job.Data.OwnerRevision)) { Finish(job); } else if (job.NativeSource && Plugin.Network.Capable(state.Source)) { Cancel(job.Data.m_uid, force: true); controller.Wake(job.Data.m_uid); } else if (state.Phase == HandoffPhase.Commit && now - job.CommitAt > 12f) { Cancel(job.Data.m_uid, force: true); controller.Wake(job.Data.m_uid); } else if (!controller.Available(state.Source, job.Data.GetPosition()) || !controller.Available(state.Target, job.Data.GetPosition())) { Cancel(job.Data.m_uid); } else if (state.Phase != HandoffPhase.Commit && controller.Desired(job.Data, job.Kind, job.MobTarget) != state.Target) { Cancel(job.Data.m_uid); } else { if (now < state.Next) { continue; } if (state.Phase == HandoffPhase.Blocked) { state.Wake(now); } if (state.Phase == HandoffPhase.Prepare && job.NativeTarget) { state.Prepared(ready: true, now); job.CommitAt = now; } if (state.Phase == HandoffPhase.Commit && job.NativeSource) { if (state.MatchesSource(job.Data.GetOwner(), job.Data.OwnerRevision)) { if (!controller.NativeTransferSafe(job.Data)) { Cancel(job.Data.m_uid, force: true); continue; } if (!NativeOwnership.Idle(job.Data)) { state.Block(now); continue; } job.Data.SetOwner(state.Target); ZDOMan.instance.ForceSendZDO(state.Source, job.Data.m_uid); ZDOMan.instance.ForceSendZDO(state.Target, job.Data.m_uid); Observe(job.Data); } } else if (state.Phase == HandoffPhase.Prepare || state.Phase == HandoffPhase.Commit) { Message message = ((state.Phase == HandoffPhase.Prepare) ? Message.Prepare : Message.Commit); long uid = ((state.Phase == HandoffPhase.Prepare) ? state.Target : state.Source); state.Next = now + ((message == Message.Prepare) ? 0.5f : 2f); job.LastSend = now; Plugin.Network.Send(uid, message, delegate(ZPackage p) { Write(p, job); }); } } } scratch.Clear(); } private static void Write(ZPackage p, Job job) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) p.Write(job.State.Token); p.Write(job.Data.m_uid); p.Write(job.State.Source); p.Write(job.State.Target); p.Write(job.State.Revision); p.Write((int)job.Kind); p.Write(job.MobTarget); } internal void Receive(long sender, Message message, ZPackage packet) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) long num = packet.ReadLong(); ZDOID val = packet.ReadZDOID(); Job value2; if (message == Message.Activated) { ushort num2 = packet.ReadUShort(); if (receipts.TryGetValue(val, out var value) && value.Job.State.Token == num && value.Target == sender && value.Revision == num2) { value.Activated = true; controller.Wake(val); } else if (jobs.TryGetValue(val, out value2) && value2.State.Token == num && value2.State.Target == sender && value2.State.Revision + 1 == num2) { value2.Activated = true; } } else { if (!jobs.TryGetValue(val, out value2) || value2.State.Token != num) { return; } HandoffTransaction state = value2.State; float unscaledTime = Time.unscaledTime; if (message == Message.Prepared && sender == state.Target) { bool flag = packet.ReadBool(); state.Prepared(flag, unscaledTime); if (flag) { value2.CommitAt = unscaledTime; } } else if (message == Message.Wake && sender == state.Source) { state.Wake(unscaledTime); } else { if (message != Message.Result || sender != state.Source || state.Phase != HandoffPhase.Commit) { return; } int num3 = packet.ReadInt(); switch (num3) { case 1: state.Next = unscaledTime + 2f; Observe(value2.Data); return; case 2: state.Block(unscaledTime); return; } Cancel(val, force: true); if (num3 == 3) { controller.Rejected(val); } controller.Wake(val); } } } internal void Observe(ZDO data) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (data != null && jobs.TryGetValue(data.m_uid, out var value) && value.State.Observe(data.GetOwner(), data.OwnerRevision)) { Finish(value); } } private void Finish(Job job) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (job.State.Phase == HandoffPhase.Complete && (!job.NativeSource || !job.NativeTarget)) { receipts[job.Data.m_uid] = new Receipt { Source = job.State.Source, Target = job.State.Target, Revision = job.Data.OwnerRevision, Kind = job.Kind, Until = Time.unscaledTime + 12f, Job = job, Activated = job.Activated }; nextReceipt = Time.unscaledTime; } jobs.Remove(job.Data.m_uid); controller.Wake(job.Data.m_uid); } internal void Forget(ZDOID id) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) jobs.Remove(id); receipts.Remove(id); } internal void Cancel(ZDOID id, bool force = false) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (jobs.TryGetValue(id, out var job) && (force || job.State.Phase != HandoffPhase.Commit || !(Time.unscaledTime - job.State.Started < 12f))) { jobs.Remove(id); Plugin.Network.Send(job.State.Target, Message.Cancel, delegate(ZPackage p) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) p.Write(job.State.Token); p.Write(id); }); Plugin.Network.Send(job.State.Source, Message.Cancel, delegate(ZPackage p) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) p.Write(job.State.Token); p.Write(id); }); } } internal void Clear() { jobs.Clear(); scratch.Clear(); receipts.Clear(); expired.Clear(); nextReceipt = float.PositiveInfinity; } } internal enum HandoffPhase { Prepare, Commit, Blocked, Complete, Obsolete } internal sealed class HandoffTransaction { internal readonly long Token; internal readonly long Source; internal readonly long Target; internal readonly ushort Revision; internal HandoffPhase Phase; internal float Next; internal float Started; internal HandoffTransaction(long token, long source, long target, ushort revision, float now) { if (token <= 0 || source == 0 || target == 0 || source == target || revision == ushort.MaxValue) { throw new ArgumentException("Invalid handoff identity."); } Token = token; Source = source; Target = target; Revision = revision; Started = (Next = now); } internal bool Observe(long owner, ushort revision) { if (owner == Target && revision == Revision + 1) { Phase = HandoffPhase.Complete; return true; } if ((owner == Source && revision == Revision) || (owner == Target && revision == Revision) || (owner == Source && revision == Revision + 1)) { return false; } Phase = HandoffPhase.Obsolete; return true; } internal bool MatchesSource(long owner, ushort revision) { if (owner == Source) { return revision == Revision; } return false; } internal void Prepared(bool ready, float now) { if (Phase == HandoffPhase.Prepare) { if (ready) { Phase = HandoffPhase.Commit; } Next = now + (ready ? 0f : 0.5f); } } internal void Block(float now) { if (Phase == HandoffPhase.Commit) { Phase = HandoffPhase.Blocked; Next = now + 5f; } } internal void Wake(float now) { if (Phase == HandoffPhase.Blocked) { Phase = HandoffPhase.Prepare; Next = now; } } } internal static class IntervalConfig { internal const string Key = "MovementRotationCheckIntervalSeconds"; internal const float Default = 0.1f; } internal sealed class LocalAgent { private sealed class Ticket { internal long Token; internal long Source; internal long Target; internal ZDOID Id; internal ZDOID MobTarget; internal ushort Revision; internal Kinds Kind; internal float Until; internal bool Adopted; } private sealed class Lease { internal ushort Revision; internal float Until; } private sealed class Busy { internal Ticket Ticket; internal float Check; } private sealed class Forward { internal long Owner; internal ushort Revision; internal float Until; internal Kinds Kind; } private sealed class Replay { internal int Result; internal float Until; } private sealed class Published { internal long Owner; internal ushort Revision; } private sealed class ViewState { internal Kinds Kind; internal Character Actor; internal MonsterAI Ai; internal Tameable Animal; internal Vagon Cart; internal Container Container; internal int Inventories; } private readonly Dictionary<ZDOID, Ticket> prepared = new Dictionary<ZDOID, Ticket>(); private readonly Dictionary<ZDOID, Busy> blocked = new Dictionary<ZDOID, Busy>(); private readonly Dictionary<ZDOID, Lease> refill = new Dictionary<ZDOID, Lease>(); private readonly Dictionary<ZDOID, float> opening = new Dictionary<ZDOID, float>(); private readonly Dictionary<ZDOID, Forward> forward = new Dictionary<ZDOID, Forward>(); private readonly Dictionary<long, Replay> replay = new Dictionary<long, Replay>(); private readonly List<ZDOID> ids = new List<ZDOID>(); private readonly List<long> tokens = new List<long>(); private readonly Dictionary<Vector2s, bool> areaReady = new Dictionary<Vector2s, bool>(); private readonly ConditionalWeakTable<ZNetView, ViewState> views = new ConditionalWeakTable<ZNetView, ViewState>(); private static readonly ConditionalWeakTable<ZNetView, Published> published = new ConditionalWeakTable<ZNetView, Published>(); private readonly OwnershipCadence cadence = new OwnershipCadence(0.1f); private int readinessFrame = -1; private float nextSweep; private bool mobWatch; internal bool MobsEnabled = true; private static readonly Action<MonsterAI, float> sensed = FloatSetter(); private static readonly Func<BaseAI, ZNetView> aiView = FieldReader.Create<BaseAI, ZNetView>(AccessTools.Field(typeof(BaseAI), "m_nview")); private static readonly Func<MonsterAI, Character> aiTarget = FieldReader.Create<MonsterAI, Character>(AccessTools.Field(typeof(MonsterAI), "m_targetCreature")); private static readonly Action<BaseAI, bool> alerted = AccessTools.MethodDelegate<Action<BaseAI, bool>>(AccessTools.Method(typeof(BaseAI), "SetAlerted", (Type[])null, (Type[])null), (object)null, true); private static readonly Func<InventoryGui, Container> currentInventory = FieldReader.Create<InventoryGui, Container>(AccessTools.Field(typeof(InventoryGui), "m_currentContainer")); internal bool HasWork => prepared.Count + blocked.Count + refill.Count + opening.Count + forward.Count + replay.Count != 0; private static Action<MonsterAI, float> FloatSetter() { FieldInfo fieldInfo = AccessTools.Field(typeof(MonsterAI), "m_timeSinceSensedTargetCreature"); if (fieldInfo == null || fieldInfo.FieldType != typeof(float)) { throw new NotSupportedException("Native monster target timer unavailable."); } DynamicMethod dynamicMethod = new DynamicMethod("TMLS_ResetSensedTarget", typeof(void), new Type[2] { typeof(MonsterAI), typeof(float) }, typeof(LocalAgent).Module, skipVisibility: true); ILGenerator iLGenerator = dynamicMethod.GetILGenerator(); iLGenerator.Emit(OpCodes.Ldarg_0); iLGenerator.Emit(OpCodes.Ldarg_1); iLGenerator.Emit(OpCodes.Stfld, fieldInfo); iLGenerator.Emit(OpCodes.Ret); return (Action<MonsterAI, float>)dynamicMethod.CreateDelegate(typeof(Action<MonsterAI, float>)); } private ViewState State(ZNetView view) { return views.GetValue(view, delegate(ZNetView value) { Container[] componentsInChildren = ((Component)value).GetComponentsInChildren<Container>(true); return new ViewState { Kind = ObjectCatalog.Classify(((Component)value).gameObject), Actor = ((Component)value).GetComponent<Character>(), Ai = ((Component)value).GetComponent<MonsterAI>(), Animal = ((Component)value).GetComponent<Tameable>(), Cart = ((Component)value).GetComponent<Vagon>(), Container = ((componentsInChildren.Length == 1) ? componentsInChildren[0] : null), Inventories = componentsInChildren.Length }; }); } private static Ticket Read(ZPackage p) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) Ticket ticket = new Ticket(); ticket.Token = p.ReadLong(); ticket.Id = p.ReadZDOID(); ticket.Source = p.ReadLong(); ticket.Target = p.ReadLong(); ticket.Revision = p.ReadUShort(); ticket.Kind = (Kinds)p.ReadInt(); ticket.MobTarget = p.ReadZDOID(); ticket.Until = Time.unscaledTime + 8f; return ticket; } internal void Receive(Message message, ZPackage packet) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_03cb: Unknown result type (might be due to invalid IL or missing references) //IL_040b: Unknown result type (might be due to invalid IL or missing references) //IL_042b: Unknown result type (might be due to invalid IL or missing references) //IL_0482: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_04e3: Unknown result type (might be due to invalid IL or missing references) //IL_0363: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) if (ZDOMan.instance == null || !Object.op_Implicit((Object)(object)ZNetScene.instance)) { return; } if (message == Message.Cancel) { long num = packet.ReadLong(); ZDOID key = packet.ReadZDOID(); if (prepared.TryGetValue(key, out var value) && value.Token == num) { value.Until = Math.Min(value.Until, Time.unscaledTime + 2f); } if (!replay.TryGetValue(num, out var value2) || value2.Result != 1) { replay[num] = new Replay { Result = 0, Until = Time.unscaledTime + 15f }; } if (blocked.TryGetValue(key, out var value3) && value3.Ticket.Token == num) { blocked.Remove(key); } return; } Ticket ticket = Read(packet); long sessionID = ZDOMan.GetSessionID(); if (ticket.Token <= 0 || ticket.Source == 0 || ticket.Target == 0 || ticket.Source == ticket.Target || ticket.Revision == ushort.MaxValue) { return; } switch (message) { case Message.ActivationCheck: { if (ticket.Target != sessionID) { break; } ZDO zDO3 = ZDOMan.instance.GetZDO(ticket.Id); ZNetView val3 = ((zDO3 != null) ? ZNetScene.instance.FindInstance(zDO3) : null); if (zDO3 == null || !Object.op_Implicit((Object)(object)val3) || !val3.IsValid() || !((Component)val3).gameObject.activeInHierarchy || !AreaReady(zDO3.GetPosition())) { break; } if (zDO3.GetOwner() == sessionID && zDO3.OwnerRevision == ticket.Revision + 1) { if (ticket.Kind == Kinds.None && zDO3.GetLong(MobPolicy.WriterKey, 0L) != sessionID) { Adopt(zDO3, ticket.MobTarget); } PublishOwned(zDO3); Activation(ticket, zDO3.OwnerRevision); } else if (zDO3.GetOwner() == ticket.Source && zDO3.OwnerRevision == ticket.Revision && prepared.Count < 4096) { prepared[ticket.Id] = ticket; } break; } case Message.Prepare: if (ticket.Target == sessionID) { ZDO zDO2 = ZDOMan.instance.GetZDO(ticket.Id); ZNetView val = ((zDO2 != null) ? ZNetScene.instance.FindInstance(zDO2) : null); bool ready = zDO2 != null && zDO2.IsValid() && zDO2.GetOwner() == ticket.Source && zDO2.OwnerRevision == ticket.Revision && Object.op_Implicit((Object)(object)val) && val.IsValid() && ((Component)val).gameObject.activeInHierarchy && AreaReady(zDO2.GetPosition()); if (ready && ticket.Kind == Kinds.None) { Character val2 = TargetInstance(ticket.MobTarget); ready = MobsEnabled && Object.op_Implicit((Object)(object)State(val).Ai) && MobPolicy.Eligible(zDO2) && val2 is Player && !val2.IsDead(); } if (ready && prepared.Count < 4096) { prepared[ticket.Id] = ticket; } else { ready = false; } Reply(Message.Prepared, ticket, delegate(ZPackage p) { p.Write(ready); }); } break; case Message.Commit: { if (ticket.Source != sessionID) { break; } if (replay.TryGetValue(ticket.Token, out var value4)) { ReplyResult(ticket, value4.Result); break; } ZDO zDO = ZDOMan.instance.GetZDO(ticket.Id); ZNetView view = ((zDO != null) ? ZNetScene.instance.FindInstance(zDO) : null); float until; int num2 = Check(ticket, zDO, view, out until); switch (num2) { case 1: ZDOMan.instance.ForceSendZDO(ticket.Id); zDO.SetOwner(ticket.Target); forward[ticket.Id] = new Forward { Owner = ticket.Target, Revision = zDO.OwnerRevision, Until = Time.unscaledTime + 5f, Kind = ticket.Kind }; blocked.Remove(ticket.Id); replay[ticket.Token] = new Replay { Result = 1, Until = Time.unscaledTime + 10f }; break; case 2: if (blocked.Count < 4096) { blocked[ticket.Id] = new Busy { Ticket = ticket, Check = until }; } break; } ReplyResult(ticket, num2); break; } } } private int Check(Ticket ticket, ZDO data, ZNetView view, out float until) { //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Unknown result type (might be due to invalid IL or missing references) until = float.PositiveInfinity; if (data == null || !data.IsValid() || data.GetOwner() != ticket.Source || data.OwnerRevision != ticket.Revision || !Object.op_Implicit((Object)(object)view) || !view.IsValid() || !object.ReferenceEquals(view.GetZDO(), data) || !((Component)view).gameObject.activeInHierarchy || data.OwnerRevision == ushort.MaxValue) { return 0; } ViewState viewState = State(view); if (ticket.Kind == Kinds.None) { Character val = (Object.op_Implicit((Object)(object)viewState.Ai) ? TargetEvents.Read(viewState.Ai) : null); if (!MobsEnabled || !Object.op_Implicit((Object)(object)viewState.Ai) || !Object.op_Implicit((Object)(object)viewState.Actor) || !MobPolicy.Eligible(data) || viewState.Actor.IsDead() || viewState.Actor.IsBoss() || !(val is Player) || val.IsDead() || val.GetZDOID() != ticket.MobTarget) { return 0; } } else if (viewState.Kind != ticket.Kind || viewState.Kind == Kinds.Incinerators || viewState.Inventories > 1) { return 3; } if (Object.op_Implicit((Object)(object)viewState.Actor) && (viewState.Actor.InAttack() || viewState.Actor.IsStaggering() || (ticket.Kind == Kinds.TamedAnimals && Object.op_Implicit((Object)(object)viewState.Ai) && Object.op_Implicit((Object)(object)aiTarget(viewState.Ai))))) { until = Time.unscaledTime + 0.1f; return 2; } if (data.GetLong(ZDOVars.s_user, 0L) != 0 || (Object.op_Implicit((Object)(object)viewState.Cart) && viewState.Cart.InUse())) { return 2; } if (Object.op_Implicit((Object)(object)viewState.Animal) && ObjectCatalog.IsSummoned(viewState.Animal)) { return 3; } if (Object.op_Implicit((Object)(object)viewState.Animal) && (viewState.Animal.HaveRider() || (viewState.Animal.m_commandable && !string.IsNullOrEmpty(data.GetString(ZDOVars.s_follow, ""))))) { until = Time.unscaledTime + 0.1f; return 2; } if (Object.op_Implicit((Object)(object)viewState.Actor) && (FriendlyCreatures.IsFriendly(viewState.Actor) || viewState.Actor.IsDead())) { return 3; } if (Object.op_Implicit((Object)(object)viewState.Actor) && (viewState.Actor.IsAttached() || viewState.Actor.IsRiding())) { until = Time.unscaledTime + 0.1f; return 2; } if ((opening.TryGetValue(ticket.Id, out var value) && value > Time.unscaledTime) || (Object.op_Implicit((Object)(object)viewState.Container) && (viewState.Container.IsInUse() || data.GetInt(ZDOVars.s_inUse, 0) != 0 || (Object.op_Implicit((Object)(object)InventoryGui.instance) && (Object)(object)currentInventory(InventoryGui.instance) == (Object)(object)viewState.Container)))) { return 2; } if (refill.TryGetValue(ticket.Id, out var value2) && value2.Revision == data.OwnerRevision && value2.Until > Time.unscaledTime) { until = value2.Until; return 2; } return 1; } private bool AreaReady(Vector3 point) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) if (readinessFrame != Time.frameCount) { readinessFrame = Time.frameCount; areaReady.Clear(); } Vector2s zone = ZoneSystem.GetZone(point); if (!areaReady.TryGetValue(zone, out var value)) { value = ZNetScene.instance.IsAreaReady(point); areaReady.Add(zone, value); } return value; } private static Character TargetInstance(ZDOID id) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) GameObject val = ((!((ZDOID)(ref id)).IsNone() && Object.op_Implicit((Object)(object)ZNetScene.instance)) ? ZNetScene.instance.FindInstance(id) : null); if (!Object.op_Implicit((Object)(object)val)) { return null; } return val.GetComponent<Character>(); } internal void Observe(ZDO data, bool final = true) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) if (data == null || !data.IsValid() || ZDOMan.instance == null) { return; } if (prepared.TryGetValue(data.m_uid, out var value) && data.GetOwner() == ZDOMan.GetSessionID() && (data.OwnerRevision == value.Revision || data.OwnerRevision == value.Revision + 1)) { if (!value.Adopted && value.Kind == Kinds.None) { Adopt(data, value.MobTarget); value.Adopted = true; } if (final && data.OwnerRevision == value.Revision + 1) { prepared.Remove(data.m_uid); PublishOwned(data); Activation(value, data.OwnerRevision); } } if (blocked.ContainsKey(data.m_uid)) { UseEnded(data.m_uid); } if (!final || data.GetOwner() != ZDOMan.GetSessionID() || !MobsEnabled || !mobWatch || !MobPolicy.HostilePrefab(data.GetPrefab())) { return; } ZNetView val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(data) : null); MonsterAI val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<MonsterAI>() : null); if (Object.op_Implicit((Object)(object)val2) && Watched((BaseAI)(object)val2)) { if (data.GetLong(MobPolicy.WriterKey, 0L) != ZDOMan.GetSessionID()) { Adopt(data, data.GetZDOID(MobPolicy.TargetKey)); } PublishOwned(data); } } private void Adopt(ZDO data, ZDOID targetId) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) ZNetView val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(data) : null); MonsterAI val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<MonsterAI>() : null); Character val3 = TargetInstance(targetId); Character val4 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<Character>() : null); if (MobsEnabled && Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val4) && !val4.IsTamed() && Object.op_Implicit((Object)(object)val3) && !val3.IsDead() && BaseAI.IsEnemy(val4, val3)) { Vector3 val5 = ((Component)val3).transform.position - ((Component)val2).transform.position; if (!(((Vector3)(ref val5)).sqrMagnitude > 3600f)) { TargetEvents.Adopt(val2, val3); sensed(val2, 0f); alerted((BaseAI)(object)val2, arg2: true); } } } internal bool Watched(BaseAI ai) { if (!MobsEnabled || !mobWatch || !(ai is MonsterAI)) { return false; } ZNetView val = aiView(ai); if (!Object.op_Implicit((Object)(object)val) || !val.IsValid() || !val.IsOwner()) { return false; } Character component = ((Component)ai).GetComponent<Character>(); if (Object.op_Implicit((Object)(object)component) && !component.IsBoss() && !component.IsDead() && !FriendlyCreatures.IsFriendly(component)) { return !component.IsTamed(); } return false; } internal void TargetChanged(BaseAI ai) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) if (Watched(ai)) { ZDO data = aiView(ai).GetZDO(); Character val = TargetEvents.Read((MonsterAI)ai); ZDOID id = ((val is Player && !val.IsDead()) ? val.GetZDOID() : ZDOID.None); long owner = data.GetOwner(); ushort revision = data.OwnerRevision; if (data.GetZDOID(MobPolicy.TargetKey) != id || data.GetLong(MobPolicy.WriterKey, 0L) != owner || data.GetLong(MobPolicy.RevisionKey, -1L) != revision) { data.Set(MobPolicy.TargetKey, id); data.Set(MobPolicy.WriterKey, owner); data.Set(MobPolicy.RevisionKey, (long)revision); } Published orCreateValue = published.GetOrCreateValue(aiView(ai)); orCreateValue.Owner = owner; orCreateValue.Revision = revision; Plugin.Network.ToServer(Message.Target, delegate(ZPackage p) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) p.Write(data.m_uid); p.Write(revision); p.Write(id); }); } } private void PublishOwned(ZDO data) { if (data.GetOwner() != ZDOMan.GetSessionID() || !Object.op_Implicit((Object)(object)ZNetScene.instance)) { return; } ZNetView val = ZNetScene.instance.FindInstance(data); MonsterAI val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<MonsterAI>() : null); if (Object.op_Implicit((Object)(object)val2) && Watched((BaseAI)(object)val2)) { Published orCreateValue = published.GetOrCreateValue(val); if (orCreateValue.Owner != data.GetOwner() || orCreateValue.Revision != data.OwnerRevision) { TargetChanged((BaseAI)(object)val2); } } } internal void SetMobWatch(bool value) { value &= MobsEnabled; if (mobWatch == value) { return; } mobWatch = value; if (!value) { return; } foreach (BaseAI allInstance in BaseAI.GetAllInstances()) { if (Watched(allInstance)) { TargetChanged(allInstance); } } } internal void Instance(ZNetView view) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)view) || !view.IsValid()) { return; } ZDO zDO = view.GetZDO(); Observe(zDO); if (MobsEnabled && MobPolicy.HostilePrefab(zDO.GetPrefab())) { if (zDO.IsOwner() && zDO.GetLong(MobPolicy.WriterKey, 0L) != ZDOMan.GetSessionID()) { Adopt(zDO, zDO.GetZDOID(MobPolicy.TargetKey)); } PublishOwned(zDO); } } internal void Outgoing(ZDOID id, int method) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (!ZNet.IsSinglePlayer && (method == StringExtensionMethods.GetStableHashCode("RPC_RequestOpen") || method == StringExtensionMethods.GetStableHashCode("RPC_RequestStack") || method == StringExtensionMethods.GetStableHashCode("RPC_RequestTakeAll"))) { opening[id] = Time.unscaledTime + 5f; } } internal void Response(Container container) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)container)) { ZNetView val = (Object.op_Implicit((Object)(object)container.m_rootObjectOverride) ? ((Component)container.m_rootObjectOverride).GetComponent<ZNetView>() : ((Component)container).GetComponent<ZNetView>()); if (Object.op_Implicit((Object)(object)val) && val.IsValid()) { opening.Remove(val.GetZDO().m_uid); UseEnded(val.GetZDO().m_uid); } } } internal void Refilling(ZDO data, int method) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) if (!ZNet.IsSinglePlayer && ObjectCatalog.IsRefillMethod(method) && data != null && data.IsOwner() && Object.op_Implicit((Object)(object)ZNetScene.instance)) { ZNetView val = ZNetScene.instance.FindInstance(data); if (Object.op_Implicit((Object)(object)val) && ObjectCatalog.RefillRequest(State(val).Kind, method)) { refill[data.m_uid] = new Lease { Revision = data.OwnerRevision, Until = Time.unscaledTime + 10f }; } } } internal bool MayForward(ZDO data, int method) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) if (data == null || !forward.TryGetValue(data.m_uid, out var value) || Time.unscaledTime > value.Until || data.GetOwner() != value.Owner || data.OwnerRevision != value.Revision) { return false; } if (value.Kind != Kinds.None) { return ObjectCatalog.OwnerRequest(value.Kind, method); } return method == StringExtensionMethods.GetStableHashCode("RPC_Damage"); } internal bool Prepared(ZDOID id) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return prepared.ContainsKey(id); } internal bool NeedsFinalImport(ZDO data) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (prepared.ContainsKey(data.m_uid)) { return true; } if (!MobsEnabled || !mobWatch || data.GetOwner() != ZDOMan.GetSessionID() || !MobPolicy.HostilePrefab(data.GetPrefab())) { return false; } ZNetView val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(data) : null); if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)((Component)val).GetComponent<MonsterAI>())) { return false; } Published orCreateValue = published.GetOrCreateValue(val); if (orCreateValue.Owner == data.GetOwner() && orCreateValue.Revision == data.OwnerRevision) { return data.GetLong(MobPolicy.WriterKey, 0L) != data.GetOwner(); } return true; } internal bool ForwardDestination(ZDOID id, ZDO data, int method, out long owner, out ushort revision) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) owner = 0L; revision = 0; if (!forward.TryGetValue(id, out var value) || Time.unscaledTime > value.Until || (data != null && (data.GetOwner() != value.Owner || data.OwnerRevision != value.Revision))) { return false; } if (!((value.Kind == Kinds.None) ? (method == StringExtensionMethods.GetStableHashCode("RPC_Damage")) : ObjectCatalog.OwnerRequest(value.Kind, method))) { return false; } owner = value.Owner; revision = value.Revision; return true; } internal void UseEnded(ZDOID id) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) if (blocked.TryGetValue(id, out var value)) { ZDO val = ((ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(id) : null); ZNetView view = ((val != null && Object.op_Implicit((Object)(object)ZNetScene.instance)) ? ZNetScene.instance.FindInstance(val) : null); float until; int num = Check(value.Ticket, val, view, out until); if (num == 2) { value.Check = until; return; } blocked.Remove(id); Reply(Message.Wake, value.Ticket, delegate { }); } } internal void Tick(float now) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) if (!cadence.Due(now)) { return; } ids.Clear(); foreach (KeyValuePair<ZDOID, Busy> item in blocked) { if (now >= item.Value.Check) { ids.Add(item.Key); } } foreach (ZDOID id in ids) { UseEnded(id); } ids.Clear(); if (now < nextSweep) { return; } nextSweep = now + 2f; Sweep(prepared, (Ticket t) => now >= t.Until); Sweep(refill, (Lease t) => now >= t.Until); Sweep(opening, (float t) => now >= t); Sweep(forward, (Forward t) => now >= t.Until); tokens.Clear(); foreach (KeyValuePair<long, Replay> item2 in replay) { if (now >= item2.Value.Until) { tokens.Add(item2.Key); } } foreach (long token in tokens) { replay.Remove(token); } tokens.Clear(); ids.Clear(); foreach (KeyValuePair<ZDOID, Busy> item3 in blocked) { if (ZDOMan.instance == null || ZDOMan.instance.GetZDO(item3.Key) == null || now - item3.Value.Ticket.Until > 10f) { ids.Add(item3.Key); } } foreach (ZDOID id2 in ids) { blocked.Remove(id2); } ids.Clear(); } private void Sweep<T>(Dictionary<ZDOID, T> source, Predicate<T> expired) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) ids.Clear(); foreach (KeyValuePair<ZDOID, T> item in source) { if (expired(item.Value)) { ids.Add(item.Key); } } foreach (ZDOID id in ids) { source.Remove(id); } ids.Clear(); } private static void Reply(Message message, Ticket ticket, Action<ZPackage> write) { Plugin.Network.ToServer(message, delegate(ZPackage p) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) p.Write(ticket.Token); p.Write(ticket.Id); write(p); }); } private static void ReplyResult(Ticket ticket, int result) { Reply(Message.Result, ticket, delegate(ZPackage p) { p.Write(result); }); } private static void Activation(Ticket ticket, ushort revision) { Reply(Message.Activated, ticket, delegate(ZPackage p) { p.Write(revision); }); } internal void Reset() { prepared.Clear(); blocked.Clear(); refill.Clear(); opening.Clear(); forward.Clear(); replay.Clear(); areaReady.Clear(); mobWatch = false; } } internal sealed class MobPolicy { private sealed class Record { internal ZDO Data; internal ZDOID Target; internal long Writer; internal ushort Revision; internal float Next; } internal static readonly KeyValuePair<int, int> TargetKey = ZDO.GetHashZDOID("botanic.toomanylags.standalone.target"); internal static readonly int WriterKey = StringExtensionMethods.GetStableHashCode("botanic.toomanylags.standalone.targetWriter"); internal static readonly int RevisionKey = StringExtensionMethods.GetStableHashCode("botanic.toomanylags.standalone.targetRevision"); private readonly Dictionary<ZDOID, Record> records = new Dictionary<ZDOID, Record>(); private readonly List<ZDOID> removing = new List<ZDOID>(); private static readonly Dictionary<int, int> types = new Dictionary<int, int>(); private readonly AuthorityController controller; private float next = float.PositiveInfinity; internal MobPolicy(AuthorityController controller) { this.controller = controller; } private static int PrefabType(int hash) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) if (types.TryGetValue(hash, out var value)) { return value; } GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.GetPrefab(hash) : null); if (!Object.op_Implicit((Object)(object)val)) { return 0; } Character component = val.GetComponent<Character>(); value = ((Object.op_Implicit((Object)(object)component) && !(component is Player) && (int)component.GetFaction() == 0) ? 1 : ((Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)val.GetComponent<MonsterAI>()) && !component.IsBoss()) ? 2 : 0)); types.Add(hash, value); return value; } internal static bool FriendlyPrefab(int hash) { return PrefabType(hash) == 1; } internal static bool HostilePrefab(int hash) { if (hash != 0) { return PrefabType(hash) == 2; } return false; } internal static bool Eligible(ZDO data) { if (data != null && data.IsValid() && data.GetOwner() != 0 && data.GetFloat(ZDOVars.s_health, 1f) > 0f && !data.GetBool(ZDOVars.s_tamed, false) && data.GetLong(ZDOVars.s_user, 0L) == 0) { return PrefabType(data.GetPrefab()) == 2; } return false; } internal void Report(long sender, ZDOID id, ushort revision, ZDOID target) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Options.Mobs || ZDOMan.instance == null) { return; } ZDO zDO = ZDOMan.instance.GetZDO(id); if (!Eligible(zDO) || zDO.GetOwner() != sender || zDO.OwnerRevision != revision || !Plugin.Network.MobCapable(sender)) { return; } if (((ZDOID)(ref target)).IsNone()) { Forget(id); controller.Handoffs.Cancel(id); return; } bool flag = false; foreach (PlayerPeer player in controller.Players) { if (player.Character.m_uid == target) { flag = true; break; } } if (flag && (!records.TryGetValue(id, out var value) || !object.ReferenceEquals(value.Data, zDO) || !(value.Target == target) || value.Writer != sender || value.Revision != revision)) { records[id] = new Record { Data = zDO, Target = target, Writer = sender, Revision = revision, Next = Time.unscaledTime }; next = Time.unscaledTime; } } internal void Changed(ZDO data) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Options.Mobs || data == null || data.GetPrefab() == 0) { return; } if (!Eligible(data)) { if (records.ContainsKey(data.m_uid)) { Forget(data.m_uid); } return; } if (records.TryGetValue(data.m_uid, out var value) && (value.Writer != data.GetOwner() || value.Revision != data.OwnerRevision)) { Wake(data.m_uid); } if (data.GetLong(WriterKey, 0L) == data.GetOwner() && data.GetLong(RevisionKey, -1L) == data.OwnerRevision) { Report(data.GetOwner(), data.m_uid, data.OwnerRevision, data.GetZDOID(TargetKey)); } } internal long Desired(ZDO data, ZDOID target) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.Options.Mobs || !Eligible(data) || ((ZDOID)(ref target)).IsNone() || !Plugin.Network.MobCapable(data.GetOwner())) { return 0L; } PlayerPeer playerPeer = default(PlayerPeer); int num = 0; foreach (PlayerPeer player in controller.Players) { if (player.Character.m_uid == target) { playerPeer = player; } if (Plugin.Network.MobCapable(player.Uid) && controller.Available(player.Uid, data.GetPosition())) { num++; } } if (num < 2 || playerPeer.Uid == 0 || !Plugin.Network.MobCapable(playerPeer.Uid) || !controller.Available(playerPeer.Uid, data.GetPosition())) { return 0L; } float num2 = Plugin.Network.Latency(playerPeer.Uid); if (float.IsInfinity(num2) || num2 <= (float)Plugin