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 Server Side v0.13.2
TooManyLagsServerSide.dll
Decompiled a week 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.Globalization; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using Steamworks; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: AssemblyVersion("0.13.2.0")] namespace TooManyLagsDedicatedAddon; internal enum GridCoveragePattern { Square3x3, Rounded5x5, Rounded7x7 } internal enum FieldTier : byte { Outer, Inner, Forward, ImmediateForward } internal struct Cell : IEquatable<Cell> { internal int X; internal int Z; internal Cell(int x, int z) { X = x; Z = z; } internal static bool At(float x, float z, out Cell cell) { return At(x, z, 8f, out cell); } internal static bool At(float x, float z, float size, out Cell cell) { cell = default(Cell); int num = Subdivisions(size); if (num == 0) { return false; } if (float.IsNaN(x) || float.IsNaN(z) || float.IsInfinity(x) || float.IsInfinity(z)) { return false; } double num2 = (double)num / 64.0; double num3 = Math.Floor(((double)x + 32.0) * num2); double num4 = Math.Floor(((double)z + 32.0) * num2); int num5 = 256 * num; if (num3 < (double)(-num5) || num3 >= (double)num5 || num4 < (double)(-num5) || num4 >= (double)num5) { return false; } cell = new Cell((int)num3, (int)num4); return true; } internal static int Subdivisions(float size) { if (size != 4f) { if (size != 6.4f) { if (size != 8f) { if (size != 16f) { return 0; } return 4; } return 8; } return 10; } return 16; } internal static double Edge(int coordinate, int subdivisions) { return (double)coordinate * 64.0 / (double)subdivisions - 32.0; } internal static Cell Zone(Cell fine, float size) { if (size == 6.4f) { return new Cell(Floor10(fine.X), Floor10(fine.Z)); } int num = ((size == 4f) ? 4 : ((size == 16f) ? 2 : 3)); return new Cell(fine.X >> num, fine.Z >> num); } private static int Floor10(int value) { if (value < 0) { return (value + 1) / 10 - 1; } return value / 10; } public bool Equals(Cell other) { if (X == other.X) { return Z == other.Z; } return false; } public override bool Equals(object other) { if (other is Cell) { return Equals((Cell)other); } return false; } public override int GetHashCode() { return (X * 397) ^ Z; } } internal struct GridPeer { internal long Uid; internal Cell Cell; internal float Yaw; internal int Coverage; internal bool FastForward; internal bool Large; internal GridPeer(long uid, Cell cell, float yaw = 0f, int coverage = -1, bool fastForward = false, bool large = false) { Uid = uid; Cell = cell; Yaw = yaw; Coverage = coverage; FastForward = fastForward; Large = large; } internal static GridPeer Covered(long uid, Cell cell, int coverage) { return new GridPeer(uid, cell, float.NaN, coverage); } } internal sealed class AuthorityGrid { private sealed class Policy { internal long Candidate; internal long Owner; internal long Uids; internal int Count; internal int FastCount; internal int ForwardCount; internal int ImmediateCount; internal bool Shared; internal float Since; internal float Wait; internal float Deadline; internal float OverlapUntil; internal bool Deferred; internal Request First; internal List<Request> Others; } private struct Request { internal long Uid; internal float Since; internal FieldTier Tier; internal Request(long uid, float since, FieldTier tier) { Uid = uid; Since = since; Tier = tier; } } private sealed class PeerState { internal Cell Cell; internal float Yaw; internal int Heading; internal bool Refreshed; } private struct Offset { internal Cell Cell; internal FieldTier Tier; internal Offset(int x, int z, FieldTier tier) { Cell = new Cell(x, z); Tier = tier; } } internal const int CoverageCount = 56; private const int LargeStart = 112; private readonly Dictionary<Cell, Policy> policies = new Dictionary<Cell, Policy>(); private readonly Stack<Policy> reusable = new Stack<Policy>(); private readonly Dictionary<long, PeerState> previous = new Dictionary<long, PeerState>(); private readonly Dictionary<Cell, long> remembered = new Dictionary<Cell, long>(); private readonly HashSet<long> seen = new HashSet<long>(); private readonly List<long> departed = new List<long>(); private readonly HashSet<Cell> dirty = new HashSet<Cell>(); private readonly HashSet<Cell> pending = new HashSet<Cell>(); private readonly List<Cell> due = new List<Cell>(); internal readonly List<Cell> Changes = new List<Cell>(); private readonly long server; private readonly Offset[][] footprints = new Offset[128][]; private readonly Dictionary<Cell, FieldTier>[] maskTypes = new Dictionary<Cell, FieldTier>[128]; internal readonly HashSet<Cell> Accelerations = new HashSet<Cell>(); internal Action<Cell> Renewed; private readonly bool directional; private readonly bool discardVacant; private float innerDelay; private float outerDelay; private float forwardDelay; private float overlapCooldown; internal float Delay; private float lastTime; private float nextDue = float.PositiveInfinity; private int deferredCells; private int coolingCells; private readonly Func<long, float> latency; private readonly float margin; private bool ranksDirty; internal int Rebuilds; internal int NeighborReads; internal int PlayerUpdates; internal int TimerVisits; internal int PolicyAllocations; internal int RememberedCells => remembered.Count; internal IEnumerable<Cell> CoveredCells { get { foreach (KeyValuePair<Cell, Policy> item in policies) { KeyValuePair<Cell, Policy> keyValuePair = item; if (keyValuePair.Value.Count != 0) { KeyValuePair<Cell, Policy> keyValuePair2 = item; yield return keyValuePair2.Key; } } } } internal bool Directional => directional; internal bool HasPeers => previous.Count != 0; internal int CachedCells => policies.Count; internal int ReusablePolicies => reusable.Count; internal int DeferredCells => deferredCells; internal int CoolingCells => coolingCells; internal AuthorityGrid(long server, float delay, GridCoveragePattern pattern = GridCoveragePattern.Square3x3, bool directional = false, float innerDelay = 0f, float outerDelay = 3f, bool discardVacant = false, bool forward = false, float forwardDelay = 0.2f, Func<long, float> latency = null, float margin = 30f, float overlapCooldown = 0f, bool fullRear = false, bool includeOuter = true) { this.latency = latency; this.margin = margin; this.server = server; Delay = delay; this.directional = directional; this.discardVacant = discardVacant; this.innerDelay = SafeDelay(innerDelay, 0f); this.outerDelay = SafeDelay(outerDelay, 3f); this.forwardDelay = SafeDelay(forwardDelay, 0.2f); this.overlapCooldown = SafeDelay(overlapCooldown, 1f); int num = pattern switch { GridCoveragePattern.Rounded5x5 => 2, GridCoveragePattern.Rounded7x7 => 3, _ => 1, }; forward = directional && forward && num > 1; int[] array = new int[8] { 0, 1, 1, 1, 0, -1, -1, -1 }; int[] array2 = new int[8] { 1, 1, 0, -1, -1, -1, 0, 1 }; for (int i = 0; i < 8; i++) { List<Offset> list = new List<Offset>(37); for (int j = -num; j <= num; j++) { for (int k = -num; k <= num; k++) { int num2 = j * array[i] + k * array2[i]; int value = -j * array2[i] + k * array[i]; bool flag = array[i] != 0 && array2[i] != 0; bool flag2 = forward && ((!flag) ? (num2 == 2 && Math.Abs(value) <= 1) : ((j == 2 * array[i] && (k == array2[i] || k == 2 * array2[i])) || (j == array[i] && k == 2 * array2[i]))); if ((num == 1 || j * j + k * k <= num * (num + 1) || flag2) && (!directional || fullRear || num2 >= 0 || (num2 >= -1 && Math.Abs(value) > 1))) { bool flag3 = false; if (directional) { flag3 = (fullRear ? (Math.Abs(j) <= 1 && Math.Abs(k) <= 1) : ((array[i] != 0 && array2[i] != 0) ? ((j == 0 && k == 0) || (num2 == 0 && Math.Abs(j) == 1 && Math.Abs(k) == 1) || (j == array[i] && k == array2[i]) || (j == array[i] && k == 0) || (j == 0 && k == array2[i])) : (num2 >= 0 && num2 <= 1 && Math.Abs(value) <= 1))); } if (!directional || includeOuter || flag3 || flag2) { list.Add(new Offset(j, k, flag3 ? FieldTier.Inner : (flag2 ? FieldTier.Forward : FieldTier.Outer))); } } } } footprints[i] = list.ToArray(); Dictionary<Cell, FieldTier> dictionary = new Dictionary<Cell, FieldTier>(list.Count); foreach (Offset item in list) { dictionary.Add(item.Cell, item.Tier); } maskTypes[i] = dictionary; } for (int l = 2; l <= 3; l++) { for (int m = 0; m < 8; m++) { int num3 = (l - 1) * 8 + m; int num4 = (l - 2) * 8 + m; if (!directional) { footprints[num3] = footprints[m]; maskTypes[num3] = maskTypes[m]; continue; } Dictionary<Cell, FieldTier> dictionary2 = new Dictionary<Cell, FieldTier>(maskTypes[num4]); Offset[] array3 = footprints[(m + l - 1) & 7]; for (int n = 0; n < array3.Length; n++) { Offset offset = array3[n]; if (!dictionary2.TryGetValue(offset.Cell, out var value2) || offset.Tier == FieldTier.Inner || (value2 == FieldTier.Outer && offset.Tier == FieldTier.Forward)) { dictionary2[offset.Cell] = offset.Tier; } } List<Offset> list2 = new List<Offset>(dictionary2.Count); foreach (KeyValuePair<Cell, FieldTier> item2 in dictionary2) { list2.Add(new Offset(item2.Key.X, item2.Key.Z, item2.Value)); } footprints[num3] = list2.ToArray(); maskTypes[num3] = dictionary2; } } for (int num5 = 0; num5 < 16; num5++) { for (int num6 = 0; num6 < 2; num6++) { int num7 = 24 + num6 * 16 + num5; if (!directional) { footprints[num7] = footprints[num5]; maskTypes[num7] = maskTypes[num5]; continue; } int num8 = num5 & 7; int num9 = ((num6 == 0) ? ((num8 + ((num5 < 8) ? 1 : 2)) & 7) : ((num8 + 7) & 7)); Dictionary<Cell, FieldTier> dictionary3 = new Dictionary<Cell, FieldTier>(maskTypes[num5]); Offset[] array4 = footprints[num9]; for (int num10 = 0; num10 < array4.Length; num10++) { Offset offset2 = array4[num10]; if (!dictionary3.ContainsKey(offset2.Cell)) { dictionary3.Add(offset2.Cell, FieldTier.Inner); } } List<Offset> list3 = new List<Offset>(dictionary3.Count); foreach (KeyValuePair<Cell, FieldTier> item3 in dictionary3) { list3.Add(new Offset(item3.Key.X, item3.Key.Z, item3.Value)); } footprints[num7] = list3.ToArray(); maskTypes[num7] = dictionary3; } } for (int num11 = 0; num11 < 56; num11++) { bool flag4 = false; Offset[] array5 = footprints[num11]; for (int num12 = 0; num12 < array5.Length; num12++) { Offset offset3 = array5[num12]; if (offset3.Tier == FieldTier.Forward) { flag4 = true; break; } } if (!flag4) { footprints[num11 + 56] = footprints[num11]; maskTypes[num11 + 56] = maskTypes[num11]; continue; } Offset[] array6 = new Offset[footprints[num11].Length]; Dictionary<Cell, FieldTier> dictionary4 = new Dictionary<Cell, FieldTier>(array6.Length); for (int num13 = 0; num13 < array6.Length; num13++) { Offset offset4 = footprints[num11][num13]; if (offset4.Tier == FieldTier.Forward) { offset4.Tier = FieldTier.ImmediateForward; } array6[num13] = offset4; dictionary4.Add(offset4.Cell, offset4.Tier); } footprints[num11 + 56] = array6; maskTypes[num11 + 56] = dictionary4; } for (int num14 = 0; num14 < 8; num14++) { Dictionary<Cell, FieldTier> dictionary5 = (includeOuter ? new Dictionary<Cell, FieldTier>(maskTypes[num14]) : new Dictionary<Cell, FieldTier>()); for (int num15 = -2; num15 <= 2; num15++) { for (int num16 = -1; num16 <= 2; num16++) { if ((num16 != -1 && num16 != 2) || Math.Abs(num15) != 2) { LargeCell(dictionary5, num15, num16, num14 / 2, FieldTier.Inner); if ((num14 & 1) != 0) { LargeCell(dictionary5, num16, -num15, num14 / 2, FieldTier.Inner); } } } } if ((num14 & 1) != 0) { LargeCell(dictionary5, 2, 2, num14 / 2, FieldTier.Inner); } for (int num17 = -1; num17 <= 1; num17++) { if ((num14 & 1) == 0) { LargeCell(dictionary5, num17, 3, num14 / 2, FieldTier.Forward); } else { LargeCell(dictionary5, (num17 == -1) ? 2 : 3, (num17 == 1) ? 2 : 3, num14 / 2, FieldTier.Forward); } } for (int num18 = 0; num18 < 2; num18++) { int num19 = 112 + num18 * 8 + num14; Dictionary<Cell, FieldTier> dictionary6 = new Dictionary<Cell, FieldTier>(dictionary5.Count); List<Offset> list4 = new List<Offset>(dictionary5.Count); foreach (KeyValuePair<Cell, FieldTier> item4 in dictionary5) { FieldTier fieldTier = ((num18 != 0 && item4.Value == FieldTier.Forward) ? FieldTier.ImmediateForward : item4.Value); dictionary6.Add(item4.Key, fieldTier); list4.Add(new Offset(item4.Key.X, item4.Key.Z, fieldTier)); } footprints[num19] = list4.ToArray(); maskTypes[num19] = dictionary6; } } } private static void LargeCell(Dictionary<Cell, FieldTier> types, int x, int z, int turns, FieldTier tier) { for (int i = 0; i < turns; i++) { int num = x; x = z; z = -num; } Cell key = new Cell(x, z); if (!types.TryGetValue(key, out var value) || value != FieldTier.Inner) { types[key] = tier; } } internal static int HeadingCoverage(float yaw, float overlapDegrees = 30f) { if (float.IsNaN(yaw) || float.IsInfinity(yaw)) { return -1; } yaw %= 360f; if (yaw < 0f) { yaw += 360f; } int num = (int)(yaw / 45f); float num2 = yaw - (float)num * 45f; if (float.IsNaN(overlapDegrees) || float.IsInfinity(overlapDegrees)) { overlapDegrees = 30f; } overlapDegrees = Math.Max(0f, Math.Min(45f, overlapDegrees)); if (overlapDegrees == 0f) { if (!(num2 < 22.5f)) { return (num + 1) & 7; } return num; } float num3 = (45f - overlapDegrees) * 0.5f; float num4 = (45f + overlapDegrees) * 0.5f; if (!(num2 < num3)) { if (!(num2 <= num4)) { return (num + 1) & 7; } return 8 + num; } return num; } internal void ConfigureDelays(float full, float inner, float forward, float outer) { Delay = SafeDelay(full, 2f); innerDelay = SafeDelay(inner, 0f); forwardDelay = SafeDelay(forward, 0.2f); outerDelay = SafeDelay(outer, 3f); } internal void ConfigureOverlapCooldown(float seconds) { overlapCooldown = SafeDelay(seconds, 1f); } private static float SafeDelay(float value, float fallback) { if (!float.IsNaN(value) && !float.IsInfinity(value)) { return Math.Max(0f, Math.Min(30f, value)); } return fallback; } private static float Yaw(float value, float fallback) { if (float.IsNaN(value) || float.IsInfinity(value)) { return fallback; } value %= 360f; if (!(value < 0f)) { return value; } return value + 360f; } internal long Owner(Cell cell) { if (!policies.TryGetValue(cell, out var value)) { return 0L; } return value.Owner; } internal int Participants(Cell cell) { if (!policies.TryGetValue(cell, out var value)) { return 0; } return value.Count; } internal bool Covers(long uid, Cell cell) { if (previous.TryGetValue(uid, out var value)) { return maskTypes[value.Heading].ContainsKey(new Cell(cell.X - value.Cell.X, cell.Z - value.Cell.Z)); } return false; } internal bool CoverageStart(long uid, Cell cell, out float since) { since = 0f; if (!policies.TryGetValue(cell, out var value) || value.Count == 0) { return false; } if (value.First.Uid == uid) { since = value.First.Since; return true; } if (value.Others != null) { foreach (Request other in value.Others) { if (other.Uid == uid) { since = other.Since; return true; } } } return false; } internal long Candidate(Cell cell) { if (!policies.TryGetValue(cell, out var value)) { return 0L; } return value.Candidate; } internal int WorkPriority(Cell cell) { if (!policies.TryGetValue(cell, out var value) || value.Count == 0) { return 3; } if (directional) { if (value.FastCount == 0 && value.ImmediateCount == 0) { if (value.ForwardCount == 0) { return 2; } return 1; } return 0; } return 2; } private float FieldWait(Policy policy) { if (!directional) { return Delay; } if (policy.ImmediateCount != 0) { return 0f; } if (policy.ForwardCount != 0) { float num = forwardDelay; if (policy.FastCount != 0) { num = Math.Min(num, innerDelay); } if (policy.Count > policy.FastCount + policy.ForwardCount) { num = Math.Min(num, outerDelay); } return num; } if (policy.FastCount == 0) { return outerDelay; } if (policy.FastCount != policy.Count) { return Math.Min(innerDelay, outerDelay); } return innerDelay; } internal float Wait(Cell cell) { if (!policies.TryGetValue(cell, out var value)) { if (!directional) { return Delay; } return outerDelay; } return FieldWait(value); } internal float Remaining(Cell cell, float now) { if (!policies.TryGetValue(cell, out var value) || value.Owner == value.Candidate) { return 0f; } return Math.Max(0f, value.Deadline - now); } internal bool IsRemembered(Cell cell, long uid) { if (uid != 0 && uid != server && remembered.TryGetValue(cell, out var value)) { return value == uid; } return false; } internal void Remember(Cell cell, long uid) { if (uid != 0 && uid != server && Owner(cell) == uid && Candidate(cell) == uid) { remembered[cell] = uid; } } internal void Forget(Cell cell) { remembered.Remove(cell); } internal bool FastForward(long uid) { if (previous.TryGetValue(uid, out var value)) { if (value.Heading < 112) { return value.Heading >= 56; } return value.Heading >= 120; } return false; } internal bool LargeCoverage(long uid) { if (previous.TryGetValue(uid, out var value)) { return value.Heading >= 112; } return false; } private void Contribute(long uid, PeerState peer, bool add, float now) { Offset[] array = footprints[peer.Heading]; for (int i = 0; i < array.Length; i++) { Offset offset = array[i]; Contribution(uid, new Cell(peer.Cell.X + offset.Cell.X, peer.Cell.Z + offset.Cell.Z), offset.Tier, add, now); } } private void Contribution(long uid, Cell cell, FieldTier tier, bool add, float now) { NeighborReads++; if (!policies.TryGetValue(cell, out var value)) { if (reusable.Count != 0) { value = reusable.Pop(); } else { value = new Policy(); PolicyAllocations++; } policies.Add(cell, value); } value.Count += (add ? 1 : (-1)); value.Uids ^= uid; switch (tier) { case FieldTier.Inner: value.FastCount += (add ? 1 : (-1)); break; case FieldTier.Forward: value.ForwardCount += (add ? 1 : (-1)); break; case FieldTier.ImmediateForward: value.ImmediateCount += (add ? 1 : (-1)); break; } if (add) { Request request = new Request(uid, now, tier); if (value.Count == 1) { value.First = request; } else { if (value.Others == null) { value.Others = new List<Request>(1); } value.Others.Add(request); } if (remembered.Count != 0 && remembered.TryGetValue(cell, out var value2) && value2 != uid) { remembered.Remove(cell); } } else if (value.First.Uid == uid) { if (value.Others != null && value.Others.Count != 0) { int index = value.Others.Count - 1; value.First = value.Others[index]; value.Others.RemoveAt(index); } else { value.First = default(Request); } } else if (value.Others != null) { for (int i = 0; i < value.Others.Count; i++) { if (value.Others[i].Uid == uid) { value.Others.RemoveAt(i); break; } } } dirty.Add(cell); if (add && Renewed != null) { Renewed(cell); } } private void Reclassify(long uid, Cell cell, FieldTier before, FieldTier after) { Policy policy = policies[cell]; NeighborReads += 2; switch (before) { case FieldTier.Inner: policy.FastCount--; break; case FieldTier.Forward: policy.ForwardCount--; break; case FieldTier.ImmediateForward: policy.ImmediateCount--; break; } switch (after) { case FieldTier.Inner: policy.FastCount++; break; case FieldTier.Forward: policy.ForwardCount++; break; case FieldTier.ImmediateForward: policy.ImmediateCount++; break; } if (policy.First.Uid == uid) { policy.First.Tier = after; } else if (policy.Others != null) { for (int i = 0; i < policy.Others.Count; i++) { if (policy.Others[i].Uid == uid) { Request value = policy.Others[i]; value.Tier = after; policy.Others[i] = value; break; } } } dirty.Add(cell); } private void MoveContribution(long uid, PeerState old, Cell next, int heading, float now) { Offset[] array = footprints[old.Heading]; for (int i = 0; i < array.Length; i++) { Offset offset = array[i]; Cell cell = new Cell(old.Cell.X + offset.Cell.X, old.Cell.Z + offset.Cell.Z); if (maskTypes[heading].TryGetValue(new Cell(cell.X - next.X, cell.Z - next.Z), out var value)) { if (Renewed != null) { Renewed(cell); } if (value != offset.Tier) { Reclassify(uid, cell, offset.Tier, value); } else if ((deferredCells != 0 && policies[cell].Deferred) || (coolingCells != 0 && policies[cell].OverlapUntil != 0f)) { dirty.Add(cell); } } else { Contribution(uid, cell, offset.Tier, add: false, now); } } Offset[] array2 = footprints[heading]; for (int j = 0; j < array2.Length; j++) { Offset offset2 = array2[j]; Cell cell2 = new Cell(next.X + offset2.Cell.X, next.Z + offset2.Cell.Z); if (!maskTypes[old.Heading].ContainsKey(new Cell(cell2.X - old.Cell.X, cell2.Z - old.Cell.Z))) { Contribution(uid, cell2, offset2.Tier, add: true, now); } } } private void Commit(Cell cell, Policy policy) { pending.Remove(cell); if (policy.Owner != policy.Candidate) { policy.Owner = policy.Candidate; Changes.Add(cell); if (policy.Count > 1 && policy.Owner != 0 && overlapCooldown > 0f) { HoldOverlap(policy, lastTime + overlapCooldown); } } if (policy.Count == 0 && policy.Owner == 0 && policies.Remove(cell) && discardVacant && reusable.Count < 1024) { policy.Candidate = (policy.Owner = (policy.Uids = 0L)); policy.Count = (policy.FastCount = (policy.ForwardCount = (policy.ImmediateCount = 0))); policy.Since = (policy.Wait = (policy.Deadline = (policy.OverlapUntil = 0f))); policy.Shared = false; policy.First = default(Request); if (policy.Others != null) { policy.Others.Clear(); } reusable.Push(policy); } } private void Evaluate(Cell cell, float now) { Policy policy = policies[cell]; if (discardVacant && policy.Count == 0) { SetDeferred(policy, value: false); HoldOverlap(policy, 0f); policy.Candidate = 0L; Commit(cell, policy); return; } if (policy.OverlapUntil != 0f) { if ((policy.Owner == server || seen.Contains(policy.Owner)) && policy.Count != 0 && now < policy.OverlapUntil) { policy.Candidate = policy.Owner; policy.Shared = policy.Count > 1; pending.Remove(cell); return; } HoldOverlap(policy, 0f); } if (directional && discardVacant && policy.Count == 1 && policy.First.Tier != FieldTier.Inner && policy.First.Tier != FieldTier.ImmediateForward && (policy.Deferred || policy.Shared) && previous.TryGetValue(policy.Uids, out var value) && !value.Refreshed) { SetDeferred(policy, value: true); policy.Candidate = policy.Owner; policy.Wait = FieldWait(policy); pending.Remove(cell); return; } SetDeferred(policy, value: false); bool flag = policy.Count > 1 && !policy.Shared; long num = Resolve(policy); policy.Shared = policy.Count > 1; if (flag && policy.Owner != 0 && policy.Owner == num && overlapCooldown > 0f) { HoldOverlap(policy, now + overlapCooldown); } if (policy.Candidate != num) { if (policy.Owner == policy.Candidate || policy.Candidate == 0 || policy.Count <= 1) { policy.Since = now; } policy.Candidate = num; } float num2 = FieldWait(policy); if (num2 < policy.Wait && policy.Candidate != 0) { Accelerations.Add(cell); } policy.Wait = num2; if (policy.Count > 1 && remembered.Count != 0) { Forget(cell); } if (policy.Owner == num || (remembered.Count != 0 && IsRemembered(cell, num))) { Commit(cell, policy); return; } policy.Deadline = policy.Since + num2; if (policy.Count != 0) { policy.Deadline = RequestDeadline(policy.First, policy.Since, now); if (policy.Others != null) { foreach (Request other in policy.Others) { policy.Deadline = Math.Min(policy.Deadline, RequestDeadline(other, policy.Since, now)); } } } if (now >= policy.Deadline) { Commit(cell, policy); return; } pending.Add(cell); nextDue = Math.Min(nextDue, policy.Deadline); } private float RequestDeadline(Request request, float since, float now) { return Math.Max(Math.Min(request.Since, now), since) + ((!directional) ? Delay : ((request.Tier == FieldTier.ImmediateForward) ? 0f : ((request.Tier == FieldTier.Inner) ? innerDelay : ((request.Tier == FieldTier.Forward) ? forwardDelay : outerDelay)))); } private void SetDeferred(Policy policy, bool value) { if (policy.Deferred != value) { policy.Deferred = value; deferredCells += (value ? 1 : (-1)); } } private void HoldOverlap(Policy policy, float until) { if (policy.OverlapUntil == 0f != (until == 0f)) { coolingCells += ((until != 0f) ? 1 : (-1)); } policy.OverlapUntil = until; } private long Resolve(Policy policy) { if (policy.Count == 0) { return 0L; } if (policy.Count == 1) { return policy.Uids; } if (latency == null) { return server; } long uid = policy.First.Uid; float num = latency(uid); bool flag = policy.First.Uid == policy.Candidate; if (policy.Others != null) { foreach (Request other in policy.Others) { float num2 = latency(other.Uid); if (num2 < num || (num2 == num && other.Uid < uid)) { uid = other.Uid; num = num2; } flag |= other.Uid == policy.Candidate; } } if (flag && latency(policy.Candidate) <= num + margin) { return policy.Candidate; } return uid; } internal void RanksChanged() { ranksDirty = true; } internal void Update(List<GridPeer> peers, float now) { Changes.Clear(); Accelerations.Clear(); dirty.Clear(); seen.Clear(); departed.Clear(); bool flag = now < lastTime; lastTime = now; if (flag) { if (coolingCells != 0) { foreach (Policy value4 in policies.Values) { if (value4.OverlapUntil != 0f) { value4.OverlapUntil = now + overlapCooldown; } } } nextDue = float.PositiveInfinity; foreach (Cell item in pending) { Policy policy = policies[item]; policy.Since = now; policy.Deadline = now + policy.Wait; policy.First.Since = now; if (policy.Others != null) { for (int i = 0; i < policy.Others.Count; i++) { Request value = policy.Others[i]; value.Since = now; policy.Others[i] = value; } } nextDue = Math.Min(nextDue, policy.Deadline); } } bool flag2 = false; foreach (GridPeer peer in peers) { if (peer.Uid == 0 || peer.Uid == server || !seen.Add(peer.Uid)) { continue; } PeerState value2; bool flag3 = previous.TryGetValue(peer.Uid, out value2); if (flag3) { value2.Refreshed = false; } float num = Yaw(peer.Yaw, flag3 ? value2.Yaw : 0f); int num2 = (directional ? ((peer.Coverage >= 0 && peer.Coverage < 56) ? peer.Coverage : ((flag3 && (float.IsNaN(peer.Yaw) || float.IsInfinity(peer.Yaw))) ? ((value2.Heading >= 112) ? ((value2.Heading - 112) & 7) : (value2.Heading % 56)) : ((int)((num + 22.5f) / 45f) & 7))) : 0); if (directional && peer.Large) { num2 = 112 + ((int)((num + 22.5f) / 45f) & 7) + (peer.FastForward ? 8 : 0); } else if (directional && peer.FastForward) { num2 += 56; } if (!flag3 || !value2.Cell.Equals(peer.Cell) || value2.Heading != num2) { if (flag3) { MoveContribution(peer.Uid, value2, peer.Cell, num2, now); } else { value2 = new PeerState(); previous.Add(peer.Uid, value2); } value2.Cell = peer.Cell; value2.Yaw = num; value2.Heading = num2; value2.Refreshed = true; if (!flag3) { Contribute(peer.Uid, value2, add: true, now); } flag2 = true; PlayerUpdates++; } } foreach (KeyValuePair<long, PeerState> previou in previous) { if (!seen.Contains(previou.Key)) { departed.Add(previou.Key); } } foreach (long item2 in departed) { Contribute(item2, previous[item2], add: false, now); previous.Remove(item2); flag2 = true; } if (ranksDirty) { foreach (KeyValuePair<Cell, Policy> policy2 in policies) { if (policy2.Value.Count > 1) { dirty.Add(policy2.Key); } } ranksDirty = false; } if (flag2) { Rebuilds++; } foreach (Cell item3 in dirty) { Evaluate(item3, now); } if (pending.Count == 0) { nextDue = float.PositiveInfinity; } else { if (now < nextDue) { return; } due.Clear(); foreach (Cell item4 in pending) { due.Add(item4); } nextDue = float.PositiveInfinity; foreach (Cell item5 in due) { TimerVisits++; if (policies.TryGetValue(item5, out var value3)) { if (now >= value3.Deadline) { Commit(item5, value3); } else { nextDue = Math.Min(nextDue, value3.Deadline); } } } due.Clear(); } } } internal abstract class AuthorityNode { internal Cell Cell; internal bool Managed; internal bool Removed; internal long ManagedOwner; internal long ConfirmedFieldOwner; internal AuthorityWorld.ObjectBucket IndexedBucket; internal float NotBefore; internal abstract long CurrentOwner { get; } internal abstract ApplyResult Apply(long target); internal virtual bool CanRemember(long fieldOwner) { if (Managed && ManagedOwner == fieldOwner) { return CurrentOwner == fieldOwner; } return false; } } internal enum ApplyResult { Done, Pending, Dropped } internal sealed class AuthorityWorld { internal sealed class ObjectBucket { internal readonly HashSet<AuthorityNode> Nodes = new HashSet<AuthorityNode>(); internal long Target; internal int Unconfirmed; internal bool PriorityCached; internal int Priority; } internal readonly AuthorityGrid Grid; private readonly Dictionary<Cell, ObjectBucket> buckets = new Dictionary<Cell, ObjectBucket>(); private readonly HashSet<AuthorityNode> pending = new HashSet<AuthorityNode>(); private readonly List<ObjectBucket> priorityBuckets = new List<ObjectBucket>(); private readonly List<AuthorityNode>[] work = new List<AuthorityNode>[4] { new List<AuthorityNode>(), new List<AuthorityNode>(), new List<AuthorityNode>(), new List<AuthorityNode>() }; private readonly bool keepOnExit; private readonly long server; private float lastTime; internal Action AfterGridUpdate; internal int Attempts; internal int SkippedCells; internal int BucketCount => buckets.Count; internal int PendingCount => pending.Count; internal AuthorityWorld(long server, float delay, GridCoveragePattern pattern = GridCoveragePattern.Square3x3, bool keepOnExit = true, bool directional = false, float innerDelay = 0f, float outerDelay = 3f, bool forward = false, float forwardDelay = 0.2f, Func<long, float> latency = null, float margin = 30f, float overlapCooldown = 0f, bool fullRear = false, bool includeOuter = true) { Grid = new AuthorityGrid(server, delay, pattern, directional, innerDelay, outerDelay, keepOnExit, forward, forwardDelay, latency, margin, overlapCooldown, fullRear || !keepOnExit, includeOuter); this.keepOnExit = keepOnExit; this.server = server; } internal void Add(AuthorityNode node, float now, bool schedule = true) { node.Removed = false; ObjectBucket objectBucket = Bucket(node.Cell); if (objectBucket.Nodes.Add(node)) { node.IndexedBucket = objectBucket; node.ConfirmedFieldOwner = 0L; if (objectBucket.Target != 0) { objectBucket.Unconfirmed++; } if (keepOnExit) { Grid.Forget(node.Cell); } if (schedule) { Queue(node, now); } else { node.Managed = false; } } } internal void Remove(AuthorityNode node) { Detach(node); node.Removed = true; node.Managed = false; pending.Remove(node); } internal void Move(AuthorityNode node, Cell next, float now, bool schedule = true) { if (!node.Removed && !node.Cell.Equals(next)) { Detach(node); node.Cell = next; ObjectBucket objectBucket = Bucket(next); objectBucket.Nodes.Add(node); node.IndexedBucket = objectBucket; node.ConfirmedFieldOwner = 0L; if (objectBucket.Target != 0) { objectBucket.Unconfirmed++; } if (keepOnExit) { Grid.Forget(next); } if (schedule) { Queue(node, now + Grid.Wait(next)); return; } pending.Remove(node); node.Managed = false; } } internal void Changed(AuthorityNode node, float now, bool schedule = true) { if (!node.Removed) { Invalidate(node); if (schedule) { Queue(node, now + Grid.Wait(node.Cell)); return; } pending.Remove(node); node.Managed = false; } } internal void Received(AuthorityNode node, float now) { if (!node.Removed) { Invalidate(node); Queue(node, Math.Max(now, node.NotBefore)); } } internal void Tick(List<GridPeer> peers, float now) { if (peers.Count == 0 && !Grid.HasPeers && Grid.CachedCells == 0 && pending.Count == 0) { Grid.Changes.Clear(); Grid.Accelerations.Clear(); return; } if (now < lastTime) { foreach (AuthorityNode item in pending) { item.NotBefore = now + Grid.Wait(item.Cell); } } lastTime = now; Grid.Update(peers, now); if (AfterGridUpdate != null) { AfterGridUpdate(); } foreach (Cell change in Grid.Changes) { if ((keepOnExit && Grid.Owner(change) == 0) || !buckets.TryGetValue(change, out var value)) { continue; } long num = Grid.Owner(change); if (keepOnExit && Grid.IsRemembered(change, num) && value.Target == num && value.Unconfirmed == 0) { SkippedCells++; continue; } value.Target = ((keepOnExit && num != server) ? num : 0); value.Unconfirmed = ((value.Target != 0) ? value.Nodes.Count : 0); bool flag = false; float num2 = 0f; foreach (AuthorityNode node in value.Nodes) { node.ConfirmedFieldOwner = 0L; if (pending.Add(node)) { node.NotBefore = now; } else if (Grid.Directional) { if (!flag) { num2 = Grid.Wait(change); flag = true; } node.NotBefore = Math.Min(node.NotBefore, now + num2); } } } if (pending.Count == 0) { return; } ClearPriorityBatch(); try { foreach (AuthorityNode item2 in pending) { work[BatchPriority(item2)].Add(item2); } } finally { ClearPriorityBatch(); } try { List<AuthorityNode>[] array = work; foreach (List<AuthorityNode> list in array) { foreach (AuthorityNode item3 in list) { if (item3.Removed || !pending.Contains(item3)) { continue; } if (Grid.Accelerations.Count != 0 && Grid.Accelerations.Contains(item3.Cell)) { item3.NotBefore = Math.Min(item3.NotBefore, now + Grid.Wait(item3.Cell)); } if (now < item3.NotBefore) { continue; } Attempts++; ApplyResult applyResult = item3.Apply(Grid.Owner(item3.Cell)); if (applyResult != ApplyResult.Pending) { pending.Remove(item3); } switch (applyResult) { case ApplyResult.Dropped: item3.Managed = false; break; case ApplyResult.Done: if (keepOnExit && !item3.Removed) { Confirm(item3); } break; } } } } finally { List<AuthorityNode>[] array2 = work; foreach (List<AuthorityNode> list2 in array2) { list2.Clear(); } } } internal void Dismiss(AuthorityNode node) { Invalidate(node); pending.Remove(node); node.Managed = false; } internal int BatchPriority(AuthorityNode node) { ObjectBucket indexedBucket = node.IndexedBucket; if (indexedBucket == null || indexedBucket.Nodes.Count == 1) { return Grid.WorkPriority(node.Cell); } if (!indexedBucket.PriorityCached) { indexedBucket.Priority = Grid.WorkPriority(node.Cell); indexedBucket.PriorityCached = true; priorityBuckets.Add(indexedBucket); } return indexedBucket.Priority; } internal void ClearPriorityBatch() { foreach (ObjectBucket priorityBucket in priorityBuckets) { priorityBucket.PriorityCached = false; } priorityBuckets.Clear(); } private void Queue(AuthorityNode node, float at) { node.NotBefore = at; pending.Add(node); } private void Invalidate(AuthorityNode node) { if (keepOnExit) { Grid.Forget(node.Cell); if (buckets.TryGetValue(node.Cell, out var value) && value.Target != 0 && node.ConfirmedFieldOwner == value.Target) { node.ConfirmedFieldOwner = 0L; value.Unconfirmed++; } } } private void Confirm(AuthorityNode node) { if (buckets.TryGetValue(node.Cell, out var value) && value.Target != 0 && node.ConfirmedFieldOwner != value.Target && node.CanRemember(value.Target)) { node.ConfirmedFieldOwner = value.Target; value.Unconfirmed--; Certify(node.Cell, value); } } private void Certify(Cell cell, ObjectBucket bucket) { if (keepOnExit && bucket.Target != 0 && bucket.Unconfirmed == 0) { Grid.Remember(cell, bucket.Target); } } private void Detach(AuthorityNode node) { if (buckets.TryGetValue(node.Cell, out var value) && value.Nodes.Remove(node)) { node.IndexedBucket = null; bool flag = value.Target != 0 && node.ConfirmedFieldOwner != value.Target; if (flag) { value.Unconfirmed--; } node.ConfirmedFieldOwner = 0L; if (value.Nodes.Count == 0) { buckets.Remove(node.Cell); Grid.Forget(node.Cell); } else if (flag) { Certify(node.Cell, value); } } } private ObjectBucket Bucket(Cell cell) { if (!buckets.TryGetValue(cell, out var value)) { long num = Grid.Owner(cell); ObjectBucket objectBucket = new ObjectBucket(); objectBucket.Target = ((keepOnExit && num != server) ? num : 0); value = objectBucket; buckets.Add(cell, value); } return value; } } internal sealed class Controller { private sealed class Node : AuthorityNode { internal readonly Controller Controller; internal readonly ZDO Data; internal readonly ZNetView View; internal readonly Groups PrefabGroups; internal Groups Group; internal MobState Mob; internal long GraceSource; internal ushort GraceRevision; internal long LastOwner; internal bool InGrid; internal bool Released; internal bool Pinned; internal bool Writing; internal bool ControlPinned; internal bool RiderSeen; internal long AcceptedRider; internal HashSet<long> ControlRequests; internal Dictionary<long, float> NativeUseRequests; internal byte ActorState; internal RefillState Refill; internal Container Container; internal Ship Ship; internal Vagon Cart; internal Vector3 ShipAnchor; internal override long CurrentOwner => Data.GetOwner(); internal Node(Controller owner, ZDO data, ZNetView view, Groups group, Cell cell) { Controller = owner; Data = data; View = view; PrefabGroups = group; Group = CreatureGroup(data, group); Cell = cell; } internal override ApplyResult Apply(long target) { return Controller.Apply(this, target); } } private struct Latency { internal float Until; internal int Ping; } private sealed class MobState { internal Humanoid Actor; internal MonsterAI Ai; internal MobAttackProfile Profile; internal long Source; internal ushort Revision; internal bool Pending; internal bool Armed; internal bool SawAttack; internal bool Looping; internal bool Combat; internal bool IdleHandoffObserved; internal float TriggerAt; internal float IdleAt = float.PositiveInfinity; internal Vector3 Position; internal Quaternion Rotation; internal bool TargetValid; internal int SelectionEpoch; internal long Target; internal float[] Cooldowns; internal float CooldownClock = float.NegativeInfinity; internal bool ForecastDirty = true; } private struct MobPlayer { internal long Uid; internal ZDO Data; internal Vector3 Position; internal MobPlayer(long uid, ZDO data, Vector3 position) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) Uid = uid; Data = data; Position = position; } } private sealed class RefillState { private readonly ZDO data; private readonly bool fuel; private readonly bool queue; private readonly bool ammo; private readonly int[] slots; private readonly bool[] filled; private float lastFuel; private int lastQueue; private int lastAmmo; internal float Until; internal RefillState(ZDO data, ZNetView view) { this.data = data; int num = 0; MonoBehaviour[] componentsInChildren = ((Component)view).GetComponentsInChildren<MonoBehaviour>(true); foreach (MonoBehaviour val in componentsInChildren) { if (!Object.op_Implicit((Object)(object)val) || (Object)(object)((Component)val).GetComponentInParent<ZNetView>(true) != (Object)(object)view) { continue; } if (val is Smelter) { fuel = true; queue = true; } if (val is Fireplace || val is ShieldGenerator) { fuel = true; } if (val is Turret) { ammo = true; } CookingStation val2 = (CookingStation)(object)((val is CookingStation) ? val : null); if (Object.op_Implicit((Object)(object)val2)) { fuel = true; if (val2.m_slots != null) { num = Math.Max(num, val2.m_slots.Length); } } } if (num != 0) { slots = new int[num]; filled = new bool[num]; for (int j = 0; j < num; j++) { slots[j] = StringExtensionMethods.GetStableHashCode("slot" + j); } } Read(); } internal bool Read() { bool flag = false; if (fuel) { float num = data.GetFloat(ZDOVars.s_fuel, 0f); flag |= num > lastFuel; lastFuel = num; } if (queue) { int num2 = data.GetInt(ZDOVars.s_queued, 0); flag |= num2 > lastQueue; lastQueue = num2; } if (ammo) { int num3 = data.GetInt(ZDOVars.s_ammo, 0); flag |= num3 > lastAmmo; lastAmmo = num3; } if (slots != null) { for (int i = 0; i < slots.Length; i++) { bool flag2 = !string.IsNullOrEmpty(data.GetString(slots[i], "")); flag |= flag2 && !filled[i]; filled[i] = flag2; } } return flag; } } private readonly Settings options; private readonly long server; private readonly Catalog catalog; private readonly AuthorityWorld world; private readonly NativeInventoryHandoff inventory; private readonly Dictionary<ZDO, Node> nodes = new Dictionary<ZDO, Node>(); private readonly Dictionary<Cell, HashSet<Node>> waiting = new Dictionary<Cell, HashSet<Node>>(); private readonly List<GridPeer> peers = new List<GridPeer>(); private bool hadPeers; private int shipCount; private readonly Dictionary<ZDO, Node> mobile = new Dictionary<ZDO, Node>(); private readonly HashSet<Node> changed = new HashSet<Node>(); private readonly HashSet<Node> defaults = new HashSet<Node>(); private readonly List<Node> changedWork = new List<Node>(); private readonly List<Node> defaultWork = new List<Node>(); private readonly Dictionary<Cell, bool> readyAreas = new Dictionary<Cell, bool>(); private float readyAt = float.NegativeInfinity; private readonly List<long> expiredUseRequests = new List<long>(); private readonly List<KeyValuePair<long, float>> boundUseRequests = new List<KeyValuePair<long, float>>(); private readonly Dictionary<long, Latency> latency = new Dictionary<long, Latency>(); private readonly DueQueue<Node> grace = new DueQueue<Node>(); private float nextLatencyPrune; private readonly List<long> stalePeers = new List<long>(); private readonly HashSet<Node> forecastWaiting = new HashSet<Node>(); private readonly Dictionary<int, bool> forecastKinds = new Dictionary<int, bool>(); private Collider[] forecastColliders; private readonly HashSet<Character> forecastCharacters = new HashSet<Character>(); private readonly HashSet<StaticTarget> forecastBuildings = new HashSet<StaticTarget>(); private bool forecastDirty; internal int MobForecastChecks; internal int MobForecastQueries; internal int MobAbilityObservations; internal int MobForecastPhysicsQueries; internal int MobForecastCharacterSweeps; private readonly Dictionary<ZDO, Node> mobs = new Dictionary<ZDO, Node>(); private readonly Dictionary<int, MobAttackProfile> mobProfiles = new Dictionary<int, MobAttackProfile>(); private readonly HashSet<Node> sharedMobs = new HashSet<Node>(); private readonly HashSet<Node> mobDirty = new HashSet<Node>(); private readonly HashSet<Node> pendingMobs = new HashSet<Node>(); private readonly DueQueue<Node> mobDue = new DueQueue<Node>(); private List<MobPlayer> mobPlayers = new List<MobPlayer>(); private List<MobPlayer> previousMobPlayers = new List<MobPlayer>(); private bool mobPlayersChanged; private int mobPlayerEpoch; internal int MobGuardChecks; internal int MobConeChecks; private Node mobTestNode; private long mobTestPlayer; private long mobTestTarget; private float mobTestStarted; private float mobTestNext; private float mobTestReport; private bool mobTestStopped; private bool mobTestPending; private int mobTestRequests; private int mobTestTransfers; internal Action<string> MobTestLog; private int passes; private int registrationCalls; private int validRegistrations; private int assignCalls; internal int TrackedObjects => nodes.Count; internal int WaitingCells => waiting.Count; internal int RememberedCells => world.Grid.RememberedCells; internal int PendingTransfers => grace.Count; internal bool HasWaitingForecast => forecastWaiting.Count != 0; internal int PendingMobTransfers => pendingMobs.Count; internal int SharedMobCount => sharedMobs.Count; internal bool HasMobNodes => mobs.Count != 0; internal bool MobTestActive => mobTestNode != null; internal int MobTestTransfers => mobTestTransfers; internal bool ProtectRefills => options.RefillHold > 0f; private static bool CartIdle(ZDO data) { if (!data.GetBool(ZDOVars.s_attachJointHash, false)) { return data.GetInt(ZDOVars.s_inUse, 0) == 0; } return false; } private ApplyResult ApplyCart(Node node, long target, long owner) { PruneUseRequests(node); if (!Object.op_Implicit((Object)(object)node.Cart) || !Object.op_Implicit((Object)(object)node.Container) || !CartIdle(node.Data) || node.Cart.InUse() || (node.NativeUseRequests != null && node.NativeUseRequests.Count != 0)) { Wait(node); return ApplyResult.Dropped; } if (owner != target && (target == server || owner != server) && !MobileAreaReady(node)) { Wait(node); return ApplyResult.Dropped; } return ApplyContainer(node, target, owner); } internal void CartDenied(ZDO data, long requester) { if (data != null && nodes.TryGetValue(data, out var value) && value.Group == Groups.Carts && value.NativeUseRequests != null && value.NativeUseRequests.Remove(requester)) { changed.Add(value); } } private void CartAttached(Node node, long owner) { if (node.NativeUseRequests != null && node.Data.GetBool(ZDOVars.s_attachJointHash, false)) { node.NativeUseRequests.Remove(owner); } } internal Controller(Settings options, long server, Func<int, bool> reserved = null) { this.options = options; this.server = server; catalog = new Catalog(options.Groups, reserved); inventory = new NativeInventoryHandoff(server); world = new AuthorityWorld(server, options.FullDelay, options.Pattern, options.Keep, options.Directional, options.InnerDelay, options.OuterDelay, forward: true, options.ForwardDelay, null, 30f, options.OverlapHold, fullRear: true, options.IncludeOuter); world.Grid.Renewed = Renew; world.AfterGridUpdate = RefreshMobCones; } internal void Tick(float now) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) passes++; peers.Clear(); BeginMobPlayers(); foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (!peer.IsReady() || peer.m_uid == 0 || peer.m_uid == server || ((ZDOID)(ref peer.m_characterID)).IsNone()) { continue; } ZDO zDO = ZDOMan.instance.GetZDO(peer.m_characterID); if (zDO == null || !zDO.IsValid() || zDO.GetOwner() != peer.m_uid) { continue; } Vector3 position = zDO.GetPosition(); if (Cell.At(position.x, position.z, options.CellSize, out var cell)) { bool flag = LargeGrid(peer.m_uid, now); bool fastForward = false; if (options.Directional && (options.Pattern != GridCoveragePattern.Square3x3 || flag) && options.ForwardDelay > 0f && options.FastMovementSpeed > 0f) { Vector3 vec = zDO.GetVec3(ZDOVars.s_velHash, Vector3.zero); float num = vec.x * vec.x + vec.z * vec.z; float num2 = options.FastMovementSpeed * (world.Grid.FastForward(peer.m_uid) ? 0.75f : 1f); fastForward = !float.IsNaN(num) && !float.IsInfinity(num) && num >= num2 * num2; } List<GridPeer> list = peers; long uid = peer.m_uid; Cell cell2 = cell; Quaternion rotation = zDO.GetRotation(); list.Add(new GridPeer(uid, cell2, ((Quaternion)(ref rotation)).eulerAngles.y, -1, fastForward, flag)); AddMobPlayer(peer.m_uid, zDO, position); } } if (peers.Count == 0) { ClearTransfers(); } if (peers.Count != 0 || hadPeers || world.Grid.CachedCells != 0) { hadPeers = peers.Count != 0; if (mobTestNode != null && peers.Count != 1) { StopMobTest("requires exactly one online player"); } RefreshChanges(now); world.Tick(peers, now); if (options.MobOwnershipTest) { TickMobTest(now); } ServiceDefaults(now); } } internal void Add(ZDO data, ZNetView view) { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) ForecastTargetChanged(data, null, worldEvent: true); registrationCalls++; if (data == null || !data.IsValid() || !Object.op_Implicit((Object)(object)view) || !view.IsValid() || nodes.ContainsKey(data)) { return; } validRegistrations++; Groups groups = catalog.Get(data.GetPrefab()); if (groups == Groups.None) { return; } Vector3 position = data.GetPosition(); if (!Cell.At(position.x, position.z, options.CellSize, out var cell)) { return; } Node node = new Node(this, data, view, groups, cell); node.LastOwner = data.GetOwner(); Node node2 = node; nodes.Add(data, node2); if (groups == Groups.Containers || groups == Groups.Ships || groups == Groups.Carts) { Container[] componentsInChildren = ((Component)view).GetComponentsInChildren<Container>(true); foreach (Container val in componentsInChildren) { if (((groups == Groups.Ships || groups == Groups.Carts) && (Object)(object)val.m_rootObjectOverride == (Object)(object)view) || (Object)(object)((Component)val).GetComponentInParent<ZNetView>(true) == (Object)(object)view) { node2.Container = val; break; } } } if (groups == Groups.Carts) { node2.Cart = ((Component)view).GetComponent<Vagon>(); if (!Object.op_Implicit((Object)(object)node2.Cart) || !Object.op_Implicit((Object)(object)node2.Container) || (Object)(object)node2.Cart.m_container != (Object)(object)node2.Container || (Object)(object)node2.Container.m_wagon != (Object)(object)node2.Cart) { nodes.Remove(data); return; } } if (groups == Groups.Ships) { node2.Ship = ((Component)view).GetComponent<Ship>(); if (!Object.op_Implicit((Object)(object)node2.Ship) || !Object.op_Implicit((Object)(object)node2.Ship.m_shipControlls)) { nodes.Remove(data); return; } node2.ShipAnchor = ((Component)view).transform.InverseTransformPoint(((Component)node2.Ship.m_shipControlls).transform.position); shipCount++; } if (Catalog.LocalOnly(groups)) { if (Catalog.Idle(data) && ActiveArea(position)) { Assign(node2, server); } return; } if ((groups & Groups.RefillStations) != Groups.None && options.RefillHold > 0f) { node2.Refill = new RefillState(data, view); } if (Catalog.Mobile(groups)) { mobile.Add(data, node2); node2.ActorState = State(node2); if (groups == Groups.Fish && node2.LastOwner != 0 && node2.LastOwner != server) { node2.Pinned = true; } } if ((groups & Groups.HostileMobs) != Groups.None) { AddMob(node2); } Refresh(node2, Time.unscaledTime); } internal void Remove(ZDO data) { ForecastTargetChanged(data, null, worldEvent: true); if (data != null && nodes.TryGetValue(data, out var value)) { if (value.Group == Groups.Ships) { shipCount--; } ForgetMob(value); grace.Remove(value); Unwait(value); inventory.Forget(data); if (value.InGrid) { world.Remove(value); } value.Removed = true; changed.Remove(value); defaults.Remove(value); mobile.Remove(data); nodes.Remove(data); } } internal long NativeDestination(ZDO data, long proposed) { //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) if (proposed != server || data == null || !data.Persistent || !Catalog.Idle(data)) { return proposed; } Groups groups = catalog.Get(data.GetPrefab()); if (groups == Groups.None) { return proposed; } if ((groups & Groups.HostileMobs) != Groups.None) { return proposed; } if ((groups == Groups.Containers || groups == Groups.Ships || groups == Groups.Carts) && data.GetOwner() != 0) { return proposed; } if (groups == Groups.Ships || groups == Groups.Carts) { return proposed; } if (Catalog.LocalOnly(groups) || (groups == Groups.StaticBuildings && !options.StaticGrid)) { return server; } if (nodes.TryGetValue(data, out var value) && (value.Pinned || value.Released || value.ControlPinned)) { return proposed; } if (value != null && value.Refill != null && Time.unscaledTime < value.Refill.Until) { return proposed; } if (groups == Groups.TamedAnimals && (ActorState(data) != 1 || data.GetFloat(ZDOVars.s_health, 1f) <= 0f)) { return proposed; } if ((groups == Groups.Fish && data.GetInt(ZDOVars.s_hooked, 0) != 0) || (groups == Groups.LooseItems && data.GetBool(ZDOVars.s_piece, false))) { return proposed; } if (Catalog.Pickup(groups) && data.GetOwner() != 0 && data.GetOwner() != server) { return proposed; } Vector3 position = data.GetPosition(); if (!Cell.At(position.x, position.z, options.CellSize, out var cell)) { return proposed; } long num = world.Grid.Owner(cell); if (num == 0 || num == server || !Available(num, position)) { return proposed; } return num; } private ApplyResult Apply(Node node, long target) { return Apply(node, target, due: false); } private ApplyResult Apply(Node node, long target, bool due) { //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_0301: Unknown result type (might be due to invalid IL or missing references) Unwait(node); if (peers.Count == 0) { return ApplyResult.Dropped; } if (!node.Data.IsValid() || !Object.op_Implicit((Object)(object)node.View) || !node.View.IsValid() || !object.ReferenceEquals(node.View.GetZDO(), node.Data)) { return ApplyResult.Dropped; } if (node.Released || node.Pinned || node.ControlPinned || (node.Group == Groups.Fish && node.Data.GetOwner() != 0 && node.Data.GetOwner() != server)) { return ApplyResult.Dropped; } if (node.Group == Groups.TamedAnimals && (ActorState(node.Data) != 1 || node.Data.GetFloat(ZDOVars.s_health, 1f) <= 0f)) { grace.Remove(node); Wait(node); return ApplyResult.Dropped; } if (target == 0 && options.Keep) { grace.Remove(node); CancelMob(node); sharedMobs.Remove(node); return ApplyResult.Done; } if (target == 0) { target = server; } long owner = node.Data.GetOwner(); if (node.Group == Groups.HostileMobs) { return ApplyMob(node, target, owner); } if (node.Group == Groups.Ships) { return ApplyShip(node, target, owner); } if (node.Group == Groups.Carts) { return ApplyCart(node, target, owner); } if (node.Group == Groups.Containers) { return ApplyContainer(node, target, owner); } if (node.Group == Groups.LooseItems) { PruneUseRequests(node); } if (owner == target) { grace.Remove(node); node.Managed = true; node.ManagedOwner = target; return ApplyResult.Done; } if (node.Group == Groups.LooseItems && ItemPickupProtected(node, owner)) { grace.Remove(node); Wait(node); return ApplyResult.Dropped; } if (node.Refill != null && Time.unscaledTime < node.Refill.Until && (owner == server || Available(owner, node.Data.GetPosition()))) { grace.Remove(node); Wait(node); return ApplyResult.Dropped; } if (Catalog.Terminal(node.View) || !Catalog.Idle(node.Data) || !Available(target, node.Data.GetPosition()) || (Catalog.Mobile(node.Group) && target == server && !MobileAreaReady(node)) || (target == server && !((Component)node.View).gameObject.activeInHierarchy)) { grace.Remove(node); Wait(node); return ApplyResult.Dropped; } if (target == server && owner != 0 && Catalog.NeedsGrace(node.Group) && !due && (node.Group != Groups.LooseItems || Available(owner, node.Data.GetPosition()))) { if (grace.Contains(node)) { return ApplyResult.Dropped; } if (!Grace(owner, Time.unscaledTime, out var value)) { Wait(node); return ApplyResult.Dropped; } if (value > 0f) { node.GraceSource = owner; node.GraceRevision = node.Data.OwnerRevision; grace.Add(node, Time.unscaledTime + value); return ApplyResult.Dropped; } } grace.Remove(node); Assign(node, target); if (node.Data.GetOwner() != target) { Wait(node); return ApplyResult.Dropped; } node.Managed = true; node.ManagedOwner = target; return ApplyResult.Done; } internal bool Available(long uid, Vector3 position) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (uid == server) { return true; } ZNetPeer peer = ZNet.instance.GetPeer(uid); if (peer != null && peer.IsReady()) { return ZNetScene.InActiveArea(position, ZoneSystem.GetZone(peer.GetRefPos())); } return false; } private static bool ActiveArea(Vector3 position) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer.IsReady() && ZNetScene.InActiveArea(position, ZoneSystem.GetZone(peer.GetRefPos()))) { return true; } } return false; } internal bool Request(ZDO data, int method) { if (data == null || !data.IsValid()) { return false; } Groups groups = CreatureGroup(data, catalog.Get(data.GetPrefab())); if (groups != Groups.None) { return Catalog.OwnerRequest(groups, method); } return false; } internal bool GridManagesFire(int prefab) { if (!options.RespectFireOwnership) { return (catalog.Get(prefab) & Groups.RefillStations) != 0; } return false; } private void Wait(Node node) { if (node.InGrid) { if (!waiting.TryGetValue(node.Cell, out var value)) { value = new HashSet<Node>(); waiting.Add(node.Cell, value); } value.Add(node); } } private void Unwait(Node node) { if (waiting.TryGetValue(node.Cell, out var value) && value.Remove(node) && value.Count == 0) { waiting.Remove(node.Cell); } } private void Renew(Cell cell) { if (!waiting.TryGetValue(cell, out var value)) { return; } foreach (Node item in value) { world.Received(item, Time.unscaledTime); } waiting.Remove(cell); } private bool MobileAreaReady(Node node) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) if (readyAt != Time.unscaledTime) { readyAt = Time.unscaledTime; readyAreas.Clear(); } Cell cell = node.Cell; if (Object.op_Implicit((Object)(object)node.Ship)) { Vector3 position = node.Data.GetPosition(); if (!Cell.At(position.x, position.z, options.CellSize, out cell)) { return false; } } Cell key = Cell.Zone(cell, options.CellSize); if (!readyAreas.TryGetValue(key, out var value)) { value = ZNetScene.instance.IsAreaReady(node.Data.GetPosition()); readyAreas.Add(key, value); } return value; } private static byte ActorState(ZDO data) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) byte b = (byte)(data.GetBool(ZDOVars.s_tamed, false) ? 1 : 0); if (data.GetLong(ZDOVars.s_user, 0L) == 0 && string.IsNullOrEmpty(data.GetString(ZDOVars.s_follow, "")) && string.IsNullOrEmpty(data.GetString(ZDOVars.s_attachJointHash, ""))) { ZDOID connectionZDOID = data.GetConnectionZDOID((ConnectionType)2); if (((ZDOID)(ref connectionZDOID)).IsNone()) { goto IL_0068; } } b |= 2; goto IL_0068; IL_0068: if (data.GetBool(ZDOVars.s_alert, false) || data.GetBool(ZDOVars.s_haveTargetHash, false)) { b |= 4; } return b; } private static byte State(Node node) { if (node.Group == Groups.None) { return 0; } if (node.Group == Groups.HostileMobs) { if ((ActorState(node.Data) & 3) != 0) { return 2; } return 1; } if (node.Group == Groups.Ships) { return 1; } if (node.Group == Groups.Carts) { if (!node.Data.GetBool(ZDOVars.s_attachJointHash, false)) { return 1; } return 2; } if (node.Group == Groups.TamedAnimals) { return ActorState(node.Data); } if (node.Group == Groups.LooseItems) { if (!node.Data.GetBool(ZDOVars.s_piece, false)) { return 1; } return 2; } if (node.Data.GetInt(ZDOVars.s_hooked, 0) == 0) { return 1; } return 2; } private bool UsesGrid(Node node) { if (node.Group == Groups.None || node.Released || node.Pinned || node.ControlPinned || Catalog.LocalOnly(node.Group)) { return false; } if (node.Group == Groups.StaticBuildings) { return options.StaticGrid; } if (Catalog.Mobile(node.Group)) { return node.ActorState == 1; } return true; } private void Assign(Node node, long target) { assignCalls++; if (Object.op_Implicit((Object)(object)node.Ship) && target == server && node.Data.GetOwner() != server) { ShipOwnership.Prepare(node.Ship); } node.Writing = true; try { node.Data.SetOwner(target); } finally { node.Writing = false; node.LastOwner = node.Data.GetOwner(); } if (node.Group == Groups.Fish && target != 0 && target != server) { node.Pinned = true; changed.Add(node); } } internal void OwnerAssigned(ZDO data, long owner) { if (nodes.TryGetValue(data, out var value) && !value.Writing && value.LastOwner != owner) { value.LastOwner = owner; value.Released = owner == 0; MobOwnerChanged(value); if (Object.op_Implicit((Object)(object)value.Ship) && owner == server) { ShipOwnership.Prepare(value.Ship); } if (value.Group == Groups.Carts) { CartAttached(value, owner); } if (value.Group == Groups.Fish && owner != 0 && owner != server) { value.Pinned = true; } if (!Catalog.LocalOnly(value.Group)) { changed.Add(value); } } } internal void PositionChanged(ZDO data) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) if (mobile.Count != 0 && mobile.TryGetValue(data, out var value) && !value.Pinned && !value.Released && value.ActorState == 1) { MobPoseChanged(value); Vector3 val = GridPosition(value); if (Cell.At(val.x, val.z, options.CellSize, out var cell) && !value.Cell.Equals(cell)) { changed.Add(value); } } } internal void DataReceived(ZDO data, long source) { SourceData(data, source); RefillChanged(data); StateChanged(data); if (mobs.Count != 0 && mobs.TryGetValue(data, out var value)) { MobPoseChanged(value); } } internal void StateChanged(ZDO data) { if (mobile.Count == 0 || !mobile.TryGetValue(data, out var value)) { return; } Groups groups = CreatureGroup(data, value.PrefabGroups); if (groups != value.Group) { CancelMob(value); sharedMobs.Remove(value); value.Group = groups; changed.Add(value); } if (value.Mob != null) { bool flag = data.GetBool(ZDOVars.s_alert, false) || data.GetBool(ZDOVars.s_haveTargetHash, false); if (flag != value.Mob.Combat) { value.Mob.Combat = flag; WakeMob(value); } } if (value.Group == Groups.Ships) { PositionChanged(data); return; } if (value.Group == Groups.Carts && value.NativeUseRequests != null && value.NativeUseRequests.Count != 0) { CartAttached(value, data.GetOwner()); } if (value.ControlPinned && value.Group == Groups.TamedAnimals) { if (data.GetLong(ZDOVars.s_user, 0L) != 0) { value.RiderSeen = true; } else if (value.RiderSeen) { value.RiderSeen = false; value.AcceptedRider = 0L; value.ControlPinned = value.ControlRequests != null && value.ControlRequests.Count != 0; changed.Add(value); } } byte b = State(value); if (b != value.ActorState) { value.ActorState = b; if (value.Group == Groups.Fish && b != 1) { value.Pinned = true; } changed.Add(value); } } private bool ItemPickupProtected(Node node, long owner) { if (owner == 0 || owner == server || !world.Grid.Covers(owner, node.Cell)) { if (node.NativeUseRequests != null) { return node.NativeUseRequests.Count != 0; } return false; } return true; } private void PruneUseRequests(Node node, bool moved = false) { if (node.NativeUseRequests == null || node.NativeUseRequests.Count == 0) { return; } expiredUseRequests.Clear(); boundUseRequests.Clear(); foreach (KeyValuePair<long, float> nativeUseRequest in node.NativeUseRequests) { if (!world.Grid.CoverageStart(nativeUseRequest.Key, node.Cell, out var since) || (!moved && !float.IsNaN(nativeUseRequest.Value) && nativeUseRequest.Value != since)) { expiredUseRequests.Add(nativeUseRequest.Key); } else if (moved || float.IsNaN(nativeUseRequest.Value)) { boundUseRequests.Add(new KeyValuePair<long, float>(nativeUseRequest.Key, since)); } } foreach (long expiredUseRequest in expiredUseRequests) { node.NativeUseRequests.Remove(expiredUseRequest); } foreach (KeyValuePair<long, float> boundUseRequest in boundUseRequests) { node.NativeUseRequests[boundUseRequest.Key] = boundUseRequest.Value; } expiredUseRequests.Clear(); boundUseRequests.Clear(); } internal void PinPickup(ZDO data, long requester = 0L) { if (data == null || !nodes.TryGetValue(data, out var value) || (!Catalog.Pickup(value.Group) && value.Group != Groups.Carts)) { return; } if ((value.Group == Groups.LooseItems || value.Group == Groups.Carts) && requester != 0) { if (value.NativeUseRequests == null) { value.NativeUseRequests = new Dictionary<long, float>(); } PruneUseRequests(value); value.NativeUseRequests[requester] = (world.Grid.CoverageStart(requester, value.Cell, out var since) ? since : float.NaN); grace.Remove(value); Unwait(value); if (value.InGrid) { world.Dismiss(value); Wait(value); } } else { value.Pinned = true; grace.Remove(value); Unwait(value); if (value.InGrid) { world.Remove(value); value.InGrid = false; } } } internal void PinControl(ZDO data, long requester = 0L) { if (data != null && mobile.TryGetValue(data, out var value) && value.Group == Groups.TamedAnimals) { if (value.ControlRequests == null) { value.ControlRequests = new HashSet<long>(); } value.ControlRequests.Add(requester); value.ControlPinned = true; value.RiderSeen |= data.GetLong(ZDOVars.s_user, 0L) != 0; grace.Remove(value); Unwait(value); if (value.InGrid) { world.Remove(value); value.InGrid = false; } } } internal void ControlReply(ZDO data, bool allowed, long recipient = 0L) { if (data != null && mobile.TryGetValue(data, out var value) && value.ControlPinned && value.ControlRequests != null && value.ControlRequests.Remove(recipient)) { if (allowed) { value.AcceptedRider = recipient; } if (!allowed && value.AcceptedRider == 0 && !value.RiderSeen && value.ControlRequests.Count == 0) { value.ControlPinned = false; changed.Add(value); } } } internal bool ExpectsControlReply(ZDO data, long recipient) { if (data != null && mobile.TryGetValue(data, out var value) && value.ControlPinned && value.ControlRequests != null) { return value.ControlRequests.Contains(recipient); } return false; } private void Refresh(Node node, float now) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (node.Removed && !nodes.ContainsKey(node.Data)) { return; } bool flag = UsesGrid(node); Vector3 val = GridPosition(node); if (node.Group == Groups.Fish) { Fish component = ((Component)node.View).GetComponent<Fish>(); if (Object.op_Implicit((Object)(object)component) && component.IsHooked()) { node.Pinned = true; flag = false; } } if (node.Group == Groups.LooseItems) { ItemDrop component2 = ((Component)node.View).GetComponent<ItemDrop>(); if (Object.op_Implicit((Object)(object)component2) && component2.IsPiece()) { flag = false; } } if (!Cell.At(val.x, val.z, options.CellSize, out var cell)) { flag = false; } if (!flag) { CancelMob(node); sharedMobs.Remove(node); mobDirty.Remove(node); if (node.InGrid) { world.Remove(node); node.InGrid = false; } grace.Remove(node); Unwait(node); if (node.Group == Groups.StaticBuildings && !node.Released) { node.Removed = false; defaults.Add(node); } } else if (!node.InGrid) { bool flag2 = node.Group == Groups.Carts && !node.Cell.Equals(cell); node.Cell = cell; node.InGrid = true; world.Add(node, now); if (flag2) { PruneUseRequests(node, moved: true); } } else if (!node.Cell.Equals(cell)) { grace.Remove(node); Unwait(node); if (node.Group == Groups.LooseItems || node.Group == Groups.Carts) { PruneUseRequests(node); } world.Move(node, cell, now); if (node.Group == Groups.LooseItems || node.Group == Groups.Carts) { PruneUseRequests(node, moved: true); } } else { grace.Remove(node); world.Received(node, now); } } private void RefreshChanges(float now) { if (changed.Count == 0) { return; } changedWork.Clear(); changedWork.AddRange(changed); changed.Clear(); foreach (Node item in changedWork) { Refresh(item, now); } changedWork.Clear(); } private void ServiceDefaults(float now) { if (peers.Count == 0 || defaults.Count == 0) { return; } defaultWork.Clear(); defaultWork.AddRange(defaults); defaults.Clear(); foreach (Node item in defaultWork) { if (!item.Removed && !item.Released) { Apply(item, server); } } defaultWork.Clear(); } private long Desired(Node node) { if (node.Group != Groups.StaticBuildings || options.StaticGrid) { return world.Grid.Owner(node.Cell); } return server; } private bool Grace(long uid, float now, out float value) { value = options.MinimumGrace; if (options.GraceMultiplier == 0f) { return true; } int num = Ping(uid, now); if (num <= 0) { return false; } value = Math.Max(value, (float)num * 0.001f * options.GraceMultiplier); return true; } private int Ping(long uid, float now) { if (latency.TryGetValue(uid, out var value) && now < value.Until) { return value.Ping; } ZNetPeer peer = ZNet.instance.GetPeer(uid); if (peer == null || !peer.IsReady() || peer.m_socket == null) { return 0; } int num; try { num = SocketLatency.Read(peer.m_socket); } catch (Exception) { num = 0; } latency[uid] = new Latency { Until = now + 1f, Ping = num }; return num; } private bool LargeGrid(long uid, float now) { if (!options.Directional || options.AdaptivePing <= 0) { return false; } int num = Ping(uid, now); bool flag = world.Grid.LargeCoverage(uid); if (num > 0) { return (float)num >= (float)options.AdaptivePing * (flag ? 0.75f : 1f); } return flag; } internal void SourceData(ZDO data, long source) { if (grace.Count != 0 && source != 0 && nodes.TryGetValue(data, out var value) && value.GraceSource == source && grace.Remove(value)) { value.Managed = false; if (value.InGrid) { world.Dismiss(value); } Wait(value); } } internal void SourceRequest(ZDO data, long source) { SourceData(data, source); } internal void Service(float now) { ServiceMobs(now); Node value; while (grace.Take(now, out value)) { if (value.Removed || !nodes.ContainsKey(value.Data) || !value.Data.IsValid()) { continue; } if (value.Data.GetOwner() != value.GraceSource || value.Data.OwnerRevision != value.GraceRevision) { Wait(value); continue; } long num = Desired(value); if (num == 0 && !options.Keep) { num = server; } if (num == server) { Apply(value, server, due: true); } } if (!(now >= nextLatencyPrune) || latency.Count == 0) { return; } nextLatencyPrune = now + 1f; stalePeers.Clear(); foreach (KeyValuePair<long, Latency> item in latency) { if (ZNet.instance.GetPeer(item.Key) == null) { stalePeers.Add(item.Key); } } foreach (long stalePeer in stalePeers) { latency.Remove(stalePeer); } } private void ClearTransfers() { grace.Clear(); latency.Clear(); inventory.Clear(); ClearMobs(); } internal bool FinishedRequest(ZDO data, int method) { if (!Catalog.PickRequest(method)) { return false; } if (nodes.TryGetValue(data, out var value) && Object.op_Implicit((Object)(object)value.View)) { return Catalog.FinishedPick(value.View); } return false; } private ApplyResult ApplyContainer(Node node, long target, long owner) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)node.Container) || (int)node.Container.m_privacy != 2) { return ApplyResult.Dropped; } if (inventory.Transitioning(node.Data) || inventory.Pending(node.Data)) { Wait(node); return ApplyResult.Dropped; } if (owner == target) { inventory.Forget(node.Data); node.Managed = true; node.ManagedOwner = target; return ApplyResult.Done; } int num = world.Grid.Participants(node.Cell); if (target == server && num == 1) { return ApplyResult.Dropped; } if (!((Component)node.View).gameObject.activeInHierarchy || !(Object.op_Implicit((Object)(object)node.Cart) ? CartIdle(node.Data) : (Object.op_Implicit((Object)(object)node.Ship) ? ShipIdle(node.Data) : Catalog.Idle(node.Data))) || !Available(target, node.Data.GetPosition()) || node.Data.OwnerRevision == ushort.MaxValue) { Wait(node); return ApplyResult.Dropped; } if (owner == server) { if (node.Container.IsInUse() || inventory.Acquire(node.Data, node.View, node.Container) != 2) { Wait(node); return ApplyResult.Dropped; } } else if ((num > 0 || Object.op_Implicit((Object)(object)node.Ship) || Object.op_Implicit((Object)(object)node.Cart)) && owner != 0 && Available(owner, node.Data.GetPosition())) { int num2 = inventory.Acquire(node.Data, node.View, node.Container); if (num2 != 2) { Wait(node); return ApplyResult.Dropped; } } else { inventory.Forget(node.Data); } if (node.Data.GetOwner() != target) { if (target != server) { ZDOMan.instance.ForceSendZDO(target, node.Data.m_uid); } Assign(node, target); } if (node.Data.GetOwner() != target) { Wait(node); return ApplyResult.Dropped; } inventory.Forget(node.Data); node.Managed = true; node.ManagedOwner = target; return ApplyResult.Done; } internal bool ExpectsInventoryReply(ZDO data) { return inventory.Pending(data); } internal void InventoryReply(ZDO data, long sender, bool granted) { if (data == null || !nodes.TryGetValue(data, out var value) || !Object.op_Implicit((Object)(object)value.Container)) { return; } if (inventory.Response(value.Container, sender, granted)) { if (value.InGrid) { world.Dismiss(value); Wait(value); } } else if (granted && inventory.Received(data)) { changed.Add(value); } } internal bool WatchesForecastTurn(ZDO data) { if (forecastWaiting.Count != 0 && data != null && mobs.TryGetValue(data, out var value)) { return forecastWaiting.Contains(value); } return false; } internal void ForecastMobTurned(ZDO data) { if (forecastWaiting.Count != 0 && data != null && mobs.TryGetValue(data, out var value) && forecastWaiting.Contains(value)) { value.Mob.ForecastDirty = true; WakeMob(value); } } private void ObserveMobAbility(Node node, string trigger, long source) { MobForecastProfile forecast = node.Mob.Profile.Forecast; if (forecast != null && forecast.Triggers.TryGetValue(trigger, out var value) && value >= 0) { MobAbilityObservations++; node.Mob.ForecastDirty = true; if (node.Mob.Cooldowns == null) { node.Mob.Cooldowns = new float[forecast.Abilities.Count]; ResetMobCooldowns(node.Mob); } CheckForecastClock(node.Mob); int num = ((source != server) ? Ping(source, Time.unscaledTime) : 0); float num2 = ((source != server && num <= 0) ? 0f : Math.Max(0f, forecast.Abilities[value].Cooldown - 0.1f - (float)num * 0.001f)); node.Mob.Cooldowns[value] = Time.time + num2; } } private static void CheckForecastClock(MobState mob) { if (Time.time < mob.CooldownClock) { ResetMobCooldowns(mob); } mob.CooldownClock = Time.time; } private static void ResetMobCooldowns(MobState mob) { if (mob.Cooldowns != null) { for (int i = 0; i < mob.Cooldowns.Length; i++) { mob.Cooldowns[i] = float.NegativeInfinity; } } } internal void ForecastTargetChanged(ZDO data, Vector3? previous = null, bool worldEvent = false) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) if (forecastWaiting.Count == 0 || data == null || !ForecastTarget(data)) { return; } ZNetView val = (worldEvent ? null : ZNetScene.instance.FindInstance(data)); Character val2 = (Object.op_Implicit((Object)(object)val) ? ((Component)val).GetComponent<Character>() : null); if (!Object.op_Implicit((Object)(object)val2)) { forecastDirty = true; return; } float radius; try { radius = val2.GetRadius(); } catch (NullReferenceException) { forecastDirty = true; return; } Vector3 position = data.GetPosition(); if (float.IsNaN(radius) || float.IsInfinity(radius) || radius < 0f || !Finite(position)) { forecastDirty = true; return; } foreach (Node item in forecastWaiting) { float range = item.Mob.Profile.Forecast.MaximumRange + radius; Vector3 position2 = item.Data.GetPosition(); if (object.ReferenceEquals(item.Data, data) || Near(position2, position, range) || (previous.HasValue && Near(position2, previous.Value, range))) { item.Mob.ForecastDirty = true; WakeMob(item); } } } private static bool Near(Vector3 a, Vector3 b, float range) { float num = a.x - b.x; float num2 = a.y - b.y; float num3 = a.z - b.z; return num * num + num2 * num2 + num3 * num3 <= range * range; } private bool ForecastTarget(ZDO data) { int prefab = data.GetPrefab(); if (!forecastKinds.TryGetValue(prefab, out var value)) { GameObject prefab2 = ZNetScene.instance.GetPrefab(prefab); value = Object.op_Implicit((Object)(object)prefab2) && (Object.op_Implicit((Object)(object)prefab2.GetComponent<Character>()) || Object.op_Implicit((Object)(object)prefab2.GetComponentInChildren<StaticTarget>(true))); forecastKinds[prefab] = value; } return value; } internal ForecastReceiveState CaptureForecastReceive(ZDO data) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) if (forecastWaiting.Count == 0 || data == null || !ForecastTarget(data)) { return default(ForecastReceiveState); } bool flag = WatchesForecastTurn(data); return new ForecastReceiveState { Tracked = true, TrackRotation = flag, Rotation = (Quaternion)(flag ? data.GetRotation() : default(Quaternion)), Living = (data.GetFloat(ZDOVars.s_health, 1f) > 0f), Tamed = data.GetBool(ZDOVars.s_tamed, false), Aggravated = data.GetBool(ZDOVars.s_aggravated, false) }; } internal void ForecastReceived(ZDO data, ForecastReceiveState previous) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (previous.Tracked) { if (previous.TrackRotation && !((Quaternion)(ref previous.Rotation)).Equals(data.GetRotation())) { ForecastMobTurned(data); } if (previous.Living != data.GetFloat(ZDOVars.s_health, 1f) > 0f || previous.Tamed != data.GetBool(ZDOVars.s_tamed, false) || previous.Aggravated != data.GetBool(ZDOVars.s_aggravated, false)) { ForecastTargetChanged(data); } } } private bool ForecastMobAttack(Node node) { //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Unknown result type (might be due to invalid IL or missing references) //IL_02c4: Unknown result type (might be due to invalid IL or missing references) //IL_036d: Unknown result type (might be due to invalid IL or missing references) //IL_036f: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Unknown result type (might be due to invalid IL or missing references) MobForecastProfile forecast = node.Mob.Profile.Forecast; if (forecast == null) { return false; } if (forecastWaiting.Contains(node) && !node.Mob.ForecastDirty) { return true; } node.Mob.ForecastDirty = false; MobForecastChecks++; if (forecast.Unknown) { return true; } CheckForecastClock(node.Mob); float time = Time.time; float num = 0f; for (int i = 0; i < forecast.Abilities.Count; i++) { if (node.Mob.Cooldowns == null || time >= node.Mob.Cooldowns[i]) { num = Math.Max(num, forecast.Abilities[i].Range); } } if (num == 0f) { return false; } Vector3 position = node.Data.GetPosition(); Vector3 val = node.Data.GetRotation() * new Vector3(0f, 0f, 1f); if (!Finite(position) || !Finite(val)) { return true; } MobForecastQueries++; int num2 = 0; try { foreach (MobPlayer mobPlayer in mobPlayers) { ZNetView val2 = ZNetScene.instance.FindInstance(mobPlayer.Data); if (Object.op_Implicit((Object)(object)val2) && ForecastCharacter(node, forecast, time, num, position, val, ((Component)val2).GetComponent<Character>())) { return true; } } MobForecastCharacterSweeps++; foreach (Character allCharacter in Character.GetAllCharacters()) { if (ForecastCharacter(node, forecast, time, num, position, val, allCharacter)) { return true; } } if (!node.Mob.Ai.m_attackPlayerObjects) { return false; } if (forecastColliders == null) { forecastColliders = (Collider[])(object)new Collider[128]; } MobForecastPhysicsQueries++; num2 = Physics.OverlapSphereNonAlloc(position, num + 0.5f, forecastColliders, -1, (QueryTriggerInteraction)1); if (num2 >= forecastColliders.Length) { return true; } for (int j = 0; j < num2; j++) { Collider val3 = forecastColliders[j]; if (!Object.op_Implicit((Object)(object)val3)) { continue; } StaticTarget componentInParent = ((Component)val3).GetComponentInParent<StaticTarget>(true); if (!Object.op_Implicit((Object)(object)componentInParent) || !forecastBuildings.Add(componentInParent) || (!componentInParent.IsPriorityTarget() && !componentInParent.IsRandomTarget())) { continue; } Vector3 value = componentInParent.FindClosestPoint(position); Vector3 center = componentInParent.GetCenter(); if (!Finite(value) || !Finite(center)) { return true; } for (int k = 0; k < forecast.Abilities.Count; k++) { MobForecastProfile.Ability ability = forecast.Abilities[k]; if (ability.Enemy && (node.Mob.Cooldowns == null || !(time < node.Mob.Cooldowns[k]))) { float num3 = value.x - position.x; float num4 = value.y - position.y; float num5 = value.z - position.z; if (num3 * num3 + num4 * num4 + num5 * num5 <= ability.Range * ability.Range && FacingBuilding(ability, position, val, center)) { return true; } } } } return false; } catch (NullReferenceException) { return true; } finally { if (forecastColliders != null) { Array.Clear(forecastColliders, 0, Math.Min(num2, forecastColliders.Length)); } forecastCharacters.Clear(); forecastBuildings.Clear(); } } private bool ForecastCharacter(Node node, MobForecastProfile profile, float now, float maximum, Vector3 origin, Vector3 forward, Character target) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)target) || (Object)(object)target == (Object)(object)node.Mob.Actor || !forecastCharacters.Add(target) || target.IsDead()) { return false; } ZNetView component = ((Component)target).GetComponent<ZNetView>(); Vector3 val = ((Object.op_Implicit((Object)(object)component) && component.IsValid()) ? component.GetZDO().GetPosition() : ((Component)target).transform.position); float radius = target.GetRadius(); if (!Finite(val) || float.IsNaN(radius) || float.IsInfinity(radius) || radius < 0f) { return true; } if (!Near(origin, val, maximum + radius)) { return false; } bool flag = BaseAI.IsEnemy((Character)(object)node.Mob.Actor, target); for (int i = 0; i < profile.Abilities.Count; i++) { if ((node.Mob.Cooldowns == null || now >= node.Mob.Cooldowns[i]) && profile.Abilities[i].Enemy == flag && MobForecastProfile.Contains(profile.Abilities[i], origin, forward, val, radius)) { return true; } } return false; } private static bool FacingBuilding(MobForecastProfile.Ability ability, Vector3 origin, Vector3 forward, Vector3 center) { float num = center.x - origin.x; float num2 = center.z - origin.z; float num3 = (num * num + num2 * num2) * (forward.x * forward.x + forward.z * forward.z); float num4 = ((num3 <= 1E-06f) ? 1f : ((num * forward.x + num2 * forward.z) / (float)Math.Sqrt(num3))); if (!ability.Inverted) { return num4 + 1E-06f >= ability.Cosine; } return num4 <= ability.Cosine + 1E-06f; } private static Groups CreatureGroup(ZDO data, Groups groups) { if ((groups & (Groups.TamedAnimals | Groups.HostileMobs)) == 0) { return groups; } return (Groups)((int)groups & (data.GetBool(ZDOVars.s_tamed, false) ? 4096 : 524288)); } private void AddMob(Node node) { //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) Humanoid component = ((Component)node.View).GetComponent<Humanoid>(); MonsterAI component2 = ((Component)node.View).GetComponent<MonsterAI>(); if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component2)) { if (!mobProfiles.TryGetValue(node.Data.GetPrefab(), out var value)) { GameObject prefab = ZNetScene.instance.GetPrefab(node.Data.GetPrefab()); value = new MobAttackProfile(Object.op_Implicit((Object)(object)prefab) ? prefab.GetComponent<Humanoid>() : component, options.MobAttackForecast); mobProfiles.Add(node.Data.GetPrefab(), value); } node.Mob = new MobState { Actor = component, Ai = component2, Profile = value, Source = node.Data.GetOwner(), Revision = node.Data.OwnerRevision, Position = node.Data.GetPosition(), Rotation = node.Data.GetRotation(), Combat = (node.Data.GetBool(ZDOVars.s_alert, false) || node.Data.GetBool(ZDOVars.s_haveTargetHash, false)) }; mobs.Add(node.Data, node); } } private void ForgetMob(Node node) { CancelMob(node); mobs.Remove(node.Data); sharedMobs.Remove(node); mobDirty.Remove(node); } private void CancelMob(Node node) { mobDue.Remove(node); pendingMobs.Remove(node); forecastWaiting.Remove(node); if (node.Mob != null) { node.Mob.Pending = false; } } private void MobOwnerChanged(Node node) { if (node.Mob != null) { CancelMob(node); MobState mob = node.Mob; mob.Source = node.Data.GetOwner(); mob.Revision = node.Data.OwnerRevision; mob.Armed = (mob.SawAttack = (mob.Looping = false)); mob.IdleAt = float.PositiveInfinity; mob.IdleHandoffObserved = false; mob.TargetValid = false; ResetMobCooldowns(mob); mob.ForecastDirty = true; } } private void BeginMobPlayers() { List<MobPlayer> list = previousMobPlayers; previousMobPlayers = mobPlayers; mobPlayers = list; mobPlayers.Clear(); mobPlayersChanged = false; } private void AddMobPlayer(long uid, ZDO data, Vector3 position) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) if ((options.Groups & Groups.HostileMobs) != Groups.None) { int count = mobPlayers.Count; if (count >= previousMobPlayers.Count || previousMobPlayers[count].Uid != uid || !Same(position, previousMobPlayers[count].Position)) { mobPlayersChanged = true; } mobPlayers.Add(new MobPlayer(uid, data, position)); } } private static bool Same(Vector3 a, Vector3 b) { if (a.x == b.x && a.y == b.y) { return a.z == b.z; } return false; } private void MobPoseChanged(Node node) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (node.Mob != null && sharedMobs.Contains(node)) { Vector3 position = node.Data.GetPosition(); Quaternion rotation = node.Data.GetRotation(); if (!Same(position, node.Mob.Position) || !((Quaternion)(ref rotation)).Equals(node.Mob.Rotation)) { node.Mob.Position = position; node.Mob.Rotation = rotation; node.Mob.TargetValid = false; mobDirty.Add(node); } } } private void RefreshMobCones() { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) if (mobs.Count == 0) { return; } if (mobPlayersChanged || mobPlayers.Count != previousMobPlayers.Count) { mobPlayerEpoch++; if (forecastWaiting.Count != 0) { if (mobPlayers.Count != previousMobPlayers.Count) { forecastDirty = true; } else { for (int i = 0; i < mobPlayers.Count; i++) { if (mobPlayers[i].Uid != previousMobPlayers[i].Uid) { forecastDirty = true; break; } if (!Same(mobPlayers[i].Position, previousMobPlayers[i].Position)) { ForecastTargetChanged(mobPlayers[i].Data, previousMobPlayers[i].Position); } } } } foreach (Node sharedMob in sharedMobs) { if (sharedMob.InGrid && !sharedMob.Removed) { world.Received(sharedMob, Time.unscaledTime); } } } foreach (Node item in mobDirty) { if (item.InGrid && !item.Removed) { world.Received(item, Time.unscaledTime); } } mobDirty.Clear(); } private long MobTarget(Node node, long field, bool force = false) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) if (object.ReferenceEquals(node, mobTestNode)) { return mobTestTarget; } if (field != server || world.Grid.Participants(node.Cell) < 2) { sharedMobs.Remove(node); node.Mob.TargetValid = false; return field; } sharedMobs.Add(node); if (!force && node.Mob.TargetValid && node.Mob.SelectionEpoch == mobPlayerEpoch) { return node.Mob.Target; } MobConeChecks++; Vector3 position = node.Data.GetPosition(); Vector3 value = node.Data.GetRotation() * new Vector3(0f, 0f, 1f); if (!Finite(position) || !Finite(value)) { return server; } float num = (((Character)node.Mob.Actor).IsBoss() ? options.BossMobConeRange : options.MobConeRange); float num2 = num * num; float num3 = num2; int num4 = 0; long uid = server; long owner = node.Data.GetOwner(); foreach (MobPlayer mobPlayer in mobPlayers) { if (!mobPlayer.Data.IsValid() || mobPlayer.Data.GetOwner() != mobPlayer.Uid || mobPlayer.Data.GetFloat(ZDOVars.s_health, 1f) <= 0f) { continue; } Vector3 position2 = mobPlayer.Data.GetPosition(); if (!Finite(position2)) { continue; } float num5 = position2.x - position.x; float num6 = position2.y - position.y; float num7 = position2.z - position.z; float num8 = num5 * n