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 SkadiNet v1.0.0
SkadiNet.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JetBrains.Annotations; using Microsoft.CodeAnalysis; using ServerSync; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("SkadiNet")] [assembly: AssemblyTitle("SkadiNet")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SkadiNet { internal enum ClientCleanupKind { GarbageCollect, UnloadUnusedAssets } internal enum ClientCriticalWindow { InitialSync, Teleport, FullSnapshotBurst, Combat, ShipTravel } internal struct MemoryPressureSnapshot { public bool Known; public ulong TotalMB; public ulong AvailableMB; public int LoadPercent; public override string ToString() { if (!Known) { return "unknown"; } return $"load={LoadPercent}% available={AvailableMB}MB total={TotalMB}MB"; } } internal static class ClientStutterGuard { [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] private struct MEMORYSTATUSEX { public uint dwLength; public uint dwMemoryLoad; public ulong ullTotalPhys; public ulong ullAvailPhys; public ulong ullTotalPageFile; public ulong ullAvailPageFile; public ulong ullTotalVirtual; public ulong ullAvailVirtual; public ulong ullAvailExtendedVirtual; } [CompilerGenerated] private sealed class <CleanupScheduler>d__42 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <CleanupScheduler>d__42(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; if (IsActive && RunCleanupWhenIdle) { TryRunPendingCleanup(forced: false); } } else { <>1__state = -1; } float num2 = Math.Max(0.25f, EffectiveConfig.ClientStutterIdleCleanupPollSeconds); <>2__current = (object)new WaitForSeconds(num2); <>1__state = 1; return true; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private static readonly Dictionary<ClientCriticalWindow, double> CriticalUntil = new Dictionary<ClientCriticalWindow, double>(); private static Plugin _plugin; private static Coroutine _cleanupCoroutine; private static bool _pendingGc; private static bool _pendingUnload; private static double _firstPendingSince; private static bool _runningCleanup; private static AsyncOperation _lastUnloadOperation; private static bool DelayGcCollect => true; private static bool DelayUnusedAssetCleanup => false; private static bool DelayDuringInitialSync => true; private static bool DelayDuringTeleport => true; private static bool DelayDuringFullSnapshotBurst => true; private static bool DelayDuringCombat => true; private static bool DelayDuringShipTravel => true; private static bool RunCleanupWhenIdle => true; private static bool UseMemoryPressureGate => true; internal static bool IsActive { get { if (!EffectiveConfig.ClientStutterGuardEnabled) { return false; } if (IsDedicatedLike()) { return false; } return true; } } internal static void Initialize(Plugin plugin) { _plugin = plugin; CriticalUntil.Clear(); _pendingGc = false; _pendingUnload = false; _firstPendingSince = 0.0; _runningCleanup = false; _lastUnloadOperation = null; EnsureCleanupScheduler(); } internal static void Shutdown() { try { if (_cleanupCoroutine != null && (Object)(object)_plugin != (Object)null) { ((MonoBehaviour)_plugin).StopCoroutine(_cleanupCoroutine); } } catch { } _cleanupCoroutine = null; _plugin = null; } private static bool IsDedicatedLike() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Invalid comparison between Unknown and I4 try { if (NetReflection.IsDedicatedServer()) { return true; } } catch { } try { if (Application.isBatchMode) { return true; } if ((int)SystemInfo.graphicsDeviceType == 4) { return true; } } catch { } return false; } internal static void MarkCriticalWindow(ClientCriticalWindow window, float seconds) { if (IsActive && !(seconds <= 0f) && IsWindowEnabled(window)) { double num = Time.realtimeSinceStartupAsDouble + (double)seconds; if (!CriticalUntil.TryGetValue(window, out var value) || value < num) { CriticalUntil[window] = num; } } } internal static void MarkInitialSyncWindow() { MarkCriticalWindow(ClientCriticalWindow.InitialSync, Math.Max(0.1f, EffectiveConfig.ClientStutterInitialSyncWindowSeconds)); } internal static void MarkTeleportWindow() { MarkCriticalWindow(ClientCriticalWindow.Teleport, Math.Max(0.1f, EffectiveConfig.ClientStutterTeleportWindowSeconds)); } internal static void MarkFullSnapshotBurstWindow() { MarkCriticalWindow(ClientCriticalWindow.FullSnapshotBurst, Math.Max(0.1f, EffectiveConfig.ClientStutterFullSnapshotWindowSeconds)); } internal static void MarkCombatWindow() { MarkCriticalWindow(ClientCriticalWindow.Combat, Math.Max(0.1f, EffectiveConfig.ClientStutterCombatWindowSeconds)); } internal static void MarkShipTravelWindow() { MarkCriticalWindow(ClientCriticalWindow.ShipTravel, Math.Max(0.1f, EffectiveConfig.ClientStutterShipWindowSeconds)); } internal static bool TryHandleGcCollect() { if (!IsActive || !DelayGcCollect || _runningCleanup) { return true; } if (!RunCleanupWhenIdle) { return true; } if (ShouldDelayCleanup(ClientCleanupKind.GarbageCollect, out var reason)) { RequestPending(ClientCleanupKind.GarbageCollect, reason); return false; } return true; } internal static bool TryHandleUnloadUnusedAssetsPrefix(ref AsyncOperation result) { if (!IsActive || !DelayUnusedAssetCleanup || _runningCleanup) { return true; } if (!RunCleanupWhenIdle) { return true; } if (ShouldDelayCleanup(ClientCleanupKind.UnloadUnusedAssets, out var reason)) { RequestPending(ClientCleanupKind.UnloadUnusedAssets, reason); if (_lastUnloadOperation != null) { result = _lastUnloadOperation; return false; } return true; } return true; } internal static void OnUnloadUnusedAssetsPostfix(AsyncOperation result) { if (result != null) { _lastUnloadOperation = result; } } private static void RequestPending(ClientCleanupKind kind, string reason) { if (kind == ClientCleanupKind.GarbageCollect) { _pendingGc = true; } if (kind == ClientCleanupKind.UnloadUnusedAssets) { _pendingUnload = true; } if (_firstPendingSince <= 0.0) { _firstPendingSince = Time.realtimeSinceStartupAsDouble; } EnsureCleanupScheduler(); if (ModConfig.DebugLogging.Value) { Plugin.Log.LogDebug((object)$"ClientStutterGuard: deferred {kind} ({reason})."); } } private static void EnsureCleanupScheduler() { if (_cleanupCoroutine == null && !((Object)(object)_plugin == (Object)null) && IsActive && RunCleanupWhenIdle) { _cleanupCoroutine = ((MonoBehaviour)_plugin).StartCoroutine(CleanupScheduler()); } } [IteratorStateMachine(typeof(<CleanupScheduler>d__42))] private static IEnumerator CleanupScheduler() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <CleanupScheduler>d__42(0); } private static bool TryRunPendingCleanup(bool forced) { if (!_pendingGc && !_pendingUnload) { return false; } if (!IsActive && !forced) { return false; } if (_runningCleanup) { return false; } double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; bool flag = _firstPendingSince > 0.0 && realtimeSinceStartupAsDouble - _firstPendingSince >= (double)Math.Max(1f, EffectiveConfig.ClientStutterMaxDelaySeconds); if (!forced && !flag && TryGetCriticalReason(out var reason)) { if (ModConfig.DebugLogging.Value) { Plugin.Log.LogDebug((object)("ClientStutterGuard: pending cleanup waits for " + reason + ".")); } return false; } if (!forced && !flag && UseMemoryPressureGate && IsMemoryPlentiful(out var snapshot)) { if (ModConfig.DebugLogging.Value) { Plugin.Log.LogDebug((object)$"ClientStutterGuard: pending cleanup waits; memory plentiful ({snapshot})."); } return false; } bool flag2 = _pendingUnload && DelayUnusedAssetCleanup; bool flag3 = _pendingGc && DelayGcCollect; _pendingUnload = false; _pendingGc = false; _firstPendingSince = 0.0; _runningCleanup = true; try { if (flag2) { if (ModConfig.DebugLogging.Value) { Plugin.Log.LogDebug((object)"ClientStutterGuard: running coalesced Resources.UnloadUnusedAssets."); } AsyncOperation val = Resources.UnloadUnusedAssets(); if (val != null) { _lastUnloadOperation = val; } } if (flag3) { if (ModConfig.DebugLogging.Value) { Plugin.Log.LogDebug((object)"ClientStutterGuard: running coalesced GC.Collect."); } GC.Collect(); } } finally { _runningCleanup = false; } return flag2 || flag3; } private static bool ShouldDelayCleanup(ClientCleanupKind kind, out string reason) { reason = "safe window"; if (!IsActive) { return false; } if (_firstPendingSince > 0.0 && Time.realtimeSinceStartupAsDouble - _firstPendingSince >= (double)Math.Max(1f, EffectiveConfig.ClientStutterMaxDelaySeconds)) { reason = "max delay exceeded"; return false; } if (TryGetCriticalReason(out var reason2)) { if (UseMemoryPressureGate && IsMemoryPressure(out var snapshot)) { reason = $"memory pressure overrides critical window ({snapshot})"; return false; } reason = reason2; return true; } if (UseMemoryPressureGate && IsMemoryPlentiful(out var snapshot2)) { reason = $"memory plentiful ({snapshot2})"; return true; } return false; } private static bool TryGetCriticalReason(out string reason) { reason = null; double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; double num = 0.0; ClientCriticalWindow clientCriticalWindow = ClientCriticalWindow.InitialSync; foreach (KeyValuePair<ClientCriticalWindow, double> item in CriticalUntil) { if (item.Value > realtimeSinceStartupAsDouble && item.Value > num && IsWindowEnabled(item.Key)) { clientCriticalWindow = item.Key; num = item.Value; } } if (num <= realtimeSinceStartupAsDouble) { return false; } reason = $"{clientCriticalWindow} for {num - realtimeSinceStartupAsDouble:F1}s"; return true; } private static bool IsWindowEnabled(ClientCriticalWindow window) { return window switch { ClientCriticalWindow.InitialSync => DelayDuringInitialSync, ClientCriticalWindow.Teleport => DelayDuringTeleport, ClientCriticalWindow.FullSnapshotBurst => DelayDuringFullSnapshotBurst, ClientCriticalWindow.Combat => DelayDuringCombat, ClientCriticalWindow.ShipTravel => DelayDuringShipTravel, _ => true, }; } private static bool IsMemoryPlentiful(out MemoryPressureSnapshot snapshot) { snapshot = GetMemorySnapshot(); if (!snapshot.Known) { return false; } if (snapshot.LoadPercent < Math.Max(1, EffectiveConfig.ClientStutterMemoryPressureThresholdPercent)) { return snapshot.AvailableMB >= (ulong)Math.Max(0, EffectiveConfig.ClientStutterMinimumFreeMemoryMB); } return false; } private static bool IsMemoryPressure(out MemoryPressureSnapshot snapshot) { snapshot = GetMemorySnapshot(); if (!snapshot.Known) { return false; } if (snapshot.LoadPercent < Math.Max(1, EffectiveConfig.ClientStutterMemoryPressureThresholdPercent)) { return snapshot.AvailableMB < (ulong)Math.Max(0, EffectiveConfig.ClientStutterMinimumFreeMemoryMB); } return true; } private static MemoryPressureSnapshot GetMemorySnapshot() { if (TryGetWindowsMemory(out var snapshot)) { return snapshot; } if (TryGetProcMemInfo(out var snapshot2)) { return snapshot2; } try { if (SystemInfo.systemMemorySize > 0) { ulong num = (ulong)SystemInfo.systemMemorySize; MemoryPressureSnapshot result = default(MemoryPressureSnapshot); result.Known = true; result.TotalMB = num; result.AvailableMB = num; result.LoadPercent = 0; return result; } } catch { } MemoryPressureSnapshot result2 = default(MemoryPressureSnapshot); result2.Known = false; return result2; } private static bool TryGetWindowsMemory(out MemoryPressureSnapshot snapshot) { snapshot = default(MemoryPressureSnapshot); try { MEMORYSTATUSEX lpBuffer = default(MEMORYSTATUSEX); lpBuffer.dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX)); if (!GlobalMemoryStatusEx(ref lpBuffer)) { return false; } snapshot.Known = true; snapshot.TotalMB = lpBuffer.ullTotalPhys / 1024 / 1024; snapshot.AvailableMB = lpBuffer.ullAvailPhys / 1024 / 1024; snapshot.LoadPercent = (int)lpBuffer.dwMemoryLoad; return snapshot.TotalMB != 0; } catch { return false; } } private static bool TryGetProcMemInfo(out MemoryPressureSnapshot snapshot) { snapshot = default(MemoryPressureSnapshot); try { if (!File.Exists("/proc/meminfo")) { return false; } ulong num = 0uL; ulong num2 = 0uL; string[] array = File.ReadAllLines("/proc/meminfo"); foreach (string text in array) { if (text.StartsWith("MemTotal:", StringComparison.Ordinal)) { num = ParseKb(text); } else if (text.StartsWith("MemAvailable:", StringComparison.Ordinal)) { num2 = ParseKb(text); } } if (num == 0L || num2 == 0L) { return false; } snapshot.Known = true; snapshot.TotalMB = num / 1024; snapshot.AvailableMB = num2 / 1024; snapshot.LoadPercent = (int)Math.Round(100.0 * (1.0 - (double)num2 / (double)num)); return true; } catch { return false; } } private static ulong ParseKb(string line) { string[] array = line.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 1; i < array.Length; i++) { if (ulong.TryParse(array[i], out var result)) { return result; } } return 0uL; } [DllImport("kernel32.dll", SetLastError = true)] private static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer); } [HarmonyPatch] internal static class ClientStutterGuardGcCollectPatch { [CompilerGenerated] private sealed class <TargetMethods>d__0 : IEnumerable<MethodBase>, IEnumerable, IEnumerator<MethodBase>, IDisposable, IEnumerator { private int <>1__state; private MethodBase <>2__current; private int <>l__initialThreadId; private MethodInfo[] <>7__wrap1; private int <>7__wrap2; MethodBase IEnumerator<MethodBase>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <TargetMethods>d__0(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>7__wrap1 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; goto IL_006e; } <>1__state = -1; <>7__wrap1 = typeof(GC).GetMethods(BindingFlags.Static | BindingFlags.Public); <>7__wrap2 = 0; goto IL_007c; IL_006e: <>7__wrap2++; goto IL_007c; IL_007c: if (<>7__wrap2 < <>7__wrap1.Length) { MethodInfo methodInfo = <>7__wrap1[<>7__wrap2]; if (methodInfo.Name == "Collect") { <>2__current = methodInfo; <>1__state = 1; return true; } goto IL_006e; } <>7__wrap1 = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<MethodBase> IEnumerable<MethodBase>.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new <TargetMethods>d__0(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<MethodBase>)this).GetEnumerator(); } } [IteratorStateMachine(typeof(<TargetMethods>d__0))] private static IEnumerable<MethodBase> TargetMethods() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <TargetMethods>d__0(-2); } private static bool Prefix() { return ClientStutterGuard.TryHandleGcCollect(); } } [HarmonyPatch] internal static class ClientStutterGuardUnloadUnusedAssetsPatch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(Resources), "UnloadUnusedAssets", (Type[])null, (Type[])null); } private static bool Prefix(ref AsyncOperation __result) { return ClientStutterGuard.TryHandleUnloadUnusedAssetsPrefix(ref __result); } private static void Postfix(AsyncOperation __result) { ClientStutterGuard.OnUnloadUnusedAssetsPostfix(__result); } } [HarmonyPatch] internal static class ClientStutterGuardZNetConnectionPatch { private static MethodBase TargetMethod() { Type type = ReflectionCache.ZNetType ?? AccessTools.TypeByName("ZNet"); Type type2 = ReflectionCache.ZNetPeerType ?? AccessTools.TypeByName("ZNetPeer"); return AccessTools.Method(type, "OnNewConnection", (!(type2 != null)) ? null : new Type[1] { type2 }, (Type[])null) ?? AccessTools.Method(type, "OnNewConnection", (Type[])null, (Type[])null); } private static void Postfix() { ClientStutterGuard.MarkInitialSyncWindow(); } } [HarmonyPatch] internal static class ClientStutterGuardZdoDataPatch { [CompilerGenerated] private sealed class <TargetMethods>d__0 : IEnumerable<MethodBase>, IEnumerable, IEnumerator<MethodBase>, IDisposable, IEnumerator { private int <>1__state; private MethodBase <>2__current; private int <>l__initialThreadId; private MethodInfo[] <>7__wrap1; private int <>7__wrap2; MethodBase IEnumerator<MethodBase>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <TargetMethods>d__0(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>7__wrap1 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; goto IL_0084; } <>1__state = -1; Type type = ReflectionCache.ZDOManType ?? AccessTools.TypeByName("ZDOMan"); if (type == null) { return false; } <>7__wrap1 = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); <>7__wrap2 = 0; goto IL_0092; IL_0092: if (<>7__wrap2 < <>7__wrap1.Length) { MethodInfo methodInfo = <>7__wrap1[<>7__wrap2]; if (methodInfo.Name == "RPC_ZDOData") { <>2__current = methodInfo; <>1__state = 1; return true; } goto IL_0084; } <>7__wrap1 = null; return false; IL_0084: <>7__wrap2++; goto IL_0092; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<MethodBase> IEnumerable<MethodBase>.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new <TargetMethods>d__0(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<MethodBase>)this).GetEnumerator(); } } [IteratorStateMachine(typeof(<TargetMethods>d__0))] private static IEnumerable<MethodBase> TargetMethods() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <TargetMethods>d__0(-2); } private static void Prefix(object[] __args) { try { if (NetReflection.IsServer() || __args == null || ReflectionCache.ZPackageType == null) { return; } foreach (object obj in __args) { if (obj != null && ReflectionCache.ZPackageType.IsInstanceOfType(obj) && ZPackageTools.Size(obj) >= 32768) { ClientStutterGuard.MarkFullSnapshotBurstWindow(); break; } } } catch { } } } [HarmonyPatch] internal static class ClientStutterGuardLoadingScreenPatch { private static MethodBase TargetMethod() { return AccessTools.Method(AccessTools.TypeByName("ZNetScene"), "InLoadingScreen", (Type[])null, (Type[])null); } private static void Postfix(bool __result) { if (__result) { ClientStutterGuard.MarkTeleportWindow(); } } } [HarmonyPatch] internal static class ClientStutterGuardShipTravelPatch { private static FieldInfo _bodyField; private static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("Ship"); _bodyField = ReflectionCache.SilentField(type, "m_body"); return AccessTools.Method(type, "CustomFixedUpdate", (Type[])null, (Type[])null) ?? AccessTools.Method(type, "FixedUpdate", (Type[])null, (Type[])null); } private static void Postfix(object __instance) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (__instance == null) { return; } try { object? obj = _bodyField?.GetValue(__instance); Rigidbody val = (Rigidbody)((obj is Rigidbody) ? obj : null); if ((Object)(object)val != (Object)null) { Vector3 linearVelocity = val.linearVelocity; if (((Vector3)(ref linearVelocity)).sqrMagnitude > 4f) { ClientStutterGuard.MarkShipTravelWindow(); } } } catch { } } } [HarmonyPatch] internal static class MonsterAISetTargetOwnershipPatch { private static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("MonsterAI"); Type type2 = AccessTools.TypeByName("Character"); return AccessTools.Method(type, "SetTarget", (!(type2 != null)) ? null : new Type[1] { type2 }, (Type[])null) ?? AccessTools.Method("MonsterAI:SetTarget", (Type[])null, (Type[])null); } private static void Postfix(object __instance, object[] __args) { if (__args != null && __args.Length != 0) { ClientStutterGuard.MarkCombatWindow(); OwnershipManager.TryTransferCombatOwnership(__instance, __args[0]); } } } internal static class CompressionDiagnostics { private const double SummaryIntervalSeconds = 10.0; private static int _encoded; private static int _decoded; private static long _rawEncodeBytes; private static long _compressedEncodeBytes; private static long _compressedDecodeBytes; private static long _rawDecodeBytes; private static double _encodeSeconds; private static double _decodeSeconds; private static double _maxEncodeSeconds; private static double _maxDecodeSeconds; private static double _nextSummaryTime; internal static void RecordEncode(int rawBytes, int compressedBytes, double seconds) { if (ModConfig.DebugLogging.Value) { _encoded++; _rawEncodeBytes += Math.Max(0, rawBytes); _compressedEncodeBytes += Math.Max(0, compressedBytes); _encodeSeconds += Math.Max(0.0, seconds); if (seconds > _maxEncodeSeconds) { _maxEncodeSeconds = seconds; } LogIfDue(); } } internal static void RecordDecode(int compressedBytes, int rawBytes, double seconds) { if (ModConfig.DebugLogging.Value) { _decoded++; _compressedDecodeBytes += Math.Max(0, compressedBytes); _rawDecodeBytes += Math.Max(0, rawBytes); _decodeSeconds += Math.Max(0.0, seconds); if (seconds > _maxDecodeSeconds) { _maxDecodeSeconds = seconds; } LogIfDue(); } } private static void LogIfDue() { double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; if (!(realtimeSinceStartupAsDouble < _nextSummaryTime)) { _nextSummaryTime = realtimeSinceStartupAsDouble + 10.0; if (_encoded > 0 || _decoded > 0) { float num = ((_rawEncodeBytes > 0) ? ((float)_compressedEncodeBytes / (float)_rawEncodeBytes) : 0f); Plugin.Log.LogDebug((object)($"Compression summary {10.0:F0}s: encoded={_encoded} raw={FormatBytes(_rawEncodeBytes)} compressed={FormatBytes(_compressedEncodeBytes)} ratio={num:F2} " + $"encodeAvgMs={_encodeSeconds / (double)Math.Max(1, _encoded) * 1000.0:F2} encodeMaxMs={_maxEncodeSeconds * 1000.0:F2} " + $"decoded={_decoded} compressedIn={FormatBytes(_compressedDecodeBytes)} rawOut={FormatBytes(_rawDecodeBytes)} " + $"decodeAvgMs={_decodeSeconds / (double)Math.Max(1, _decoded) * 1000.0:F2} decodeMaxMs={_maxDecodeSeconds * 1000.0:F2}")); } _encoded = 0; _decoded = 0; _rawEncodeBytes = 0L; _compressedEncodeBytes = 0L; _compressedDecodeBytes = 0L; _rawDecodeBytes = 0L; _encodeSeconds = 0.0; _decodeSeconds = 0.0; _maxEncodeSeconds = 0.0; _maxDecodeSeconds = 0.0; } } private static string FormatBytes(long bytes) { if (bytes >= 1048576) { return $"{(float)bytes / 1048576f:F1}MB"; } if (bytes >= 1024) { return $"{(float)bytes / 1024f:F1}KB"; } return $"{bytes}B"; } } [HarmonyPatch] internal static class ZSteamSocketSendCompressionPatch { private static MethodBase TargetMethod() { return ReflectionCache.ZSteamSocketSendMethod ?? AccessTools.Method("ZSteamSocket:Send", (Type[])null, (Type[])null); } private static void Prefix(object __instance, ref object __0) { if (!EffectiveConfig.CompressionEnabled || __instance == null || __0 == null || !FeatureNegotiation.IsCompressionActiveForSocket(__instance)) { return; } try { int rawBytes = ZPackageTools.Size(__0); double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; if (ZPackageTools.TryBuildCompressedPackage(__0, out var compressedPackage)) { __0 = compressedPackage; CompressionDiagnostics.RecordEncode(rawBytes, ZPackageTools.Size(compressedPackage), Time.realtimeSinceStartupAsDouble - realtimeSinceStartupAsDouble); } } catch (Exception ex) { FeatureNegotiation.RecordCompressionFailure(__instance); if (ModConfig.DebugLogging.Value) { Plugin.Log.LogWarning((object)("ZSteamSocket.Send compression failed: " + ex.Message)); } } } } [HarmonyPatch] internal static class ZSteamSocketRecvCompressionPatch { private static MethodBase TargetMethod() { return ReflectionCache.ZSteamSocketRecvMethod ?? AccessTools.Method("ZSteamSocket:Recv", (Type[])null, (Type[])null); } private static void Postfix(object __instance, ref object __result) { if (__result == null) { return; } try { int compressedBytes = ZPackageTools.Size(__result); double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; if (ZPackageTools.TryDecompressPackage(__result, out var rawPackage)) { __result = rawPackage; CompressionDiagnostics.RecordDecode(compressedBytes, ZPackageTools.Size(rawPackage), Time.realtimeSinceStartupAsDouble - realtimeSinceStartupAsDouble); } } catch (Exception ex) { FeatureNegotiation.RecordCompressionFailure(__instance); if (ModConfig.DebugLogging.Value) { Plugin.Log.LogWarning((object)("ZSteamSocket.Recv decompression failed: " + ex.Message)); } } } } internal enum ConfigSyncScope { ServerSynced, ClientLocal, MigrationOnly } internal static class ConfigSyncManager { private static MethodInfo _addConfigEntryMethod; internal static ConfigSync Sync { get; private set; } internal static ConfigEntry<bool> LockServerConfig { get; private set; } internal static void Initialize(ConfigFile config) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown Sync = new ConfigSync("sighsorry.SkadiNet") { DisplayName = "SkadiNet", CurrentVersion = "1.0.0", MinimumRequiredVersion = "1.0.0", ModRequired = true }; LockServerConfig = config.Bind<bool>("General", "LockServerConfig", true, new ConfigDescription("Lock server-synced config for non-admin clients.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = 90 } })); Sync.AddLockingConfigEntry<bool>(LockServerConfig).SynchronizedConfig = true; } internal static ConfigEntry<T> Bind<T>(ConfigFile config, string group, string name, T value, string description, ConfigSyncScope scope) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown return Bind(config, group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), scope); } internal static ConfigEntry<T> Bind<T>(ConfigFile config, string group, string name, T value, ConfigDescription description, ConfigSyncScope scope) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown string text = SuffixFor(scope); ConfigDescription val = new ConfigDescription(description.Description + text, description.AcceptableValues, description.Tags); ConfigEntry<T> val2 = config.Bind<T>(group, name, value, val); if (scope != ConfigSyncScope.MigrationOnly) { Register((ConfigEntryBase)(object)val2, scope == ConfigSyncScope.ServerSynced); } return val2; } private static string SuffixFor(ConfigSyncScope scope) { return scope switch { ConfigSyncScope.ServerSynced => " [Synced with Server]", ConfigSyncScope.ClientLocal => " [Client Local; Not Synced with Server]", ConfigSyncScope.MigrationOnly => " [Migration Only; Not Synced with Server]", _ => throw new ArgumentOutOfRangeException("scope", scope, null), }; } private static void Register(ConfigEntryBase entry, bool synchronized) { if (Sync != null && entry != null && (LockServerConfig == null || !object.Equals(entry.Definition, ((ConfigEntryBase)LockServerConfig).Definition)) && GetAddConfigEntryMethod().MakeGenericMethod(entry.SettingType).Invoke(Sync, new object[1] { entry }) is OwnConfigEntryBase ownConfigEntryBase) { ownConfigEntryBase.SynchronizedConfig = synchronized; } } private static MethodInfo GetAddConfigEntryMethod() { if (_addConfigEntryMethod != null) { return _addConfigEntryMethod; } _addConfigEntryMethod = typeof(ConfigSync).GetMethods(BindingFlags.Instance | BindingFlags.Public).First((MethodInfo method) => method.Name == "AddConfigEntry" && method.IsGenericMethodDefinition); return _addConfigEntryMethod; } } internal sealed class ConfigurationManagerAttributes { public int? Order; } internal static class EffectiveConfig { internal const int SteamSendRateBytes = 36000000; internal static bool SchedulerEnabled { get { if (ModConfig.Enabled.Value) { return IsPositive(ModConfig.SchedulerThroughput); } return false; } } internal static bool PayloadReducerEnabled { get { if (ModConfig.Enabled.Value) { return IsPositive(ModConfig.PayloadReducerStrength); } return false; } } internal static bool CompressionEnabled { get { if (ModConfig.Enabled.Value) { return IsPositive(ModConfig.CompressionAggression); } return false; } } internal static bool RpcAoiEnabled { get { if (ModConfig.Enabled.Value) { return IsPositive(ModConfig.RpcAoiAggression); } return false; } } internal static bool ClientStutterGuardEnabled { get { if (ModConfig.Enabled.Value) { return IsPositive(ModConfig.ClientStutterGuardStrength); } return false; } } internal static bool AdaptiveOwnershipEnabled { get { if (ModConfig.Enabled.Value) { return IsPositive(ModConfig.OwnershipIntensity); } return false; } } internal static bool PeerQualityEnabled => AdaptiveOwnershipEnabled; internal static bool OwnerHintsEnabled => AdaptiveOwnershipEnabled; internal static float SendInterval => Map(ModConfig.SchedulerThroughput, 0.1f, 0.05f, 0.02f); internal static float MinSendInterval => Map(ModConfig.SchedulerThroughput, 0.06f, 0.03f, 0.015f); internal static float MaxSendInterval => Map(ModConfig.SchedulerThroughput, 0.2f, 0.1f, 0.045f); internal static int BasePeersPerTick => MapInt(ModConfig.SchedulerThroughput, 1, 4, 12); internal static int MaxPeersPerTick => MapInt(ModConfig.SchedulerThroughput, 4, 12, 32); internal static int ZdoQueueLimitBytes => MapInt(ModConfig.SchedulerThroughput, 10240, 65536, 196608); internal static int ZdoQueueMinPackageBytes => MapInt(ModConfig.SchedulerThroughput, 2048, 2048, 768); internal static int PeerQueueSoftLimitBytes => MapInt(ModConfig.SchedulerThroughput, 1572864, 524288, 196608); internal static int PeerQueueHardLimitBytes => MapInt(ModConfig.SchedulerThroughput, 6291456, 2097152, 786432); internal static float LaggingPeerMaxSkipSeconds => Map(ModConfig.SchedulerThroughput, 3f, 1f, 0.2f); internal static float PayloadVec3CullSize => Map(ModConfig.PayloadReducerStrength, 0.005f, 0.04f, 0.12f); internal static float PayloadQuaternionDotThreshold => Map(ModConfig.PayloadReducerStrength, 0.9998f, 0.995f, 0.985f); internal static float PayloadForceRefreshSeconds => Map(ModConfig.PayloadReducerStrength, 0.15f, 1f, 3f); internal static int CompressionThresholdBytes => MapInt(ModConfig.CompressionAggression, 8192, 1024, 128); internal static float CompressionMinUsefulRatio => Map(ModConfig.CompressionAggression, 0.7f, 0.9f, 0.99f); internal static int CompressionDisableAfterFailures => 1; internal static float RpcAoiVisualRadius => Map(ModConfig.RpcAoiAggression, 640f, 192f, 64f); internal static float ClientStutterInitialSyncWindowSeconds => Map(ModConfig.ClientStutterGuardStrength, 2f, 10f, 24f); internal static float ClientStutterTeleportWindowSeconds => Map(ModConfig.ClientStutterGuardStrength, 1f, 5f, 14f); internal static float ClientStutterFullSnapshotWindowSeconds => Map(ModConfig.ClientStutterGuardStrength, 0.25f, 1.5f, 5f); internal static float ClientStutterCombatWindowSeconds => Map(ModConfig.ClientStutterGuardStrength, 0.35f, 2f, 6f); internal static float ClientStutterShipWindowSeconds => Map(ModConfig.ClientStutterGuardStrength, 0.35f, 2f, 6f); internal static float ClientStutterMaxDelaySeconds => Map(ModConfig.ClientStutterGuardStrength, 6f, 30f, 90f); internal static int ClientStutterMemoryPressureThresholdPercent => MapInt(ModConfig.ClientStutterGuardStrength, 55, 75, 95); internal static int ClientStutterMinimumFreeMemoryMB => MapInt(ModConfig.ClientStutterGuardStrength, 6144, 2048, 512); internal static float ClientStutterIdleCleanupPollSeconds => Map(ModConfig.ClientStutterGuardStrength, 0.25f, 1f, 4f); internal static float PeerPingEmaHalfLifeSeconds => Map(ModConfig.OwnershipIntensity, 6f, 2.5f, 0.75f); internal static int PeerPingSampleWindow => MapInt(ModConfig.OwnershipIntensity, 120, 60, 20); internal static float PeerQualityMeanWeight => Map(ModConfig.OwnershipIntensity, 0.35f, 0f, 0f); internal static float PeerQualityStdDevWeight => Map(ModConfig.OwnershipIntensity, 0.1f, 0.25f, 0.7f); internal static float PeerQualityJitterWeight => Map(ModConfig.OwnershipIntensity, 0.2f, 0.5f, 1.2f); internal static float PeerQualityEmaWeight => 1f; internal static float MaxCandidatePingMs => Map(ModConfig.OwnershipIntensity, 320f, 220f, 140f); internal static float MaxCandidateJitterMs => Map(ModConfig.OwnershipIntensity, 180f, 100f, 45f); internal static int OwnershipScanBudget => MapInt(ModConfig.OwnershipIntensity, 24, 96, 256); internal static int OwnershipScanStride => MapInt(ModConfig.OwnershipIntensity, 10, 4, 1); internal static float OwnershipScanIntervalSeconds => Map(ModConfig.OwnershipIntensity, 3f, 1f, 0.25f); internal static float OwnershipRelativeHysteresis => Map(ModConfig.OwnershipIntensity, 0.03f, 0.15f, 0.4f); internal static float OwnershipAbsoluteHysteresisMs => Map(ModConfig.OwnershipIntensity, 5f, 20f, 90f); internal static float OwnerSwitchCooldownSeconds => Map(ModConfig.OwnershipIntensity, 0.75f, 3f, 12f); internal static float OwnerHintSwitchCooldownSeconds => Map(ModConfig.OwnershipIntensity, 1f, 5f, 16f); internal static float ShipOwnerSwitchCooldownSeconds => Map(ModConfig.OwnershipIntensity, 3f, 8f, 24f); internal static float RecoverUnownedAfterSeconds => Map(ModConfig.OwnershipIntensity, 0.75f, 2f, 6f); internal static float OwnershipCandidateRadius => Map(ModConfig.OwnershipIntensity, 80f, 160f, 360f); internal static float OwnerHintCandidateRadius => Map(ModConfig.OwnershipIntensity, 128f, 256f, 640f); internal static float OwnershipDistanceScoreWeight => Map(ModConfig.OwnershipIntensity, 0.4f, 0.2f, 0.06f); internal static float OwnershipLoadPenaltyPerZdo => Map(ModConfig.OwnershipIntensity, 0.5f, 0.35f, 0.15f); internal static float ServerFallbackPenaltyMs => Map(ModConfig.OwnershipIntensity, 450f, 650f, 1000f); internal static float OwnerHintScoreBonusMs => Map(ModConfig.OwnershipIntensity, 20f, 90f, 240f); internal static float OwnerHintLifetimeSeconds => Map(ModConfig.OwnershipIntensity, 2f, 8f, 20f); private static bool IsPositive(ConfigEntry<int> entry) { return Clamp(entry?.Value ?? 0, 0, 100) > 0; } private static float Strength(ConfigEntry<int> entry) { return (float)Clamp(entry?.Value ?? 50, 0, 100) / 100f; } private static float Map(ConfigEntry<int> entry, float safe, float current, float aggressive) { float num = Strength(entry); if (!(num <= 0.5f)) { return Lerp(current, aggressive, (num - 0.5f) * 2f); } return Lerp(safe, current, num * 2f); } private static int MapInt(ConfigEntry<int> entry, int safe, int current, int aggressive) { return (int)Math.Round(Map(entry, safe, current, aggressive)); } private static float Clamp(float value, float min, float max) { if (float.IsNaN(value) || float.IsInfinity(value)) { return min; } return Math.Max(min, Math.Min(max, value)); } private static int Clamp(int value, int min, int max) { return Math.Max(min, Math.Min(max, value)); } private static float Lerp(float a, float b, float t) { return a + (b - a) * Math.Max(0f, Math.Min(1f, t)); } } [Flags] internal enum PeerFeatureFlags { None = 0, Compression = 1, RpcAoi = 4 } internal sealed class PeerFeatureState { public object Rpc; public object Socket; public long Uid; public bool RegisteredRpc; public bool HandshakeSent; public bool HandshakeReceived; public int RemoteProtocol; public PeerFeatureFlags RemoteCapabilities; public bool CompressionActive; public bool RpcAoiActive; public int CompressionFailures; public double LastHandshakeTime; } internal static class FeatureNegotiation { internal const int ProtocolVersion = 2; internal const int FeatureMagic = 1179536211; internal const string RpcName = "SkadiNet_Features"; private static readonly object Lock = new object(); private static readonly Dictionary<object, PeerFeatureState> ByRpc = new Dictionary<object, PeerFeatureState>(); private static readonly Dictionary<object, PeerFeatureState> BySocket = new Dictionary<object, PeerFeatureState>(); private static readonly Dictionary<long, PeerFeatureState> ByUid = new Dictionary<long, PeerFeatureState>(); internal static PeerFeatureFlags LocalCapabilities => PeerFeatureFlags.Compression | PeerFeatureFlags.RpcAoi; internal static void Initialize() { lock (Lock) { ByRpc.Clear(); BySocket.Clear(); ByUid.Clear(); } } internal static PeerFeatureState GetOrCreateByRpc(object rpc, long uid = 0L) { if (rpc == null) { return null; } lock (Lock) { if (!ByRpc.TryGetValue(rpc, out var value)) { value = new PeerFeatureState { Rpc = rpc, Uid = uid }; ByRpc[rpc] = value; } if (uid != 0L) { value.Uid = uid; ByUid[uid] = value; } object socketFromRpc = NetReflection.GetSocketFromRpc(rpc); if (socketFromRpc != null) { value.Socket = socketFromRpc; BySocket[socketFromRpc] = value; } return value; } } internal static PeerFeatureState GetBySocket(object socket) { if (socket == null) { return null; } lock (Lock) { BySocket.TryGetValue(socket, out var value); return value; } } internal static PeerFeatureState GetByUid(long uid) { lock (Lock) { ByUid.TryGetValue(uid, out var value); return value; } } internal static void ClearPeer(object peerOrRpc, long uid) { object obj = NetReflection.GetPeerRpc(peerOrRpc); if (obj == null && peerOrRpc != null && ReflectionCache.ZRpcType != null && ReflectionCache.ZRpcType.IsInstanceOfType(peerOrRpc)) { obj = peerOrRpc; } object socketFromRpc = NetReflection.GetSocketFromRpc(obj); lock (Lock) { PeerFeatureState value = null; if (uid != 0L) { ByUid.TryGetValue(uid, out value); } if (value == null && obj != null) { ByRpc.TryGetValue(obj, out value); } if (value == null && socketFromRpc != null) { BySocket.TryGetValue(socketFromRpc, out value); } if (uid != 0L) { ByUid.Remove(uid); } if (obj != null) { ByRpc.Remove(obj); } if (socketFromRpc != null) { BySocket.Remove(socketFromRpc); } if (value != null) { if (value.Uid != 0L) { ByUid.Remove(value.Uid); } if (value.Rpc != null) { ByRpc.Remove(value.Rpc); } if (value.Socket != null) { BySocket.Remove(value.Socket); } RemoveState(ByRpc, value); RemoveState(BySocket, value); RemoveState(ByUid, value); } } } private static void RemoveState<TKey>(Dictionary<TKey, PeerFeatureState> map, PeerFeatureState state) { if (state == null || map.Count == 0) { return; } List<TKey> list = new List<TKey>(); foreach (KeyValuePair<TKey, PeerFeatureState> item in map) { if (item.Value == state) { list.Add(item.Key); } } foreach (TKey item2 in list) { map.Remove(item2); } } internal static bool IsCompressionActiveForSocket(object socket) { if (!EffectiveConfig.CompressionEnabled) { return false; } PeerFeatureState bySocket = GetBySocket(socket); if (bySocket == null) { return false; } if (bySocket.CompressionFailures >= EffectiveConfig.CompressionDisableAfterFailures) { return false; } if (bySocket.HandshakeReceived) { return Supports(bySocket, PeerFeatureFlags.Compression); } return false; } internal static bool IsRpcAoiActiveForUid(long uid) { if (!EffectiveConfig.RpcAoiEnabled) { return false; } PeerFeatureState byUid = GetByUid(uid); if (byUid == null) { return true; } if (byUid.HandshakeReceived) { return Supports(byUid, PeerFeatureFlags.RpcAoi); } return true; } internal static void RecordCompressionFailure(object socket) { PeerFeatureState bySocket = GetBySocket(socket); if (bySocket != null) { bySocket.CompressionFailures++; bySocket.CompressionActive = false; } } internal static void OnNewConnection(object znetPeer) { if (ModConfig.Enabled.Value) { object peerRpc = NetReflection.GetPeerRpc(znetPeer); if (peerRpc != null) { NetReflection.TryGetPeerUid(znetPeer, out var uid); PeerFeatureState orCreateByRpc = GetOrCreateByRpc(peerRpc, uid); RegisterRpc(peerRpc, orCreateByRpc); SendHello(peerRpc, orCreateByRpc); } } } private static void RegisterRpc(object rpc, PeerFeatureState state) { if (rpc == null || state == null || state.RegisteredRpc) { return; } try { if (!(ReflectionCache.ZRpcRegisterGenericPackageMethod == null) && !(ReflectionCache.ZRpcType == null) && !(ReflectionCache.ZPackageType == null)) { MethodInfo method = typeof(FeatureNegotiation).GetMethod("RPC_Features_Generic", BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(ReflectionCache.ZRpcType, ReflectionCache.ZPackageType); Delegate @delegate = Delegate.CreateDelegate(typeof(Action<, >).MakeGenericType(ReflectionCache.ZRpcType, ReflectionCache.ZPackageType), method); ReflectionCache.ZRpcRegisterGenericPackageMethod.Invoke(rpc, new object[2] { "SkadiNet_Features", @delegate }); state.RegisteredRpc = true; } } catch (Exception ex) { if (ModConfig.DebugLogging.Value) { Plugin.Log.LogWarning((object)("Could not register SkadiNet_Features: " + ex.Message)); } } } private static void SendHello(object rpc, PeerFeatureState state) { if (rpc == null || state == null || state.HandshakeSent) { return; } try { object obj = ZPackageTools.NewPackage(); ZPackageTools.WriteInt(obj, 1179536211); ZPackageTools.WriteInt(obj, 2); ZPackageTools.WriteInt(obj, (int)LocalCapabilities); ZPackageTools.WriteString(obj, "1.0.0"); ReflectionCache.ZRpcInvokeMethod?.Invoke(rpc, new object[2] { "SkadiNet_Features", new object[1] { obj } }); state.HandshakeSent = true; state.LastHandshakeTime = Time.realtimeSinceStartupAsDouble; } catch (Exception ex) { if (ModConfig.DebugLogging.Value) { Plugin.Log.LogWarning((object)("Could not send SkadiNet feature handshake: " + ex.Message)); } } } private static void RPC_Features_Generic<TRpc, TPkg>(TRpc rpc, TPkg pkg) { RPC_Features(rpc, pkg); } private static void RPC_Features(object rpc, object pkg) { try { PeerFeatureState orCreateByRpc = GetOrCreateByRpc(rpc, 0L); if (orCreateByRpc == null || pkg == null) { return; } int pos = ZPackageTools.GetPos(pkg); ZPackageTools.SetPos(pkg, 0); if (ZPackageTools.ReadInt(pkg) != 1179536211) { ZPackageTools.SetPos(pkg, pos); return; } int num = ZPackageTools.ReadInt(pkg); int remoteCapabilities = ZPackageTools.ReadInt(pkg); string text = ZPackageTools.ReadString(pkg); orCreateByRpc.RemoteProtocol = num; orCreateByRpc.RemoteCapabilities = (PeerFeatureFlags)remoteCapabilities; orCreateByRpc.HandshakeReceived = num >= 1; RefreshActiveFlags(orCreateByRpc); if (ModConfig.DebugLogging.Value) { Plugin.Log.LogDebug((object)$"SkadiNet feature handshake: protocol={num}, capabilities={orCreateByRpc.RemoteCapabilities}, version={text}, compression={orCreateByRpc.CompressionActive}, rpcAoi={orCreateByRpc.RpcAoiActive}"); } if (!orCreateByRpc.HandshakeSent) { SendHello(rpc, orCreateByRpc); } } catch (Exception ex) { if (ModConfig.DebugLogging.Value) { Plugin.Log.LogWarning((object)("SkadiNet feature handshake receive failed: " + ex.Message)); } } } private static bool Supports(PeerFeatureState state, PeerFeatureFlags flag) { if (state != null) { return (state.RemoteCapabilities & flag) != 0; } return false; } private static void RefreshActiveFlags(PeerFeatureState state) { if (state != null) { state.CompressionActive = EffectiveConfig.CompressionEnabled && Supports(state, PeerFeatureFlags.Compression); state.RpcAoiActive = EffectiveConfig.RpcAoiEnabled && Supports(state, PeerFeatureFlags.RpcAoi); } } } [HarmonyPatch] internal static class ZNetOnNewConnectionFeatureHandshakePatch { private static MethodBase TargetMethod() { Type type = ReflectionCache.ZNetType ?? AccessTools.TypeByName("ZNet"); Type type2 = ReflectionCache.ZNetPeerType ?? AccessTools.TypeByName("ZNetPeer"); return AccessTools.Method(type, "OnNewConnection", (!(type2 != null)) ? null : new Type[1] { type2 }, (Type[])null) ?? AccessTools.Method(type, "OnNewConnection", (Type[])null, (Type[])null); } private static void Postfix(object __0) { FeatureNegotiation.OnNewConnection(__0); } } internal static class FrameHitchDiagnostics { private struct NetworkSnapshot { public bool IsServer; public bool IsDedicated; public int ZNetPeers; public int ZdoPeers; public int QueueTotalBytes; public int QueueMaxBytes; public int QueueSoftPeers; public int QueueHardPeers; public int ZdoSectors; public int ZdoObjectsApprox; public bool ZdoObjectCountCapped; public long ManagedMemoryBytes; public override string ToString() { string text = (ZdoObjectCountCapped ? "+" : ""); return $"network[server={IsServer} dedicated={IsDedicated} znetPeers={ZNetPeers} zdoPeers={ZdoPeers} " + $"queueTotal={FormatBytes(QueueTotalBytes)} queueMax={FormatBytes(QueueMaxBytes)} queueSoftPeers={QueueSoftPeers} queueHardPeers={QueueHardPeers} " + $"zdoSectors={ZdoSectors} zdoObjects~={ZdoObjectsApprox}{text} managedMem={FormatBytes(ManagedMemoryBytes)}]"; } } private const double HitchThresholdSeconds = 0.12; private const double SevereHitchThresholdSeconds = 0.25; private const double SummaryIntervalSeconds = 10.0; private const double HitchLogCooldownSeconds = 0.75; private const int MaxZdoObjectsSnapshotCount = 50000; private static double _lastRealtime; private static double _lastHitchLogTime; private static double _nextSummaryTime; private static double _summarySeconds; private static int _summaryFrames; private static int _summaryHitches; private static int _summarySevereHitches; private static double _summaryMaxFrameSeconds; internal static void Update() { if (!DiagnosticsEnabled()) { return; } double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; if (_lastRealtime <= 0.0) { _lastRealtime = realtimeSinceStartupAsDouble; _nextSummaryTime = realtimeSinceStartupAsDouble + 10.0; return; } double num = Math.Max(0.0, realtimeSinceStartupAsDouble - _lastRealtime); _lastRealtime = realtimeSinceStartupAsDouble; double num2 = Math.Max(Time.unscaledDeltaTime, num); _summaryFrames++; _summarySeconds += num2; if (num2 > _summaryMaxFrameSeconds) { _summaryMaxFrameSeconds = num2; } if (num2 >= 0.12) { _summaryHitches++; } if (num2 >= 0.25) { _summarySevereHitches++; } if (num2 >= 0.12 && realtimeSinceStartupAsDouble - _lastHitchLogTime >= 0.75) { _lastHitchLogTime = realtimeSinceStartupAsDouble; LogFrameSnapshot(num2, num, (num2 >= 0.25) ? "severe" : "mild"); } if (realtimeSinceStartupAsDouble >= _nextSummaryTime) { LogSummary(); _nextSummaryTime = realtimeSinceStartupAsDouble + 10.0; } } private static bool DiagnosticsEnabled() { try { return Plugin.Log != null && ModConfig.Enabled != null && ModConfig.Enabled.Value && ModConfig.DebugLogging != null && ModConfig.DebugLogging.Value; } catch { return false; } } private static void LogFrameSnapshot(double frameDelta, double realtimeDelta, string severity) { NetworkSnapshot networkSnapshot = CaptureNetworkSnapshot(); string text = ZDOManSendSchedulerPatch.DescribeRecentState(); string text2 = OwnershipManager.DescribeRecentState(); Plugin.Log.LogInfo((object)($"Frame hitch {severity}: frameMs={frameDelta * 1000.0:F1} realtimeGapMs={realtimeDelta * 1000.0:F1} " + $"instantFps={SafeFps(frameDelta):F1} avgFps10s={AverageFps():F1} " + $"{networkSnapshot} {text} {text2} sliders[scheduler={Value(ModConfig.SchedulerThroughput)} payload={Value(ModConfig.PayloadReducerStrength)} " + $"compression={Value(ModConfig.CompressionAggression)} ownership={Value(ModConfig.OwnershipIntensity)} rpcAoi={Value(ModConfig.RpcAoiAggression)} " + $"stutterGuard={Value(ModConfig.ClientStutterGuardStrength)}]")); } private static void LogSummary() { if (_summaryFrames > 0) { NetworkSnapshot networkSnapshot = CaptureNetworkSnapshot(); Plugin.Log.LogInfo((object)($"Frame diagnostics {10.0:F0}s: frames={_summaryFrames} avgFps={AverageFps():F1} " + $"maxFrameMs={_summaryMaxFrameSeconds * 1000.0:F1} hitches>={120.0:F0}ms={_summaryHitches} " + $"severe>={250.0:F0}ms={_summarySevereHitches} {networkSnapshot}")); _summaryFrames = 0; _summarySeconds = 0.0; _summaryHitches = 0; _summarySevereHitches = 0; _summaryMaxFrameSeconds = 0.0; } } private static double AverageFps() { if (!(_summarySeconds > 0.0)) { return 0.0; } return (double)_summaryFrames / _summarySeconds; } private static double SafeFps(double frameDelta) { if (!(frameDelta > 0.0)) { return 0.0; } return 1.0 / frameDelta; } private static int Value(ConfigEntry<int> entry) { try { return entry?.Value ?? 0; } catch { return 0; } } private static NetworkSnapshot CaptureNetworkSnapshot() { NetworkSnapshot networkSnapshot = default(NetworkSnapshot); networkSnapshot.IsServer = NetReflection.IsServer(); networkSnapshot.IsDedicated = NetReflection.IsDedicatedServer(); NetworkSnapshot snapshot = networkSnapshot; object zDOManInstance = ZdoReflection.ZDOManInstance; foreach (object item in ZdoReflection.EnumeratePeers(zDOManInstance)) { snapshot.ZdoPeers++; int sendQueueSizeForPeer = NetReflection.GetSendQueueSizeForPeer(item); snapshot.QueueTotalBytes += sendQueueSizeForPeer; if (sendQueueSizeForPeer > snapshot.QueueMaxBytes) { snapshot.QueueMaxBytes = sendQueueSizeForPeer; } if (sendQueueSizeForPeer > EffectiveConfig.PeerQueueSoftLimitBytes) { snapshot.QueueSoftPeers++; } if (sendQueueSizeForPeer > EffectiveConfig.PeerQueueHardLimitBytes) { snapshot.QueueHardPeers++; } } foreach (object item2 in NetReflection.EnumerateZNetPeers()) { _ = item2; snapshot.ZNetPeers++; } CountZdoSectors(zDOManInstance, ref snapshot); snapshot.ManagedMemoryBytes = GC.GetTotalMemory(forceFullCollection: false); return snapshot; } private static void CountZdoSectors(object zdoMan, ref NetworkSnapshot snapshot) { try { if (zdoMan == null || ReflectionCache.ZDOObjectsBySectorField == null) { return; } object value = ReflectionCache.ZDOObjectsBySectorField.GetValue(zdoMan); if (value == null) { return; } if (value is IDictionary dictionary) { snapshot.ZdoSectors = dictionary.Count; { foreach (object value2 in dictionary.Values) { int num = 50000 - snapshot.ZdoObjectsApprox; if (num <= 0) { snapshot.ZdoObjectCountCapped = true; break; } snapshot.ZdoObjectsApprox += CountEnumerable(value2, num, ref snapshot.ZdoObjectCountCapped); } return; } } if (!(value is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { snapshot.ZdoSectors++; int num2 = 50000 - snapshot.ZdoObjectsApprox; if (num2 <= 0) { snapshot.ZdoObjectCountCapped = true; break; } snapshot.ZdoObjectsApprox += CountSectorEntry(item, num2, ref snapshot.ZdoObjectCountCapped); } } catch { } } private static int CountSectorEntry(object entry, int cap, ref bool capped) { if (entry == null) { return 0; } try { object value = entry; Type type = entry.GetType(); if (type.IsGenericType && type.FullName != null && type.FullName.StartsWith("System.Collections.Generic.KeyValuePair", StringComparison.Ordinal)) { value = type.GetProperty("Value")?.GetValue(entry, null) ?? entry; } return CountEnumerable(value, cap, ref capped); } catch { return 0; } } private static int CountEnumerable(object value, int cap, ref bool capped) { if (value == null) { return 0; } if (value is ICollection collection) { if (collection.Count > cap) { capped = true; } return Math.Min(collection.Count, cap); } if (!(value is IEnumerable enumerable)) { return 0; } int num = 0; foreach (object item in enumerable) { _ = item; num++; if (num >= cap) { capped = true; break; } } return num; } private static string FormatBytes(long bytes) { if (bytes >= 1073741824) { return $"{(float)bytes / 1.0737418E+09f:F2}GB"; } if (bytes >= 1048576) { return $"{(float)bytes / 1048576f:F1}MB"; } if (bytes >= 1024) { return $"{(float)bytes / 1024f:F1}KB"; } return $"{bytes}B"; } } internal static class GameplayReflection { internal static void Initialize() { } internal static object GetZdoFromNView(object nview) { if (nview == null) { return null; } try { return ReflectionCache.ZNetViewGetZDOMethod?.Invoke(nview, null) ?? ReflectionCache.ZNetViewZdoField?.GetValue(nview); } catch { return null; } } internal static object GetNViewFromCharacterLike(object instance) { if (instance == null) { return null; } try { return ReflectionCache.CharacterNViewField?.GetValue(instance); } catch { } try { return ReflectionCache.MonsterAINViewField?.GetValue(instance); } catch { } try { return ReflectionCache.CachedField(instance.GetType(), "m_nview")?.GetValue(instance); } catch { } return null; } internal static object GetZdoFromCharacterLike(object instance) { return GetZdoFromNView(GetNViewFromCharacterLike(instance)); } internal static bool TryGetPlayerId(object player, out long id) { id = 0L; if (player == null || ReflectionCache.PlayerGetPlayerIDMethod == null) { return false; } try { return ReflectionCache.TryConvertToLong(ReflectionCache.PlayerGetPlayerIDMethod.Invoke(player, null), out id); } catch { } return false; } internal static bool LooksLikePlayer(object obj) { if (obj == null) { return false; } Type type = obj.GetType(); if (!(type.Name == "Player")) { if (ReflectionCache.PlayerType != null) { return ReflectionCache.PlayerType.IsAssignableFrom(type); } return false; } return true; } } internal static class ModConfig { private const string GeneralSection = "General"; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> LockServerConfig; internal static ConfigEntry<bool> DebugLogging; internal static ConfigEntry<int> SchedulerThroughput; internal static ConfigEntry<int> PayloadReducerStrength; internal static ConfigEntry<int> OwnershipIntensity; internal static ConfigEntry<int> CompressionAggression; internal static ConfigEntry<int> RpcAoiAggression; internal static ConfigEntry<int> ClientStutterGuardStrength; internal static void Bind(ConfigFile config) { Enabled = ConfigSyncManager.Bind(config, "General", "Enabled", value: true, Description("Master switch.", 100), ConfigSyncScope.ServerSynced); LockServerConfig = ConfigSyncManager.LockServerConfig; DebugLogging = ConfigSyncManager.Bind(config, "General", "DebugLogging", value: false, Description("Diagnostic log output. Enable temporarily to inspect frame hitches, FPS, network queues, scheduler stalls, compression cost, and ownership scan cost; keep this off during normal live play.", 80), ConfigSyncScope.ClientLocal); SchedulerThroughput = ConfigSyncManager.Bind(config, "General", "SchedulerThroughput", 35, FeatureSliderDescription("0 disables the adaptive scheduler. 1 keeps package caps close to vanilla while using the gentlest adaptive scheduler; 35 is recommended; 50 is balanced; 100 favors lower latency, higher ZDO throughput, and faster lagging-peer backfill. Steam send-rate is fixed internally at 36 MB/s while SkadiNet is enabled.", 70), ConfigSyncScope.ServerSynced); PayloadReducerStrength = ConfigSyncManager.Bind(config, "General", "PayloadReducerStrength", 30, FeatureSliderDescription("0 disables the payload reducer. 1 favors sync fidelity; 50 is balanced; 100 applies stronger Vector3/Quaternion micro-update reduction.", 60), ConfigSyncScope.ServerSynced); CompressionAggression = ConfigSyncManager.Bind(config, "General", "CompressionAggression", 50, FeatureSliderDescription("0 disables negotiated package compression. 1 compresses only large/high-value packets; 50 is balanced; 100 considers smaller packets and smaller savings.", 50), ConfigSyncScope.ServerSynced); OwnershipIntensity = ConfigSyncManager.Bind(config, "General", "OwnershipIntensity", 45, FeatureSliderDescription("0 disables Profile A adaptive ownership, peer-quality gates, and combat owner hints. 1 is very conservative with low CPU, narrow candidate reach, weak hints, and forgiving peer quality; 45 is recommended; 50 is balanced; 100 scans farther/faster, uses stronger hints, and rejects poor ping/jitter candidates more aggressively.", 40), ConfigSyncScope.ServerSynced); ClientStutterGuardStrength = ConfigSyncManager.Bind(config, "General", "ClientStutterGuardStrength", 50, FeatureSliderDescription("0 disables the client stutter guard. 50 is the balanced default. 1 runs cleanup sooner under pressure; 100 protects longer against cleanup stutter.", 30), ConfigSyncScope.ClientLocal); RpcAoiAggression = ConfigSyncManager.Bind(config, "General", "RpcAoiAggression", 35, FeatureSliderDescription("0 disables RPC AoI. 35 is the conservative default. 1 keeps a larger radius for safe visual RPCs; 50 is balanced; 100 routes eligible visual RPCs to smaller local areas. Unknown, unresolved, global, animation, noise, and state-critical RPCs always use vanilla routing.", 20), ConfigSyncScope.ServerSynced); } private static ConfigDescription Description(string description, int order) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown return new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { Order = order } }); } private static ConfigDescription FeatureSliderDescription(string description, int order) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown return new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), new object[1] { new ConfigurationManagerAttributes { Order = order } }); } } internal static class NetReflection { [CompilerGenerated] private sealed class <EnumerateZNetPeers>d__24 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; private int <>l__initialThreadId; private IEnumerator <>7__wrap1; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <EnumerateZNetPeers>d__24(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap1 = null; <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: { <>1__state = -1; object zNetInstance = ZNetInstance; if (zNetInstance == null) { return false; } IEnumerable enumerable = null; try { enumerable = ReflectionCache.ZNetGetPeersMethod?.Invoke(zNetInstance, null) as IEnumerable; } catch { } if (enumerable == null) { try { enumerable = ReflectionCache.ZNetPeersField?.GetValue(zNetInstance) as IEnumerable; } catch { } } if (enumerable == null) { return false; } <>7__wrap1 = enumerable.GetEnumerator(); <>1__state = -3; break; } case 1: <>1__state = -3; break; } if (<>7__wrap1.MoveNext()) { object current = <>7__wrap1.Current; <>2__current = current; <>1__state = 1; return true; } <>m__Finally1(); <>7__wrap1 = null; return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap1 is IDisposable disposable) { disposable.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<object> IEnumerable<object>.GetEnumerator() { if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; return this; } return new <EnumerateZNetPeers>d__24(0); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<object>)this).GetEnumerator(); } } private static Func<object, object> _peerRpcGetter; private static Func<object, object> _peerUidGetter; private static Func<object, Vector3> _peerRefPosGetter; private static Func<object, object> _rpcSocketGetter; private static Func<object, object> _socketSendQueueSizeGetter; internal static Type ZNetType => ReflectionCache.ZNetType; internal static Type ZNetPeerType => ReflectionCache.ZNetPeerType; internal static Type ZRpcType => ReflectionCache.ZRpcType; internal static Type ZPackageType => ReflectionCache.ZPackageType; internal static object ZNetInstance => ReflectionCache.ZNetInstanceField?.GetValue(null); internal static void Initialize() { _peerRpcGetter = ReflectionDelegateFactory.BoxedFieldGetter(ReflectionCache.PeerRpcField); _peerUidGetter = ReflectionDelegateFactory.BoxedFieldGetter(ReflectionCache.PeerUidField); _peerRefPosGetter = ReflectionDelegateFactory.Vector3FieldGetter(ReflectionCache.PeerRefPosField); _rpcSocketGetter = ReflectionDelegateFactory.BoxedInstanceMethod(ReflectionCache.ZRpcGetSocketMethod); _socketSendQueueSizeGetter = ReflectionDelegateFactory.BoxedInstanceMethod(ReflectionCache.ZSteamSocketGetSendQueueSizeMethod); } internal static bool IsServer() { try { object zNetInstance = ZNetInstance; if (zNetInstance == null || ReflectionCache.ZNetIsServerMethod == null) { return false; } return (bool)ReflectionCache.ZNetIsServerMethod.Invoke(zNetInstance, null); } catch { return false; } } internal static bool IsDedicatedServer() { try { object zNetInstance = ZNetInstance; if (zNetInstance == null || ReflectionCache.ZNetIsDedicatedMethod == null) { return false; } return (bool)ReflectionCache.ZNetIsDedicatedMethod.Invoke(zNetInstance, null); } catch { return false; } } internal static bool TryGetPeerUid(object peerOrRpc, out long uid) { uid = 0L; if (peerOrRpc == null) { return false; } try { if (ReflectionCache.TryConvertToLong(TryGet(_peerUidGetter, peerOrRpc), out uid)) { return true; } if (ReflectionCache.TryConvertToLong(ReflectionCache.CachedField(peerOrRpc.GetType(), "m_uid")?.GetValue(peerOrRpc), out uid)) { return true; } return TryGetUidFromPeerObject(GetPeerRpc(peerOrRpc), out uid); } catch { return false; } } internal static bool TryGetUidFromPeerObject(object peerOrRpc, out long uid) { uid = 0L; if (peerOrRpc == null) { return false; } try { return ReflectionCache.TryConvertToLong(ReflectionCache.CachedField(peerOrRpc.GetType(), "m_uid")?.GetValue(peerOrRpc), out uid); } catch { return false; } } internal static object GetPeerRpc(object peer) { if (peer == null) { return null; } try { object obj = ReflectionCache.CachedField(peer.GetType(), "m_rpc")?.GetValue(peer); if (obj != null) { return obj; } object obj2 = TryGet(_peerRpcGetter, peer); if (obj2 == null) { return null; } return ReflectionCache.CachedField(obj2.GetType(), "m_rpc")?.GetValue(obj2) ?? obj2; } catch { return null; } } internal static object GetSocketFromRpc(object rpc) { if (rpc == null) { return null; } try { return TryGet(_rpcSocketGetter, rpc) ?? ReflectionCache.ZRpcGetSocketMethod?.Invoke(rpc, null); } catch { return null; } } internal static Vector3 GetPeerRefPos(object peer) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: 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_00ac: Unknown result type (might be due to invalid IL or missing references) if (peer == null) { return Vector3.zero; } try { if (TryGetVector3(_peerRefPosGetter, peer, out var value)) { return value; } if (ReflectionCache.CachedField(peer.GetType(), "m_refPos")?.GetValue(peer) is Vector3 result) { return result; } object obj = ReflectionCache.CachedField(peer.GetType(), "m_peer")?.GetValue(peer); if (obj != null && obj != peer) { object obj2 = ReflectionCache.CachedField(obj.GetType(), "m_refPos")?.GetValue(obj); if (obj2 is Vector3) { return (Vector3)obj2; } } } catch { } return Vector3.zero; } internal static int GetSendQueueSizeForPeer(object zdoPeer) { try { object peerRpc = GetPeerRpc(zdoPeer); if (peerRpc == null) { return 0; } object socketFromRpc = GetSocketFromRpc(peerRpc); if (socketFromRpc == null) { return 0; } object obj = TryGet(_socketSendQueueSizeGetter, socketFromRpc) ?? ReflectionCache.ZSteamSocketGetSendQueueSizeMethod?.Invoke(socketFromRpc, null) ?? AccessTools.Method(socketFromRpc.GetType(), "GetSendQueueSize", (Type[])null, (Type[])null)?.Invoke(socketFromRpc, null); if (obj is int result) { return result; } if (obj is long) { long val = (long)obj; return (int)Math.Min(2147483647L, val); } } catch { } return 0; } [IteratorStateMachine(typeof(<EnumerateZNetPeers>d__24))] internal static IEnumerable<object> EnumerateZNetPeers() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <EnumerateZNetPeers>d__24(-2); } internal static Vector3 GetReferencePosition(Vector3 fallback) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) try { object zNetInstance = ZNetInstance; if (zNetInstance != null && ReflectionCache.ZNetGetReferencePositionMethod != null) { object obj = ReflectionCache.ZNetGetReferencePositionMethod.Invoke(zNetInstance, null); if (obj is Vector3) { return (Vector3)obj; } } } catch { } return fallback; } private static object TryGet(Func<object, object> getter, object instance) { try { return (getter != null && instance != null) ? getter(instance) : null; } catch { return null; } } private static bool TryGetVector3(Func<object, Vector3> getter, object instance, out Vector3 value) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) value = Vector3.zero; try { if (getter == null || instance == null) { return false; } value = getter(instance); return true; } catch { return false; } } } internal enum OwnershipCandidateReason { Generic, CombatTarget, DisconnectedOwner, LongUnownedPersistent } internal sealed class OwnerState { public long CurrentOwner; public long PreviousOwner; public double LastOwnerChangeTime; public double LastCombatOwnerChangeTime; public double LastSeenUnownedTime; public double LastTouchedTime; public int ChangeCount; public long CombatTargetUid; public double CombatTargetHintTime; } internal sealed class OwnerCandidate { public long Uid; public object Peer; public float Distance; public float Quality; public float Score; public int EstimatedLoad; public bool IsCombatTarget; } internal static class OwnershipManager { private const int MaxOwnerStates = 50000; private const double OwnerStateTtlSeconds = 600.0; private const double OwnerStatePruneIntervalSeconds = 30.0; private const bool AllowShipOwnership = false; private const bool AllowHealthyOwnerChallenge = false; private const bool AllowServerFallbackForPersistentRecovery = true; private static readonly Dictionary<ZdoIdKey, OwnerState> ByZdoId = new Dictionary<ZdoIdKey, OwnerState>(); private static readonly Dictionary<long, int> OwnerLoadEstimate = new Dictionary<long, int>(); private static double _nextProfileAScan; private static double _nextOwnerStatePrune; private static int _scanCursor; private static int _sectorCursor; private static double _lastScanTime; private static double _lastScanSeconds; private static int _lastScanVisited; private static int _lastScanBudget; private static int _lastScanOwnerChanges; private static int _lastScanPeerCount; private static int _lastScanSectorCursor; private static bool _lastScanSkippedNoPeers; internal static bool ProfileAEnabled { get { if (!EffectiveConfig.AdaptiveOwnershipEnabled || !NetReflection.IsServer()) { return false; } return true; } } internal static void Initialize() { ByZdoId.Clear(); OwnerLoadEstimate.Clear(); _nextProfileAScan = 0.0; _nextOwnerStatePrune = 0.0; _scanCursor = 0; _sectorCursor = 0; _lastScanTime = 0.0; _lastScanSeconds = 0.0; _lastScanVisited = 0; _lastScanBudget = 0; _lastScanOwnerChanges = 0; _lastScanPeerCount = 0; _lastScanSectorCursor = 0; _lastScanSkippedNoPeers = false; } internal static bool TryTransferCombatOwnership(object monsterAI, object target) { if (!ProfileAEnabled || !EffectiveConfig.OwnerHintsEnabled) { return false; } if (!GameplayReflection.LooksLikePlayer(target)) { return false; } if (!GameplayReflection.TryGetPlayerId(target, out var id) || id == 0L) { return false; } object zdoFromCharacterLike = GameplayReflection.GetZdoFromCharacterLike(monsterAI); if (zdoFromCharacterLike == null) { return false; } OwnerState ownerState = GetOwnerState(zdoFromCharacterLike); ownerState.CombatTargetUid = id; ownerState.CombatTargetHintTime = Time.realtimeSinceStartupAsDouble; return TryMaybeImproveOwner(zdoFromCharacterLike, OwnershipCandidateReason.CombatTarget); } internal static void ClearPeer(long uid) { if (uid == 0L) { return; } OwnerLoadEstimate.Remove(uid); foreach (OwnerState value in ByZdoId.Values) { if (value.CombatTargetUid == uid) { value.CombatTargetUid = 0L; value.CombatTargetHintTime = 0.0; } } } internal static void TickLightweight(object zdoMan) { if (!ProfileAEnabled) { return; } if (!HasAnyZdoPeer(zdoMan, out var peerCount)) { _lastScanSkippedNoPeers = true; _lastScanPeerCount = 0; return; } double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; PruneOwnerStatesIfDue(realtimeSinceStartupAsDouble); if (!(realtimeSinceStartupAsDouble < _nextProfileAScan)) { _nextProfileAScan = realtimeSinceStartupAsDouble + (double)Math.Max(0.1f, EffectiveConfig.OwnershipScanIntervalSeconds); _lastScanPeerCount = peerCount; TryProfileAScan(zdoMan); } } internal static string DescribeRecentState() { double num = ((_lastScanTime > 0.0) ? Math.Max(0.0, Time.realtimeSinceStartupAsDouble - _lastScanTime) : (-1.0)); string text = ((num >= 0.0) ? $"{num:F2}s" : "n/a"); return $"ownershipRecent age={text} scanMs={_lastScanSeconds * 1000.0:F2} visited={_lastScanVisited}/{_lastScanBudget} " + $"ownerChanges={_lastScanOwnerChanges} zdoPeers={_lastScanPeerCount} sectorCursor={_lastScanSectorCursor} " + $"skippedNoPeers={_lastScanSkippedNoPeers}"; } private static bool HasAnyZdoPeer(object zdoMan, out int peerCount) { peerCount = 0; try { if (zdoMan == null || ReflectionCache.ZDOManPeersField == null) { return false; } object value = ReflectionCache.ZDOManPeersField.GetValue(zdoMan); if (value is ICollection collection) { peerCount = collection.Count; return peerCount > 0; } if (value is IEnumerable enumerable) { { IEnumerator enumerator = enumerable.GetEnumerator(); try { if (enumerator.MoveNext()) { _ = enumerator.Current; peerCount++; return true; } } finally { IDisposable disposable = enumerator as IDisposable; if (disposable != null) { disposable.Dispose(); } } } } } catch { } return false; } private static void TryProfileAScan(object zdoMan) { try { double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; OwnerLoadEstimate.Clear(); object obj = ReflectionCache.ZDOObjectsBySectorField?.GetValue(zdoMan); if (!(obj is IEnumerable enumerable)) { return; } int visited = 0; int acted = 0; int num = Math.Max(1, EffectiveConfig.OwnershipScanBudget); if (obj is IList list && list.Count > 0) { if (_sectorCursor < 0 || _sectorCursor >= list.Count) { _sectorCursor = 0; } int sectorCursor = _sectorCursor; int num2 = 0; while (true) { if (num2 < list.Count) { int num3 = (sectorCursor + num2) % list.Count; if (ScanBucket(list[num3], num, ref visited, ref acted)) { _sectorCursor = (num3 + 1) % list.Count; break; } num2++; continue; } _sectorCursor = 0; break; } } else { { IEnumerator enumerator = enumerable.GetEnumerator(); try { while (enumerator.MoveNext() && !ScanBucket(enumerator.Current, num, ref visited, ref acted)) { } } finally { IDisposable disposable = enumerator as IDisposable; if (disposable != null) { disposable.Dispose(); } } } } double num4 = Time.realtimeSinceStartupAsDouble - realtimeSinceStartupAsDouble; _lastScanTime = Time.realtimeSinceStartupAsDouble; _lastScanSeconds = num4; _lastScanVisited = visited; _lastScanBudget = num; _lastScanOwnerChanges = acted; _lastScanSectorCursor = _sectorCursor; _lastScanSkippedNoPeers = false; if (ModConfig.DebugLogging.Value && (acted > 0 || visited > 0)) { bool flag = num4 >= 0.008; if (acted > 0 || flag) { Plugin.Log.LogInfo((object)$"Adaptive ownership scan: elapsed={num4 * 1000.0:F2}ms slow={flag} visited={visited}/{num}, ownerChanges={acted}, zdoPeers={_lastScanPeerCount}, loadOwners={OwnerLoadEstimate.Count}, sectorCursor={_sectorCursor}, stride={EffectiveConfig.OwnershipScanStride}"); } } } catch (Exception ex) { if (ModConfig.DebugLogging.Value) { Plugin.Log.LogWarning((object)("Adaptive ownership scan failed: " + ex.Message)); } } } private static bool ScanBucket(object bucket, int budget, ref int visited, ref int acted) { if (!(bucket is IEnumerable enumerable)) { return false; } foreach (object item in enumerable) { if (item == null) { continue; } _scanCursor++; int num = Math.Max(1, EffectiveConfig.OwnershipScanStride); if (_scanCursor % num == 0) { TrackCurrentOwnerLoad(item); visited++; if (TryMaybeImproveOwner(item, OwnershipCandidateReason.Generic)) { acted++; } if (visited >= budget) { return true; } } } return false; } private static void TrackCurrentOwnerLoad(object zdo) { if (ZdoReflection.TryGetOwner(zdo, out var owner) && owner != 0L) { OwnerLoadEstimate.TryGetValue(owner, out var value); OwnerLoadEstimate[owner] = value + 1; } } private static bool TryMaybeImproveOwner(object zdo, OwnershipCandidateReason reason) { //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || ReflectionCache.ZDOGetPositionMethod == null) { return false; } if (!ZdoReflection.TryGetOwner(zdo, out var owner)) { owner = 0L; } bool persistent; bool flag = ZdoReflection.TryGetPersistent(zdo, out persistent) && persistent; bool num = ZdoKeyPolicy.LooksPlayerLike(zdo); bool flag2 = ZdoKeyPolicy.LooksShipLike(zdo); if (num) { return false; } if (flag2 && reason != OwnershipCandidateReason.DisconnectedOwner) { return false; } OwnerState ownerState = GetOwnerState(zdo); double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; bool flag3 = owner != 0L && IsUidCurrentlyConnected(owner); long uid; bool flag4 = owner != 0L && ZdoReflection.TryGetServerSessionId(out uid) && owner == uid; if (owner == 0L) { if (ownerState.LastSeenUnownedTime <= 0.0) { ownerState.LastSeenUnownedTime = realtimeSinceStartupAsDouble; } if (flag && realtimeSinceStartupAsDouble - ownerState.LastSeenUnownedTime >= (double)EffectiveConfig.RecoverUnownedAfterSeconds) { reason = OwnershipCandidateReason.LongUnownedPersistent; } } else { ownerState.LastSeenUnownedTime = 0.0; } if (flag && reason != OwnershipCandidateReason.LongUnownedPersistent && reason != OwnershipCandidateReason.DisconnectedOwner) { return false; } if (!flag3 && owner != 0L && !flag4) { reason = OwnershipCandidateReason.DisconnectedOwner; } Vector3 position = ZdoReflection.GetPosition(zdo, Vector3.zero); if (reason == OwnershipCandidateReason.Generic && flag3) { return false; } OwnerCandidate ownerCandidate = FindBestCandidate(zdo, position, ownerState, reason); if (ownerCandidate == null) { if (reason == OwnershipCandidateReason.LongUnownedPersistent) { return RecoverToServerOwner(zdo, ownerState, owner, reason); } return false; } if (ownerCandidate.Uid == owner) { return false; } float num2 = ComputeCurrentOwnerScore(owner, position, ownerState, reason, flag3, flag4); if (!IsCandidateBetter(ownerCandidate.Score, num2, reason)) { return false; } float num3 = ((reason == OwnershipCandidateReason.CombatTarget) ? EffectiveConfig.OwnerHintSwitchCooldownSeconds : EffectiveConfig.OwnerSwitchCooldownSeconds); if (flag2) { num3 = Math.Max(num3, EffectiveConfig.ShipOwnerSwitchCooldownSeconds); } if (realtimeSinceStartupAsDouble - ownerState.LastOwnerChangeTime < (double)num3) { return false; } if (reason == OwnershipCandidateReason.CombatTarget && realtimeSinceStartupAsDouble - ownerState.LastCombatOwnerChangeTime < (double)EffectiveConfig.OwnerHintSwitchCooldownSeconds) { return false; } if (!ZdoReflection.TrySetOwner(zdo, ownerCandidate.Uid)) { return false; } ownerState.PreviousOwner = owner; ownerState.CurrentOwner = ownerCandidate.Uid; ownerState.LastOwnerChangeTime = realtimeSinceStartupAsDouble; if (reason == OwnershipCandidateReason.CombatTarget) { ownerState.LastCombatOwnerChangeTime = realtimeSinceStartupAsDouble; } ownerState.ChangeCount++; ZdoReflection.ForceSend(zdo); if (ModConfig.DebugLogging.Value) { Plugin.Log.LogDebug((object)$"ProfileA owner {reason}: {owner}->{ownerCandidate.Uid}, best={ownerCandidate.Score:F1}, current={num2:F1}, q={ownerCandidate.Quality:F1}, dist={ownerCandidate.Distance:F1}, load={ownerCandidate.EstimatedLoad}"); } return true; } private static OwnerCandidate FindBestCandidate(object zdo, Vector3 zdoPosition, OwnerState state, OwnershipCandidateReason reason) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) OwnerCandidate ownerCandidate = null; float num = ((reason == OwnershipCandidateReason.CombatTarget) ? Math.Max(EffectiveConfig.OwnershipCandidateRadius, EffectiveConfig.OwnerHintCandidateRadius) : EffectiveConfig.OwnershipCandidateRadius); foreach (object item in ZdoReflection.EnumeratePeers(ZdoReflection.ZDOManInstance)) { if (!NetReflection.TryGetPeerUid(item, out var uid) || uid == 0L) { continue; } Vector3 peerRefPos = NetReflection.GetPeerRefPos(item); float num2 = Vector3.Distance(zdoPosition, peerRefPos); if (num2 > num) { continue; } PeerQualityState peerQualityState = PeerQualityMeter.UpdateFromPeer(item) ?? PeerQualityMeter.GetByUid(uid) ?? PeerQualityMeter.GetByPeer(item); if (CandidateConnectionAllowed(peerQualityState, reason)) { int num3 = EstimateOwnerLoad(uid); float num4 = ComputeCandidateScore(uid, num2, peerQualityState, num3, state, reason); if (ownerCandidate == null || num4 < ownerCandidate.Score) { ownerCandidate = new OwnerCandidate { Uid = uid, Peer = item, Distance = num2, Quality = (peerQualityState?.ConnectionQualityMs ?? 999f), Score = num4, EstimatedLoad = num3, IsCombatTarget = (uid == state.CombatTargetUid && IsCombatHintFresh(state)) }; } } } return ownerCandidate; } private static float ComputeCandidateScore(long uid, float distance, PeerQualityState quality, int load, OwnerState state, OwnershipCandidateReason reason) { float num = quality?.ConnectionQualityMs ?? 999f; num += distance * Math.Max(0f, EffectiveConfig.OwnershipDistanceScoreWeight); num += (float)load * Math.Max(0f, EffectiveConfig.OwnershipLoadPenaltyPerZdo); if (uid == state.CombatTargetUid && IsCombatHintFresh(state)) { num -= Math.Max(0f, EffectiveConfig.OwnerHintScoreBonusMs); } return num; } private static float ComputeCurrentOwnerScore(long currentOwner, Vector3 zdoPosition, OwnerState state, OwnershipCandidateReason reason, bool currentConnected, bool currentIsServer) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (currentOwner == 0L) { return 999f; } if (!currentConnected && !currentIsServer) { return 999f; } if (currentIsServer) { return EffectiveConfig.ServerFallbackPenaltyMs; } PeerQualityState byUid = PeerQualityMeter.GetByUid(currentOwner); object obj = FindZdoPeerByUid(currentOwner); float distance = ((obj != null) ? Vector3.Distance(zdoPosition, NetReflection.GetPeerRefPos(obj)) : 0f); return ComputeCandidateScore(currentOwner, distance, byUid, EstimateOwnerLoad(currentOwner), state, reason); } private static bool RecoverToServerOwner(object zdo, OwnerState state, long currentOwner, OwnershipCandidateReason reason) { if (!ZdoReflection.TryGetServerSessionId(out var uid) || uid == 0L) { return false; } if (!ZdoReflection.TrySetOwner(zdo, uid)) { return false; } double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; state.PreviousOwner = currentOwner; state.CurrentOwner = uid; state.LastOwnerChangeTime = realtimeSinceStartupAsDouble; state.ChangeCount++; ZdoReflection.ForceSend(zdo); if (ModConfig.DebugLogging.Value) { Plugin.Log.LogDebug((object)$"ProfileA recovery to server owner {reason}: {currentOwner}->{uid}"); } return true; } private static int EstimateOwnerLoad(long uid) { if (uid == 0L) { return 0; } if (OwnerLoadEstimate.TryGetValue(uid, out var value)) { return value; } return PeerQualityMeter.GetByUid(uid)?.OwnedDynamicEstimate ?? 0; } private static bool CandidateConnectionAllowed(PeerQualityState candidate, OwnershipCandidateReason reason) { if (!EffectiveConfig.PeerQualityEnabled) { return true; } if (candidate == null || !candidate.HasAnySample) { return false; } if (candidate.PingEmaMs > EffectiveConfig.MaxCandidatePingMs) { return false; } if (candidate.PingJitterMs > EffectiveConfig.MaxCandidateJitterMs) { return false; } return true; } private static bool IsCandidateBetter(float candidateScore, float currentScore, OwnershipCandidateReason reason) { if (currentScore <= 0f || currentScore >= 900f) { return true; } float num = Math.Max(0f, EffectiveConfig.OwnershipRelativeHysteresis); float val = Math.Max(0f, EffectiveConfig.OwnershipAbsoluteHysteresisMs); if (reason == OwnershipCandidateReason.DisconnectedOwner || reason == OwnershipCandidateReason.LongUnownedPersistent) { val = Math.Min(val, 5f); } float num2 = Math.Max(val, currentScore * num); return currentScore - candidateScore >= num2; } private static OwnerState GetOwnerState(object zdo) { if (!ZdoReflection.TryGetIdKey(zdo, out var key)) { key = ZdoIdKey.FromRuntimeObject(zdo); } if (!ByZdoId.TryGetValue(key, out var value)) { value = new OwnerState(); ByZdoId[key] = value; } value.LastTouchedTime = Time.realtimeSinceStartupAsDouble; return value; } private static void PruneOwnerStatesIfDue(double now) { if (now < _nextOwnerStatePrune) { return; } _nextOwnerStatePrune = now + 30.0; List<ZdoIdKey> list = new List<ZdoIdKey>(); foreach (KeyValuePair<ZdoIdKey, OwnerState> item in ByZdoId) { OwnerState value = item.Value; if (value == null || (value.LastTouchedTime > 0.0 && now - value.LastTouchedTime >= 600.0)) { list.Add(item.Key); } } foreach (ZdoIdKey item2 in list) { ByZdoId.Remove(item2); } while (ByZdoId.Count > 50000 && RemoveOldestOwnerState()) { } } private static bool RemoveOldestOwnerState() { ZdoIdKey key = default(ZdoIdKey); double num = double.MaxValue; bool flag = false; foreach (KeyValuePair<ZdoIdKey, OwnerState> item in ByZdoId) { double num2 = item.Value?.LastTouchedTime ?? 0.0; if (num2 < num) { num = num2; key = item.Key; flag = true; } } if (!flag) { return false; } ByZdoId.Remove(key); return true; } private static bool IsCombatHintFresh(OwnerState state) { if (state == null || state.CombatTargetUid == 0L) { return false; } return Time.realtimeSinceStartupAsDouble - state.CombatTargetHintTime <= (double)Math.Max(0.1f, EffectiveConfig.OwnerHintLifetimeSeconds); } private static bool IsUidCurrentlyConnected(long uid) { return FindZdoPeerByUid(uid) != null; } private static object FindZdoPeerByUid(long uid) { if (uid == 0L) { return null; } foreach (object item in ZdoReflection.EnumeratePeers(ZdoReflection.ZDOManInstance)) { if (NetReflection.TryGetPeerUid(item, out var uid2) && uid2 == uid) { return item; } } return null; } } internal enum PayloadCullValueKind : byte { Vector3, Quaternion } internal readonly struct PayloadCullKey : IEquatable<PayloadCullKey> { private readonly ZdoIdKey _zdoId; private readonly int _hash; private readonly PayloadCullValueKind _kind; internal PayloadCullKey(ZdoIdKey zdoId, int hash, PayloadCullValueKind kind) { _zdoId = zdoId; _hash = hash; _kind = kind; } public bool Equals(PayloadCullKey other) { if (_zdoId.Equals(other._zdoId) && _hash == other._hash) { return _kind == other._kind; } return false; } public override bool Equals(object obj) { if (obj is PayloadCullKey other) { return Equals(other); } return false; } public override int GetHashCode() { return (_zdoId.GetHashCode() * 31 + _hash) * 31 + (int)_kind; } } internal static class PayloadCullState { private const int MaxEntries = 65536; private const double MinEntryTtlSeconds = 60.0; private const double PruneIntervalSeconds = 30.0; private static readonly Dictionary<PayloadCullKey, double> LastAllowedByZdoKey = new Dictionary<PayloadCullKey, double>(); private static readonly object Lock = new object(); private static double _nextPruneTime; internal static bool ShouldAllowByTime(object zdo, int hash, PayloadCullValueKind kind) { float num = Math.Max(0.05f, EffectiveConfig.PayloadForceRefreshSeconds); double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; PayloadCullKey key = BuildKey(zdo, hash, kind); lock (Lock) { PruneIfDue(realtimeSinceStartupAsDouble); if (!LastAllowedByZdoKey.TryGetValue(key, out var value) || realtimeSinceStartupAsDouble - value >= (double)num) { LastAllowedByZdoKey[key] = realtimeSinceStartupAsDouble; return true; } } return false; } internal static void MarkAllowed(object zdo, int hash, PayloadCullValueKind kind) { PayloadCullKey key = BuildKey(zdo, hash, kind); double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; lock (Lock) { PruneIfDue(realtimeSinceStartupAsDouble); LastAllowedByZdoKey[key] = realtimeSinceStartupAsDouble; } } private static void PruneIfDue(double now) { if (now < _nextPruneTime) { return; } _nextPruneTime = now + 30.0; double num = Math.Max(60.0, (double)Math.Max(0.05f, EffectiveConfig.PayloadForceRefreshSeconds) * 20.0); List<PayloadCullKey> list = new List<PayloadCullKey>(); foreach (KeyValuePair<PayloadCullKey, double> item in LastAllowedByZdoKey) { if (now - item.Value >= num) { list.Add(item.Key); } } foreach (PayloadCullKey item2 in list) { LastAllowedByZdoKey.Remove(item2); } while (LastAllowedByZdoKey.Count > 65536 && RemoveOldest()) { } } private static bool RemoveOldest() { PayloadCullKey key = default(PayloadCullKey); double num = double.MaxValue; bool flag = false; foreach (KeyValuePair<PayloadCullKey, double> item in LastAllowedByZdoKey) { if (item.Value < num) { num = item.Value; key = item.Key; flag = true; } } if (!flag) { return false; } LastAllowedByZdoKey.Remove(key); return true; } private static PayloadCullKey BuildKey(object zdo, int hash, PayloadCullValueKind kind) { if (!ZdoReflection.TryGetIdKey(zdo, out var key)) { key = ZdoIdKey.FromRuntimeObject(zdo); } return new PayloadCullKey(key, hash, kind); } } [HarmonyPatch] internal static class ZDOSetVector3ReducerPatch { private static MethodBase TargetMethod() { return AccessTools.Method(ReflectionCache.ZDOType ?? AccessTools.TypeByName("ZDO"), "Set", new Type[2] { typeof(int), typeof(Vector3) }, (Type[])null); } private static bool Prefix(object __instance, int __0, Vector3 __1) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) i