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.1.6
SkadiNet.dll
Decompiled 3 days 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 System.Threading; using BepInEx; using BepInEx.Bootstrap; 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: AssemblyFileVersion("1.1.6.0")] [assembly: AssemblyInformationalVersion("1.1.6")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyProduct("SkadiNet")] [assembly: AssemblyTitle("SkadiNet")] [assembly: AssemblyVersion("1.1.6.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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 ConfigSyncScope { ServerSynced, ClientLocal } internal static class ConfigSyncManager { private static ConfigSync Sync { get; set; } internal static void Initialize() { Sync = new ConfigSync("sighsorry.SkadiNet") { DisplayName = "SkadiNet", CurrentVersion = "1.1.6", MinimumRequiredVersion = "1.1.6", ModRequired = true, IsLocked = true }; } 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); SyncedConfigEntry<T> syncedConfigEntry = Sync?.AddConfigEntry<T>(val2); if (syncedConfigEntry != null) { syncedConfigEntry.SynchronizedConfig = 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]", _ => throw new ArgumentOutOfRangeException("scope", scope, null), }; } } internal static class EffectiveConfig { internal const float DungeonLayerHeight = 1500f; internal const float DungeonLayerTransitionGraceSeconds = 2.5f; internal const int CompressionFailureLimitPerPeer = 1; internal const float OwnershipRelativeHysteresis = 0.15f; internal const float OwnershipAbsoluteHysteresisMs = 20f; internal const float OwnerSwitchCooldownSeconds = 3f; internal const float OwnerHintSwitchCooldownSeconds = 5f; internal const float ShipOwnerSwitchCooldownSeconds = 8f; internal const float RecoverUnownedAfterSeconds = 2f; internal static bool SchedulerEnabled { get { if (ModConfig.Enabled.Value) { return IsPositive(ModConfig.SchedulerThroughput); } return false; } } internal static bool DungeonLayerFilteringEnabled { get { if (ModConfig.Enabled.Value) { return ModConfig.DungeonLayerFiltering.Value; } 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 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 float SendInterval => Map(ModConfig.SchedulerThroughput, 0.1f, 0.05f, 0.02f); 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 float PayloadVec3CullSize => Map(ModConfig.PayloadReducerStrength, 0.005f, 0.04f, 0.1f); internal static float PayloadQuaternionDotThreshold => Map(ModConfig.PayloadReducerStrength, 0.9998f, 0.995f, 0.99f); internal static float PayloadForceRefreshSeconds => Map(ModConfig.PayloadReducerStrength, 0.15f, 1f, 2f); internal static int CompressionThresholdBytes => MapInt(ModConfig.CompressionAggression, 8192, 1024, 256); internal static float CompressionMinUsefulRatio => Map(ModConfig.CompressionAggression, 0.7f, 0.9f, 0.97f); 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, 60f); internal static int ClientStutterMemoryPressureThresholdPercent => MapInt(ModConfig.ClientStutterGuardStrength, 55, 75, 90); internal static int ClientStutterMinimumFreeMemoryMB => MapInt(ModConfig.ClientStutterGuardStrength, 6144, 2048, 1024); 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, 192); internal static int OwnershipScanStride => MapInt(ModConfig.OwnershipIntensity, 10, 4, 2); internal static float OwnershipScanIntervalSeconds => Map(ModConfig.OwnershipIntensity, 3f, 1f, 0.5f); internal static float OwnershipCandidateRadius => Map(ModConfig.OwnershipIntensity, 80f, 160f, 280f); internal static float OwnerHintCandidateRadius => Map(ModConfig.OwnershipIntensity, 128f, 256f, 512f); internal static float OwnershipDistanceScoreWeight => Map(ModConfig.OwnershipIntensity, 0.4f, 0.2f, 0.06f); 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)); } } internal static class ModConfig { private const string GeneralSection = "General"; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> DungeonLayerFiltering; internal static ConfigEntry<string> DungeonLayerAlwaysSendPrefabs; internal static ConfigEntry<int> SchedulerThroughput; internal static ConfigEntry<int> PayloadReducerStrength; internal static ConfigEntry<int> OwnershipIntensity; internal static ConfigEntry<int> CompressionAggression; 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); DungeonLayerFiltering = ConfigSyncManager.Bind(config, "General", "DungeonLayerFiltering", value: false, Description("Limit routine ZDO sending and loading to the player's current world layer while preserving essential and explicit priority updates. Enable this only on servers that benefit from separating surface and dungeon objects.", 65), ConfigSyncScope.ServerSynced); DungeonLayerAlwaysSendPrefabs = ConfigSyncManager.Bind(config, "General", "DungeonLayerAlwaysSendPrefabs", string.Empty, Description("Comma-separated ZDO prefab names for entrance floors or other essential pieces in custom dungeons that SkadiNet cannot detect automatically. Leave empty unless a custom dungeon fails to load its entrance or start area. Data is sent ahead on both layers, but objects are shown only on the current layer.", 64), ConfigSyncScope.ServerSynced); SchedulerThroughput = ConfigSyncManager.Bind(config, "General", "SchedulerThroughput", 35, FeatureSliderDescription("0 uses Valheim's normal ZDO queue limit and scheduler. 1 is conservative; 35 is recommended; 50 is balanced; 100 serves more peers per tick and allows a larger ZDO queue.", 63), 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 within fixed ownership-switch safety guards, 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); } 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 sealed class ConfigurationManagerAttributes { public int? Order; } internal struct MemoryPressureSnapshot { public bool Known; public ulong TotalMB; public ulong AvailableMB; public int LoadPercent; } 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; } private static Plugin _plugin; private static Coroutine _cleanupCoroutine; private static volatile int _mainThreadId; private static double _criticalUntil; private static bool _pendingGc; private static double _firstPendingSince; private static bool _runningCleanup; internal static bool IsActive { get { if (!EffectiveConfig.ClientStutterGuardEnabled) { return false; } if (IsDedicatedLike()) { return false; } return true; } } internal static void Initialize(Plugin plugin) { _plugin = plugin; _mainThreadId = Thread.CurrentThread.ManagedThreadId; _criticalUntil = 0.0; _pendingGc = false; _firstPendingSince = 0.0; _runningCleanup = false; EnsureCleanupScheduler(); } internal static void Shutdown() { RunPendingCleanup(); try { if (_cleanupCoroutine != null && (Object)(object)_plugin != (Object)null) { ((MonoBehaviour)_plugin).StopCoroutine(_cleanupCoroutine); } } catch { } _cleanupCoroutine = null; _plugin = null; _mainThreadId = 0; _criticalUntil = 0.0; } 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; } private static void ExtendCriticalWindow(float seconds) { if (IsActive && !(seconds <= 0f)) { double num = Time.realtimeSinceStartupAsDouble + (double)seconds; if (num > _criticalUntil) { _criticalUntil = num; } } } internal static void MarkInitialSyncWindow() { ExtendCriticalWindow(Math.Max(0.1f, EffectiveConfig.ClientStutterInitialSyncWindowSeconds)); } internal static void MarkTeleportWindow() { ExtendCriticalWindow(Math.Max(0.1f, EffectiveConfig.ClientStutterTeleportWindowSeconds)); } internal static void MarkFullSnapshotBurstWindow() { ExtendCriticalWindow(Math.Max(0.1f, EffectiveConfig.ClientStutterFullSnapshotWindowSeconds)); } internal static void MarkCombatWindow() { ExtendCriticalWindow(Math.Max(0.1f, EffectiveConfig.ClientStutterCombatWindowSeconds)); } internal static void MarkShipTravelWindow() { ExtendCriticalWindow(Math.Max(0.1f, EffectiveConfig.ClientStutterShipWindowSeconds)); } internal static bool TryHandleGcCollect() { if (Thread.CurrentThread.ManagedThreadId != _mainThreadId) { return true; } if (_runningCleanup) { return true; } if (!IsActive) { ClearPendingCleanup(); return true; } if (ShouldDelayCleanup()) { RequestPending(); return false; } ClearPendingCleanup(); return true; } private static void RequestPending() { _pendingGc = true; if (_firstPendingSince <= 0.0) { _firstPendingSince = Time.realtimeSinceStartupAsDouble; } EnsureCleanupScheduler(); } private static void EnsureCleanupScheduler() { if (_cleanupCoroutine == null && !((Object)(object)_plugin == (Object)null) && IsActive) { _cleanupCoroutine = ((MonoBehaviour)_plugin).StartCoroutine(CleanupScheduler()); } } private static IEnumerator CleanupScheduler() { while (true) { float num = Math.Max(0.25f, EffectiveConfig.ClientStutterIdleCleanupPollSeconds); yield return (object)new WaitForSecondsRealtime(num); TryRunPendingCleanup(); } } private static bool TryRunPendingCleanup() { if (!_pendingGc) { return false; } if (_runningCleanup) { return false; } if (ShouldDelayCleanup()) { return false; } return RunPendingCleanup(); } private static bool RunPendingCleanup() { if (!_pendingGc || _runningCleanup) { return false; } ClearPendingCleanup(); _runningCleanup = true; try { GC.Collect(); } finally { _runningCleanup = false; } return true; } private static void ClearPendingCleanup() { _pendingGc = false; _firstPendingSince = 0.0; } private static bool ShouldDelayCleanup() { if (!IsActive) { return false; } if (_firstPendingSince > 0.0 && Time.realtimeSinceStartupAsDouble - _firstPendingSince >= (double)Math.Max(1f, EffectiveConfig.ClientStutterMaxDelaySeconds)) { return false; } if (_criticalUntil > Time.realtimeSinceStartupAsDouble) { return !IsMemoryPressure(); } return IsMemoryPlentiful(); } private static bool IsMemoryPlentiful() { MemoryPressureSnapshot memorySnapshot = GetMemorySnapshot(); if (!memorySnapshot.Known) { return false; } if (memorySnapshot.LoadPercent < Math.Max(1, EffectiveConfig.ClientStutterMemoryPressureThresholdPercent)) { return memorySnapshot.AvailableMB >= (ulong)Math.Max(0, EffectiveConfig.ClientStutterMinimumFreeMemoryMB); } return false; } private static bool IsMemoryPressure() { MemoryPressureSnapshot memorySnapshot = GetMemorySnapshot(); if (!memorySnapshot.Known) { return false; } if (memorySnapshot.LoadPercent < Math.Max(1, EffectiveConfig.ClientStutterMemoryPressureThresholdPercent)) { return memorySnapshot.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; } return new MemoryPressureSnapshot { Known = false }; } private static bool TryGetWindowsMemory(out MemoryPressureSnapshot snapshot) { snapshot = default(MemoryPressureSnapshot); if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { return false; } try { MEMORYSTATUSEX lpBuffer = new MEMORYSTATUSEX { 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 { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(GC), "Collect", Type.EmptyTypes, (Type[])null); } private static bool Prefix() { return ClientStutterGuard.TryHandleGcCollect(); } } [HarmonyPatch] internal static class ClientStutterGuardTerminalGcCollectPatch { private static MethodBase TargetMethod() { return AccessTools.Method(typeof(GC), "Collect", new Type[4] { typeof(int), typeof(GCCollectionMode), typeof(bool), typeof(bool) }, (Type[])null); } private static bool Prefix(int generation, GCCollectionMode mode) { if (generation < 0 || !Enum.IsDefined(typeof(GCCollectionMode), mode)) { return true; } return ClientStutterGuard.TryHandleGcCollect(); } } [HarmonyPatch] internal static class ClientStutterGuardZNetConnectionPatch { [HarmonyPrepare] private static bool Prepare() { return TargetMethod() != null; } private static MethodBase TargetMethod() { return ReflectionCache.ZNetOnNewConnectionMethod; } private static void Postfix() { ClientStutterGuard.MarkInitialSyncWindow(); } } [HarmonyPatch] internal static class ClientStutterGuardZdoDataPatch { private static IEnumerable<MethodBase> TargetMethods() { Type type = ReflectionCache.ZDOManType ?? AccessTools.TypeByName("ZDOMan"); if (type == null) { yield break; } MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "RPC_ZDOData") { yield return methodInfo; } } } 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 { [HarmonyPrepare] private static bool Prepare() { return TargetMethod() != null; } private static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("ZNetScene"); if (!(type == null)) { return AccessTools.Method(type, "InLoadingScreen", Type.EmptyTypes, (Type[])null); } return null; } private static void Postfix(bool __result) { if (__result) { ClientStutterGuard.MarkTeleportWindow(); } } } [HarmonyPatch] internal static class ClientStutterGuardShipTravelPatch { private static FieldInfo _bodyField; [HarmonyPrepare] private static bool Prepare() { return TargetMethod() != null; } private static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("Ship"); if (type == null) { return null; } _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 { [HarmonyPrepare] private static bool Prepare() { return TargetMethod() != null; } private static MethodBase TargetMethod() { Type type = AccessTools.TypeByName("MonsterAI"); Type type2 = AccessTools.TypeByName("Character"); if (!(type == null) && !(type2 == null)) { return AccessTools.Method(type, "SetTarget", new Type[1] { type2 }, (Type[])null); } return null; } private static void Postfix(object __instance, object[] __args) { if (__args != null && __args.Length != 0) { ClientStutterGuard.MarkCombatWindow(); OwnershipManager.TryTransferCombatOwnership(__instance, __args[0]); } } } internal enum OwnershipCandidateReason { Generic, CombatTarget, DisconnectedOwner, LongUnownedPersistent } internal sealed class OwnerState { public double LastOwnerChangeTime; public double LastSeenUnownedTime; public double LastTouchedTime; public long CombatTargetUid; public double CombatTargetHintTime; } internal static class OwnershipManager { private const int MaxOwnerStates = 50000; private const double OwnerStateTtlSeconds = 600.0; private const double OwnerStatePruneIntervalSeconds = 30.0; private static readonly Dictionary<ZdoIdKey, OwnerState> ByZdoId = new Dictionary<ZdoIdKey, OwnerState>(); private static double _nextProfileAScan; private static double _nextOwnerStatePrune; private static int _sectorCursor; private static int _bucketCursor; private static int _scanPhase; private static bool _loggedUnsupportedSectorShape; internal static bool ProfileAEnabled { get { if (!EffectiveConfig.AdaptiveOwnershipEnabled || !ZdoKeyPolicy.OwnershipReady || !NetReflection.IsServer()) { return false; } return true; } } internal static void Initialize() { ByZdoId.Clear(); _nextProfileAScan = 0.0; _nextOwnerStatePrune = 0.0; _sectorCursor = 0; _bucketCursor = 0; _scanPhase = 0; _loggedUnsupportedSectorShape = false; } internal static bool TryTransferCombatOwnership(object monsterAI, object target) { if (!ProfileAEnabled) { return false; } if (!GameplayReflection.LooksLikePlayer(target)) { return false; } object zdoFromCharacterLike = GameplayReflection.GetZdoFromCharacterLike(target); if (zdoFromCharacterLike == null || !ZdoReflection.TryGetOwner(zdoFromCharacterLike, out var owner) || owner == 0L) { return false; } object zdoFromCharacterLike2 = GameplayReflection.GetZdoFromCharacterLike(monsterAI); if (zdoFromCharacterLike2 == null) { return false; } return TryMaybeImproveOwner(zdoFromCharacterLike2, OwnershipCandidateReason.CombatTarget, owner); } internal static void ClearPeer(long uid) { if (uid == 0L) { return; } foreach (OwnerState value in ByZdoId.Values) { if (value.CombatTargetUid == uid) { value.CombatTargetUid = 0L; value.CombatTargetHintTime = 0.0; } } } internal static void TickLightweight() { if (!EffectiveConfig.AdaptiveOwnershipEnabled || !ZdoKeyPolicy.OwnershipReady) { return; } double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; if (realtimeSinceStartupAsDouble < _nextProfileAScan) { return; } _nextProfileAScan = realtimeSinceStartupAsDouble + (double)Math.Max(0.1f, EffectiveConfig.OwnershipScanIntervalSeconds); if (NetReflection.IsServer()) { object zDOManInstance = ZdoReflection.ZDOManInstance; if (HasAnyZdoPeer(zDOManInstance)) { PruneOwnerStatesIfDue(realtimeSinceStartupAsDouble); TryProfileAScan(zDOManInstance); } } } private static bool HasAnyZdoPeer(object zdoMan) { try { if (zdoMan == null || ReflectionCache.ZDOManPeersField == null) { return false; } object value = ReflectionCache.ZDOManPeersField.GetValue(zdoMan); if (value is ICollection collection) { return collection.Count > 0; } if (value is IEnumerable enumerable) { { IEnumerator enumerator = enumerable.GetEnumerator(); try { if (enumerator.MoveNext()) { _ = enumerator.Current; return true; } } finally { IDisposable disposable = enumerator as IDisposable; if (disposable != null) { disposable.Dispose(); } } } } } catch { } return false; } private static void TryProfileAScan(object zdoMan) { try { object obj = ReflectionCache.ZDOObjectsBySectorField?.GetValue(zdoMan); if (obj != null) { int visited = 0; int budget = Math.Max(1, EffectiveConfig.OwnershipScanBudget); ScanSectorBuckets(obj, budget, ref visited); } } catch { } } private static void ScanSectorBuckets(object sectors, int budget, ref int visited) { if (!(sectors is IList list)) { if (!_loggedUnsupportedSectorShape) { _loggedUnsupportedSectorShape = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Adaptive ownership scan disabled: unsupported m_objectsBySector type " + (sectors?.GetType().FullName ?? "null") + ".")); } } } else { if (list == null || list.Count <= 0) { return; } if (_sectorCursor < 0 || _sectorCursor >= list.Count) { _sectorCursor = 0; } int num = Math.Max(1, EffectiveConfig.OwnershipScanStride); _scanPhase = Math.Max(0, _scanPhase % num); int num2 = 0; while (num2 < list.Count && visited < budget) { if (list[_sectorCursor] is IList { Count: not 0 } list2) { if (_bucketCursor < 0 || _bucketCursor >= list2.Count) { _bucketCursor = 0; } while (_bucketCursor < list2.Count && visited < budget) { int num3 = _bucketCursor++; object obj = list2[num3]; if (obj != null && (num3 + _scanPhase) % num == 0) { visited++; TryMaybeImproveOwner(obj, OwnershipCandidateReason.Generic, 0L); } } if (_bucketCursor >= list2.Count) { _bucketCursor = 0; AdvanceSector(list.Count, num); num2++; } } else { _bucketCursor = 0; AdvanceSector(list.Count, num); num2++; } } } } private static void AdvanceSector(int sectorCount, int stride) { _sectorCursor++; if (_sectorCursor >= sectorCount) { _sectorCursor = 0; _scanPhase = (_scanPhase + 1) % Math.Max(1, stride); } } private static bool TryMaybeImproveOwner(object zdo, OwnershipCandidateReason reason, long combatTargetUid = 0L) { //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0198: 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)) { return false; } if (!ZdoReflection.TryGetPersistent(zdo, out var persistent)) { return false; } double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; object obj = ((owner != 0L) ? FindZdoPeerByUid(owner) : null); bool flag = obj != null; long uid; bool flag2 = ZdoReflection.TryGetServerSessionId(out uid); bool flag3 = owner != 0 && flag2 && owner == uid; if (owner != 0L && !flag && !flag2) { return false; } if (!flag && owner != 0L && !flag3) { reason = OwnershipCandidateReason.DisconnectedOwner; } if (persistent && owner != 0L && reason != OwnershipCandidateReason.DisconnectedOwner) { return false; } if (reason == OwnershipCandidateReason.Generic && flag) { return false; } ZdoKeyPolicy.ClassifyOwnership(zdo, out var playerLike, out var shipLike); if (playerLike) { return false; } if (shipLike && reason != OwnershipCandidateReason.DisconnectedOwner) { return false; } if (!ZdoReflection.TryGetIdKey(zdo, out var key)) { return false; } OwnerState ownerState = FindOwnerState(key, realtimeSinceStartupAsDouble); bool flag4 = false; if (owner == 0L) { if (persistent && ownerState == null) { ownerState = CreateOwnerState(key, realtimeSinceStartupAsDouble); } if (ownerState != null && ownerState.LastSeenUnownedTime <= 0.0) { ownerState.LastSeenUnownedTime = realtimeSinceStartupAsDouble; } if (persistent) { if (realtimeSinceStartupAsDouble - ownerState.LastSeenUnownedTime >= 2.0) { reason = OwnershipCandidateReason.LongUnownedPersistent; } else { flag4 = true; } } } else if (ownerState != null) { ownerState.LastSeenUnownedTime = 0.0; } if (combatTargetUid != 0L && ownerState != null) { ownerState.CombatTargetUid = combatTargetUid; ownerState.CombatTargetHintTime = realtimeSinceStartupAsDouble; } if (flag4) { return false; } if (!ZdoReflection.TryGetPosition(zdo, out var position)) { return false; } long hintedUid = ((combatTargetUid != 0L) ? combatTargetUid : (IsCombatHintFresh(ownerState) ? ownerState.CombatTargetUid : 0)); if (!TryFindBestCandidate(position, reason, hintedUid, out var bestUid, out var bestScore)) { if (reason == OwnershipCandidateReason.LongUnownedPersistent) { return RecoverToServerOwner(zdo, ownerState); } return false; } if (bestUid == owner) { return false; } if (!TryComputeCurrentOwnerScore(owner, position, hintedUid, obj, flag3, out var score)) { return false; } if (!IsCandidateBetter(bestScore, score, reason)) { return false; } float num = ((reason == OwnershipCandidateReason.CombatTarget) ? 5f : 3f); if (shipLike) { num = Math.Max(num, 8f); } if (realtimeSinceStartupAsDouble - (ownerState?.LastOwnerChangeTime ?? 0.0) < (double)num) { return false; } if (ownerState == null) { ownerState = CreateOwnerState(key, realtimeSinceStartupAsDouble); if (owner == 0L) { ownerState.LastSeenUnownedTime = realtimeSinceStartupAsDouble; } if (combatTargetUid != 0L) { ownerState.CombatTargetUid = combatTargetUid; ownerState.CombatTargetHintTime = realtimeSinceStartupAsDouble; } } if (!ZdoReflection.TrySetOwner(zdo, bestUid)) { return false; } ownerState.LastOwnerChangeTime = realtimeSinceStartupAsDouble; ZdoReflection.ForceSend(zdo); return true; } private static bool TryFindBestCandidate(Vector3 zdoPosition, OwnershipCandidateReason reason, long hintedUid, out long bestUid, out float bestScore) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) bestUid = 0L; bestScore = 0f; bool flag = false; 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 || !DungeonLayerFilter.TryGetPeerPosition(item, out var position) || !DungeonLayerFilter.IsOwnershipCandidateAllowed(zdoPosition, position)) { continue; } float num2 = Vector3.Distance(zdoPosition, position); if (num2 > num) { continue; } PeerQualityState byPeer = PeerQualityMeter.GetByPeer(item); if (CandidateConnectionAllowed(byPeer, reason)) { float num3 = ComputeCandidateScore(uid, num2, byPeer, hintedUid); if (!flag || num3 < bestScore) { bestUid = uid; bestScore = num3; flag = true; } } } return flag; } private static float ComputeCandidateScore(long uid, float distance, PeerQualityState quality, long hintedUid) { float num = quality?.ConnectionQualityMs ?? 999f; num += distance * Math.Max(0f, EffectiveConfig.OwnershipDistanceScoreWeight); if (hintedUid != 0L && uid == hintedUid) { num -= Math.Max(0f, EffectiveConfig.OwnerHintScoreBonusMs); } return num; } private static bool TryComputeCurrentOwnerScore(long currentOwner, Vector3 zdoPosition, long hintedUid, object currentOwnerPeer, bool currentIsServer, out float score) { //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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) score = 999f; if (currentOwner == 0L || (currentOwnerPeer == null && !currentIsServer)) { return true; } if (currentIsServer) { score = EffectiveConfig.ServerFallbackPenaltyMs; return true; } if (!DungeonLayerFilter.TryGetPeerPosition(currentOwnerPeer, out var position)) { return false; } if (!DungeonLayerFilter.IsOwnershipCandidateAllowed(zdoPosition, position)) { score = 999f; return true; } PeerQualityState byPeer = PeerQualityMeter.GetByPeer(currentOwnerPeer); float distance = Vector3.Distance(zdoPosition, position); score = ComputeCandidateScore(currentOwner, distance, byPeer, hintedUid); return true; } private static bool RecoverToServerOwner(object zdo, OwnerState state) { if (!ZdoReflection.TryGetServerSessionId(out var uid) || uid == 0L) { return false; } if (!ZdoReflection.TrySetOwner(zdo, uid)) { return false; } double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; state.LastOwnerChangeTime = realtimeSinceStartupAsDouble; ZdoReflection.ForceSend(zdo); return true; } private static bool CandidateConnectionAllowed(PeerQualityState candidate, OwnershipCandidateReason reason) { if (!EffectiveConfig.PeerQualityEnabled) { return true; } if (candidate == null || !candidate.HasAnySample) { if (reason != OwnershipCandidateReason.DisconnectedOwner) { return reason == OwnershipCandidateReason.LongUnownedPersistent; } return true; } 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, 0.15f); float val = Math.Max(0f, 20f); 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 FindOwnerState(ZdoIdKey id, double now) { if (!ByZdoId.TryGetValue(id, out var value) || value == null) { return null; } value.LastTouchedTime = now; return value; } private static OwnerState CreateOwnerState(ZdoIdKey id, double now) { if (ByZdoId.Count >= 50000) { bool flag = false; ZdoIdKey key = default(ZdoIdKey); double num = double.MaxValue; foreach (KeyValuePair<ZdoIdKey, OwnerState> item in ByZdoId) { double num2 = item.Value?.LastTouchedTime ?? double.MinValue; if (!flag || !(num2 >= num)) { flag = true; key = item.Key; num = num2; } } if (flag) { ByZdoId.Remove(key); } } OwnerState ownerState = new OwnerState { LastTouchedTime = now }; ByZdoId[id] = ownerState; return ownerState; } 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); } } 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 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; } } [HarmonyPatch] internal static class ZSteamSocketSendCompressionPatch { [HarmonyPrepare] private static bool Prepare() { return ReflectionCache.ZSteamSocketSendMethod != null; } private static MethodBase TargetMethod() { return ReflectionCache.ZSteamSocketSendMethod; } private static void Prefix(object __instance, ref object __0) { if (!EffectiveConfig.CompressionEnabled || __instance == null || __0 == null || !FeatureNegotiation.IsCompressionActiveForSocket(__instance)) { return; } try { if (ZPackageTools.TryBuildCompressedPackage(__0, out var compressedPackage)) { __0 = compressedPackage; } } catch (Exception) { FeatureNegotiation.RecordCompressionFailure(__instance); } } } [HarmonyPatch] internal static class ZSteamSocketRecvCompressionPatch { [HarmonyPrepare] private static bool Prepare() { return ReflectionCache.ZSteamSocketRecvMethod != null; } private static MethodBase TargetMethod() { return ReflectionCache.ZSteamSocketRecvMethod; } private static void Postfix(object __instance, ref object __result) { if (__result == null || !FeatureNegotiation.CanDecodeCompressionFromSocket(__instance)) { return; } try { if (ZPackageTools.TryDecompressPackage(__result, out var rawPackage)) { __result = rawPackage; } } catch (Exception) { FeatureNegotiation.RecordCompressionFailure(__instance); __result = null; } } } [Flags] internal enum PeerFeatureFlags { None = 0, Compression = 1, DungeonLayers = 2 } internal sealed class PeerFeatureState { public object Rpc; public object Socket; public bool RegisteredRpc; public bool SendingHandshake; public bool HandshakeSent; public bool HandshakeReceived; public PeerFeatureFlags RemoteCapabilities; public int CompressionFailures; } 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>(); internal static bool Ready { get { if (ZPackageTools.Ready && ReflectionCache.ZRpcRegisterGenericPackageMethod != null) { return ReflectionCache.ZRpcInvokeMethod != null; } return false; } } internal static void Initialize() { lock (Lock) { ByRpc.Clear(); BySocket.Clear(); } } private static PeerFeatureFlags GetLocalCapabilities(object rpc) { PeerFeatureFlags peerFeatureFlags = (SupportsCompressionTransport(NetReflection.GetSocketFromRpc(rpc)) ? PeerFeatureFlags.Compression : PeerFeatureFlags.None); if (DungeonLayerFilter.CanNegotiate) { peerFeatureFlags |= PeerFeatureFlags.DungeonLayers; } return peerFeatureFlags; } private static bool SupportsCompressionTransport(object socket) { if (ZPackageTools.Ready && socket != null && ReflectionCache.ZSteamSocketType != null && ReflectionCache.ZSteamSocketType.IsInstanceOfType(socket) && ReflectionCache.ZSteamSocketSendMethod != null) { return ReflectionCache.ZSteamSocketRecvMethod != null; } return false; } internal static PeerFeatureState GetOrCreateByRpc(object rpc) { if (rpc == null) { return null; } lock (Lock) { if (!ByRpc.TryGetValue(rpc, out var value)) { value = new PeerFeatureState { Rpc = rpc }; ByRpc[rpc] = value; } object socketFromRpc = NetReflection.GetSocketFromRpc(rpc); if (socketFromRpc != null) { value.Socket = socketFromRpc; BySocket[socketFromRpc] = value; } return value; } } internal static void ClearPeer(object peerOrRpc) { 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 (obj != null) { ByRpc.TryGetValue(obj, out value); } if (value == null && socketFromRpc != null) { BySocket.TryGetValue(socketFromRpc, out value); } if (obj != null) { ByRpc.Remove(obj); } if (socketFromRpc != null) { BySocket.Remove(socketFromRpc); } if (value != null) { if (value.Rpc != null) { ByRpc.Remove(value.Rpc); } if (value.Socket != null) { BySocket.Remove(value.Socket); } RemoveState(ByRpc, value); RemoveState(BySocket, 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 || !SupportsCompressionTransport(socket)) { return false; } lock (Lock) { if (!BySocket.TryGetValue(socket, out var value)) { return false; } if (value.CompressionFailures >= 1) { return false; } return !value.SendingHandshake && value.HandshakeSent && value.HandshakeReceived && Supports(value, PeerFeatureFlags.Compression); } } internal static bool CanDecodeCompressionFromSocket(object socket) { if (!SupportsCompressionTransport(socket)) { return false; } lock (Lock) { if (!BySocket.TryGetValue(socket, out var value)) { return false; } return value.HandshakeSent && value.HandshakeReceived && Supports(value, PeerFeatureFlags.Compression); } } internal static bool IsDungeonLayerNegotiatedForPeer(object peer) { object peerRpc = NetReflection.GetPeerRpc(peer); if (peerRpc == null) { return false; } lock (Lock) { if (!ByRpc.TryGetValue(peerRpc, out var value)) { return false; } return value.HandshakeSent && value.HandshakeReceived && Supports(value, PeerFeatureFlags.DungeonLayers); } } internal static bool HasNegotiatedDungeonLayerPeer() { lock (Lock) { foreach (PeerFeatureState value in ByRpc.Values) { if (value.HandshakeSent && value.HandshakeReceived && Supports(value, PeerFeatureFlags.DungeonLayers)) { return true; } } } return false; } internal static void RecordCompressionFailure(object socket) { if (socket == null) { return; } lock (Lock) { if (BySocket.TryGetValue(socket, out var value)) { value.CompressionFailures++; } } } internal static void OnNewConnection(object znetPeer) { if (!Ready) { return; } object peerRpc = NetReflection.GetPeerRpc(znetPeer); if (peerRpc != null) { PeerFeatureState orCreateByRpc = GetOrCreateByRpc(peerRpc); if (RegisterRpc(peerRpc, orCreateByRpc)) { SendHello(peerRpc, orCreateByRpc); } } } private static bool RegisterRpc(object rpc, PeerFeatureState state) { if (rpc == null || state == null || !ZPackageTools.Ready || ReflectionCache.ZRpcRegisterGenericPackageMethod == null || ReflectionCache.ZRpcInvokeMethod == null || ReflectionCache.ZRpcType == null || ReflectionCache.ZPackageType == null) { return false; } lock (Lock) { if (state.RegisteredRpc) { return true; } } try { MethodInfo method = typeof(FeatureNegotiation).GetMethod("RPC_Features_Generic", BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(ReflectionCache.ZRpcType, ReflectionCache.ZPackageType); Delegate obj = Delegate.CreateDelegate(typeof(Action<, >).MakeGenericType(ReflectionCache.ZRpcType, ReflectionCache.ZPackageType), method); ReflectionCache.ZRpcRegisterGenericPackageMethod.Invoke(rpc, new object[2] { "SkadiNet_Features", obj }); lock (Lock) { state.RegisteredRpc = true; } return true; } catch (Exception) { return false; } } private static void SendHello(object rpc, PeerFeatureState state, bool refresh = false) { if (rpc == null || state == null || !Ready) { return; } lock (Lock) { if ((!refresh && state.HandshakeSent) || state.SendingHandshake || !state.RegisteredRpc) { return; } state.SendingHandshake = true; } try { object obj = ZPackageTools.NewPackage(); ZPackageTools.WriteInt(obj, 1179536211); ZPackageTools.WriteInt(obj, 2); ZPackageTools.WriteInt(obj, (int)GetLocalCapabilities(rpc)); ZPackageTools.WriteString(obj, "1.1.6"); ReflectionCache.ZRpcInvokeMethod.Invoke(rpc, new object[2] { "SkadiNet_Features", new object[1] { obj } }); lock (Lock) { state.HandshakeSent = true; } } catch (Exception) { } finally { lock (Lock) { state.SendingHandshake = false; } } } internal static void RefreshLocalCapabilities() { if (!Ready) { return; } List<PeerFeatureState> list; lock (Lock) { list = new List<PeerFeatureState>(ByRpc.Values); } foreach (PeerFeatureState item in list) { if (item != null && item.Rpc != null) { SendHello(item.Rpc, item, refresh: true); } } } 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 { if (!ZPackageTools.Ready) { return; } PeerFeatureState orCreateByRpc = GetOrCreateByRpc(rpc); if (orCreateByRpc == null || pkg == null) { return; } int pos = ZPackageTools.GetPos(pkg); ZPackageTools.SetPos(pkg, 0); if (ZPackageTools.ReadInt(pkg) != 1179536211) { ZPackageTools.SetPos(pkg, pos); } else if (ZPackageTools.ReadInt(pkg) == 2) { int remoteCapabilities = ZPackageTools.ReadInt(pkg); ZPackageTools.ReadString(pkg); bool flag; lock (Lock) { orCreateByRpc.RemoteCapabilities = (PeerFeatureFlags)remoteCapabilities; orCreateByRpc.HandshakeReceived = true; flag = !orCreateByRpc.HandshakeSent; } if (flag) { SendHello(rpc, orCreateByRpc); } } } catch (Exception) { } } private static bool Supports(PeerFeatureState state, PeerFeatureFlags flag) { if (state != null) { return (state.RemoteCapabilities & flag) != 0; } return false; } } [HarmonyPatch] internal static class ZNetOnNewConnectionFeatureHandshakePatch { [HarmonyPrepare] private static bool Prepare() { return TargetMethod() != null; } private static MethodBase TargetMethod() { return ReflectionCache.ZNetOnNewConnectionMethod; } private static void Postfix(object __0) { FeatureNegotiation.OnNewConnection(__0); } } internal static class PeerLifecycle { internal static void ClearDisconnectedPeer(object peer) { long uid = 0L; NetReflection.TryGetPeerUid(peer, out uid); FeatureNegotiation.ClearPeer(peer); PeerQualityMeter.ClearPeer(peer); OwnershipManager.ClearPeer(uid); } } [HarmonyPatch] internal static class PeerLifecycleDisposePatch { [HarmonyPrepare] private static bool Prepare() { return TargetMethod() != null; } private static MethodBase TargetMethod() { Type type = ReflectionCache.ZNetPeerType ?? AccessTools.TypeByName("ZNetPeer"); if (!(type == null)) { return AccessTools.Method(type, "Dispose", Type.EmptyTypes, (Type[])null); } return null; } private static void Prefix(object __instance) { if (__instance != null) { PeerLifecycle.ClearDisconnectedPeer(__instance); } } } internal sealed class PeerQualityState { public readonly Queue<float> Samples = new Queue<float>(); public float LastPingMs; public float PingEmaMs; public float PingMeanMs; public float PingStdDevMs; public float PingJitterMs; public float ConnectionQualityMs; public double LastUpdateTime; public bool HasAnySample; } internal static class PeerQualityMeter { private static readonly Dictionary<object, PeerQualityState> ByRpc = new Dictionary<object, PeerQualityState>(); private static readonly Dictionary<Type, Func<object, int>> SocketPingReaders = new Dictionary<Type, Func<object, int>>(); private static readonly object Lock = new object(); private static Func<object, float> _zrpcPingGetter; internal static void Initialize() { lock (Lock) { ByRpc.Clear(); SocketPingReaders.Clear(); } if (ReflectionCache.ZRpcType != null) { _zrpcPingGetter = ReflectionDelegateFactory.SingleFieldGetter(ReflectionCache.SilentField(ReflectionCache.ZRpcType, "m_ping")); } } internal static PeerQualityState GetOrCreateByRpc(object rpc) { if (rpc == null) { return null; } lock (Lock) { if (!ByRpc.TryGetValue(rpc, out var value)) { value = new PeerQualityState { PingEmaMs = 999f, ConnectionQualityMs = 999f }; ByRpc[rpc] = value; } return value; } } internal static void ClearPeer(object peerOrRpc) { object obj = NetReflection.GetPeerRpc(peerOrRpc); if (obj == null && peerOrRpc != null && ReflectionCache.ZRpcType != null && ReflectionCache.ZRpcType.IsInstanceOfType(peerOrRpc)) { obj = peerOrRpc; } if (obj == null) { return; } lock (Lock) { ByRpc.Remove(obj); } } internal static PeerQualityState GetByPeer(object zdoPeer) { return GetOrCreateByRpc(NetReflection.GetPeerRpc(zdoPeer)); } internal static void UpdateFromRpc(object rpc) { if (!EffectiveConfig.PeerQualityEnabled || rpc == null) { return; } float num = TryReadPingMs(rpc); if (num <= 0f || float.IsNaN(num) || float.IsInfinity(num)) { GetOrCreateByRpc(rpc); return; } PeerQualityState orCreateByRpc = GetOrCreateByRpc(rpc); if (orCreateByRpc == null) { return; } double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; lock (Lock) { double num2 = ((orCreateByRpc.LastUpdateTime > 0.0) ? Math.Max(0.001, realtimeSinceStartupAsDouble - orCreateByRpc.LastUpdateTime) : 0.05); orCreateByRpc.LastUpdateTime = realtimeSinceStartupAsDouble; float lastPingMs = orCreateByRpc.LastPingMs; orCreateByRpc.LastPingMs = num; double num3 = (double)Math.Max(0.1f, EffectiveConfig.PeerPingEmaHalfLifeSeconds) / Math.Log(2.0); float num4 = (float)(1.0 - Math.Exp((0.0 - num2) / num3)); orCreateByRpc.PingEmaMs = (orCreateByRpc.HasAnySample ? (num4 * num + (1f - num4) * orCreateByRpc.PingEmaMs) : num); orCreateByRpc.HasAnySample = true; orCreateByRpc.Samples.Enqueue(num); while (orCreateByRpc.Samples.Count > Math.Max(4, EffectiveConfig.PeerPingSampleWindow)) { orCreateByRpc.Samples.Dequeue(); } RecalculateWindowStats(orCreateByRpc, lastPingMs); orCreateByRpc.ConnectionQualityMs = orCreateByRpc.PingMeanMs * EffectiveConfig.PeerQualityMeanWeight + orCreateByRpc.PingStdDevMs * EffectiveConfig.PeerQualityStdDevWeight + orCreateByRpc.PingJitterMs * EffectiveConfig.PeerQualityJitterWeight + orCreateByRpc.PingEmaMs * EffectiveConfig.PeerQualityEmaWeight; } } private static float TryReadPingMs(object rpc) { if (TryReadSocketPingMs(NetReflection.GetSocketFromRpc(rpc), out var pingMs)) { return pingMs; } try { if (_zrpcPingGetter == null) { return -1f; } float num = _zrpcPingGetter(rpc); return (num < 10f) ? (num * 1000f) : num; } catch { } return -1f; } private static bool TryReadSocketPingMs(object socket, out float pingMs) { pingMs = 0f; if (socket == null) { return false; } try { Func<object, int> socketPingReader = GetSocketPingReader(socket.GetType()); if (socketPingReader == null) { return false; } int num = socketPingReader(socket); if (num > 0) { pingMs = num; return true; } } catch { } return false; } private static Func<object, int> GetSocketPingReader(Type socketType) { if (socketType == null) { return null; } lock (Lock) { if (SocketPingReaders.TryGetValue(socketType, out var value)) { return value; } Func<object, int> func = null; MethodInfo[] methods = socketType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo.Name != "GetConnectionQuality")) { func = CreateSocketPingReader(methodInfo); if (func != null) { break; } } } SocketPingReaders[socketType] = func; return func; } } private static Func<object, int> CreateSocketPingReader(MethodInfo method) { try { if (method == null || method.IsStatic || method.ContainsGenericParameters || method.ReturnType != typeof(void)) { return null; } ParameterInfo[] parameters = method.GetParameters(); Type type = typeof(float).MakeByRefType(); Type type2 = typeof(int).MakeByRefType(); if (parameters.Length != 5 || parameters[0].ParameterType != type || parameters[1].ParameterType != type || parameters[2].ParameterType != type2 || parameters[3].ParameterType != type || parameters[4].ParameterType != type) { return null; } ParameterExpression parameterExpression = Expression.Parameter(typeof(object), "socket"); ParameterExpression parameterExpression2 = Expression.Variable(typeof(float), "localQuality"); ParameterExpression parameterExpression3 = Expression.Variable(typeof(float), "remoteQuality"); ParameterExpression parameterExpression4 = Expression.Variable(typeof(int), "ping"); ParameterExpression parameterExpression5 = Expression.Variable(typeof(float), "outByteSec"); ParameterExpression parameterExpression6 = Expression.Variable(typeof(float), "inByteSec"); MethodCallExpression methodCallExpression = Expression.Call(Expression.Convert(parameterExpression, method.DeclaringType), method, parameterExpression2, parameterExpression3, parameterExpression4, parameterExpression5, parameterExpression6); return Expression.Lambda<Func<object, int>>(Expression.Block(new ParameterExpression[5] { parameterExpression2, parameterExpression3, parameterExpression4, parameterExpression5, parameterExpression6 }, methodCallExpression, parameterExpression4), new ParameterExpression[1] { parameterExpression }).Compile(); } catch { return null; } } private static void RecalculateWindowStats(PeerQualityState state, float previousLast) { int count = state.Samples.Count; if (count == 0) { return; } float num = 0f; foreach (float sample in state.Samples) { num += sample; } float num2 = num / (float)count; float num3 = 0f; foreach (float sample2 in state.Samples) { float num4 = sample2 - num2; num3 += num4 * num4; } state.PingMeanMs = num2; state.PingStdDevMs = (float)Math.Sqrt(num3 / (float)Math.Max(1, count)); state.PingJitterMs = ((previousLast > 0f) ? Math.Abs(state.LastPingMs - previousLast) : 0f); } } [HarmonyPatch] internal static class ZRpcReceivePingPatch { [HarmonyPrepare] private static bool Prepare() { return TargetMethod() != null; } private static MethodBase TargetMethod() { Type type = ReflectionCache.ZRpcType ?? AccessTools.TypeByName("ZRpc"); if (!(type == null)) { return AccessTools.Method(type, "ReceivePing", (Type[])null, (Type[])null); } return null; } private static void Postfix(object __instance) { PeerQualityMeter.UpdateFromRpc(__instance); } } [HarmonyPatch] internal static class ZSteamSocketRegisterGlobalCallbacksTransportPolicyPatch { private const int VanillaSteamSendRateBytes = 153600; private const int VanillaSteamSendBufferBytes = 524288; private const int FixedSteamSendRateBytes = 50000000; private const int FixedSteamSendBufferBytes = 100000000; private const int MaxPolicyUpdateAttempts = 3; private const double PolicyUpdateRetrySeconds = 1.0; private static int _policyUpdateRequested; private static int _policyUpdateAttemptsRemaining; private static double _nextPolicyUpdateTime; private static MethodInfo _setConfigValue; private static bool _policyAppliedLogged; private static bool _policyFailureLogged; private static int CurrentSendRateBytes { get { if (!ModConfig.Enabled.Value) { return 153600; } return 50000000; } } private static int CurrentSendBufferBytes { get { if (!ModConfig.Enabled.Value) { return 524288; } return 100000000; } } internal static void Initialize() { if (ModConfig.Enabled != null) { Interlocked.Exchange(ref _policyUpdateRequested, 0); _policyUpdateAttemptsRemaining = 0; _nextPolicyUpdateTime = 0.0; _policyAppliedLogged = false; _policyFailureLogged = false; ModConfig.Enabled.SettingChanged -= OnEnabledChanged; ModConfig.Enabled.SettingChanged += OnEnabledChanged; } } internal static void Shutdown() { if (ModConfig.Enabled != null) { ModConfig.Enabled.SettingChanged -= OnEnabledChanged; } Interlocked.Exchange(ref _policyUpdateRequested, 0); _policyUpdateAttemptsRemaining = 0; _nextPolicyUpdateTime = 0.0; ApplyTransportPolicy(153600, 524288); } internal static void ApplyPendingPolicy() { if (Interlocked.Exchange(ref _policyUpdateRequested, 0) != 0) { _policyUpdateAttemptsRemaining = 3; _nextPolicyUpdateTime = 0.0; } if (_policyUpdateAttemptsRemaining <= 0) { return; } double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble; if (realtimeSinceStartupAsDouble < _nextPolicyUpdateTime) { return; } if (ApplyCurrentPolicy()) { _policyUpdateAttemptsRemaining = 0; return; } _policyUpdateAttemptsRemaining--; if (_policyUpdateAttemptsRemaining > 0) { _nextPolicyUpdateTime = realtimeSinceStartupAsDouble + 1.0; } else { LogPolicyFailureOnce(); } } private static void OnEnabledChanged(object sender, EventArgs args) { RequestPolicyUpdate(); } private static bool ApplyCurrentPolicy() { bool num = ApplyTransportPolicy(CurrentSendRateBytes, CurrentSendBufferBytes); if (num && ModConfig.Enabled.Value && !_policyAppliedLogged) { _policyAppliedLogged = true; ManualLogSource log = Plugin.Log; if (log == null) { return num; } log.LogInfo((object)("Steam transport policy applied via " + _setConfigValue?.DeclaringType?.FullName + ": " + $"sendRate={50000000}, sendBuffer={100000000}.")); } return num; } private static void RequestPolicyUpdate() { if (_setConfigValue != null) { Interlocked.Exchange(ref _policyUpdateRequested, 1); } } private static void LogPolicyFailureOnce() { if (!_policyFailureLogged) { _policyFailureLogged = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Could not apply all Steam transport settings via " + (_setConfigValue?.DeclaringType?.FullName ?? "unknown API") + " after repeated attempts.")); } } } private static bool ApplyTransportPolicy(int sendRateBytes, int sendBufferBytes) { try { MethodInfo setConfigValue = _setConfigValue; ParameterInfo[] array = setConfigValue?.GetParameters(); if (array == null || array.Length != 5) { return false; } Type parameterType = array[0].ParameterType; Type parameterType2 = array[1].ParameterType; Type parameterType3 = array[3].ParameterType; object value = Enum.Parse(parameterType, "k_ESteamNetworkingConfig_SendRateMin"); object value2 = Enum.Parse(parameterType, "k_ESteamNetworkingConfig_SendRateMax"); object value3 = Enum.Parse(parameterType, "k_ESteamNetworkingConfig_SendBufferSize"); object scope = Enum.Parse(parameterType2, "k_ESteamNetworkingConfig_Global"); object dataType = Enum.Parse(parameterType3, "k_ESteamNetworkingConfig_Int32"); bool flag = SetInt32ConfigValue(setConfigValue, value, scope, dataType, sendRateBytes); bool flag2 = SetInt32ConfigValue(setConfigValue, value2, scope, dataType, sendRateBytes); bool flag3 = SetInt32ConfigValue(setConfigValue, value3, scope, dataType, sendBufferBytes); return flag && flag2 && flag3; } catch (Exception) { return false; } } private static bool SetInt32ConfigValue(MethodInfo setConfigValue, object value, object scope, object dataType, int setting) { GCHandle gCHandle = default(GCHandle); try { gCHandle = GCHandle.Alloc(setting, GCHandleType.Pinned); object[] parameters = new object[5] { value, scope, IntPtr.Zero, dataType, gCHandle.AddrOfPinnedObject() }; object obj = setConfigValue.Invoke(null, parameters); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } finally { if (gCHandle.IsAllocated) { gCHandle.Free(); } } } private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = new List<CodeInstruction>(instructions); MethodInfo methodInfo = null; bool flag = false; foreach (CodeInstruction item in list) { if (item.operand is MethodInfo methodInfo2 && IsCompatibleSetConfigValue(methodInfo2)) { if (methodInfo == null) { methodInfo = methodInfo2; } else if (methodInfo != methodInfo2) { flag = true; } } } _setConfigValue = (flag ? null : methodInfo); if (_setConfigValue == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Steam transport policy disabled: RegisterGlobalCallbacks has no unique SetConfigValue target."); } } return list; } private static bool IsCompatibleSetConfigValue(MethodInfo method) { if (method == null || method.Name != "SetConfigValue" || !method.IsStatic || method.ReturnType != typeof(bool)) { return false; } ParameterInfo[] parameters = method.GetParameters(); if (parameters.Length != 5 || !parameters[0].ParameterType.IsEnum || !parameters[1].ParameterType.IsEnum || parameters[2].ParameterType != typeof(IntPtr) || !parameters[3].ParameterType.IsEnum || parameters[4].ParameterType != typeof(IntPtr)) { return false; } try { Enum.Parse(parameters[0].ParameterType, "k_ESteamNetworkingConfig_SendBufferSize"); Enum.Parse(parameters[0].ParameterType, "k_ESteamNetworkingConfig_SendRateMin"); Enum.Parse(parameters[0].ParameterType, "k_ESteamNetworkingConfig_SendRateMax"); Enum.Parse(parameters[1].ParameterType, "k_ESteamNetworkingConfig_Global"); Enum.Parse(parameters[3].ParameterType, "k_ESteamNetworkingConfig_Int32"); return true; } catch { return false; } } [HarmonyPrepare] private static bool Prepare() { return TargetMethod() != null; } private static MethodBase TargetMethod() { Type type = ReflectionCache.ZSteamSocketType ?? AccessTools.TypeByName("ZSteamSocket"); if (!(type == null)) { return AccessTools.Method(type, "RegisterGlobalCallbacks", Type.EmptyTypes, (Type[])null); } return null; } private static void Postfix() { if (!(_setConfigValue == null)) { if (ApplyCurrentPolicy()) { Interlocked.Exchange(ref _policyUpdateRequested, 0); _policyUpdateAttemptsRemaining = 0; } else { RequestPolicyUpdate(); } } } } internal static class ZPackageTools { internal const int CompressionMagic = 1129204563; internal const int CompressionProtocol = 1; internal const int CompressionAlgoDeflate = 1; private const int MaxDecompressedPackageBytes = 67108864; private const int InflateBufferBytes = 81920; private static ConstructorInfo _ctorEmpty; private static ConstructorInfo _ctorBytes; private static MethodInfo _writeInt; private static MethodInfo _writeString; private static MethodInfo _writeByteArray; private static MethodInfo _readInt; private static MethodInfo _readByteArray; private static MethodInfo _readString; private static MethodInfo _getArray; private static MethodInfo _size; private static MethodInfo _setPos; private static MethodInfo _getPos; private static Func<object, object> _getArrayReader; private static Func<object, int> _sizeReader; private static Func<object, int> _getPosReader; internal static bool Ready { get; private set; } internal static void Initialize() { Ready = false; Type zPackageType = ReflectionCache.ZPackageType; if (!(zPackageType == null)) { _ctorEmpty = AccessTools.Constructor(zPackageType, Type.EmptyTypes, false); _ctorBytes = AccessTools.Constructor(zPackageType, new Type[1] { typeof(byte[]) }, false); _writeInt = AccessTools.Method(zPackageType, "Write", new Type[1] { typeof(int) }, (Type[])null); _writeString = AccessTools.Method(zPackageType, "Write", new Type[1] { typeof(string) }, (Type[])null); _writeByteArray = AccessTools.Method(zPackageType, "Write", new Type[1] { typeof(byte[]) }, (Type[])null); _readInt = AccessTools.Method(zPackageType, "ReadInt", (Type[])null, (Type[])null); _readByteArray = AccessTools.Method(zPackageType, "ReadByteArray", Type.EmptyTypes, (Type[])null); _readString = AccessTools.Method(zPackageType, "ReadString", (Type[])null, (Type[])null); _getArray = AccessTools.Method(zPackageType, "GetArray", (Type[])null, (Type[])null); _size = AccessTools.Method(zPackageType, "Size", (Type[])null, (Type[])null); _setPos = AccessTools.Method(zPackageType, "SetPos", new Type[1] { typeof(int) }, (Type[])null); _getPos = AccessTools.Method(zPackageType, "GetPos", (Type[])null, (Type[])null); _getArrayReader = ReflectionDelegateFactory.BoxedInstanceMethod(_getArray); _sizeReader = ReflectionDelegateFactory.Int32InstanceMethod(_size); _getPosReader = ReflectionDelegateFactory.Int32InstanceMethod(_getPos); Ready = _ctorEmpty != null && _ctorBytes != null && _writeInt != null && _writeString != null && _writeByteArray != null && _readInt != null && _readString != null && _readByteArray != null && _getArray != null && _size != null && _setPos != null && _getPos != null; } } internal static object NewPackage() { if (!Ready) { throw new InvalidOperationException("ZPackage reflection is not ready."); } return _ctorEmpty.Invoke(null); } internal static object NewPackage(byte[] bytes) { if (!Ready) { throw new InvalidOperationException("ZPackage reflection is not ready."); } return _ctorBytes.Invoke(new object[1] { bytes }); } internal static int Size(object pkg) { try { if (pkg == null || _size == null) { return 0; } if (_sizeReader != null) { return _sizeReader(pkg); } return (int)_size.Invoke(pkg, null); } catch { return 0; } } internal static byte[] GetArray(object pkg) { try { if (pkg == null || _getArray == null) { return Array.Empty<byte>(); } if (_getArrayReader != null) { return (_getArrayReader(pkg) as byte[]) ?? Array.Empty<byte>(); } return (_getArray.Invoke(pkg, null) as byte[]) ?? Array.Empty<byte>(); } catch { return Array.Empty<byte>(); } } internal static int GetPos(object pkg) { try { if (pkg == null || _getPos == null) { return 0; } if (_getPosReader != null) { return _getPosReader(pkg); } return (int)_getPos.Invoke(pkg, null); } catch { return 0; } } internal static void SetPos(object pkg, int pos) { try { _setPos?.Invoke(pkg, new object[1] { pos }); } catch { } } internal static void WriteInt(object pkg, int v) { _writeInt.Invoke(pkg, new object[1] { v }); } internal static void WriteString(object pkg, string v) { _writeString.Invoke(pkg, new object[1] { v ?? string.Empty }); } internal static void WriteByteArray(object pkg, byte[] v) { _writeByteArray.Invoke(pkg, new object[1] { v ?? Array.Empty<byte>() }); } internal static int ReadInt(object pkg) { return (int)_readInt.Invoke(pkg, null); } internal static string ReadString(object pkg) { return (string)_readString.Invoke(pkg, null); } internal static byte[] ReadByteArray(object pkg) { return (_readByteArray.Invoke(pkg, null) as byte[]) ?? Array.Empty<byte>(); } internal static bool TryBuildCompressedPackage(object original, out object compressedPackage) { compressedPackage = null; if (!Ready) { return false; } int num = Size(original); if (num < Math.Max(64, EffectiveConfig.CompressionThresholdBytes) || num > 67108864) { return false; } byte[] array = GetArray(original); if (array == null || array.Length < Math.Max(64, EffectiveConfig.CompressionThresholdBytes) || array.Length > 67108864) { return false; } if (LooksCompressed(original)) { return false; } byte[] array2 = Deflate(array); if (array2 == null || array2.Length == 0) { return false; } if ((float)array2.Length / (float)Math.Max(1, array.Length) >= EffectiveConfig.CompressionMinUsefulRatio) { return false; } object obj = NewPackage(); WriteInt(obj, 1129204563); WriteInt(obj, 1); WriteInt(obj, 1); WriteInt(obj, array.Length); WriteByteArray(obj, array2); compressedPackage = obj; return true; } internal static bool TryDecompressPackage(object maybeCompressed, out object rawPackage) { rawPackage = maybeCompressed; if (!Ready || maybeCompressed == null) { return false; } if (Size(maybeCompressed) < 20) { return false; } int pos = GetPos(maybeCompressed); try { SetPos(maybeCompressed, 0); if (ReadInt(maybeCompressed) != 1129204563) { return false; } int num = ReadInt(maybeCompressed); int num2 = ReadInt(maybeCompressed); int num3 = ReadInt(maybeCompressed); int pos2 = GetPos(maybeCompressed); int num4 = ReadInt(maybeCompressed); int num5 = Size(maybeCompressed) - GetPos(maybeCompressed); if (num4 <= 0 || num4 > num5 || num4 > 67108864) { throw new InvalidDataException($"Invalid SkadiNet compressed payload length {num4}, remaining={num5}."); } SetPos(maybeCompressed, pos2); byte[] array = ReadByteArray(maybeCompressed); if (num != 1 || num2 != 1 || num3 <= 0 || num3 > 67108864) { throw new InvalidDataException($"Unsupported SkadiNet compression header protocol={num}, algo={num2}, size={num3}"); } if (array == null || array.Length != num4) { throw new InvalidDataException($"SkadiNet compression payload length mismatch expected={num4}, actual={((array != null) ? array.Length : 0)}."); } byte[] bytes = Inflate(array, num3); rawPackage = NewPackage(bytes); return true; } finally { SetPos(maybeCompressed, pos); } } internal static bool LooksCompressed(object pkg) { if (!Ready || pkg == null) { return false; } int pos = GetPos(pkg); try { if (Size(pkg) < 16) { return false; } SetPos(pkg, 0); return ReadInt(pkg) == 1129204563; } catch { return false; } finally { SetPos(pkg, pos); } } private static byte[] Deflate(byte[] raw) { using MemoryStream memoryStream = new MemoryStream(); using (DeflateStream deflateStream = new DeflateStream(memoryStream, CompressionLevel.Fastest, leaveOpen: true)) { deflateStream.Write(raw, 0, raw.Length); } return memoryStream.ToArray(); } private static byte[] Inflate(byte[] compressed, int expectedSize) { using MemoryStream stream = new MemoryStream(compressed); using DeflateStream deflateStream = new DeflateStream(stream, CompressionMode.Decompress); using MemoryStream memoryStream = new MemoryStream(Math.Min(expectedSize, 1048576)); byte[] array = new byte[81920]; int num = 0; int num2; while ((num2 = deflateStream.Read(array, 0, array.Length)) > 0) { num += num2; if (num > expectedSize || num > 67108864) { throw new InvalidDataException($"Inflated payload exceeds declared size {expectedSize}."); } memoryStream.Write(array, 0, num2); } byte[] array2 = memoryStream.ToArray(); if (array2.Length != expectedSize) { throw new InvalidDataException($"Inflated size mismatch expected={expectedSize}, actual={array2.Length}"); } return array2; } } [BepInPlugin("sighsorry.SkadiNet", "SkadiNet", "1.1.6")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "sighsorry.SkadiNet"; public const string PluginName = "SkadiNet"; public const string ModVersion = "1.1.6"; internal static ManualLogSource Log; private Harmony _harmony; private void Awake() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; ConfigSyncManager.Initialize(); ModConfig.Bind(((BaseUnityPlugin)this).Config); ReflectionCache.Initialize(); ZSteamSocketRegisterGlobalCallbacksTransportPolicyPatch.Initialize(); FeatureNegotiation.Initialize(); PeerQualityMeter.Initialize(); ZdoKeyPolicy.Initialize(); DungeonLayerFilter.Initialize(); OwnershipManager.Initialize(); ClientStutterGuard.Initialize(this); _harmony = new Harmony("sighsorry.SkadiNet"); _harmony.PatchAll(typeof(Plugin).Assembly); DungeonLayerFilter.LogStartupStatus(); Log.LogInfo((object)("SkadiNet 1.1.6 loaded. Master switch=" + (ModConfig.Enabled.Value ? "on" : "off") + ". Stable core: throughput-based ZDO scheduling, Steam transport policy, micro-update reducer, peer quality, adaptive client ownership, optional dungeon layers, optional client stutter guard. Slider-gated features: scheduler=" + (EffectiveConfig.SchedulerEnabled ? "on" : "off") + ", payload reducer=" + (EffectiveConfig.PayloadReducerEnabled ? "on" : "off") + ", compression=" + (EffectiveConfig.CompressionEnabled ? "on" : "off") + ", ClientStutterGuard=" + (EffectiveConfig.ClientStutterGuardEnabled ? "on" : "off") + ".")); } private void Update() { ZSteamSocketRegisterGlobalCallbacksTransportPolicyPatch.ApplyPendingPolicy(); OwnershipManager.TickLightweight(); } private void OnDestroy() { try { ClientStutterGuard.Shutdown(); DungeonLayerFilter.Shutdown(); ZSteamSocketRegisterGlobalCallbacksTransportPolicyPatch.Shutdown(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception arg) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)$"Failed to unpatch cleanly: {arg}"); } } } } internal static class GameplayReflection { 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.CachedField(instance.GetType(), "m_nview")?.GetValue(instance); } catch { } return null; } internal static object GetZdoFromCharacterLike(object instance) { return GetZdoFromNView(GetNViewFromCharacterLike(instance)); } 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 NetReflection { private static Func<object, object> _peerRpcGetter; private static Func<object, object> _peerUidGetter; private static Func<object, Vector3> _peerRefPosGetter; private static Func<object, object> _peerCharacterIdGetter; private static Func<object, Vector3> _znetReferencePositionGetter; private static Func<object, object> _rpcSocketGetter; private static Func<object, bool> _znetIsServer; private static Func<object, bool> _znetIsDedicated; 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); _peerCharacterIdGetter = ReflectionDelegateFactory.BoxedFieldGetter(ReflectionCache.PeerCharacterIdField); _znetReferencePositionGetter = ReflectionDelegateFactory.Vector3InstanceMethod(ReflectionCache.ZNetGetReferencePositionMethod); _rpcSocketGetter = ReflectionDelegateFactory.BoxedInstanceMethod(ReflectionCache.ZRpcGetSocketMethod); _znetIsServer = ReflectionDelegateFactory.BooleanInstanceMethod(ReflectionCache.ZNetIsServerMethod); _znetIsDedicated = ReflectionDelegateFactory.BooleanInstanceMethod(ReflectionCache.ZNetIsDedicatedMethod); } internal static bool IsServer() { try { object zNetInstance = ZNetInstance; if (zNetInstance == null || ReflectionCache.ZNetIsServerMethod == null) { return false; } if (_znetIsServer != null) { return _znetIsServer(zNetInstance); } 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; } if (_znetIsDedicated != null) { return _znetIsDedicated(zNetInstance); } 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 { object netPeer = GetNetPeer(peerOrRpc); if (ReflectionCache.TryConvertToLong(TryGet(_peerUidGetter, netPeer), out uid)) { return true; } if (ReflectionCache.TryConvertToLong(((netPeer == null) ? null : ReflectionCache.CachedField(netPeer.GetType(), "m_uid"))?.GetValue(netPeer), out uid)) { return true; } return TryGetUidFromPeerObject(peerOrRpc, out uid); } catch { return false; } } private 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 { if (ReflectionCache.ZRpcType != null && ReflectionCache.ZRpcType.IsInstanceOfType(peer)) { return peer; } object netPeer = GetNetPeer(peer); object obj = TryGet(_peerRpcGetter, netPeer); if (obj != null) { return obj; } return ((netPeer == null) ? null : ReflectionCache.CachedField(netPeer.GetType(), "m_rpc"))?.GetValue(netPeer); } 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 bool TryGetPeerCharacterId(object peer, out object characterId) { characterId = null; if (peer == null) { return false; } try { object netPeer = GetNetPeer(peer); characterId = TryGet(_peerCharacterIdGetter, netPeer); return characterId != null; } catch { return false; } } internal static bool TryGetPeerRefPos(object peer, out Vector3 position) { //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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: 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_005f: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; if (peer == null) { return false; } try { object netPeer = GetNetPeer(peer); if (TryGetVector3(_peerRefPosGetter, netPeer, out position)) { return true; } if (((netPeer == null) ? null : ReflectionCache.CachedField(netPeer.GetType(), "m_refPos"))?.GetValue(netPeer) is Vector3 val) { position = val; return true; } } catch { } return false; } internal static bool TryGetReferencePosition(out Vector3 position) { //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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_004b: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; try { object zNetInstance = ZNetInstance; if (TryGetVector3(_znetReferencePositionGetter, zNetInstance, out position)) { return true; } if (zNetInstance != null && ReflectionCache.ZNetGetReferencePositionMethod?.Invoke(zNetInstance, null) is Vector3 val) { position = val; return true; } } catch { } return false; } private static object GetNetPeer(object candidate) { if (candidate == null) { return null; } try { if (ReflectionCache.ZNetPeerType != null && ReflectionCache.ZNetPeerType.IsInstanceOfType(candidate)) { return candidate; } object obj = ReflectionCache.CachedField(candidate.GetType(), "m_peer")?.GetValue(candidate); if (obj != null && obj != candidate) { return obj; } if (ReflectionCache.CachedField(candidate.GetType(), "m_rpc") != null) { return candidate; } } catch { } return null; } 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 static class ReflectionCache { private static readonly Dictionary<Type, Dictionary<string, FieldInfo>> FieldCache = new Dictionary<Type, Dictionary<string, FieldInfo>>(); private static readonly Dictionary<Type, PropertyInfo> KeyPropertiesByType = new Dictionary<Type, PropertyInfo>(); internal static Type ZNetViewType; internal static Type PlayerType; internal static FieldInfo ZNetViewZdoField; internal static FieldInfo PlayerLocalPlayerField; internal static MethodInfo ZNetViewGetZDOMethod; internal static Type ZNetType; internal static Type ZNetPeerType; internal static Type ZRpcType; internal static Type ZPackageType; internal static Type ZSteamSocketType; internal static FieldInfo ZNetInstanceField; internal static FieldInfo PeerRpcField; internal static FieldInfo PeerUidField; internal static FieldInfo PeerRefPosField; internal static FieldInfo PeerCharacterIdField; internal static MethodInfo ZNetIsServerMethod; internal static MethodInfo ZNetIsDedicatedMethod; internal static MethodInfo ZNetGetReferencePositionMethod; internal static MethodInfo ZNetOnNewConnectionMethod; internal static MethodInfo ZRpcGetSocketMethod; internal static MethodInfo ZRpcInvokeMethod; internal static MethodInfo ZRpcRegisterGenericPackageMethod; internal static MethodInfo ZSteamSocketSendMethod; internal static MethodInfo ZSteamSocketRecvMethod; internal static Type ZDOManType; internal static Type ZDOType; internal static Type ZDOIDType; internal static Type ZDOExtraDataType; internal static Type ZDOVarsType; internal static Type ZNetSceneType; internal static Type ZoneSystemType; internal static Type TeleportType; internal static Type DungeonGeneratorType; internal static FieldInfo ZDOManPeersField; internal static FieldInfo ZDOManNextSendPeerField; internal static FieldInfo ZDOManSendTimerField; internal static FieldInfo ZDOManSessionIdField; internal static FieldInfo ZDOManInstanceField; internal static FieldInfo ZDOUidField; internal static FieldInfo ZDOObjectsBySectorField; internal static FieldInfo ZDOManTempSectorObjectsField; internal static FieldInfo ZDOManTempToSyncDistantField; internal static FieldInfo ZNetSceneInstanceField; internal static FieldInfo ZNetSceneNamedPrefabsField; internal static FieldInfo ZNetSceneTempCurrentObjectsField; internal static FieldInfo ZNetSceneTempCurrentDistantObjectsField; internal static MethodInfo SendZDOsMethod; internal static MethodInfo ZDOGetVec3Method; internal static MethodInfo ZDOGetQuaternionMethod; internal static MethodInfo ZDOGetPositionMethod; internal static MethodInfo ZDOGetPrefabMethod; internal static MethodInfo ZDOGetOwnerMethod; internal static MethodInfo ZDOSetOwnerMethod; internal static MethodInfo ZDOPersistentGetter; internal static MethodInfo ZDOIDUserIDGetter; internal static MethodInfo ZDOIDIDGetter; internal static MethodInfo ZDOManForceSendZDOMethod; internal static MethodInfo ZDOManGetSessionIdMethod; internal static MethodInfo ZDOManGetZDOMethod; internal static MethodInfo ZDOManFindSectorObjectsMethod; internal static MethodInfo ZDOManCreateSyncListMethod; internal static MethodInfo ZDOManServerSortSendZdosMethod; internal static MethodInfo ZDOManAddForceSendZdosMethod; internal static MethodInfo ZDOManReleaseNearbyZdosMethod; internal static MethodInfo ZNetSceneCreateDestroyObjectsMethod; internal static MethodInfo ZNetSceneCreateObjectsMethod; internal static MethodInfo ZNetSceneRemoveObjectsMethod; internal static MethodInfo ZNetSceneIsAreaReadyMethod; internal static MethodInfo ZNetSceneInActiveAreaMethod; internal static MethodInfo ZdoListGetEnumeratorMethod; internal static MethodInfo ZoneSystemStartMethod; internal static MethodInfo ZDOExtraDataGetDataMethod; internal static void Initialize() { FieldCache.Clear(); KeyPropertiesByType.Clear(); InitializeNetReflection(); InitializeZdoReflection(); InitializeGameplayReflection(); NetReflection.Initialize(); ZdoReflection.Initialize(); ZPackageTools.Initialize(); } internal static FieldInfo SilentField(Type type, string name) { if (type == null || string.IsNullOrEmpty(name)) { return null; } Type type2 = type; while (type2 != null) { FieldInfo field = type2.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field; } type2 = type2.BaseType; } return null; } private static void InitializeGameplayReflection() { ZNetViewType = AccessTools.TypeByName("ZNetView"); PlayerType = AccessTools.TypeByName("Player"); ZNetViewZdoField = SilentField(ZNetViewType, "m_zdo"); PlayerLocalPlayerField = SilentField(PlayerType, "m_localPlayer"); ZNetViewGetZDOMethod = AccessTools.Method(ZNetViewType, "GetZDO", (Type[])null, (Type[])null); } private static void InitializeNetReflection() { ZNetType = AccessTools.TypeByName("ZNet"); ZNetPeerType = AccessTools.TypeByName("ZNetPeer"); ZRpcType = AccessTools.TypeByName("ZRpc"); ZPackageType = AccessTools.TypeByName("ZPackage"); ZSteamSocketType = AccessTools.TypeByName("ZSteamSocket"); ZNetInstanceField = SilentField(ZNetType, "instance") ?? SilentField(ZNetType, "m_instance") ?? SilentField(ZNetType, "s_instance"); PeerRpcField = SilentField(ZNetPeerType, "m_rpc"); PeerUidField = SilentField(ZNetPeerType, "m_uid"); PeerRefPosField = SilentField(ZNetPeerType, "m_refPos"); PeerCharacterIdField = SilentField(ZNetPeerType, "m_characterID"); ZNetIsServerMethod = AccessTools.Method(ZNetType, "IsServer", Type.EmptyTypes, (Type[])null); ZNetIsDedicatedMethod = AccessTools.Method(ZNetType, "IsDedicated", Type.EmptyTypes, (Type[])null); ZNetGetReferencePositionMethod = AccessTools.Method(ZNetType, "GetReferencePosition", Type.EmptyTypes, (Type[])null); ZNetOnNewConnectionMethod = ((ZNetPeerType == null) ? null : AccessTools.Method(ZNetType, "OnNewConnection", new Type[1] { ZNetPeerType }, (Type[])null)); ZRpcGetSocketMethod = AccessTools.Method(ZRpcType, "GetSocket", Type.EmptyTypes, (Type[])null); ZRpcInvokeMethod = AccessTools.Method(ZRpcType, "Invoke", new Type[2] { typeof(string), typeof(object[]) }, (Type[])null); ZRpcRegisterGenericPackageMethod = FindZRpcPackageRegisterMethod(); ZSteamSocketSendMethod = ((ZPackageType == null) ? null : AccessTools.Method(ZSteamSocketType, "Send", new Type[1] { ZPackageType }, (Type[])null)); ZSteamSocketRecvMethod = AccessTools.Method(ZSteamSocketType, "Recv", Type.EmptyTypes, (Type[])null); } private static MethodInfo FindZRpcPackageRegisterMethod() { if (ZRpcType == null || ZPackageType == null) { return null; } MethodInfo[] methods = ZRpcType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (!(methodInfo.Name != "Register") && methodInfo.IsGenericMethodDefinition && methodInfo.GetGenericArguments().Length == 1) { ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length == 2 && parameters[0].ParameterType == typeof(string)) { return methodInfo.MakeGenericMethod(ZPackageType); } } } return null; } internal static bool TryConvertToLong(object value, out long result) { result = 0L; if (value is long num) { result = num; return true; } if (value is ulong num2) { result = (long)num2; return true; } if (value is int num3) { result = num3; return true; } if (value is uint num4) { result = num4; return true; } if (value is short num5) { result = num5; return true; } if (value is ushort num6) { result = num6; return true; } return false; } internal static bool TryConvertToUInt(object value, out uint result) { result = 0u; if (value is uint num) { result = num; return true; } if (value is int num2 && num2 >= 0) { result = (uint)num2; return true; } if (value is ulong num3 && num3 <= uint.MaxValue) { result = (uint)num3; return true; } if (value is long num4 && num4 >= 0 && num4 <= uint.MaxValue) { result = (uint)num4; return true; } if (value is ushort num5) { result = num5; return true; } if (value is short num6 && num6 >= 0) { result = (uint)num6; return true; } if (value is byte b) { result = b; return true; } return false; } internal static FieldInfo CachedField(Type type, string name) { if (type == null || string.IsNullOrEmpty(name)) { return null; } lock (FieldCache) { if (!FieldCache.TryGetValue(type, out var value)) { value = new Dictionary<string, FieldInfo>(); FieldCache[type] = value; } if (!value.TryGetValue(name, out var value2)) { value2 = (value[name] = SilentField(type, name)); } return value2; } } internal static boo