using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FiresGhettoNetworkMod.AutoTune; using HarmonyLib; using Microsoft.CodeAnalysis; using Steamworks; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("VAGhettoNetworking")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("VAGhettoNetworking")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("f234bd11-429c-45e2-b280-1fb9121d050d")] [assembly: AssemblyFileVersion("1.4.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.4.0.0")] [module: UnverifiableCode] [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 FiresGhettoNetworkMod { [BepInPlugin("com.Fire.FiresGhettoNetworkMod", "FiresGhettoNetworkMod", "1.3.0")] public class FiresGhettoNetworkMod : BaseUnityPlugin { [HarmonyPatch] public static class WackyDatabaseCompatibilityPatch { public static void Init(Harmony harmony) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown Type type = Type.GetType("wackydatabase.Util.Functions, WackysDatabase"); if (type == null) { LoggerOptions.LogInfo("WackyDatabase not detected — skipping compatibility patch."); return; } MethodInfo method = type.GetMethod("SnapshotItem", BindingFlags.Static | BindingFlags.Public); if (method == null) { LoggerOptions.LogWarning("WackyDatabase detected but SnapshotItem method not found — patch skipped."); return; } harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(WackyDatabaseCompatibilityPatch), "SnapshotItem_Prefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); LoggerOptions.LogInfo("WackyDatabase compatibility patch applied — will skip snapshots for invalid/broken clones."); } [HarmonyPrefix] public static bool SnapshotItem_Prefix(ref ItemDrop item) { if ((Object)(object)item == (Object)null) { LoggerOptions.LogWarning("WDB: Skipping snapshot for null ItemDrop (likely broken clone)."); return false; } if ((Object)(object)((Component)item).gameObject == (Object)null) { LoggerOptions.LogWarning("WDB: Skipping snapshot for " + ((Object)item).name + " — gameObject is null (missing prefab from removed mod)."); return false; } bool flag = ((Component)item).GetComponentsInChildren(true).Length != 0; bool flag2 = ((Component)item).GetComponentsInChildren(true).Length != 0; if (!flag && !flag2) { LoggerOptions.LogWarning("WDB: Skipping snapshot for " + ((Object)item).name + " — no renderers or meshes (broken model)."); return false; } return true; } } [CompilerGenerated] private sealed class d__56 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public FiresGhettoNetworkMod <>4__this; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__56(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; break; case 1: <>1__state = -1; break; } if (ZRoutedRpc.instance == null) { <>2__current = null; <>1__state = 1; return true; } if (_dummyRpcRegistered) { ((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)"Dummy ForceUpdateZDO RPC already registered — skipping."); return false; } ZRoutedRpc.instance.Register("ForceUpdateZDO", (Action)delegate { }); _dummyRpcRegistered = true; ((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)"Dummy ForceUpdateZDO RPC registered."); 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(); } } public const string PluginGUID = "com.Fire.FiresGhettoNetworkMod"; public const string PluginName = "FiresGhettoNetworkMod"; public const string PluginVersion = "1.3.0"; public static ConfigEntry ConfigLogLevel; public static ConfigEntry ConfigEnableCompression; public static ConfigEntry ConfigUpdateRate; public static ConfigEntry ConfigSendRateMin; public static ConfigEntry ConfigSendRateMax; public static ConfigEntry ConfigQueueSize; public static ConfigEntry ConfigForceCrossplay; public static ConfigEntry ConfigPlayerLimit; public static ConfigEntry ConfigAdvertisedPlayerLimit; public static ConfigEntry ConfigEnableShipFixes; public static ConfigEntry ConfigEnableServerSideShipSimulation; public static ConfigEntry ConfigExtendedZoneRadius; public static ConfigEntry ConfigEnableZDOThrottling; public static ConfigEntry ConfigZDOThrottleDistance; public static ConfigEntry ConfigEnableAILOD; public static ConfigEntry ConfigAILODNearDistance; public static ConfigEntry ConfigAILODFarDistance; public static ConfigEntry ConfigAILODThrottleFactor; public static ConfigEntry ConfigZoneLoadBatchSize; public static ConfigEntry ConfigZPackageReceiveBufferSize; public static ConfigEntry ConfigEnableTimeSliceInstantiation; public static ConfigEntry ConfigInstantiationBudgetMs; public static ConfigEntry ConfigMaxInstancesPerFrame; public static ConfigEntry ConfigSafetyFallbackEnabled; public static ConfigEntry ConfigSafetyFallbackThreshold; public static ConfigEntry ConfigEnablePredictiveZoneStreaming; public static ConfigEntry ConfigPredictionLookaheadSec; public static ConfigEntry ConfigPredictionMinVelocity; public static ConfigEntry ConfigPredictionMaxLookaheadZones; public static ConfigEntry ConfigEnableInvulnerableSupportSkip; public static ConfigEntry ConfigEnableRpcRouter; public static ConfigEntry ConfigEnableRpcAoI; public static ConfigEntry ConfigRpcAoIRadius; public static ConfigEntry ConfigEnableZDODelta; public static ConfigEntry ConfigEnableWNTServerOptimization; public static ConfigEntry ConfigEnableServerAuthority; private static bool _dummyRpcRegistered; private bool _delayedInitDone = false; internal static Harmony Harmony { get; private set; } public static FiresGhettoNetworkMod Instance { get; private set; } private void Awake() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown Instance = this; Harmony = new Harmony("com.Fire.FiresGhettoNetworkMod"); BindConfigs(); try { ((BaseUnityPlugin)this).Config.Save(); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Failed to save config file immediately: " + ex.Message)); } LoggerOptions.Init(((BaseUnityPlugin)this).Logger); ServerClientUtils.Detect(((BaseUnityPlugin)this).Logger); bool isDedicatedServerDetected = ServerClientUtils.IsDedicatedServerDetected; if (isDedicatedServerDetected) { LoggerOptions.LogMessage("FiresGhettoNetworkMod v1.3.0 — Running on DEDICATED SERVER, enabling server-side features."); } else { LoggerOptions.LogMessage("FiresGhettoNetworkMod v1.3.0 — Running on CLIENT or SINGLE-PLAYER/LISTEN SERVER, only client-safe features will be applied."); } SafeInvokeInit("FiresGhettoNetworkMod.CompressionGroup", "InitConfig", new object[1] { ((BaseUnityPlugin)this).Config }); SafeInvokeInit("FiresGhettoNetworkMod.NetworkingRatesGroup", "Init", new object[1] { ((BaseUnityPlugin)this).Config }); SafeInvokeInit("FiresGhettoNetworkMod.DedicatedServerGroup", "Init", new object[1] { ((BaseUnityPlugin)this).Config }); Harmony.PatchAll(typeof(CompressionGroup)); Harmony.PatchAll(typeof(NetworkingRatesGroup)); Harmony.PatchAll(typeof(DedicatedServerGroup)); Harmony.PatchAll(typeof(BigZdoDiagnostic)); WackyDatabaseCompatibilityPatch.Init(Harmony); PlayerPositionSyncPatches.Init(((BaseUnityPlugin)this).Config); Harmony.PatchAll(typeof(PlayerPositionSyncPatches)); Harmony.PatchAll(typeof(WearNTearClientSupportPatches)); AutoTuneConfig.Init(((BaseUnityPlugin)this).Config); Harmony.PatchAll(typeof(AutoTuneProbeHooks)); Harmony.PatchAll(typeof(ZoneLoadPatches)); ServerAutoTune.InitServerSide(); if (isDedicatedServerDetected && ConfigEnableServerAuthority.Value) { Harmony.PatchAll(typeof(ShipFixesGroup)); Harmony.PatchAll(typeof(ZDOMemoryManager)); Harmony.PatchAll(typeof(ServerAuthorityPatches)); Harmony.PatchAll(typeof(MonsterAIPatches)); Harmony.PatchAll(typeof(ZDOThrottlingPatches)); Harmony.PatchAll(typeof(AILODPatches)); if (ConfigEnableRpcRouter.Value) { Harmony.PatchAll(typeof(RpcRouterPatches)); DamageTextHandler.Register(); HealthChangedHandler.Register(); WNTHealthChangedHandler.Register(); SetTargetHandler.Register(); AddNoiseHandler.Register(); TriggerAnimationHandler.Register(); TriggerOnDeathHandler.Register(); TalkerSayHandler.Register(); SpawnedZoneHandler.Register(); LoggerOptions.LogInfo("RPC Router enabled — DamageText, HealthChanged, WNTHealthChanged, SetTarget, AddNoise, TriggerAnimation, TriggerOnDeath, TalkerSay, SpawnedZone handlers registered."); } if (ConfigEnableZDODelta.Value) { Harmony.PatchAll(typeof(ZDODeltaPatches)); LoggerOptions.LogInfo("ZDO delta compression enabled."); } if (ConfigEnableWNTServerOptimization.Value) { Harmony.PatchAll(typeof(WearNTearServerPatches)); LoggerOptions.LogInfo("WearNTear server optimization enabled."); } LoggerOptions.LogInfo("All server-side features and authority patches enabled."); } else if (!isDedicatedServerDetected) { LoggerOptions.LogInfo("Server-side features skipped — not running on a dedicated server."); } else { LoggerOptions.LogInfo("Server-side features disabled via ConfigEnableServerAuthority = false."); } ((MonoBehaviour)this).StartCoroutine(RegisterDummyRpcWhenReady()); ((BaseUnityPlugin)this).Logger.LogInfo((object)"FiresGhettoNetworkMod v1.3.0 loaded."); } private void OnApplicationQuit() { } private void TryPatchAll(Type type) { if (type == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Tried to patch a null type!"); } else { Harmony.PatchAll(type); } } private void SafeInvokeInit(string typeName, string methodName, object[] args) { try { Type type = Type.GetType(typeName); if (type == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Type " + typeName + " not found; skipping " + methodName + ".")); return; } MethodInfo method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method == null) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Method " + methodName + " not found on " + typeName + ".")); } else { method.Invoke(null, args); } } catch (TypeLoadException arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"TypeLoadException while invoking {typeName}.{methodName}: {arg}"); } catch (Exception arg2) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Exception while invoking {typeName}.{methodName}: {arg2}"); } } private void BindConfigs() { //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Expected O, but got Unknown //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Expected O, but got Unknown //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Expected O, but got Unknown //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Expected O, but got Unknown //IL_01fd: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Expected O, but got Unknown //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Expected O, but got Unknown //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Expected O, but got Unknown //IL_034b: Unknown result type (might be due to invalid IL or missing references) //IL_0355: Expected O, but got Unknown //IL_0376: Unknown result type (might be due to invalid IL or missing references) //IL_0380: Expected O, but got Unknown //IL_03a7: Unknown result type (might be due to invalid IL or missing references) //IL_03b1: Expected O, but got Unknown //IL_0404: Unknown result type (might be due to invalid IL or missing references) //IL_040e: Expected O, but got Unknown //IL_0441: Unknown result type (might be due to invalid IL or missing references) //IL_044b: Expected O, but got Unknown //IL_0474: Unknown result type (might be due to invalid IL or missing references) //IL_047e: Expected O, but got Unknown //IL_04d1: Unknown result type (might be due to invalid IL or missing references) //IL_04db: Expected O, but got Unknown //IL_052e: Unknown result type (might be due to invalid IL or missing references) //IL_0538: Expected O, but got Unknown //IL_056b: Unknown result type (might be due to invalid IL or missing references) //IL_0575: Expected O, but got Unknown //IL_05a8: Unknown result type (might be due to invalid IL or missing references) //IL_05b2: Expected O, but got Unknown //IL_05e1: Unknown result type (might be due to invalid IL or missing references) //IL_05eb: Expected O, but got Unknown ConfigLogLevel = ((BaseUnityPlugin)this).Config.Bind("General", "Log Level", LogLevel.Message, "Controls verbosity in BepInEx log."); ConfigEnableCompression = ((BaseUnityPlugin)this).Config.Bind("Networking", "Enable Compression", true, "Enable ZSTD network compression (highly recommended)."); ConfigUpdateRate = ((BaseUnityPlugin)this).Config.Bind("Networking", "Update Rate", UpdateRateOptions._100, "Server ZDO update frequency. Higher = smoother player movement, more bandwidth.\n100% (20Hz) matches vanilla and is the safe default; 150% (30Hz) is recommended\nfor high-pop servers with bandwidth headroom."); ConfigSendRateMin = ((BaseUnityPlugin)this).Config.Bind("Networking.Steamworks", "Send Rate Min", SendRateMinOptions._256KB, "Minimum send rate Steam will attempt."); ConfigSendRateMax = ((BaseUnityPlugin)this).Config.Bind("Networking.Steamworks", "Send Rate Max", SendRateMaxOptions._512KB, "Maximum send rate Steam will attempt."); ConfigQueueSize = ((BaseUnityPlugin)this).Config.Bind("Networking", "Queue Size", QueueSizeOptions._32KB, "Send queue size. Higher helps high-player servers."); ConfigForceCrossplay = ((BaseUnityPlugin)this).Config.Bind("Dedicated Server", "Force Crossplay", ForceCrossplayOptions.steamworks, "Requires restart.\nsteamworks = Force crossplay DISABLED (Steam friends only)\nplayfab = Force crossplay ENABLED (PlayFab matchmaking)\nvanilla = Respect command-line -crossplay flag (default Valheim behavior)"); ConfigPlayerLimit = ((BaseUnityPlugin)this).Config.Bind("Dedicated Server", "Player Limit", 10, new ConfigDescription("Max players on dedicated server. Requires restart.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 999), Array.Empty())); ConfigAdvertisedPlayerLimit = ((BaseUnityPlugin)this).Config.Bind("Dedicated Server", "Advertised Player Limit", 0, new ConfigDescription("Max players advertised to matchmaking (Steam server browser → BattleMetrics, PlayFab session/Party). Independent of the actual in-game limit set by 'Player Limit' — useful when an operator wants their server listed as '/500' for marketing while running a real 30-slot cap. 0 = mirror 'Player Limit' (advertised matches reality). Requires restart.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 9999), Array.Empty())); ConfigZoneLoadBatchSize = ((BaseUnityPlugin)this).Config.Bind("Client Performance", "Zone Load Batch Size", 1, new ConfigDescription("How aggressively the client consumes incoming zone-stream backlog per frame.\n1 = vanilla (one CreateObjects pass per frame, capped by Valheim).\n2 = double the per-frame cap (faster zone load, bigger frame hitches).\n4 = quadruple (zone-cross stutter masking on capable machines).\nAuto-Tune may override this on the client based on measured frame time.\nCLIENT-ONLY — no effect on server.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 8), Array.Empty())); ConfigZPackageReceiveBufferSize = ((BaseUnityPlugin)this).Config.Bind("Client Performance", "ZPackage Receive Buffer Bytes", 262144, new ConfigDescription("Steam recv-buffer size for inbound network packages. Bigger = fewer dropped packets\nif the client briefly stalls (GC pause, disk hitch), but uses more RAM.\nAuto-Tune may override this on the client based on measured tier.", (AcceptableValueBase)(object)new AcceptableValueRange(65536, 4194304), Array.Empty())); ConfigEnableTimeSliceInstantiation = ((BaseUnityPlugin)this).Config.Bind("Client Performance", "Enable Time-Slice Instantiation", true, "Replace vanilla's fixed 10/100 per-frame ZDO instantiation cap with a per-frame ms\nbudget that drains incoming objects across multiple ticks. Eliminates the big spike\nwhen crossing into a heavy zone. Disable to fall back to the cap-bump transpiler\n(set 'Zone Load Batch Size' to control its multiplier).\nCLIENT-ONLY — no effect on dedicated server."); ConfigInstantiationBudgetMs = ((BaseUnityPlugin)this).Config.Bind("Client Performance", "Instantiation Budget Ms", 3, new ConfigDescription("Per-frame millisecond budget for ZDO → GameObject instantiation when time-slicing\nis enabled. Lower = smoother frame times during zone load (longer ramp-in); higher\n= zone loads finish faster (bigger spikes). 3 ms keeps 60-fps clients comfortable.\nAuto-Tune overrides this with tier-specific values when active.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 16), Array.Empty())); ConfigMaxInstancesPerFrame = ((BaseUnityPlugin)this).Config.Bind("Client Performance", "Max Instances Per Frame", 100, new ConfigDescription("Hard ceiling on instantiations per frame regardless of how much budget remains.\nBelt-and-suspenders against a runaway pending list.\nAuto-Tune overrides this with tier-specific values when active.", (AcceptableValueBase)(object)new AcceptableValueRange(10, 500), Array.Empty())); ConfigSafetyFallbackEnabled = ((BaseUnityPlugin)this).Config.Bind("Client Performance", "Safety Fallback Enabled", true, "When the pending instantiation list grows past 'Safety Fallback Threshold' items\n(teleport into megabase, freshly-loaded zones), widen the per-frame budget so we\ndon't bleed across many seconds. Capped at 16 ms so we never burn an entire frame."); ConfigSafetyFallbackThreshold = ((BaseUnityPlugin)this).Config.Bind("Client Performance", "Safety Fallback Threshold", 5000, new ConfigDescription("Pending-ZDO count at which the safety fallback widens the instantiation budget.", (AcceptableValueBase)(object)new AcceptableValueRange(100, 50000), Array.Empty())); ConfigEnableShipFixes = ((BaseUnityPlugin)this).Config.Bind("Ship Fixes", "Enable Universal Ship Fixes", true, "Apply permanent autopilot + jitter fixes to ALL ships."); ConfigEnableServerSideShipSimulation = ((BaseUnityPlugin)this).Config.Bind("Ship Fixes", "Server-Side Ship Simulation", false, "Server authoritatively simulates ship physics.\nDisabled by default — enable manually if you want the server to drive ship physics."); ConfigEnableRpcRouter = ((BaseUnityPlugin)this).Config.Bind("Server Authority", "Enable RPC Router", true, "Server-side RPC filtering — drops unnecessary DamageText/HealthChanged RPCs before routing\nand prevents SetTarget exploits (targeting players/tamed creatures).\nSaves bandwidth and hardens server security.\nSERVER-ONLY — no effect on client."); ConfigEnableRpcAoI = ((BaseUnityPlugin)this).Config.Bind("Server Authority", "Enable RPC Area-of-Interest", true, "When enabled, broadcast RPCs targeting a specific ZDO are only forwarded to peers\nwithin the configured radius of that ZDO. Massively reduces bandwidth on busy servers.\nRPCs without a target ZDO (global RPCs) are always broadcast to all peers.\nRequires Enable RPC Router = true. SERVER-ONLY."); ConfigRpcAoIRadius = ((BaseUnityPlugin)this).Config.Bind("Server Authority", "RPC AoI Radius", 256f, new ConfigDescription("Distance (meters) from the target ZDO within which peers will receive the RPC.\nVanilla active area is ~7 zones × 64m = ~448m. Default 256m covers nearby players.\nSet higher if players report missing interactions at distance.", (AcceptableValueBase)(object)new AcceptableValueRange(64f, 1024f), Array.Empty())); ConfigEnableServerAuthority = ((BaseUnityPlugin)this).Config.Bind("Server Authority", "Enable Server-Side Simulation", false, new ConfigDescription("Makes the server fully authoritative over zones, ZDO ownership, monster AI, events, etc. (does NOT override your existing ship fixes).\n\nDisabled by default — enable manually on your DEDICATED SERVER if desired.\n\nWARNING: THIS IS A SERVER-ONLY FEATURE!\nEnabling this on a CLIENT will cause INFINITE LOADING SCREEN.\nThe mod automatically disables it on clients regardless of this setting.", (AcceptableValueBase)null, Array.Empty())); ConfigExtendedZoneRadius = ((BaseUnityPlugin)this).Config.Bind("Server Authority", "Extended Zone Radius", 1, new ConfigDescription("Additional zone layers the server pre-loads around players for smoother zone transitions.\n0 = vanilla (no extra pre-load)\n1 = +1 layer (recommended, ~7x7 zones total)\n2 = +2 layers (~9x9 zones)\n3 = +3 layers (~11x11 zones)\n\nHigher values reduce stutter when crossing zone borders but increase server CPU/RAM usage.\nSERVER-ONLY — clients ignore this setting.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 3), Array.Empty())); ConfigEnablePredictiveZoneStreaming = ((BaseUnityPlugin)this).Config.Bind("Server Authority", "Enable Predictive Zone Streaming", true, "Bias each peer's active-area center forward along their velocity vector so the\nserver starts loading zones BEFORE the peer crosses the boundary. Composes with\n'Extended Zone Radius' — the symmetric ring still expands, the center just slides\nforward. By the time the peer arrives, the predicted zones are already loaded.\nSERVER-ONLY — clients ignore this setting."); ConfigPredictionLookaheadSec = ((BaseUnityPlugin)this).Config.Bind("Server Authority", "Prediction Lookahead Sec", 3f, new ConfigDescription("How many seconds ahead the server projects each peer's position when deciding\nwhich zones to pre-load. 3 s gives one zone of headroom at running speed.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 10f), Array.Empty())); ConfigPredictionMinVelocity = ((BaseUnityPlugin)this).Config.Bind("Server Authority", "Prediction Min Velocity", 2f, new ConfigDescription("Minimum smoothed speed (m/s) below which prediction is suppressed and the peer's\nreal position is used. Stops idle / slow-walking peers from triggering pre-load.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 20f), Array.Empty())); ConfigPredictionMaxLookaheadZones = ((BaseUnityPlugin)this).Config.Bind("Server Authority", "Prediction Max Lookahead Zones", 9, new ConfigDescription("Hard cap on prediction distance, expressed in zones (64 m each). Stops a\nteleporting / glitching peer from subscribing to zones across the world.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 25), Array.Empty())); ConfigEnableZDOThrottling = ((BaseUnityPlugin)this).Config.Bind("Server Authority", "Enable ZDO Throttling", true, "Reduce update frequency for distant ZDOs (creatures/structures far away) to save bandwidth.\nSERVER-ONLY — no effect on client."); ConfigZDOThrottleDistance = ((BaseUnityPlugin)this).Config.Bind("Server Authority", "ZDO Throttle Distance", 500f, new ConfigDescription("Distance (meters) beyond which ZDOs are throttled (lower update rate).\n0 = disable throttling.\nRecommended: 400-600m.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1000f), Array.Empty())); ConfigEnableAILOD = ((BaseUnityPlugin)this).Config.Bind("Server Authority", "Enable AI LOD Throttling", true, "Reduce FixedUpdate frequency for distant AI (saves server CPU).\nNearby AI stays full speed for smooth combat.\nSERVER-ONLY — no effect on client."); ConfigAILODNearDistance = ((BaseUnityPlugin)this).Config.Bind("Server Authority", "AI LOD Near Distance", 100f, new ConfigDescription("Full-speed AI within this range (meters).", (AcceptableValueBase)(object)new AcceptableValueRange(50f, 200f), Array.Empty())); ConfigAILODFarDistance = ((BaseUnityPlugin)this).Config.Bind("Server Authority", "AI LOD Far Distance", 300f, new ConfigDescription("Beyond this distance, AI is throttled (meters).", (AcceptableValueBase)(object)new AcceptableValueRange(200f, 600f), Array.Empty())); ConfigAILODThrottleFactor = ((BaseUnityPlugin)this).Config.Bind("Server Authority", "AI LOD Throttle Factor", 0.5f, new ConfigDescription("Update multiplier for throttled AI (0.5 = half speed, 0.25 = quarter). Lower = more savings.", (AcceptableValueBase)(object)new AcceptableValueRange(0.25f, 0.75f), Array.Empty())); ZDOMemoryManager.ConfigMaxZDOs = ((BaseUnityPlugin)this).Config.Bind("Advanced", "Max Active ZDOs", 500000, new ConfigDescription("If the number of active ZDOs exceeds this value, the mod will force cleanup of orphan non-persistent ZDOs and run garbage collection.\nSet to 0 to disable. Useful on very long-running servers with high entity counts.\nDefault: 500000 (vanilla rarely goes above ~200k).", (AcceptableValueBase)(object)new AcceptableValueRange(0, 1000000), Array.Empty())); ConfigEnableZDODelta = ((BaseUnityPlugin)this).Config.Bind("Advanced", "Enable ZDO Delta Compression", true, "On re-syncs, only send ZDO fields that changed since last send to each peer.\nInitial sync always sends full ZDO state. Re-syncs only send the diff.\nSignificant bandwidth reduction for high-field ZDOs (creatures, players) where\nonly 1-2 fields change per tick (e.g. health, position).\nSERVER-ONLY — no effect on client."); ConfigEnableWNTServerOptimization = ((BaseUnityPlugin)this).Config.Bind("Advanced", "Enable WearNTear Server Optimization", true, "Skips structural support recalculation for building pieces that are at full health,\nnot wet, and not in the Ashlands. Support cannot change for intact static pieces,\nso this is a safe CPU saving on servers with large player bases.\nAlso short-circuits damaged-but-invulnerable pieces (Infinity Hammer, admin-flagged)\nsince their support state can't change either.\nSERVER-ONLY — no effect on client."); ConfigEnableInvulnerableSupportSkip = ((BaseUnityPlugin)this).Config.Bind("Advanced", "Enable Invulnerable Support Skip", true, "CLIENT-side counterpart to the WearNTear server optimization. Short-circuits the\nexpensive WearNTear.UpdateSupport call (Physics.OverlapBoxNonAlloc per piece) for\npieces whose damage modifiers are all Immune/Ignore — e.g. Infinity Hammer pieces.\nPins m_support at the material's max value so neighbouring mortal pieces still\nsee full support when querying. Massive steady-state CPU saving in megabases\ndominated by invulnerable pieces."); ConfigEntryBase[] array = (ConfigEntryBase[])(object)new ConfigEntryBase[37] { (ConfigEntryBase)ConfigLogLevel, (ConfigEntryBase)ConfigEnableCompression, (ConfigEntryBase)ConfigUpdateRate, (ConfigEntryBase)ConfigSendRateMin, (ConfigEntryBase)ConfigSendRateMax, (ConfigEntryBase)ConfigQueueSize, (ConfigEntryBase)ConfigForceCrossplay, (ConfigEntryBase)ConfigPlayerLimit, (ConfigEntryBase)ConfigAdvertisedPlayerLimit, (ConfigEntryBase)ConfigEnableShipFixes, (ConfigEntryBase)ConfigEnableServerSideShipSimulation, (ConfigEntryBase)ConfigEnableRpcRouter, (ConfigEntryBase)ConfigEnableRpcAoI, (ConfigEntryBase)ConfigRpcAoIRadius, (ConfigEntryBase)ConfigEnableServerAuthority, (ConfigEntryBase)ConfigExtendedZoneRadius, (ConfigEntryBase)ConfigEnableZDOThrottling, (ConfigEntryBase)ConfigZDOThrottleDistance, (ConfigEntryBase)ConfigEnableAILOD, (ConfigEntryBase)ConfigAILODNearDistance, (ConfigEntryBase)ConfigAILODFarDistance, (ConfigEntryBase)ConfigAILODThrottleFactor, (ConfigEntryBase)ZDOMemoryManager.ConfigMaxZDOs, (ConfigEntryBase)ConfigEnableZDODelta, (ConfigEntryBase)ConfigEnableWNTServerOptimization, (ConfigEntryBase)ConfigZoneLoadBatchSize, (ConfigEntryBase)ConfigZPackageReceiveBufferSize, (ConfigEntryBase)ConfigEnableTimeSliceInstantiation, (ConfigEntryBase)ConfigInstantiationBudgetMs, (ConfigEntryBase)ConfigMaxInstancesPerFrame, (ConfigEntryBase)ConfigSafetyFallbackEnabled, (ConfigEntryBase)ConfigSafetyFallbackThreshold, (ConfigEntryBase)ConfigEnablePredictiveZoneStreaming, (ConfigEntryBase)ConfigPredictionLookaheadSec, (ConfigEntryBase)ConfigPredictionMinVelocity, (ConfigEntryBase)ConfigPredictionMaxLookaheadZones, (ConfigEntryBase)ConfigEnableInvulnerableSupportSkip }; ConfigEntryBase[] array2 = array; foreach (ConfigEntryBase val in array2) { Type type = ((object)val).GetType(); EventInfo @event = type.GetEvent("SettingChanged"); if (@event != null) { EventHandler handler = delegate(object sender, EventArgs __) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown string text = (((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) ? "SERVER" : "CLIENT"); ConfigEntryBase val2 = (ConfigEntryBase)sender; LoggerOptions.LogInfo($"[{text}] Config changed: {val2.Definition.Section} → {val2.Definition.Key} = {val2.BoxedValue}"); }; @event.AddEventHandler(val, handler); } } if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { ConfigEnableServerAuthority.Value = false; } } private void Start() { ((MonoBehaviour)this).StartCoroutine(RegisterDummyRpcWhenReady()); } [IteratorStateMachine(typeof(d__56))] private IEnumerator RegisterDummyRpcWhenReady() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__56(0) { <>4__this = this }; } } public enum LogLevel { [Description("Errors/Warnings only")] Warning, [Description("Errors/Warnings/Messages [default]")] Message, [Description("Everything including Info")] Info } public enum UpdateRateOptions { [Description("150% - 30 updates/sec [recommended for high-pop]")] _150, [Description("100% - 20 updates/sec [default, vanilla]")] _100, [Description("75% - 15 updates/sec")] _75, [Description("50% - 10 updates/sec")] _50 } public enum SendRateMinOptions { [Description("1024 KB/s | 8 Mbit/s")] _1024KB, [Description("768 KB/s | 6 Mbit/s")] _768KB, [Description("512 KB/s | 4 Mbit/s")] _512KB, [Description("256 KB/s | 2 Mbit/s [default]")] _256KB, [Description("150 KB/s | 1.2 Mbit/s [vanilla]")] _150KB } public enum SendRateMaxOptions { [Description("1024 KB/s | 8 Mbit/s")] _1024KB, [Description("768 KB/s | 6 Mbit/s")] _768KB, [Description("512 KB/s | 4 Mbit/s [default]")] _512KB, [Description("256 KB/s | 2 Mbit/s")] _256KB, [Description("150 KB/s | 1.2 Mbit/s [vanilla]")] _150KB } public enum QueueSizeOptions { [Description("80 KB")] _80KB, [Description("64 KB")] _64KB, [Description("48 KB")] _48KB, [Description("32 KB [default]")] _32KB, [Description("Vanilla (~10 KB)")] _vanilla } public enum ForceCrossplayOptions { [Description("Vanilla behaviour - respect -crossplay flag [default]")] vanilla, [Description("Force crossplay ENABLED (use PlayFab backend)")] playfab, [Description("Force crossplay DISABLED (use Steamworks backend)")] steamworks } [HarmonyPatch] public static class AILODPatches { [HarmonyPatch(typeof(Character), "CustomFixedUpdate")] [HarmonyPrefix] public static bool CustomFixedUpdate_Prefix(Character __instance, float dt) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsDedicated() || !FiresGhettoNetworkMod.ConfigEnableAILOD.Value) { return true; } if (__instance.IsPlayer() || __instance.IsTamed()) { return true; } float num = float.MaxValue; foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer != (Object)null) { float num2 = Vector3.Distance(((Component)__instance).transform.position, ((Component)allPlayer).transform.position); if (num2 < num) { num = num2; } } } if (num <= FiresGhettoNetworkMod.ConfigAILODNearDistance.Value) { return true; } if (num > FiresGhettoNetworkMod.ConfigAILODFarDistance.Value) { int hashCode = ((object)__instance).GetHashCode(); float num3 = 1f / FiresGhettoNetworkMod.ConfigAILODThrottleFactor.Value; if ((Time.time + (float)hashCode * 0.001f) % num3 > dt) { return false; } } return true; } } [HarmonyPatch] public static class BigZdoDiagnostic { private const int Threshold = 100; private const int SerializeTruncationCeiling = 255; [HarmonyPatch(typeof(ZDO), "Save")] [HarmonyPrefix] public static void ZDO_Save_Prefix(ZDO __instance) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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_0060: Unknown result type (might be due to invalid IL or missing references) if (__instance == null) { return; } try { ZDOID uid = __instance.m_uid; InspectBuckets(__instance, "Save", canTruncate: false, ZDOExtraData.GetSaveFloats(uid).Count, ZDOExtraData.GetSaveVec3s(uid).Count, ZDOExtraData.GetSaveQuaternions(uid).Count, ZDOExtraData.GetSaveInts(uid).Count, ZDOExtraData.GetSaveLongs(uid).Count, ZDOExtraData.GetSaveStrings(uid).Count, ZDOExtraData.GetSaveByteArrays(uid).Count); } catch (Exception ex) { LoggerOptions.LogWarning("[BigZdoDiag] Save prefix threw: " + ex.Message); } } [HarmonyPatch(typeof(ZDO), "Serialize")] [HarmonyPrefix] public static void ZDO_Serialize_Prefix(ZDO __instance) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: 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_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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_0060: Unknown result type (might be due to invalid IL or missing references) if (__instance == null) { return; } try { ZDOID uid = __instance.m_uid; InspectBuckets(__instance, "Serialize", canTruncate: true, ZDOExtraData.GetFloats(uid).Count, ZDOExtraData.GetVec3s(uid).Count, ZDOExtraData.GetQuaternions(uid).Count, ZDOExtraData.GetInts(uid).Count, ZDOExtraData.GetLongs(uid).Count, ZDOExtraData.GetStrings(uid).Count, ZDOExtraData.GetByteArrays(uid).Count); } catch (Exception ex) { LoggerOptions.LogWarning("[BigZdoDiag] Serialize prefix threw: " + ex.Message); } } private static void InspectBuckets(ZDO zdo, string source, bool canTruncate, int fl, int ve, int qu, int ii, int lo, int st, int by) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: 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_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) if (fl > 100 || ve > 100 || qu > 100 || ii > 100 || lo > 100 || st > 100 || by > 100) { string text = ResolvePrefabName(zdo.GetPrefab()); Vector3 position = zdo.GetPosition(); long owner = zdo.GetOwner(); string ctx = $"src={source} uid={zdo.m_uid} prefab='{text}'({zdo.GetPrefab()}) " + $"pos=({position.x:F1},{position.z:F1},{position.y:F1}) owner={owner}"; LogBucket(ctx, "float", fl, canTruncate); LogBucket(ctx, "Vector3", ve, canTruncate); LogBucket(ctx, "Quaternion", qu, canTruncate); LogBucket(ctx, "int", ii, canTruncate); LogBucket(ctx, "long", lo, canTruncate); LogBucket(ctx, "string", st, canTruncate); LogBucket(ctx, "byte[]", by, canTruncate); } } private static void LogBucket(string ctx, string bucket, int count, bool canTruncate) { if (count > 100) { if (canTruncate && count > 255) { LoggerOptions.LogWarning($"[BigZdoDiag TRUNCATING] {ctx} bucket={bucket,-10} count={count} " + $"(>{255} — receiver will read wrapped count, payload corrupt)"); } else { LoggerOptions.LogWarning($"[BigZdoDiag] {ctx} bucket={bucket,-10} count={count}"); } } } private static string ResolvePrefabName(int prefabHash) { try { ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance != (Object)null) { GameObject prefab = instance.GetPrefab(prefabHash); if ((Object)(object)prefab != (Object)null) { return ((Object)prefab).name; } } } catch { } return ""; } } [HarmonyPatch] public static class CompressionGroup { internal static class CompressionStatus { public class SocketStatus { public int version = 0; public bool compressionEnabled = false; public bool sendingCompressed = false; public bool receivingCompressed = false; } private const int COMPRESSION_VERSION = 6; public static readonly SocketStatus ourStatus = new SocketStatus { version = 6, compressionEnabled = false }; private static readonly Dictionary peerStatus = new Dictionary(); public static void AddPeer(ISocket socket) { if (socket != null) { if (peerStatus.ContainsKey(socket)) { peerStatus.Remove(socket); } peerStatus[socket] = new SocketStatus(); LoggerOptions.LogMessage("Compression: New peer connected " + socket.GetEndPointString()); } } public static void RemovePeer(ISocket socket) { peerStatus.Remove(socket); } public static SocketStatus GetStatus(ISocket socket) { SocketStatus value; return peerStatus.TryGetValue(socket, out value) ? value : null; } public static bool IsCompatible(ISocket socket) { SocketStatus status = GetStatus(socket); return status != null && status.version == ourStatus.version; } public static bool GetSendCompressionStarted(ISocket socket) { return GetStatus(socket)?.sendingCompressed ?? false; } public static bool GetReceiveCompressionStarted(ISocket socket) { return GetStatus(socket)?.receivingCompressed ?? false; } public static void SetSendCompressionStarted(ISocket socket, bool started) { GetStatus(socket).sendingCompressed = started; } public static void SetReceiveCompressionStarted(ISocket socket, bool started) { GetStatus(socket).receivingCompressed = started; } } private static string ZSTD_DICT_RESOURCE_NAME = "FiresGhettoNetworkMod.dict.small"; private static int ZSTD_LEVEL = 1; private static object compressor; private static object decompressor; public static ConfigEntry ConfigCompressionEnabled; private const string RPC_COMPRESSION_VERSION = "FiresGhetto.CompressionVersion"; private const string RPC_COMPRESSION_ENABLED = "FiresGhetto.CompressionEnabled"; private const string RPC_COMPRESSION_STARTED = "FiresGhetto.CompressedStarted"; public static void InitConfig(ConfigFile config) { ConfigCompressionEnabled = FiresGhettoNetworkMod.ConfigEnableCompression; ConfigCompressionEnabled.SettingChanged += delegate { SetCompressionEnabledFromConfig(); }; CompressionStatus.ourStatus.compressionEnabled = ConfigCompressionEnabled?.Value ?? false; } public static void InitCompressor() { try { Type type = Type.GetType("ZstdSharp.Compressor, ZstdSharp"); Type type2 = Type.GetType("ZstdSharp.Decompressor, ZstdSharp"); if (type == null || type2 == null) { LoggerOptions.LogWarning("ZstdSharp assembly not found - compression disabled."); return; } Assembly executingAssembly = Assembly.GetExecutingAssembly(); byte[] array; using (Stream stream = executingAssembly.GetManifestResourceStream(ZSTD_DICT_RESOURCE_NAME)) { if (stream == null) { LoggerOptions.LogError("Compression dictionary resource not found. Compression disabled."); return; } array = new byte[stream.Length]; stream.Read(array, 0, array.Length); } compressor = Activator.CreateInstance(type, ZSTD_LEVEL); type.GetMethod("LoadDictionary")?.Invoke(compressor, new object[1] { array }); decompressor = Activator.CreateInstance(type2); type2.GetMethod("LoadDictionary")?.Invoke(decompressor, new object[1] { array }); LoggerOptions.LogInfo("ZSTD compression dictionary loaded successfully."); } catch (Exception arg) { LoggerOptions.LogError($"Failed to initialize compressor: {arg}"); } } private static void SetCompressionEnabledFromConfig() { bool value = ConfigCompressionEnabled.Value; CompressionStatus.ourStatus.compressionEnabled = value; LoggerOptions.LogMessage("Network compression: " + (value ? "Enabled" : "Disabled")); SendCompressionEnabledStatusToAll(); } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] [HarmonyPostfix] private static void OnNewConnection(ZNetPeer peer) { if (compressor != null) { CompressionStatus.AddPeer(peer.m_socket); RegisterRPCs(peer); SendCompressionVersion(peer); } } [HarmonyPatch(typeof(ZNet), "Disconnect")] [HarmonyPostfix] private static void OnDisconnect(ZNetPeer peer) { CompressionStatus.RemovePeer(peer.m_socket); } private static void RegisterRPCs(ZNetPeer peer) { peer.m_rpc.Register("FiresGhetto.CompressionVersion", (Action)RPC_CompressionVersion); peer.m_rpc.Register("FiresGhetto.CompressionEnabled", (Action)RPC_CompressionEnabled); peer.m_rpc.Register("FiresGhetto.CompressedStarted", (Action)RPC_CompressionStarted); } private static void SendCompressionVersion(ZNetPeer peer) { peer.m_rpc.Invoke("FiresGhetto.CompressionVersion", new object[1] { CompressionStatus.ourStatus.version }); } private static void RPC_CompressionVersion(ZRpc rpc, int version) { ZNetPeer val = FindPeerByRpc(rpc); if (val != null) { CompressionStatus.SocketStatus status = CompressionStatus.GetStatus(val.m_socket); if (status != null) { status.version = version; } if (version == CompressionStatus.ourStatus.version) { LoggerOptions.LogMessage("Compression compatible with " + GetPeerName(val)); } else { LoggerOptions.LogWarning($"Compression version mismatch with {GetPeerName(val)} (them: {version}, us: {CompressionStatus.ourStatus.version})"); } if (CompressionStatus.IsCompatible(val.m_socket)) { SendCompressionEnabledStatus(val); } } } private static void SendCompressionEnabledStatusToAll() { if ((Object)(object)ZNet.instance == (Object)null) { return; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (CompressionStatus.IsCompatible(peer.m_socket)) { SendCompressionEnabledStatus(peer); } } } private static void SendCompressionEnabledStatus(ZNetPeer peer) { peer.m_rpc.Invoke("FiresGhetto.CompressionEnabled", new object[1] { CompressionStatus.ourStatus.compressionEnabled }); bool started = CompressionStatus.ourStatus.compressionEnabled && (CompressionStatus.GetStatus(peer.m_socket)?.compressionEnabled ?? false); SendCompressionStarted(peer, started); } private static void RPC_CompressionEnabled(ZRpc rpc, bool enabled) { ZNetPeer val = FindPeerByRpc(rpc); if (val != null) { CompressionStatus.SocketStatus status = CompressionStatus.GetStatus(val.m_socket); if (status != null) { status.compressionEnabled = enabled; } bool started = CompressionStatus.ourStatus.compressionEnabled && enabled; SendCompressionStarted(val, started); } } private static void SendCompressionStarted(ZNetPeer peer, bool started) { CompressionStatus.SocketStatus status = CompressionStatus.GetStatus(peer.m_socket); if (status != null && status.sendingCompressed != started) { peer.m_rpc.Invoke("FiresGhetto.CompressedStarted", new object[1] { started }); Flush(peer); status.sendingCompressed = started; LoggerOptions.LogMessage("Compression " + (started ? "started" : "stopped") + " with " + GetPeerName(peer)); } } private static void Flush(ZNetPeer peer) { //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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 OnlineBackendType onlineBackend = ZNet.m_onlineBackend; OnlineBackendType val = onlineBackend; if ((int)val != 0) { if ((int)val == 1) { } } else { peer.m_socket.Flush(); } } private static void RPC_CompressionStarted(ZRpc rpc, bool started) { ZNetPeer val = FindPeerByRpc(rpc); if (val != null) { CompressionStatus.SocketStatus status = CompressionStatus.GetStatus(val.m_socket); if (status != null) { status.receivingCompressed = started; } LoggerOptions.LogMessage("Receiving " + (started ? "compressed" : "uncompressed") + " data from " + GetPeerName(val)); } } internal static byte[] Compress(byte[] data) { if (compressor == null) { return data; } Type type = compressor.GetType(); MethodInfo methodInfo = type.GetMethod("Wrap", new Type[1] { typeof(byte[]) }) ?? type.GetMethod("Wrap"); object obj = methodInfo.Invoke(compressor, new object[1] { data }); if (obj is byte[] result) { return result; } MethodInfo methodInfo2 = obj?.GetType().GetMethod("ToArray", Type.EmptyTypes); if (methodInfo2 != null) { return (byte[])methodInfo2.Invoke(obj, null); } return data; } internal static byte[] Decompress(byte[] data) { if (decompressor == null) { throw new Exception("Decompressor not initialized"); } Type type = decompressor.GetType(); MethodInfo methodInfo = type.GetMethod("Unwrap", new Type[1] { typeof(byte[]) }) ?? type.GetMethod("Unwrap"); object obj = methodInfo.Invoke(decompressor, new object[1] { data }); if (obj is byte[] result) { return result; } MethodInfo methodInfo2 = obj?.GetType().GetMethod("ToArray", Type.EmptyTypes); if (methodInfo2 != null) { return (byte[])methodInfo2.Invoke(obj, null); } throw new Exception("Failed to decompress data"); } [HarmonyPatch(typeof(ZSteamSocket), "SendQueuedPackages")] [HarmonyPrefix] private static bool Steam_SendCompressed(ref Queue ___m_sendQueue, ZSteamSocket __instance) { if (compressor == null || !CompressionStatus.GetSendCompressionStarted((ISocket)(object)__instance)) { return true; } ___m_sendQueue = new Queue(___m_sendQueue.Select((byte[] p) => Compress(p))); return true; } [HarmonyPatch(typeof(ZSteamSocket), "Recv")] [HarmonyPostfix] private static void Steam_RecvCompressed(ref ZPackage __result, ZSteamSocket __instance) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown if (__result == null || decompressor == null) { return; } CompressionStatus.SocketStatus status = CompressionStatus.GetStatus((ISocket)(object)__instance); if (status == null || !status.receivingCompressed) { return; } try { __result = new ZPackage(Decompress(__result.GetArray())); } catch { LoggerOptions.LogWarning("Failed to decompress incoming Steamworks package - falling back to uncompressed"); status.receivingCompressed = false; } } private static ZNetPeer FindPeerByRpc(ZRpc rpc) { try { if (rpc == null || ZRoutedRpc.instance == null) { return null; } return ((List)AccessTools.Field(typeof(ZRoutedRpc), "m_peers").GetValue(ZRoutedRpc.instance))?.FirstOrDefault((Func)((ZNetPeer p) => p.m_rpc == rpc)); } catch { return null; } } private static string GetPeerName(ZNetPeer peer) { if (peer == null) { return "unknown"; } try { if (peer.m_socket != null) { return peer.m_socket.GetEndPointString(); } } catch { } return peer.m_uid.ToString(); } } [HarmonyPatch] public static class DedicatedServerGroup { private static bool isDedicatedDetected; public static void Init(ConfigFile config) { LoggerOptions.LogInfo("Dedicated server features initialized."); isDedicatedDetected = ServerClientUtils.IsDedicatedServerDetected; if (isDedicatedDetected) { LoggerOptions.LogInfo("Running as dedicated server (ServerClientUtils)."); } else { LoggerOptions.LogInfo("Running as client/listen-server (ServerClientUtils)."); } } [HarmonyPatch(typeof(FejdStartup), "ParseServerArguments")] [HarmonyPostfix] private static void ApplyForceCrossplay() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) if (isDedicatedDetected) { switch (FiresGhettoNetworkMod.ConfigForceCrossplay.Value) { case ForceCrossplayOptions.playfab: ZNet.m_onlineBackend = (OnlineBackendType)1; LoggerOptions.LogInfo("Forcing crossplay ENABLED (PlayFab backend)."); break; case ForceCrossplayOptions.steamworks: ZNet.m_onlineBackend = (OnlineBackendType)0; LoggerOptions.LogInfo("Forcing crossplay DISABLED (Steamworks backend)."); break; default: LoggerOptions.LogInfo("Crossplay mode: vanilla (respecting command line)."); break; } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] [HarmonyTranspiler] private static IEnumerable OverridePlayerLimit(IEnumerable instructions) { //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Invalid comparison between Unknown and I4 //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Expected O, but got Unknown if (!isDedicatedDetected) { return instructions; } List list = new List(instructions); bool flag = false; for (int i = 0; i < list.Count; i++) { if (!(list[i].opcode == OpCodes.Call) || !(list[i].operand is MethodInfo methodInfo) || !(methodInfo.Name == "GetNrOfPlayers")) { continue; } for (int j = i + 1; j < list.Count; j++) { if (list[j].opcode == OpCodes.Ldc_I4_S || list[j].opcode == OpCodes.Ldc_I4 || list[j].opcode == OpCodes.Ldc_I4_0 || list[j].opcode == OpCodes.Ldc_I4_1 || list[j].opcode == OpCodes.Ldc_I4_2 || list[j].opcode == OpCodes.Ldc_I4_3 || list[j].opcode == OpCodes.Ldc_I4_4 || list[j].opcode == OpCodes.Ldc_I4_5 || list[j].opcode == OpCodes.Ldc_I4_6 || list[j].opcode == OpCodes.Ldc_I4_7 || list[j].opcode == OpCodes.Ldc_I4_8) { int num = FiresGhettoNetworkMod.ConfigPlayerLimit.Value; if ((int)ZNet.m_onlineBackend == 1) { num++; LoggerOptions.LogInfo("Applied +1 player limit for PlayFab backend."); } LoggerOptions.LogInfo($"Overriding player limit constant → {num}"); list[j] = new CodeInstruction(OpCodes.Ldc_I4, (object)num); flag = true; break; } } if (flag) { break; } } if (!flag) { LoggerOptions.LogWarning("Player limit constant not found in ZNet.RPC_PeerInfo. Patch skipped – possible game update or conflicting mod."); } return list; } private static int ResolveAdvertisedLimit(bool addPlayFabHostSlot) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 int num = FiresGhettoNetworkMod.ConfigAdvertisedPlayerLimit?.Value ?? 0; int num2 = ((num > 0) ? num : FiresGhettoNetworkMod.ConfigPlayerLimit.Value); if (addPlayFabHostSlot && (int)ZNet.m_onlineBackend == 1) { num2++; } return num2; } private static bool IsIntConstantLoad(CodeInstruction ins) { OpCode opcode = ins.opcode; return opcode == OpCodes.Ldc_I4 || opcode == OpCodes.Ldc_I4_S || opcode == OpCodes.Ldc_I4_0 || opcode == OpCodes.Ldc_I4_1 || opcode == OpCodes.Ldc_I4_2 || opcode == OpCodes.Ldc_I4_3 || opcode == OpCodes.Ldc_I4_4 || opcode == OpCodes.Ldc_I4_5 || opcode == OpCodes.Ldc_I4_6 || opcode == OpCodes.Ldc_I4_7 || opcode == OpCodes.Ldc_I4_8; } [HarmonyPatch(typeof(SteamMatchmaking), "CreateLobby")] [HarmonyPrefix] private static void OverrideSteamLobbyMaxMembers(ELobbyType eLobbyType, ref int cMaxMembers) { if (isDedicatedDetected) { int num = ResolveAdvertisedLimit(addPlayFabHostSlot: false); if (num > 0 && cMaxMembers != num) { LoggerOptions.LogInfo($"Overriding SteamMatchmaking.CreateLobby cMaxMembers: {cMaxMembers} → {num}"); cMaxMembers = num; } } } [HarmonyPatch(typeof(SteamGameServer), "SetMaxPlayerCount")] [HarmonyPrefix] private static void OverrideSteamGameServerMaxPlayers(ref int cPlayersMax) { if (isDedicatedDetected) { int num = ResolveAdvertisedLimit(addPlayFabHostSlot: false); if (num > 0 && cPlayersMax != num) { LoggerOptions.LogInfo($"Overriding SteamGameServer.SetMaxPlayerCount cPlayersMax: {cPlayersMax} → {num}"); cPlayersMax = num; } } } private static List RewriteConstBeforeFieldStore(IEnumerable instructions, string fieldName, string methodLabel, bool addPlayFabHostSlot, bool silentIfNotFound = false) { //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown List list = (instructions as List) ?? new List(instructions); bool flag = false; for (int i = 0; i < list.Count; i++) { CodeInstruction val = list[i]; if (!(val.opcode != OpCodes.Stfld) && val.operand is FieldInfo fieldInfo && !(fieldInfo.Name != fieldName) && i != 0 && IsIntConstantLoad(list[i - 1])) { int num = ResolveAdvertisedLimit(addPlayFabHostSlot); LoggerOptions.LogInfo($"Overriding {methodLabel} {fieldName} → {num}"); list[i - 1] = new CodeInstruction(OpCodes.Ldc_I4, (object)num); flag = true; break; } } if (!flag && !silentIfNotFound) { LoggerOptions.LogWarning(fieldName + " constant not found in " + methodLabel + ". Patch skipped."); } return list; } private static List RewriteConstBeforePropertySet(List list, string propertyName, string methodLabel, bool addPlayFabHostSlot) { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Expected O, but got Unknown string text = "set_" + propertyName; bool flag = false; for (int i = 0; i < list.Count; i++) { CodeInstruction val = list[i]; if ((!(val.opcode != OpCodes.Callvirt) || !(val.opcode != OpCodes.Call)) && val.operand is MethodInfo methodInfo && !(methodInfo.Name != text) && i != 0 && IsIntConstantLoad(list[i - 1])) { int num = ResolveAdvertisedLimit(addPlayFabHostSlot); LoggerOptions.LogInfo($"Overriding {methodLabel} {propertyName} → {num}"); list[i - 1] = new CodeInstruction(OpCodes.Ldc_I4, (object)num); flag = true; break; } } if (!flag) { LoggerOptions.LogWarning(propertyName + " constant not found in " + methodLabel + ". Patch skipped."); } return list; } } public static class LoggerOptions { private static ManualLogSource logger; public static void Init(ManualLogSource source) { logger = source; } public static void LogError(object data) { logger.LogError(data); } public static void LogWarning(object data) { logger.LogWarning(data); } public static void LogMessage(object data) { if (FiresGhettoNetworkMod.ConfigLogLevel != null && FiresGhettoNetworkMod.ConfigLogLevel.Value >= LogLevel.Message) { logger.LogMessage(data); } } public static void LogInfo(object data) { if (FiresGhettoNetworkMod.ConfigLogLevel != null && FiresGhettoNetworkMod.ConfigLogLevel.Value >= LogLevel.Info) { logger.LogInfo(data); } } } [HarmonyPatch] public static class MonsterAIPatches { private static readonly int PlayerPrefabHash = StringExtensionMethods.GetStableHashCode("Player"); private static readonly List s_tempPlayers = new List(); private static readonly Dictionary s_eventDiagLastLogTime = new Dictionary(); private const float EVENT_DIAG_INTERVAL = 30f; private const float ZONE_PLAYER_EXTRA = 32f; private static readonly FieldInfo _f_randomEvent = AccessTools.Field(typeof(RandEventSystem), "m_randomEvent"); private static readonly FieldInfo _f_forcedEvent = AccessTools.Field(typeof(RandEventSystem), "m_forcedEvent"); private static readonly FieldInfo _f_activeEvent = AccessTools.Field(typeof(RandEventSystem), "m_activeEvent"); private static readonly MethodInfo _m_setActiveEvent = AccessTools.Method(typeof(RandEventSystem), "SetActiveEvent", new Type[2] { typeof(RandomEvent), typeof(bool) }, (Type[])null); private static readonly MethodInfo _m_isAnyPlayerIn = AccessTools.Method(typeof(RandEventSystem), "IsAnyPlayerInEventArea", new Type[1] { typeof(RandomEvent) }, (Type[])null); [HarmonyPatch(typeof(BaseAI), "UpdateAI")] [HarmonyPrefix] public static bool BaseAI_UpdateAI_Prefix(BaseAI __instance) { if ((Object)(object)__instance.m_nview == (Object)null) { return false; } if (!__instance.m_nview.IsValid()) { return false; } if (__instance.m_nview.GetZDO() == null) { return false; } if ((Object)(object)__instance.m_character == (Object)null) { return false; } return true; } [HarmonyPatch(typeof(SpawnSystem), "UpdateSpawning")] [HarmonyPrefix] private static bool UpdateSpawning_Prefix(SpawnSystem __instance, ZNetView ___m_nview, List ___m_spawnLists) { //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsDedicated()) { return true; } if ((Object)(object)___m_nview == (Object)null || !___m_nview.IsValid() || !___m_nview.IsOwner()) { return false; } RandomEvent val = (RandomEvent)(((Object)(object)RandEventSystem.instance != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); RandomEvent val2 = (RandomEvent)(((Object)(object)RandEventSystem.instance != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null); bool flag = val != null || val2 != null; bool flag2 = false; if (flag) { int instanceID = ((Object)__instance).GetInstanceID(); float time = Time.time; if (!s_eventDiagLastLogTime.TryGetValue(instanceID, out var value) || time - value >= 30f) { s_eventDiagLastLogTime[instanceID] = time; flag2 = true; } } s_tempPlayers.Clear(); GetPlayersInZone(__instance, s_tempPlayers); if (s_tempPlayers.Count == 0) { if (flag2) { Vector3 position = ((Component)__instance).transform.position; LoggerOptions.LogInfo($"[EventDiag] SpawnSystem@({position.x:F0},{position.z:F0}) SKIPPED: no players in zone. " + "ActiveEvent='" + ((val != null) ? val.m_name : "null") + "' RunningEvent='" + ((val2 != null) ? val2.m_name : "null") + "' " + $"TotalPlayers={Player.GetAllPlayers().Count} Peers={ZNet.instance.GetConnectedPeers().Count}"); } return false; } SpawnSystem.m_tempNearPlayers.Clear(); SpawnSystem.m_tempNearPlayers.AddRange(s_tempPlayers); DateTime time2 = ZNet.instance.GetTime(); foreach (SpawnSystemList ___m_spawnList in ___m_spawnLists) { if (___m_spawnList?.m_spawners != null) { __instance.UpdateSpawnList(___m_spawnList.m_spawners, time2, false); } } if ((Object)(object)RandEventSystem.instance != (Object)null) { List currentSpawners = RandEventSystem.instance.GetCurrentSpawners(); if (flag2) { Vector3 position2 = ((Component)__instance).transform.position; int num = currentSpawners?.Count ?? (-1); LoggerOptions.LogInfo($"[EventDiag] SpawnSystem@({position2.x:F0},{position2.z:F0}) RAN event path. " + "ActiveEvent='" + ((val != null) ? val.m_name : "null") + "' RunningEvent='" + ((val2 != null) ? val2.m_name : "null") + "' " + $"GetCurrentSpawners.Count={num} Players={s_tempPlayers.Count}"); } if (currentSpawners != null) { __instance.UpdateSpawnList(currentSpawners, time2, true); } } return false; } private static void GetPlayersInZone(SpawnSystem spawnSystem, List players) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer != (Object)null && InsideZone(spawnSystem, ((Component)allPlayer).transform.position, 32f)) { players.Add(allPlayer); } } } private static bool InsideZone(SpawnSystem spawnSystem, Vector3 point, float extra = 0f) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0045: 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) float num = 32f + extra; Vector3 position = ((Component)spawnSystem).transform.position; return point.x >= position.x - num && point.x <= position.x + num && point.z >= position.z - num && point.z <= position.z + num; } [HarmonyPatch(typeof(RandEventSystem), "FixedUpdate")] [HarmonyPrefix] private static void RandEventSystem_FixedUpdate_Prefix(RandEventSystem __instance) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsDedicated()) { return; } object? value = _f_forcedEvent.GetValue(__instance); RandomEvent val = (RandomEvent)((value is RandomEvent) ? value : null); if (val == null) { object? value2 = _f_randomEvent.GetValue(__instance); RandomEvent val2 = (RandomEvent)((value2 is RandomEvent) ? value2 : null); if (val2 != null && (bool)_m_isAnyPlayerIn.Invoke(__instance, new object[1] { val2 })) { _f_activeEvent.SetValue(__instance, val2); } } } [HarmonyPatch(typeof(RandEventSystem), "SetActiveEvent")] [HarmonyPrefix] private static bool RandEventSystem_SetActiveEvent_Prefix(RandEventSystem __instance, RandomEvent ev) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsDedicated()) { return true; } if (ev != null) { return true; } object? value = _f_forcedEvent.GetValue(__instance); RandomEvent val = (RandomEvent)((value is RandomEvent) ? value : null); if (val != null) { return true; } object? value2 = _f_randomEvent.GetValue(__instance); RandomEvent val2 = (RandomEvent)((value2 is RandomEvent) ? value2 : null); if (val2 == null) { return true; } return false; } [HarmonyPatch(typeof(RandEventSystem), "FixedUpdate")] [HarmonyPostfix] private static void RandEventSystem_FixedUpdate_Postfix(RandEventSystem __instance) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsDedicated()) { return; } object? value = _f_forcedEvent.GetValue(__instance); RandomEvent val = (RandomEvent)((value is RandomEvent) ? value : null); if (val == null) { object? value2 = _f_randomEvent.GetValue(__instance); RandomEvent val2 = (RandomEvent)((value2 is RandomEvent) ? value2 : null); if (val2 != null) { bool flag = (bool)_m_isAnyPlayerIn.Invoke(__instance, new object[1] { val2 }); _m_setActiveEvent.Invoke(__instance, new object[2] { flag ? val2 : null, false }); } else { _m_setActiveEvent.Invoke(__instance, new object[2] { null, false }); } } } public static Player GetNearestPlayer(Vector3 position) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) Player result = null; float num = float.MaxValue; foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer != (Object)null) { float num2 = Vector3.Distance(position, ((Component)allPlayer).transform.position); if (num2 < num) { num = num2; result = allPlayer; } } } return result; } public static bool IsAnyPlayerInActiveArea(Vector3 position) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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) if ((Object)(object)ZoneSystem.instance == (Object)null) { return false; } int activeArea = ZoneSystem.instance.m_activeArea; Vector2i zone = ZoneSystem.GetZone(position); foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer != (Object)null) { Vector2i zone2 = ZoneSystem.GetZone(((Component)allPlayer).transform.position); if (ZNetScene.InActiveArea(zone, zone2, activeArea)) { return true; } } } return false; } public static Player GetClosestPlayerInRange(Vector3 position, float maxDistance) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) Player result = null; float num = maxDistance; foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)(object)allPlayer != (Object)null && !((Character)allPlayer).IsDead()) { float num2 = Vector3.Distance(position, ((Component)allPlayer).transform.position); if (num2 < num) { num = num2; result = allPlayer; } } } return result; } public static bool IsPlayerZDO(ZDO zdo) { return zdo != null && zdo.m_prefab == PlayerPrefabHash; } } [HarmonyPatch] public static class NetworkingRatesGroup { private static bool _recvBufferProbed; private static bool _recvBufferSupported; private static bool _recvMaxMessageProbed; private static bool _recvMaxMessageSupported; private static bool _sendBufferProbed; private static bool _sendBufferSupported; public static void Init(ConfigFile config) { FiresGhettoNetworkMod.ConfigUpdateRate.SettingChanged += delegate { ApplyUpdateRate(); }; FiresGhettoNetworkMod.ConfigSendRateMin.SettingChanged += delegate { ApplySendRates(); }; FiresGhettoNetworkMod.ConfigSendRateMax.SettingChanged += delegate { ApplySendRates(); }; FiresGhettoNetworkMod.ConfigQueueSize.SettingChanged += delegate { LoggerOptions.LogInfo("Queue size changed - restart recommended."); }; ApplyUpdateRate(); ApplySendRates(); } private static void ApplyUpdateRate() { LoggerOptions.LogMessage($"Update rate set to {FiresGhettoNetworkMod.ConfigUpdateRate.Value}"); } public static void ApplySendRates() { if (!((Object)(object)ZNet.instance == (Object)null)) { int num = EffectiveConfig.SteamSendRateMin(); int num2 = EffectiveConfig.SteamSendRateMax(); SetSteamConfig("k_ESteamNetworkingConfig_SendRateMin", num); SetSteamConfig("k_ESteamNetworkingConfig_SendRateMax", num2); LoggerOptions.LogMessage($"Steam send rates applied: Min {num / 1024} KB/s, Max {num2 / 1024} KB/s"); } } public static void ApplySendBufferSize() { if ((Object)(object)ZNet.instance == (Object)null) { return; } if (!_sendBufferProbed) { _sendBufferProbed = true; _sendBufferSupported = HasSteamConfigMember("k_ESteamNetworkingConfig_SendBufferSize"); if (!_sendBufferSupported) { LoggerOptions.LogInfo("Steam ESteamNetworkingConfigValue does not expose SendBufferSize in this game build; AutoTune buffer scaling has no effect."); } } if (_sendBufferSupported) { int num = EffectiveConfig.SteamSendBufferBytes(); SetSteamConfig("k_ESteamNetworkingConfig_SendBufferSize", num); LoggerOptions.LogMessage($"Steam send buffer applied: {num / 1024} KB"); } } public static void ApplyRecvBufferSize() { if ((Object)(object)ZNet.instance == (Object)null) { return; } if (!_recvBufferProbed) { _recvBufferProbed = true; _recvBufferSupported = HasSteamConfigMember("k_ESteamNetworkingConfig_RecvBufferSize"); if (!_recvBufferSupported) { LoggerOptions.LogInfo("Steam ESteamNetworkingConfigValue does not expose RecvBufferSize in this game build; 'ZPackage Receive Buffer Bytes' setting has no effect (Valheim ships an older Steamworks SDK)."); } } if (_recvBufferSupported) { int num = EffectiveConfig.SteamRecvBufferBytes(); SetSteamConfig("k_ESteamNetworkingConfig_RecvBufferSize", num); LoggerOptions.LogMessage($"Steam recv buffer applied: {num / 1024} KB"); } } public static void ApplyRecvMaxMessageSize() { if ((Object)(object)ZNet.instance == (Object)null) { return; } if (!_recvMaxMessageProbed) { _recvMaxMessageProbed = true; _recvMaxMessageSupported = HasSteamConfigMember("k_ESteamNetworkingConfig_RecvMaxMessageSize"); if (!_recvMaxMessageSupported) { LoggerOptions.LogInfo("Steam ESteamNetworkingConfigValue does not expose RecvMaxMessageSize in this game build; per-message cap stays at Steam's 512 KB default (install FiresSteamworksPatcher on the server to enable)."); } } if (_recvMaxMessageSupported) { int num = EffectiveConfig.SteamRecvMaxMessageBytes(); SetSteamConfig("k_ESteamNetworkingConfig_RecvMaxMessageSize", num); LoggerOptions.LogMessage($"Steam recv-max-message applied: {num / 1024} KB"); } } private static bool HasSteamConfigMember(string memberName) { try { IEnumerable source = AppDomain.CurrentDomain.GetAssemblies().SelectMany(delegate(Assembly a) { try { return a.GetTypes(); } catch { return Array.Empty(); } }); Type type = source.FirstOrDefault((Type t) => t.FullName == "Steamworks.ESteamNetworkingConfigValue"); if (type == null) { return false; } string[] names = Enum.GetNames(type); foreach (string text in names) { if (text == memberName) { return true; } } return false; } catch { return false; } } private static int GetSendRateValue(object option) { string text = option.ToString(); if (1 == 0) { } int result = text switch { "_1024KB" => 1048576, "_768KB" => 786432, "_512KB" => 524288, "_256KB" => 262144, _ => 153600, }; if (1 == 0) { } return result; } [HarmonyPatch(typeof(ZDOMan), "SendZDOToPeers2")] [HarmonyPrefix] private static void AdjustUpdateInterval(ref float dt) { switch (EffectiveConfig.UpdateRate()) { case UpdateRateOptions._150: dt *= 1.5f; break; case UpdateRateOptions._75: dt *= 0.75f; break; case UpdateRateOptions._50: dt *= 0.5f; break; case UpdateRateOptions._100: break; } } [HarmonyPatch(typeof(ZNet), "Start")] [HarmonyPostfix] private static void EnsureRatesOnStart() { ApplySendRates(); ApplySendBufferSize(); ApplyRecvBufferSize(); ApplyRecvMaxMessageSize(); } private static void SetSteamConfig(string enumMemberName, int value) { IntPtr intPtr = IntPtr.Zero; try { IEnumerable source = AppDomain.CurrentDomain.GetAssemblies().SelectMany(delegate(Assembly a) { try { return a.GetTypes(); } catch { return Array.Empty(); } }); Type type = source.FirstOrDefault((Type t) => t.FullName == "Steamworks.ESteamNetworkingConfigValue"); Type type2 = source.FirstOrDefault((Type t) => t.FullName == "Steamworks.ESteamNetworkingConfigScope"); Type type3 = source.FirstOrDefault((Type t) => t.FullName == "Steamworks.ESteamNetworkingConfigDataType"); if (type == null || type2 == null || type3 == null) { LoggerOptions.LogWarning("Steamworks.NET types not found - send rate config skipped."); return; } object obj = Enum.Parse(type, enumMemberName); object obj2 = Enum.Parse(type2, "k_ESteamNetworkingConfig_Global"); object obj3 = Enum.Parse(type3, "k_ESteamNetworkingConfig_Int32"); intPtr = Marshal.AllocHGlobal(4); Marshal.WriteInt32(intPtr, value); Type type4 = ((Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsDedicated()) ? source.FirstOrDefault((Type t) => t.FullName == "Steamworks.SteamGameServerNetworkingUtils") : source.FirstOrDefault((Type t) => t.FullName == "Steamworks.SteamNetworkingUtils")); if (type4 == null) { LoggerOptions.LogWarning("Steamworks utils type not found - send rate config skipped."); return; } MethodInfo method = type4.GetMethod("SetConfigValue", BindingFlags.Static | BindingFlags.Public); if (method == null) { LoggerOptions.LogWarning("SetConfigValue method not found - send rate config skipped."); return; } method.Invoke(null, new object[5] { obj, obj2, IntPtr.Zero, obj3, intPtr }); } catch (Exception ex) { LoggerOptions.LogWarning("Failed to set Steam config " + enumMemberName + ": " + ex.Message); } finally { if (intPtr != IntPtr.Zero) { Marshal.FreeHGlobal(intPtr); } } } [HarmonyPatch(typeof(ZSteamSocket), "RegisterGlobalCallbacks")] [HarmonyPostfix] private static void ApplySendRatesOnConnect() { ApplySendRates(); ApplySendBufferSize(); ApplyRecvBufferSize(); ApplyRecvMaxMessageSize(); } [HarmonyPatch(typeof(ZDOMan), "SendZDOs")] [HarmonyTranspiler] private static IEnumerable SendZDOs_QueueLimitTranspiler(IEnumerable instructions) { List list = new List(instructions); int num = 0; int configuredQueueLimit = GetConfiguredQueueLimit(); for (int i = 0; i < list.Count; i++) { if ((!(list[i].opcode == OpCodes.Ldc_I4) && !(list[i].opcode == OpCodes.Ldc_I4_S)) || !(list[i].operand is int num2) || num2 < 10240) { continue; } bool flag = false; for (int j = Math.Max(0, i - 15); j < Math.Min(list.Count, i + 15); j++) { if (list[j].opcode == OpCodes.Callvirt && list[j].operand is MethodInfo methodInfo && methodInfo.Name == "GetSendQueueSize") { flag = true; break; } } if (flag) { LoggerOptions.LogInfo($"Overriding ZDOMan.SendZDOs queue limit #{num + 1}: original {num2} → {configuredQueueLimit} bytes"); list[i].opcode = OpCodes.Ldc_I4; list[i].operand = configuredQueueLimit; num++; } } if (num == 0) { LoggerOptions.LogWarning("No queue limit constants found in ZDOMan.SendZDOs — queue size config not applied (game update may have changed IL)."); } else { LoggerOptions.LogInfo($"Successfully patched {num} queue limit constant(s)."); } return list.AsEnumerable(); } private static int GetConfiguredQueueLimit() { QueueSizeOptions queueSizeOptions = EffectiveConfig.QueueSize(); if (1 == 0) { } int result = queueSizeOptions switch { QueueSizeOptions._80KB => 81920, QueueSizeOptions._64KB => 65536, QueueSizeOptions._48KB => 49152, QueueSizeOptions._32KB => 32768, _ => 10240, }; if (1 == 0) { } return result; } } [HarmonyPatch] public static class PlayerPositionSyncPatches { private sealed class PlayerSyncData { public Vector3 networkPos; public Vector3 prevNetworkPos; public Quaternion networkRot; public Quaternion prevNetworkRot; public Vector3 velocity; public Vector3 smoothedVelocity; public float lastNetworkUpdateTime; public float networkUpdateInterval; public bool hasData; public Vector3 renderPos; public Quaternion renderRot; public float lastFrameTime; public Vector3 acceleration; } public static ConfigEntry ConfigEnablePlayerPositionBoost; public static ConfigEntry ConfigPlayerPositionUpdateMultiplier; public static ConfigEntry ConfigEnableClientInterpolation; public static ConfigEntry ConfigEnablePlayerPrediction; public static ConfigEntry ConfigSmoothingMinInterval; public static ConfigEntry ConfigSmoothingMaxInterval; private static readonly int PlayerPrefabHash = StringExtensionMethods.GetStableHashCode("Player"); private static readonly Dictionary _syncData = new Dictionary(); public static void Init(ConfigFile config) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Expected O, but got Unknown ConfigEnablePlayerPositionBoost = config.Bind("Player Sync", "Enable High-Frequency Position Updates", true, "Boosts server send priority for player ZDOs so they sync before terrain/object ZDOs.\nReduces the floaty delayed movement you see on other players.\nSERVER-ONLY — no effect on client."); ConfigPlayerPositionUpdateMultiplier = config.Bind("Player Sync", "Position Update Multiplier", 2.5f, new ConfigDescription("How aggressively player ZDOs are prioritized over other ZDOs (1.0 = vanilla, 2.5 = recommended).\nHigher values push player positions to the front of the send queue more strongly.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 5f), Array.Empty())); ConfigEnableClientInterpolation = config.Bind("Player Sync", "Enable Client-Side Interpolation", false, "Smooths other players' movement on your client by interpolating between received network positions.\nEliminates the snapping/teleporting caused by discrete 50ms network updates.\nDisabled by default — the rest of the mod's networking improvements (higher update rate, server\nZDO priority boost, larger Steam buffers) usually deliver positions smoothly enough on their own,\nand interpolation adds a small render-lag that some players prefer to avoid. Turn ON if you still\nsee other players snap/teleport even on a healthy connection.\nCLIENT-ONLY — no server impact."); ConfigEnablePlayerPrediction = config.Bind("Player Sync", "Enable Client-Side Prediction", false, "Extrapolates other players' positions forward between network updates using their last known velocity.\nCan help on high latency (>100ms) but may cause overshooting at low ping.\nDisabled by default — only enable if interpolation alone feels laggy.\nCLIENT-ONLY — no server impact."); ConfigSmoothingMinInterval = config.Bind("Player Sync", "Smoothing Min Interval (s)", 0f, new ConfigDescription("When a remote player's packets arrive faster than this interval (seconds),\nsmoothing is disabled entirely and vanilla movement renders directly.\nDefault 0 = always smooth (recommended). The earlier default of 0.05\n(vanilla 20Hz) collapsed the smoothing-strength formula to ~0 under\nhealthy traffic, which made BOTH this slider and Smoothing Max feel\nlike no-ops and produced visible snaps every time inter-packet jitter\ncrossed the threshold. Raise above 0 only if you specifically want\nfast packets to bypass smoothing (LAN / very low-latency servers).", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 0.2f), Array.Empty())); ConfigSmoothingMaxInterval = config.Bind("Player Sync", "Smoothing Max Interval (s)", 0.2f, new ConfigDescription("When a remote player's packets arrive slower than this interval (seconds),\nsmoothing runs at full strength. Between Min and Max the strength fades\nin linearly so the handoff is invisible. Raise it for a more aggressive\nfade-in (less smoothing on borderline-laggy connections); lower it for a\nsnappier ramp to full smoothing.", (AcceptableValueBase)(object)new AcceptableValueRange(0.05f, 0.5f), Array.Empty())); } private static bool IsPlayerZDO(ZDO zdo) { return zdo != null && zdo.m_prefab == PlayerPrefabHash; } [HarmonyPatch(typeof(ZDO), "Deserialize")] [HarmonyPostfix] public static void ZDO_Deserialize_Postfix(ZDO __instance) { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0193: 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) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_030d: Unknown result type (might be due to invalid IL or missing references) //IL_0312: Unknown result type (might be due to invalid IL or missing references) //IL_0319: Unknown result type (might be due to invalid IL or missing references) //IL_031a: Unknown result type (might be due to invalid IL or missing references) //IL_0321: Unknown result type (might be due to invalid IL or missing references) //IL_0322: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_0229: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Unknown result type (might be due to invalid IL or missing references) //IL_02dc: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_02f4: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02c7: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) if (ConfigEnableClientInterpolation == null || ConfigEnablePlayerPrediction == null || (!ConfigEnableClientInterpolation.Value && !EffectiveConfig.EnablePlayerPrediction()) || (Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || __instance == null || !IsPlayerZDO(__instance)) { return; } long owner = __instance.GetOwner(); if (owner == ZNet.GetUID()) { return; } Vector3 position = __instance.GetPosition(); Quaternion rotation = __instance.GetRotation(); rotation = Quaternion.Normalize(rotation); float time = Time.time; if (!_syncData.TryGetValue(owner, out var value)) { _syncData[owner] = new PlayerSyncData { networkPos = position, prevNetworkPos = position, networkRot = rotation, prevNetworkRot = rotation, renderPos = position, renderRot = rotation, velocity = Vector3.zero, smoothedVelocity = Vector3.zero, acceleration = Vector3.zero, networkUpdateInterval = 0.05f, lastNetworkUpdateTime = time, lastFrameTime = time, hasData = true }; return; } float num = time - value.lastNetworkUpdateTime; if (position == value.networkPos) { value.velocity = Vector3.Lerp(value.velocity, Vector3.zero, 0.9f); value.acceleration = Vector3.Lerp(value.acceleration, Vector3.zero, 0.9f); if (((Vector3)(ref value.velocity)).magnitude < 0.05f) { value.velocity = Vector3.zero; value.acceleration = Vector3.zero; } } else if (num >= 0.005f && num <= 0.5f) { value.networkUpdateInterval = Mathf.Lerp(value.networkUpdateInterval, num, 0.15f); Vector3 val = (position - value.networkPos) / num; if (((Vector3)(ref val)).magnitude <= 30f) { Vector3 val2 = (val - value.velocity) / num; value.acceleration = Vector3.Lerp(value.acceleration, val2, 0.25f); Vector3 val3 = Vector3.Lerp(value.velocity, val, 0.5f); value.velocity = Vector3.Lerp(value.velocity, val3, 0.7f); value.smoothedVelocity = Vector3.Lerp(value.smoothedVelocity, value.velocity, 0.6f); } else { value.velocity = Vector3.zero; value.smoothedVelocity = Vector3.zero; value.acceleration = Vector3.zero; } } value.prevNetworkPos = value.networkPos; value.prevNetworkRot = value.networkRot; value.networkPos = position; value.networkRot = rotation; value.lastNetworkUpdateTime = time; value.hasData = true; } [HarmonyPatch(typeof(Player), "LateUpdate")] [HarmonyPostfix] public static void Player_LateUpdate_Postfix(Player __instance) { //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_02c7: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Unknown result type (might be due to invalid IL or missing references) //IL_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_02f1: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) //IL_0302: Unknown result type (might be due to invalid IL or missing references) //IL_038e: Unknown result type (might be due to invalid IL or missing references) //IL_0393: Unknown result type (might be due to invalid IL or missing references) //IL_0397: Unknown result type (might be due to invalid IL or missing references) //IL_039c: Unknown result type (might be due to invalid IL or missing references) //IL_03a9: Unknown result type (might be due to invalid IL or missing references) //IL_03af: Unknown result type (might be due to invalid IL or missing references) //IL_0427: Unknown result type (might be due to invalid IL or missing references) //IL_042d: Unknown result type (might be due to invalid IL or missing references) //IL_0434: Unknown result type (might be due to invalid IL or missing references) //IL_0446: Unknown result type (might be due to invalid IL or missing references) //IL_044b: Unknown result type (might be due to invalid IL or missing references) //IL_0451: Unknown result type (might be due to invalid IL or missing references) //IL_0458: Unknown result type (might be due to invalid IL or missing references) //IL_03ef: Unknown result type (might be due to invalid IL or missing references) //IL_03f5: Unknown result type (might be due to invalid IL or missing references) //IL_0404: Unknown result type (might be due to invalid IL or missing references) //IL_0409: Unknown result type (might be due to invalid IL or missing references) //IL_0410: Unknown result type (might be due to invalid IL or missing references) //IL_0415: Unknown result type (might be due to invalid IL or missing references) //IL_041a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || (Object)(object)__instance == (Object)(object)Player.m_localPlayer || (Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || ConfigEnableClientInterpolation == null || !ConfigEnableClientInterpolation.Value) { return; } ZNetView nview = ((Character)__instance).m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid()) { return; } ZDO zDO = nview.GetZDO(); if (zDO == null) { return; } long owner = zDO.GetOwner(); if (!_syncData.TryGetValue(owner, out var value) || !value.hasData) { return; } float networkUpdateInterval = value.networkUpdateInterval; float num = EffectiveConfig.SmoothingMinInterval(); float num2 = Mathf.Max(num + 0.001f, EffectiveConfig.SmoothingMaxInterval()); if (networkUpdateInterval < num) { return; } float num3 = Mathf.Clamp01((networkUpdateInterval - num) / (num2 - num)); bool flag = ((Vector3)(ref value.smoothedVelocity)).magnitude < 0.03f; bool flag2 = Vector3.Distance(value.renderPos, value.networkPos) < 0.005f; bool flag3 = Quaternion.Angle(value.renderRot, value.networkRot) < 0.05f; if (flag && flag2 && flag3) { return; } float time = Time.time; float num4 = time - value.lastFrameTime; value.lastFrameTime = time; float num5 = time - value.lastNetworkUpdateTime; if (num5 > 0.3f && ((Vector3)(ref value.smoothedVelocity)).magnitude > 0.05f) { float num6 = Mathf.Clamp01(num4 * 8f); value.velocity = Vector3.Lerp(value.velocity, Vector3.zero, num6); value.smoothedVelocity = Vector3.Lerp(value.smoothedVelocity, Vector3.zero, num6); value.acceleration = Vector3.Lerp(value.acceleration, Vector3.zero, num6); } float num7 = 0f; num7 = ((!EffectiveConfig.EnablePlayerPrediction() || !(((Vector3)(ref value.smoothedVelocity)).magnitude > 0.2f)) ? (value.networkUpdateInterval * 0.4f) : Mathf.Min(num5 * 0.8f, value.networkUpdateInterval * 1.2f)); Vector3 val2; if (((Vector3)(ref value.smoothedVelocity)).magnitude > 1f) { Vector3 val = value.smoothedVelocity + value.acceleration * num7 * 0.5f; val2 = value.networkPos + val * num7; } else { val2 = value.networkPos; } float num8 = Vector3.Distance(value.renderPos, val2); if (num8 > 5f) { value.renderPos = val2; value.renderRot = Quaternion.Normalize(value.networkRot); } else { float num9 = Mathf.Clamp01(((Vector3)(ref value.smoothedVelocity)).magnitude / 8f); float num10 = Mathf.Clamp01(num8 / 1.5f); float num11 = Mathf.Lerp(15f, 30f, Mathf.Max(num9, num10)); if (num8 < 0.2f) { num11 *= 0.6f; } float num12 = Mathf.Clamp01(num4 * num11); num12 = num12 * num12 * (3f - 2f * num12); value.renderPos = Vector3.Lerp(value.renderPos, val2, num12); float num13 = 25f; float num14 = Quaternion.Angle(value.renderRot, value.networkRot); if (num14 > 30f) { num13 = 40f; } else if (num14 < 5f) { num13 = 15f; } value.renderRot = Quaternion.Slerp(value.renderRot, value.networkRot, Mathf.Clamp01(num4 * num13)); value.renderRot = Quaternion.Normalize(value.renderRot); } ((Component)__instance).transform.position = Vector3.Lerp(value.networkPos, value.renderPos, num3); ((Component)__instance).transform.rotation = Quaternion.Slerp(Quaternion.Normalize(value.networkRot), value.renderRot, num3); } } public static class RoutedRpcManager { public static readonly Dictionary HashCodeToMethodNameCache = new Dictionary(); private static readonly Dictionary> _rpcMethodHandlers = new Dictionary>(); private static readonly RoutedRPCData _routedRpcData = new RoutedRPCData(); private static long _serverPeerId = 0L; private static float _aoiRadiusOverride = -1f; public static long ServerPeerId => _serverPeerId; public static void SetAoIRadiusOverride(float radius) { _aoiRadiusOverride = radius; } public static void SetupServerPeer() { _serverPeerId = ZDOMan.GetSessionID(); LoggerOptions.LogInfo($"[RpcRouter] Server peer ID set to {_serverPeerId}"); } public static void AddHandler(string methodName, RpcMethodHandler handler) { int stableHashCode = StringExtensionMethods.GetStableHashCode(methodName); HashCodeToMethodNameCache[stableHashCode] = methodName; LoggerOptions.LogInfo($"[RpcRouter] Registering handler for {methodName} ({stableHashCode}): {handler.GetType().Name}"); if (!_rpcMethodHandlers.TryGetValue(stableHashCode, out var value)) { value = new List(); _rpcMethodHandlers[stableHashCode] = value; } value.Add(handler); } public static void ProcessRoutedRPC(ZRoutedRpc routedRpc, ZRpc rpc, ZPackage package) { //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) _routedRpcData.Deserialize(package); long targetPeerID = _routedRpcData.m_targetPeerID; long id = routedRpc.m_id; if (targetPeerID == id || targetPeerID == 0) { routedRpc.HandleRoutedRPC(_routedRpcData); } if ((targetPeerID != id || targetPeerID == _serverPeerId) && ProcessHandlers(_routedRpcData)) { if (targetPeerID == 0L && FiresGhettoNetworkMod.ConfigEnableRpcAoI.Value) { float radius = ((_aoiRadiusOverride > 0f) ? _aoiRadiusOverride : FiresGhettoNetworkMod.ConfigRpcAoIRadius.Value); _aoiRadiusOverride = -1f; if (!((ZDOID)(ref _routedRpcData.m_targetZDO)).IsNone()) { SpawnedZonePositionHint.Clear(); RouteRPCWithAoI(routedRpc, _routedRpcData, radius); } else if (SpawnedZonePositionHint.HasHint) { Vector3 position = SpawnedZonePositionHint.Position; SpawnedZonePositionHint.Clear(); RouteRPCWithAoIFromPosition(routedRpc, _routedRpcData, position, radius); } else { SpawnedZonePositionHint.Clear(); routedRpc.RouteRPC(_routedRpcData); } } else { _aoiRadiusOverride = -1f; SpawnedZonePositionHint.Clear(); routedRpc.RouteRPC(_routedRpcData); } } else { _aoiRadiusOverride = -1f; SpawnedZonePositionHint.Clear(); } } private static void RouteRPCWithAoIFromPosition(ZRoutedRpc routedRpc, RoutedRPCData rpcData, Vector3 worldPos, float radius) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0051: 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) //IL_0058: 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) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) float num = radius * radius; ZPackage val = new ZPackage(); rpcData.Serialize(val); foreach (ZNetPeer peer in routedRpc.m_peers) { if (rpcData.m_senderPeerID != peer.m_uid && peer.IsReady()) { Vector3 refPos = peer.GetRefPos(); float num2 = refPos.x - worldPos.x; float num3 = refPos.z - worldPos.z; if (num2 * num2 + num3 * num3 <= num) { peer.m_rpc.Invoke("RoutedRPC", new object[1] { val }); } } } } private static void RouteRPCWithAoI(ZRoutedRpc routedRpc, RoutedRPCData rpcData, float radius) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009f: 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_00af: Unknown result type (might be due to invalid IL or missing references) if (!ZDOMan.instance.m_objectsByID.TryGetValue(rpcData.m_targetZDO, out var value)) { routedRpc.RouteRPC(rpcData); return; } Vector3 position = value.m_position; float num = radius * radius; ZPackage val = new ZPackage(); rpcData.Serialize(val); int num2 = 0; int num3 = 0; foreach (ZNetPeer peer in routedRpc.m_peers) { if (rpcData.m_senderPeerID != peer.m_uid && peer.IsReady()) { Vector3 refPos = peer.GetRefPos(); float num4 = refPos.x - position.x; float num5 = refPos.z - position.z; float num6 = num4 * num4 + num5 * num5; if (num6 <= num) { peer.m_rpc.Invoke("RoutedRPC", new object[1] { val }); num2++; } else { num3++; } } } } public static bool ProcessHandlers(RoutedRPCData routedRpcData) { if (!_rpcMethodHandlers.TryGetValue(routedRpcData.m_methodHash, out var value)) { return true; } bool flag = true; foreach (RpcMethodHandler item in value) { flag &= item.Process(routedRpcData); } return flag; } public static string MethodHashToString(int methodHash) { if (HashCodeToMethodNameCache.TryGetValue(methodHash, out var value)) { return value; } return $"RPC_{methodHash}"; } public static void Reset() { _rpcMethodHandlers.Clear(); HashCodeToMethodNameCache.Clear(); _serverPeerId = 0L; } } public sealed class AddNoiseHandler : RpcMethodHandler { private static readonly AddNoiseHandler _instance = new AddNoiseHandler(); private AddNoiseHandler() { } public static void Register() { RoutedRpcManager.AddHandler("AddNoise", _instance); } public override bool Process(RoutedRPCData routedRpcData) { ZPackage parameters = routedRpcData.m_parameters; if (parameters == null || parameters.Size() < 4) { return true; } int pos = parameters.GetPos(); parameters.SetPos(0); float num = parameters.ReadSingle(); parameters.SetPos(pos); RoutedRpcManager.SetAoIRadiusOverride(num * 2f); return true; } } public sealed class DamageTextHandler : RpcMethodHandler { private static readonly DamageTextHandler _instance = new DamageTextHandler(); private DamageTextHandler() { } public static void Register() { RoutedRpcManager.AddHandler("DamageText", _instance); } public override bool Process(RoutedRPCData routedRpcData) { return false; } } public sealed class HealthChangedHandler : RpcMethodHandler { private static readonly HealthChangedHandler _instance = new HealthChangedHandler(); private HealthChangedHandler() { } public static void Register() { RoutedRpcManager.AddHandler("RPC_HealthChanged", _instance); } public override bool Process(RoutedRPCData routedRpcData) { return false; } } public sealed class SetTargetHandler : RpcMethodHandler { private static readonly SetTargetHandler _instance = new SetTargetHandler(); public static readonly int PlayerHashCode = StringExtensionMethods.GetStableHashCode("Player"); private SetTargetHandler() { } public static void Register() { RoutedRpcManager.AddHandler("RPC_SetTarget", _instance); } public override bool Process(RoutedRPCData routedRpcData) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) ZPackage parameters = routedRpcData.m_parameters; parameters.SetPos(0); ZDOID val = parameters.ReadZDOID(); parameters.SetPos(0); if (val == ZDOID.None) { return true; } if (!ZDOMan.instance.m_objectsByID.TryGetValue(val, out var value)) { return true; } if (Utils.DistanceXZ(Vector3.zero, value.m_position) > 50000f) { LoggerOptions.LogWarning($"[RpcRouter] SetTarget suspicious — target ZDO at extreme distance ({value.m_position})"); return true; } if (value.m_prefab == PlayerHashCode || value.GetBool(ZDOVars.s_tamed, false)) { parameters.Clear(); parameters.Write(ZDOID.None); parameters.m_writer.Flush(); parameters.m_stream.Flush(); parameters.SetPos(0); routedRpcData.m_senderPeerID = ZRoutedRpc.instance.m_id; LoggerOptions.LogInfo("[RpcRouter] SetTarget cleared — target was player or tamed creature"); } return true; } } public sealed class SpawnedZoneHandler : RpcMethodHandler { private static readonly SpawnedZoneHandler _instance = new SpawnedZoneHandler(); private SpawnedZoneHandler() { } public static void Register() { RoutedRpcManager.AddHandler("SpawnedZone", _instance); } public override bool Process(RoutedRPCData routedRpcData) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //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_0057: Unknown result type (might be due to invalid IL or missing references) ZPackage parameters = routedRpcData.m_parameters; if (parameters == null || parameters.Size() < 8) { return true; } int pos = parameters.GetPos(); parameters.SetPos(0); int num = parameters.ReadInt(); int num2 = parameters.ReadInt(); parameters.SetPos(pos); Vector3 zonePos = ZoneSystem.GetZonePos(new Vector2i(num, num2)); SpawnedZonePositionHint.Position = zonePos; SpawnedZonePositionHint.HasHint = true; RoutedRpcManager.SetAoIRadiusOverride(FiresGhettoNetworkMod.ConfigRpcAoIRadius.Value); return true; } } public static class SpawnedZonePositionHint { public static bool HasHint; public static Vector3 Position; public static void Clear() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) HasHint = false; Position = Vector3.zero; } } public sealed class TalkerSayHandler : RpcMethodHandler { private static readonly TalkerSayHandler _instance = new TalkerSayHandler(); private TalkerSayHandler() { } public static void Register() { RoutedRpcManager.AddHandler("Say", _instance); } public override bool Process(RoutedRPCData routedRpcData) { return true; } } public sealed class TriggerAnimationHandler : RpcMethodHandler { private static readonly TriggerAnimationHandler _instance = new TriggerAnimationHandler(); private TriggerAnimationHandler() { } public static void Register() { RoutedRpcManager.AddHandler("TriggerAnimation", _instance); } public override bool Process(RoutedRPCData routedRpcData) { return true; } } public sealed class TriggerOnDeathHandler : RpcMethodHandler { private static readonly TriggerOnDeathHandler _instance = new TriggerOnDeathHandler(); private TriggerOnDeathHandler() { } public static void Register() { RoutedRpcManager.AddHandler("OnDeath", _instance); } public override bool Process(RoutedRPCData routedRpcData) { return true; } } public sealed class WNTHealthChangedHandler : RpcMethodHandler { private static readonly WNTHealthChangedHandler _instance = new WNTHealthChangedHandler(); private WNTHealthChangedHandler() { } public static void Register() { RoutedRpcManager.AddHandler("RPC_WNTHealthChanged", _instance); } public override bool Process(RoutedRPCData routedRpcData) { return false; } } public abstract class RpcMethodHandler { public abstract bool Process(RoutedRPCData routedRpcData); } [HarmonyPatch] public static class RpcRouterPatches { [HarmonyPatch(typeof(ZNet), "Start")] [HarmonyPostfix] public static void ZNet_Start_Postfix() { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsDedicated() && ZDOMan.instance != null) { RoutedRpcManager.SetupServerPeer(); } } [HarmonyPatch(typeof(ZRoutedRpc), "RPC_RoutedRPC")] [HarmonyPrefix] public static bool RPC_RoutedRPC_Prefix(ZRoutedRpc __instance, ZRpc rpc, ZPackage pkg) { if (__instance.m_server) { RoutedRpcManager.ProcessRoutedRPC(__instance, rpc, pkg); return false; } return true; } } [HarmonyPatch] public static class ServerAuthorityPatches { private struct PeerMotionSample { public Vector3 Pos; public float Time; public Vector3 Velocity; } private static readonly int DefaultActiveArea = 3; private static readonly int DefaultDistantArea = 5; private const float ZoneSizeMeters = 64f; private static readonly Dictionary _peerMotion = new Dictionary(); private static readonly int PlayerPrefabHash = StringExtensionMethods.GetStableHashCode("Player"); internal static Vector3 GetPredictedRefPos(ZNetPeer peer) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: 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_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: 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_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) Vector3 refPos = peer.GetRefPos(); ConfigEntry configEnablePredictiveZoneStreaming = FiresGhettoNetworkMod.ConfigEnablePredictiveZoneStreaming; if (configEnablePredictiveZoneStreaming == null || !configEnablePredictiveZoneStreaming.Value) { return refPos; } float num = FiresGhettoNetworkMod.ConfigPredictionLookaheadSec?.Value ?? 0f; if (num <= 0f) { return refPos; } long uid = peer.m_uid; float time = Time.time; if (!_peerMotion.TryGetValue(uid, out var value)) { _peerMotion[uid] = new PeerMotionSample { Pos = refPos, Time = time, Velocity = Vector3.zero }; return refPos; } float num2 = time - value.Time; if (num2 > 5f) { _peerMotion[uid] = new PeerMotionSample { Pos = refPos, Time = time, Velocity = Vector3.zero }; return refPos; } Vector3 val = value.Velocity; float num3 = refPos.x - value.Pos.x; float num4 = refPos.y - value.Pos.y; float num5 = refPos.z - value.Pos.z; float num6 = num3 * num3 + num4 * num4 + num5 * num5; if (num6 > 0.01f && num2 > 0.05f) { Vector3 val2 = new Vector3(num3, num4, num5) / num2; val = Vector3.Lerp(value.Velocity, val2, 0.5f); _peerMotion[uid] = new PeerMotionSample { Pos = refPos, Time = time, Velocity = val }; } float num7 = FiresGhettoNetworkMod.ConfigPredictionMinVelocity?.Value ?? 2f; float num8 = val.x * val.x + val.y * val.y + val.z * val.z; if (num8 < num7 * num7) { return refPos; } Vector3 val3 = val * num; int num9 = FiresGhettoNetworkMod.ConfigPredictionMaxLookaheadZones?.Value ?? 9; float num10 = (float)num9 * 64f; float num11 = val3.x * val3.x + val3.y * val3.y + val3.z * val3.z; if (num11 > num10 * num10) { val3 = ((Vector3)(ref val3)).normalized * num10; } return refPos + val3; } [HarmonyPatch(typeof(ZDOMan), "RemovePeer")] [HarmonyPostfix] public static void RemovePeer_ClearMotion_Postfix(ZNetPeer netPeer) { if (netPeer != null) { _peerMotion.Remove(netPeer.m_uid); } } [HarmonyPatch(typeof(ZoneSystem), "IsActiveAreaLoaded")] [HarmonyPriority(800)] [HarmonyPrefix] public static bool IsActiveAreaLoaded_TeleportFix(ref bool __result) { if ((Object)(object)Player.m_localPlayer != (Object)null && ((Character)Player.m_localPlayer).IsTeleporting()) { __result = true; return false; } return true; } [HarmonyPatch(typeof(ZNetScene), "CreateDestroyObjects")] [HarmonyPrefix] public static bool CreateDestroyObjects_Prefix(ZNetScene __instance) { //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsDedicated() || ZNet.instance.GetConnectedPeers().Count == 0) { return true; } try { int value = FiresGhettoNetworkMod.ConfigExtendedZoneRadius.Value; int num = (ZoneSystem.instance?.m_activeArea ?? DefaultActiveArea) + value; int num2 = (ZoneSystem.instance?.m_activeDistantArea ?? DefaultDistantArea) + value; List list = new List(); List list2 = new List(); foreach (ZNetPeer connectedPeer in ZNet.instance.GetConnectedPeers()) { if (connectedPeer.IsReady()) { Vector3 predictedRefPos = GetPredictedRefPos(connectedPeer); Vector2i zone = ZoneSystem.GetZone(predictedRefPos); ZDOMan.instance.FindSectorObjects(zone, num, num2, list, list2); } } List list3 = list.Where((ZDO zdo) => zdo != null && zdo.IsValid() && !((ZDOID)(ref zdo.m_uid)).IsNone()).Distinct().ToList(); List list4 = list2.Where((ZDO zdo) => zdo != null && zdo.IsValid() && !((ZDOID)(ref zdo.m_uid)).IsNone()).Distinct().ToList(); __instance.CreateObjects(list3, list4); __instance.RemoveObjects(list3, list4); return false; } catch (NullReferenceException ex) { LoggerOptions.LogWarning("CreateDestroyObjects encountered NRE (likely mod conflict), falling back to vanilla: " + ex.Message); return true; } } [HarmonyPatch(typeof(ZoneSystem), "IsActiveAreaLoaded")] [HarmonyPrefix] public static bool IsActiveAreaLoaded_Prefix(ref bool __result, ZoneSystem __instance) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: 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_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsDedicated() || ZNet.instance.GetPeers().Count == 0) { return true; } int value = FiresGhettoNetworkMod.ConfigExtendedZoneRadius.Value; int num = __instance.m_activeArea + value; Dictionary zones = __instance.m_zones; if (zones == null) { return true; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (!peer.IsReady()) { continue; } Vector3 predictedRefPos = GetPredictedRefPos(peer); Vector2i zone = ZoneSystem.GetZone(predictedRefPos); for (int i = zone.y - num; i <= zone.y + num; i++) { for (int j = zone.x - num; j <= zone.x + num; j++) { if (!zones.ContainsKey(new Vector2i(j, i))) { __result = false; return false; } } } } __result = true; return false; } [HarmonyPatch(typeof(ZoneSystem), "Update")] [HarmonyPostfix] public static void ZoneSystem_Update_Postfix(ZoneSystem __instance) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsDedicated() || ZNet.instance.GetPeers().Count == 0) { return; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer.IsReady()) { __instance.CreateLocalZones(GetPredictedRefPos(peer)); } } } private static bool IsPlayerZDO(ZDO zdo) { return zdo != null && zdo.m_prefab == PlayerPrefabHash; } private static bool IsPeerConnected(ZDOMan zdoMan, long uid) { if (uid == ZDOMan.GetSessionID()) { return true; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer.m_uid == uid) { return true; } } return false; } [HarmonyPatch(typeof(ZNetScene), "OutsideActiveArea", new Type[] { typeof(Vector3) })] [HarmonyPrefix] public static bool OutsideActiveArea_Prefix(ref bool __result, Vector3 point) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsDedicated() || ZNet.instance.GetPeers().Count == 0) { return true; } int value = FiresGhettoNetworkMod.ConfigExtendedZoneRadius.Value; int num = (ZoneSystem.instance?.m_activeArea ?? DefaultActiveArea) + value; __result = true; foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer.IsReady() && !ZNetScene.OutsideActiveArea(point, ZoneSystem.GetZone(GetPredictedRefPos(peer)), num)) { __result = false; break; } } return false; } [HarmonyPatch(typeof(Tameable), "Awake")] [HarmonyPrefix] public static bool Tameable_Awake_Prefix() { return (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsDedicated(); } [HarmonyPatch(typeof(Tameable), "Update")] [HarmonyPrefix] public static bool Tameable_Update_Prefix() { return (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsDedicated(); } [HarmonyPatch(typeof(Tameable), "SetText")] [HarmonyPrefix] public static bool Tameable_SetText_Prefix() { return (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsDedicated(); } [HarmonyPatch(typeof(AudioMan), "Update")] [HarmonyPrefix] public static bool AudioMan_Update_Prefix() { ZNet instance = ZNet.instance; return instance == null || !instance.IsDedicated(); } } public static class ServerClientUtils { private static ManualLogSource _earlyLogger; public static bool IsDedicatedServerDetected { get; private set; } public static void Detect(ManualLogSource logger = null) { //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Invalid comparison between Unknown and I4 _earlyLogger = logger; IsDedicatedServerDetected = false; try { string path = Process.GetCurrentProcess().MainModule?.FileName ?? ""; string text = Path.GetFileNameWithoutExtension(path).ToLowerInvariant(); string text2 = Path.GetDirectoryName(path)?.ToLowerInvariant() ?? ""; Log("Executable name: " + text); Log("Executable directory: " + text2); if (text.Contains("server")) { IsDedicatedServerDetected = true; Log("Detected dedicated server via executable name containing 'server'."); return; } if (text2.Contains("server") || text2.Contains("dedicated")) { IsDedicatedServerDetected = true; Log("Detected dedicated server via directory path containing 'server' or 'dedicated'."); return; } Log($"Application.isBatchMode = {Application.isBatchMode}"); if (Application.isBatchMode) { IsDedicatedServerDetected = true; Log("Detected dedicated server via Application.isBatchMode."); return; } string[] commandLineArgs = Environment.GetCommandLineArgs(); string[] array = commandLineArgs; foreach (string text3 in array) { string text4 = text3.ToLowerInvariant(); if (text4 == "-batchmode" || text4 == "-nographics" || text4.Contains("dedicated")) { IsDedicatedServerDetected = true; Log("Detected dedicated server via command line argument: " + text3); return; } } if ((int)SystemInfo.graphicsDeviceType == 4) { IsDedicatedServerDetected = true; Log("Detected dedicated server via null graphics device (headless mode)."); return; } Type type = AccessTools.TypeByName("ZNet") ?? Type.GetType("ZNet, Assembly-CSharp"); if (type == null) { Log("ZNet type not found; assuming client/listen-server."); return; } MethodInfo method = type.GetMethod("IsDedicated", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method != null) { object obj = method.Invoke(null, null); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { IsDedicatedServerDetected = true; Log("Detected dedicated server via ZNet.IsDedicated()."); return; } } MethodInfo method2 = type.GetMethod("IsServer", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (method2 != null) { object obj2 = method2.Invoke(null, null); bool flag2 = default(bool); int num2; if (obj2 is bool) { flag2 = (bool)obj2; num2 = 1; } else { num2 = 0; } if (((uint)num2 & (flag2 ? 1u : 0u)) != 0) { IsDedicatedServerDetected = true; Log("Detected dedicated server via ZNet.IsServer()."); return; } } object obj3 = (type.GetField("m_instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(null); if (obj3 != null) { PropertyInfo property = type.GetProperty("IsServer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { object value = property.GetValue(obj3); bool flag3 = default(bool); int num3; if (value is bool) { flag3 = (bool)value; num3 = 1; } else { num3 = 0; } if (((uint)num3 & (flag3 ? 1u : 0u)) != 0) { IsDedicatedServerDetected = true; Log("Detected dedicated server via ZNet.instance.IsServer."); return; } } MethodInfo method3 = type.GetMethod("IsDedicated", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (method3 != null) { object obj4 = method3.Invoke(obj3, null); bool flag4 = default(bool); int num4; if (obj4 is bool) { flag4 = (bool)obj4; num4 = 1; } else { num4 = 0; } if (((uint)num4 & (flag4 ? 1u : 0u)) != 0) { IsDedicatedServerDetected = true; Log("Detected dedicated server via ZNet.instance.IsDedicated."); return; } } } Log("No dedicated-server indicator found; assuming client/listen-server."); } catch (Exception ex) { LogWarn("Server detection failed: " + ex.Message); IsDedicatedServerDetected = false; } } private static void Log(string message) { ManualLogSource earlyLogger = _earlyLogger; if (earlyLogger != null) { earlyLogger.LogInfo((object)message); } } private static void LogWarn(string message) { ManualLogSource earlyLogger = _earlyLogger; if (earlyLogger != null) { earlyLogger.LogWarning((object)message); } } public static bool IsRunningOnDedicatedServer() { if (IsDedicatedServerDetected) { return true; } if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsDedicated(); } return false; } } [HarmonyPatch] public static class ShipFixesGroup { [HarmonyPatch(typeof(Ship), "CustomFixedUpdate")] [HarmonyPrefix] public static void CustomFixedUpdate_Prefix(Ship __instance, out Speed __state) { __state = (Speed)0; if (FiresGhettoNetworkMod.ConfigEnableShipFixes.Value && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsDedicated() && !((Object)(object)__instance.m_nview == (Object)null) && __instance.m_nview.IsValid() && __instance.m_nview.IsOwner()) { ZDO zDO = __instance.m_nview.GetZDO(); if (zDO != null) { __state = (Speed)zDO.GetInt(ZDOVars.s_forward, 0); } } } [HarmonyPatch(typeof(Ship), "CustomFixedUpdate")] [HarmonyPostfix] public static void CustomFixedUpdate_Postfix(Ship __instance, Speed __state) { //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Invalid comparison between Unknown and I4 //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Expected I4, but got Unknown if (!FiresGhettoNetworkMod.ConfigEnableShipFixes.Value || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsDedicated() || (Object)(object)__instance.m_nview == (Object)null || !__instance.m_nview.IsValid() || !__instance.m_nview.IsOwner()) { return; } ZDO zDO = __instance.m_nview.GetZDO(); if (zDO != null) { Speed val = (Speed)zDO.GetInt(ZDOVars.s_forward, 0); if ((int)__instance.GetSpeedSetting() == 0 && (int)val > 0) { AccessTools.Field(typeof(Ship), "m_speed").SetValue(__instance, val); } float @float = zDO.GetFloat(ZDOVars.s_rudder, 0f); if (__instance.GetRudderValue() == 0f && @float != 0f) { AccessTools.Field(typeof(Ship), "m_rudderValue").SetValue(__instance, @float); } zDO.Set(ZDOVars.s_forward, (int)__instance.GetSpeedSetting(), false); zDO.Set(ZDOVars.s_rudder, __instance.GetRudderValue()); } } [HarmonyPatch(typeof(Ship), "UpdateOwner")] [HarmonyPrefix] public static bool UpdateOwner_Prefix() { if (!FiresGhettoNetworkMod.ConfigEnableShipFixes.Value) { return true; } if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsDedicated()) { return true; } return false; } } public static class WearNTearClassifier { public static bool IsFullyInvulnerable(WearNTear wnt) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)wnt == (Object)null) { return false; } return IsZeroOrIgnored(wnt.m_damages.m_blunt) && IsZeroOrIgnored(wnt.m_damages.m_slash) && IsZeroOrIgnored(wnt.m_damages.m_pierce) && IsZeroOrIgnored(wnt.m_damages.m_chop) && IsZeroOrIgnored(wnt.m_damages.m_pickaxe) && IsZeroOrIgnored(wnt.m_damages.m_fire) && IsZeroOrIgnored(wnt.m_damages.m_frost) && IsZeroOrIgnored(wnt.m_damages.m_lightning) && IsZeroOrIgnored(wnt.m_damages.m_poison) && IsZeroOrIgnored(wnt.m_damages.m_spirit); } private static bool IsZeroOrIgnored(DamageModifier mod) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 return (int)mod == 3 || (int)mod == 4; } } [HarmonyPatch] public static class WearNTearClientSupportPatches { private static readonly FieldRef _supportField = AccessTools.FieldRefAccess("m_support"); [HarmonyReversePatch(/*Could not decode attribute arguments.*/)] [HarmonyPatch(typeof(WearNTear), "GetMaxSupport")] public static float CallGetMaxSupport(WearNTear self) { throw new NotImplementedException("Harmony reverse patch failed for WearNTear.GetMaxSupport"); } [HarmonyPatch(typeof(WearNTear), "UpdateSupport")] [HarmonyPrefix] public static bool UpdateSupport_Prefix(WearNTear __instance) { ConfigEntry configEnableInvulnerableSupportSkip = FiresGhettoNetworkMod.ConfigEnableInvulnerableSupportSkip; if (configEnableInvulnerableSupportSkip == null || !configEnableInvulnerableSupportSkip.Value) { return true; } if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsDedicated()) { return true; } if (!WearNTearClassifier.IsFullyInvulnerable(__instance)) { return true; } try { float num = CallGetMaxSupport(__instance); _supportField.Invoke(__instance) = num; ZNetView nview = __instance.m_nview; if ((Object)(object)nview != (Object)null && nview.IsValid() && nview.IsOwner()) { nview.GetZDO().Set(ZDOVars.s_support, num); } } catch (Exception ex) { LoggerOptions.LogWarning("[WNT-Support] Skip threw, falling back to vanilla: " + ex.Message); return true; } return false; } } [HarmonyPatch] public static class WearNTearServerPatches { [HarmonyPatch(typeof(WearNTear), "UpdateWear")] [HarmonyPrefix] public static bool UpdateWear_Prefix(WearNTear __instance) { if (!FiresGhettoNetworkMod.ConfigEnableWNTServerOptimization.Value) { return true; } if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsDedicated()) { return true; } ZNetView nview = __instance.m_nview; if ((Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner()) { return true; } if (WearNTearClassifier.IsFullyInvulnerable(__instance)) { return false; } if (__instance.m_createTime >= 0f && Time.time - __instance.m_createTime <= 30f) { return true; } if (__instance.m_inAshlands || __instance.m_rainWet) { return true; } float health = __instance.m_health; float @float = nview.GetZDO().GetFloat(ZDOVars.s_health, health); if (@float < health) { return true; } return false; } } [HarmonyPatch] public static class ZDODeltaPatches { private sealed class ZDOSnapshot { public readonly Dictionary floats = new Dictionary(); public readonly Dictionary vec3s = new Dictionary(); public readonly Dictionary quats = new Dictionary(); public readonly Dictionary ints = new Dictionary(); public readonly Dictionary longs = new Dictionary(); public Vector3 rotationEuler; public bool hasRotationSnapshot; public ZDOSnapshot() { //IL_0038: 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) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) Quaternion identity = Quaternion.identity; rotationEuler = ((Quaternion)(ref identity)).eulerAngles; base..ctor(); } } private static readonly Dictionary> _peerSnapshots = new Dictionary>(); private static long _currentPeerUID = 0L; private static bool _contextActive = false; [HarmonyPatch(typeof(ZDOMan), "SendZDOs")] [HarmonyPrefix] public static void SendZDOs_Prefix(ZDOPeer peer, bool flush) { if (FiresGhettoNetworkMod.ConfigEnableZDODelta == null || !FiresGhettoNetworkMod.ConfigEnableZDODelta.Value) { _contextActive = false; return; } if (!ZNet.instance.IsDedicated()) { _contextActive = false; return; } _currentPeerUID = peer.m_peer.m_uid; _contextActive = true; } [HarmonyPatch(typeof(ZDOMan), "SendZDOs")] [HarmonyPostfix] public static void SendZDOs_Postfix(ZDOPeer peer, bool flush) { _contextActive = false; } [HarmonyPatch(typeof(ZDO), "Serialize")] [HarmonyPrefix] public static bool ZDO_Serialize_Prefix(ZDO __instance, ZPackage pkg) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (!_contextActive) { return true; } if (!_peerSnapshots.TryGetValue(_currentPeerUID, out var value)) { return true; } if (!value.TryGetValue(__instance.m_uid, out var value2)) { return true; } WriteDelta(__instance, pkg, value2); return false; } [HarmonyPatch(typeof(ZDO), "Serialize")] [HarmonyPostfix] public static void ZDO_Serialize_Postfix(ZDO __instance) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) if (_contextActive) { if (!_peerSnapshots.TryGetValue(_currentPeerUID, out var value)) { value = new Dictionary(); _peerSnapshots[_currentPeerUID] = value; } if (!value.TryGetValue(__instance.m_uid, out var value2)) { value2 = new ZDOSnapshot(); value[__instance.m_uid] = value2; } UpdateSnapshot(__instance, value2); } } [HarmonyPatch(typeof(ZDOMan), "RemovePeer")] [HarmonyPostfix] public static void RemovePeer_Postfix(ZNetPeer netPeer) { _peerSnapshots.Remove(netPeer.m_uid); } private static void WriteDelta(ZDO zdo, ZPackage pkg, ZDOSnapshot snap) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Invalid comparison between Unknown and I4 //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_02c6: Unknown result type (might be due to invalid IL or missing references) //IL_02ca: Unknown result type (might be due to invalid IL or missing references) //IL_03b6: Unknown result type (might be due to invalid IL or missing references) //IL_03bd: Unknown result type (might be due to invalid IL or missing references) //IL_03ff: Unknown result type (might be due to invalid IL or missing references) //IL_042e: Unknown result type (might be due to invalid IL or missing references) //IL_0438: Expected I4, but got Unknown //IL_0441: Unknown result type (might be due to invalid IL or missing references) List> floats = ZDOExtraData.GetFloats(zdo.m_uid); List> vec3s = ZDOExtraData.GetVec3s(zdo.m_uid); List> quaternions = ZDOExtraData.GetQuaternions(zdo.m_uid); List> ints = ZDOExtraData.GetInts(zdo.m_uid); List> longs = ZDOExtraData.GetLongs(zdo.m_uid); List> strings = ZDOExtraData.GetStrings(zdo.m_uid); List> byteArrays = ZDOExtraData.GetByteArrays(zdo.m_uid); ZDOConnection connection = ZDOExtraData.GetConnection(zdo.m_uid); List> list = new List>(); List> list2 = new List>(); List> list3 = new List>(); List> list4 = new List>(); List> list5 = new List>(); foreach (KeyValuePair item in floats) { if (!snap.floats.TryGetValue(item.Key, out var value) || value != item.Value) { list.Add(item); } } foreach (KeyValuePair item2 in vec3s) { if (!snap.vec3s.TryGetValue(item2.Key, out var value2) || value2 != item2.Value) { list2.Add(item2); } } foreach (KeyValuePair item3 in quaternions) { if (!snap.quats.TryGetValue(item3.Key, out var value3) || value3 != item3.Value) { list3.Add(item3); } } foreach (KeyValuePair item4 in ints) { if (!snap.ints.TryGetValue(item4.Key, out var value4) || value4 != item4.Value) { list4.Add(item4); } } foreach (KeyValuePair item5 in longs) { if (!snap.longs.TryGetValue(item5.Key, out var value5) || value5 != item5.Value) { list5.Add(item5); } } bool flag = connection != null && (int)connection.m_type > 0; Quaternion val = zdo.GetRotation(); Vector3 eulerAngles = ((Quaternion)(ref val)).eulerAngles; val = Quaternion.identity; bool flag2 = eulerAngles != ((Quaternion)(ref val)).eulerAngles; ushort num = 0; if (flag) { num = (ushort)(num | 1u); } if (list.Count > 0) { num = (ushort)(num | 2u); } if (list2.Count > 0) { num = (ushort)(num | 4u); } if (list3.Count > 0) { num = (ushort)(num | 8u); } if (list4.Count > 0) { num = (ushort)(num | 0x10u); } if (list5.Count > 0) { num = (ushort)(num | 0x20u); } if (strings.Count > 0) { num = (ushort)(num | 0x40u); } if (byteArrays.Count > 0) { num = (ushort)(num | 0x80u); } ushort num2 = (ushort)((ulong)(int)(num | (zdo.Persistent ? 256u : 0u) | (zdo.Distant ? 512u : 0u)) | (ulong)(zdo.Type << 10) | (ulong)(int)(flag2 ? 4096u : 0u)); pkg.Write(num2); pkg.Write(zdo.GetPrefab()); if (flag2) { pkg.Write(eulerAngles); } if ((num2 & 0xFFu) != 0) { if (flag) { pkg.Write((byte)(int)connection.m_type); pkg.Write(connection.m_target); } WriteList(pkg, list, delegate(float v) { pkg.Write(v); }); WriteList(pkg, list2, delegate(Vector3 v) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) pkg.Write(v); }); WriteList(pkg, list3, delegate(Quaternion v) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) pkg.Write(v); }); WriteList(pkg, list4, delegate(int v) { pkg.Write(v); }); WriteList(pkg, list5, delegate(long v) { pkg.Write(v); }); WriteList(pkg, strings, delegate(string v) { pkg.Write(v); }); WriteList(pkg, byteArrays, delegate(byte[] v) { pkg.Write(v); }); } } private static void WriteList(ZPackage pkg, List> list, Action writeVal) { if (list.Count == 0) { return; } pkg.Write((byte)list.Count); foreach (KeyValuePair item in list) { pkg.Write(item.Key); writeVal(item.Value); } } private static void UpdateSnapshot(ZDO zdo, ZDOSnapshot snap) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) snap.floats.Clear(); snap.vec3s.Clear(); snap.quats.Clear(); snap.ints.Clear(); snap.longs.Clear(); foreach (KeyValuePair @float in ZDOExtraData.GetFloats(zdo.m_uid)) { snap.floats[@float.Key] = @float.Value; } foreach (KeyValuePair vec in ZDOExtraData.GetVec3s(zdo.m_uid)) { snap.vec3s[vec.Key] = vec.Value; } foreach (KeyValuePair quaternion in ZDOExtraData.GetQuaternions(zdo.m_uid)) { snap.quats[quaternion.Key] = quaternion.Value; } foreach (KeyValuePair @int in ZDOExtraData.GetInts(zdo.m_uid)) { snap.ints[@int.Key] = @int.Value; } foreach (KeyValuePair @long in ZDOExtraData.GetLongs(zdo.m_uid)) { snap.longs[@long.Key] = @long.Value; } Quaternion rotation = zdo.GetRotation(); snap.rotationEuler = ((Quaternion)(ref rotation)).eulerAngles; snap.hasRotationSnapshot = true; } } [HarmonyPatch] public static class ZDOMemoryManager { public static ConfigEntry ConfigMaxZDOs; private static float startupTimer; private static bool startupComplete; private static bool warningShown; private const float STARTUP_GRACE_PERIOD = 600f; public static void Init(ConfigFile config) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown ConfigMaxZDOs = config.Bind("Advanced", "Max Active ZDOs", 10000000, new ConfigDescription("Force ZDO cleanup if active ZDOs exceed this after startup (0 = disabled).\nStartup grace period (10 min) prevents spam during world load.\nThis feature is CLIENT-ONLY and will not run on dedicated servers.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 2000000), Array.Empty())); } [HarmonyPatch(typeof(ZDOMan), "Update")] [HarmonyPostfix] private static void CleanupIfTooBig(ZDOMan __instance, float dt) { if ((Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer()) || ConfigMaxZDOs.Value <= 0) { return; } if (!startupComplete) { startupTimer += dt; if (startupTimer >= 600f) { startupComplete = true; LoggerOptions.LogInfo("ZDO cleanup grace period ended — normal monitoring enabled."); } return; } FieldInfo fieldInfo = AccessTools.Field(typeof(ZDOMan), "m_objectsByID"); if (fieldInfo == null) { return; } Dictionary dictionary = (Dictionary)fieldInfo.GetValue(__instance); if (dictionary != null && dictionary.Count > ConfigMaxZDOs.Value) { if (!warningShown) { LoggerOptions.LogWarning($"ZDO pool too big ({dictionary.Count} > {ConfigMaxZDOs.Value}) — forcing cleanup..."); LoggerOptions.LogWarning("You have been Exploring a LOT. You should log out to free up RAM."); warningShown = true; } int count = dictionary.Count; AccessTools.Method(typeof(ZDOMan), "RemoveOrphanNonPersistentZDOS", (Type[])null, (Type[])null).Invoke(__instance, null); GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); dictionary = (Dictionary)fieldInfo.GetValue(__instance); LoggerOptions.LogInfo($"Cleanup complete: {count} → {dictionary?.Count ?? 0} ZDOs"); } } } [HarmonyPatch] public static class ZDOThrottlingPatches { private const float DISTANT_PENALTY = 500f; private static readonly int PlayerPrefabHash = StringExtensionMethods.GetStableHashCode("Player"); [HarmonyPatch(typeof(ZDOMan), "ServerSortSendZDOS")] [HarmonyPostfix] public static void ServerSortSendZDOS_Postfix(List objects, Vector3 refPos) { //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Invalid comparison between Unknown and I4 //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsDedicated() || FiresGhettoNetworkMod.ConfigEnableZDOThrottling == null) { return; } bool value = FiresGhettoNetworkMod.ConfigEnableZDOThrottling.Value; float num = 0f; if (value) { float value2 = FiresGhettoNetworkMod.ConfigZDOThrottleDistance.Value; num = value2 * value2; } bool flag = PlayerPositionSyncPatches.ConfigEnablePlayerPositionBoost != null && PlayerPositionSyncPatches.ConfigEnablePlayerPositionBoost.Value; float num2 = (flag ? Mathf.Max(1f, PlayerPositionSyncPatches.ConfigPlayerPositionUpdateMultiplier.Value) : 1f); if (!value && !flag) { return; } bool flag2 = false; foreach (ZDO @object in objects) { if (flag && IsPlayerZDO(@object)) { @object.m_tempSortValue = Mathf.Max(0f, @object.m_tempSortValue / num2); flag2 = true; } else if (value && (int)@object.Type != 1) { Vector3 position = @object.GetPosition(); float num3 = position.x - refPos.x; float num4 = position.z - refPos.z; if (num3 * num3 + num4 * num4 > num) { @object.m_tempSortValue += 500f; flag2 = true; } } } if (flag2) { objects.Sort((ZDO x, ZDO y) => x.m_tempSortValue.CompareTo(y.m_tempSortValue)); } } private static bool IsPlayerZDO(ZDO zdo) { return zdo != null && zdo.m_prefab == PlayerPrefabHash; } } } namespace FiresGhettoNetworkMod.AutoTune { public static class AutoTuneCache { public sealed class CacheEntry { public Tier Tier { get; set; } public int PingMedianMs { get; set; } public DateTime TimestampUtc { get; set; } public string HardwareHash { get; set; } = string.Empty; } private const int FileMagic = 1095649094; private const string FileName = "com.Fire.FiresGhettoNetworkMod_autotune.bin"; public const int SchemaVersion = 2; private static readonly TimeSpan DefaultTtl = TimeSpan.FromDays(7.0); private static Dictionary _entries; private static bool _loaded; private static string CachePath => Path.Combine(Paths.ConfigPath, "com.Fire.FiresGhettoNetworkMod_autotune.bin"); private static void EnsureLoaded() { if (_loaded) { return; } _loaded = true; _entries = new Dictionary(); if (!File.Exists(CachePath)) { return; } try { using FileStream input = File.OpenRead(CachePath); using BinaryReader binaryReader = new BinaryReader(input); int num = binaryReader.ReadInt32(); if (num != 1095649094) { LoggerOptions.LogInfo($"[AutoTune] Cache magic mismatch (0x{num:X8} != 0x{1095649094:X8}) — starting fresh."); return; } int num2 = binaryReader.ReadInt32(); if (num2 != 2) { LoggerOptions.LogInfo($"[AutoTune] Cache schema {num2} (expected {2}) — starting fresh."); return; } int num3 = binaryReader.ReadInt32(); if (num3 < 0 || num3 > 100000) { LoggerOptions.LogWarning($"[AutoTune] Cache entry count {num3} out of range — starting fresh."); return; } for (int i = 0; i < num3; i++) { string key = binaryReader.ReadString(); int tier = binaryReader.ReadInt32(); int pingMedianMs = binaryReader.ReadInt32(); long ticks = binaryReader.ReadInt64(); string text = binaryReader.ReadString(); _entries[key] = new CacheEntry { Tier = (Tier)tier, PingMedianMs = pingMedianMs, TimestampUtc = new DateTime(ticks, DateTimeKind.Utc), HardwareHash = (text ?? string.Empty) }; } } catch (Exception ex) { LoggerOptions.LogWarning("[AutoTune] Failed to load cache (" + ex.GetType().Name + ": " + ex.Message + "); starting fresh."); _entries = new Dictionary(); } } private static void SaveToDisk() { try { using FileStream output = File.Create(CachePath); using BinaryWriter binaryWriter = new BinaryWriter(output); binaryWriter.Write(1095649094); binaryWriter.Write(2); binaryWriter.Write(_entries.Count); foreach (KeyValuePair entry in _entries) { binaryWriter.Write(entry.Key ?? string.Empty); binaryWriter.Write((int)entry.Value.Tier); binaryWriter.Write(entry.Value.PingMedianMs); binaryWriter.Write(entry.Value.TimestampUtc.Ticks); binaryWriter.Write(entry.Value.HardwareHash ?? string.Empty); } } catch (Exception ex) { LoggerOptions.LogWarning("[AutoTune] Failed to write cache: " + ex.Message); } } public static CacheEntry TryGet(string serverKey, string currentHwHash, TimeSpan? ttl = null) { if (string.IsNullOrEmpty(serverKey)) { return null; } EnsureLoaded(); if (!_entries.TryGetValue(serverKey, out var value) || value == null) { return null; } TimeSpan timeSpan = ttl ?? DefaultTtl; if (DateTime.UtcNow - value.TimestampUtc > timeSpan) { return null; } if (!string.IsNullOrEmpty(currentHwHash) && !string.IsNullOrEmpty(value.HardwareHash) && value.HardwareHash != currentHwHash) { return null; } return value; } public static void Save(string serverKey, Tier tier, int pingMedianMs, string hwHash) { if (!string.IsNullOrEmpty(serverKey)) { EnsureLoaded(); _entries[serverKey] = new CacheEntry { Tier = tier, PingMedianMs = pingMedianMs, TimestampUtc = DateTime.UtcNow, HardwareHash = (hwHash ?? string.Empty) }; SaveToDisk(); } } public static void Forget(string serverKey) { if (!string.IsNullOrEmpty(serverKey)) { EnsureLoaded(); if (_entries.Remove(serverKey)) { SaveToDisk(); } } } public static void ForgetAll() { EnsureLoaded(); _entries.Clear(); SaveToDisk(); } } public static class AutoTuneConfig { public static ConfigEntry EnableClientAutoTune; public static ConfigEntry EnableServerAutoTune; public static ConfigEntry LogServerSuggestions; public static ConfigEntry RetuneOnEveryLogin; public static ConfigEntry ProbeStartDelaySeconds; public static ConfigEntry PlayerArrivalTimeoutSeconds; public static ConfigEntry ProbePingTimeoutSeconds; public static ConfigEntry ProbePingCount; public static ConfigEntry ProbeBandwidthPayloadBytes; public static ConfigEntry ProbePingAbortMs; public static ConfigEntry EnableRollingMonitor; public static ConfigEntry RollingMonitorIntervalMinutes; public static ConfigEntry RollingMonitorWindow; public static void Init(ConfigFile config) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Expected O, but got Unknown //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Expected O, but got Unknown //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Expected O, but got Unknown //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Expected O, but got Unknown //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Expected O, but got Unknown //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Expected O, but got Unknown //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_022e: Expected O, but got Unknown EnableClientAutoTune = config.Bind("Auto-Tune", "Enable Client Auto-Tune", true, "Run a brief network/hardware probe on first login to a server and pick a perf tier (LOW/MED/HIGH).\nEffective values shadow the BepInEx config — your manual settings are never overwritten.\nDisable to use only the values set above. CLIENT-SIDE only."); EnableServerAutoTune = config.Bind("Auto-Tune", "Enable Server Auto-Tune", true, "When the mod runs on a dedicated server, score the host's CPU/RAM and APPLY a tier preset to\nZDO throttle / AI LOD / RPC AoI / queue size / extended zone radius / update rate / Steam buffers.\nDefault ON — the tier preset is conservative and the asymmetric send-rate pattern keeps slow\nclients safe (Min stays at the LOW baseline regardless of tier). Disable if you've manually tuned\nevery server knob and want your values used verbatim. SERVER-SIDE only."); LogServerSuggestions = config.Bind("Auto-Tune", "Log Server Suggestions", true, "When on, the server logs its own self-tune tier + recommended values at startup, and periodically\nlogs the median tier reported by connected clients with suggested adjustments. Pure logging —\nno settings are changed. SERVER-SIDE only."); RetuneOnEveryLogin = config.Bind("Auto-Tune", "Retune On Every Login", false, "When on, ignore the cached tier on disk and re-probe every time you join. Costs ~50KB of probe\ntraffic per join but reflects current ISP/route conditions. CLIENT-SIDE only."); ProbeStartDelaySeconds = config.Bind("Auto-Tune.Probe", "Start Delay Seconds", 30f, new ConfigDescription("Wait this long AFTER the player has actually arrived in the world (Game.m_playerInitialSpawn\nfired) before starting the probe. Lets the post-spawn burst — inventory equip, ZDO\nzone-load for the spawn point, post-spawn mod work — subside so we don't sample latency\nwhile Valheim's own arrival traffic is queued ahead of our pings. 30s is the safe default;\na server with heavy mod-driven sync (large worlds, many players) may benefit from 45-60.\nWorst-case modpacks on slow servers may need 75-90s to fully settle. Lower only if you\nknow your fast-load mod handles arrival-burst traffic well.", (AcceptableValueBase)(object)new AcceptableValueRange(2f, 90f), Array.Empty())); PlayerArrivalTimeoutSeconds = config.Bind("Auto-Tune.Probe", "Player Arrival Timeout Seconds", 180f, new ConfigDescription("Hard cap on how long to wait for the local player to actually finish spawning into the\nworld before forcing the probe to start anyway. The probe gates on Game.m_playerInitialSpawn\n(the same event that fires the '$text_player_arrived' chat message) so the probe doesn't\nstart while the player is still mid-load — that event normally fires within tens of\nseconds of connect, but heavy modpacks with large worlds + slow disks can take 90s+.\nThis timeout is the safety valve: if the spawn fails entirely we eventually proceed\nanyway rather than leave the client stuck at a degraded default tier forever. 180s\ncovers worst-case modpack loads on slow disks; raise only if you have repro evidence\nthe timeout is firing on a successful spawn.", (AcceptableValueBase)(object)new AcceptableValueRange(30f, 600f), Array.Empty())); ProbePingTimeoutSeconds = config.Bind("Auto-Tune.Probe", "Ping Timeout Seconds", 5f, new ConfigDescription("How long to wait for a single ping echo before giving up. On timeout we default to LOW tier.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 15f), Array.Empty())); ProbePingCount = config.Bind("Auto-Tune.Probe", "Ping Count", 10, new ConfigDescription("Number of pings to send for the latency probe. The probe also fires one untracked\nwarmup ping first (clears Steam TCP slow-start), and drops the single worst sample\nbefore computing stats so transient post-spawn queue contention can't tank the\nresult on a healthy link. 10 is the sweet spot — enough samples for outlier\ntrimming + IQR jitter to be stable; not so many that probe traffic becomes notable.", (AcceptableValueBase)(object)new AcceptableValueRange(5, 20), Array.Empty())); ProbePingAbortMs = config.Bind("Auto-Tune.Probe", "Ping Abort Ms", 2000, new ConfigDescription("If any single ping round-trip exceeds this, we hard-default to LOW tier and skip the\nbandwidth probe entirely — the client is already struggling, no point making them prove\nit twice.", (AcceptableValueBase)(object)new AcceptableValueRange(500, 5000), Array.Empty())); ProbeBandwidthPayloadBytes = config.Bind("Auto-Tune.Probe", "Bandwidth Probe Bytes", 131072, new ConfigDescription("Size of the bandwidth-test payload requested from server. Only runs if the latency probe\nputs the client at MED or HIGH tier — LOW-tier clients skip this entirely.\n\nLarger payloads measure throughput more honestly: a request-response with a small\npayload is dominated by RTT, not actual link capacity. With a 64ms RTT, a 32KB payload\nceilings at ~500 KB/s no matter what the link can deliver. 128KB at the same RTT can\nreport up to ~2 MB/s — the transfer time finally dominates the RTT. Default raised\nfrom 32KB → 128KB on 2026-05-11 because the small-payload probe was systematically\nunder-reporting on healthy links and incorrectly tier-downgrading.\nTradeoff: probe takes ~1.5s longer per session and consumes ~256KB of extra one-time\nbandwidth on the dedicated server.", (AcceptableValueBase)(object)new AcceptableValueRange(8192, 524288), Array.Empty())); EnableRollingMonitor = config.Bind("Auto-Tune.Monitor", "Enable Rolling Monitor", true, "After the initial probe, keep periodically re-probing latency over the session and\nrefine the tier from a rolling average. Catches transient server overload (settling\nto a higher tier once the initial-sync flood ends) and ISP/route weather changes.\nEach re-probe is latency-only — no extra bandwidth probe — so cost is ~5KB per cycle.\nCLIENT-SIDE only."); RollingMonitorIntervalMinutes = config.Bind("Auto-Tune.Monitor", "Re-Probe Interval Minutes", 5f, new ConfigDescription("How often to re-probe latency after the initial probe. Smaller values catch transient\nissues faster but cost more probe traffic; larger values are quieter but slower to react.", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 30f), Array.Empty())); RollingMonitorWindow = config.Bind("Auto-Tune.Monitor", "Rolling Window Size", 5, new ConfigDescription("Number of recent re-probe results held in the rolling buffer for averaging. Tier is\nthe most-common tier in the buffer; promotion requires 2 consecutive observations,\ndemotion requires 3 — biased to keep current tier rather than oscillate.", (AcceptableValueBase)(object)new AcceptableValueRange(3, 10), Array.Empty())); } } public static class AutoTuneProbe { private struct BwResult { public int Bytes; public long Ms; } [CompilerGenerated] private sealed class d__48 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public ZNetPeer serverPeer; private int 5__1; private float 5__2; private int 5__3; private int 5__4; private List 5__5; private int 5__6; private float 5__7; private int 5__8; private int 5__9; private Stopwatch 5__10; private float 5__11; private long 5__12; private long 5__13; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__48(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__5 = null; 5__10 = null; <>1__state = -2; } private bool MoveNext() { //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Expected O, but got Unknown //IL_03e2: Unknown result type (might be due to invalid IL or missing references) //IL_03ec: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__1 = AutoTuneConfig.ProbePingCount?.Value ?? 10; 5__2 = AutoTuneConfig.ProbePingTimeoutSeconds?.Value ?? 5f; 5__3 = AutoTuneConfig.ProbePingAbortMs?.Value ?? 2000; _latLastAborted = false; _latLastRawMs = new List(5__1); if (serverPeer != null && serverPeer.m_rpc != null) { 5__6 = NextSeq(); _pingInflight[5__6] = Stopwatch.StartNew(); if (TryInvoke(serverPeer, "FiresGhetto.AutoTune.Ping", 5__6)) { 5__7 = 0f; goto IL_014a; } goto IL_0172; } goto IL_01b5; case 1: <>1__state = -1; 5__7 += Time.unscaledDeltaTime; goto IL_014a; case 2: <>1__state = -1; goto IL_01b5; case 3: <>1__state = -1; 5__11 += Time.unscaledDeltaTime; goto IL_02b6; case 4: { <>1__state = -1; 5__10 = null; goto IL_0404; } IL_02b6: if (_pingInflight.ContainsKey(5__9) && 5__11 < 5__2) { <>2__current = null; <>1__state = 3; return true; } if (_pingInflight.ContainsKey(5__9)) { _pingInflight.Remove(5__9); 5__12 = 5__3; } else { if (!_pingResultsMs.TryGetValue(5__9, out 5__13)) { goto IL_0404; } _pingResultsMs.Remove(5__9); 5__12 = 5__13; } _latLastRawMs.Add(5__12); if (5__12 >= 5__3) { 5__4++; } if (5__4 >= 2) { _latLastAborted = true; _latLastTier = Tier.Low; _latLastMedianMs = (int)ComputeMedian(_latLastRawMs); _latLastP95Ms = 5__3; _latLastJitterMs = 5__3; return false; } <>2__current = (object)new WaitForSeconds(0.2f); <>1__state = 4; return true; IL_01b5: 5__4 = 0; 5__8 = 0; goto IL_0416; IL_0172: _pingInflight.Remove(5__6); _pingResultsMs.Remove(5__6); <>2__current = (object)new WaitForSeconds(0.3f); <>1__state = 2; return true; IL_014a: if (_pingInflight.ContainsKey(5__6) && 5__7 < 5__2) { <>2__current = null; <>1__state = 1; return true; } goto IL_0172; IL_0416: if (5__8 >= 5__1 || serverPeer == null || serverPeer.m_rpc == null || _probeAborted || (Object)(object)ZNet.instance == (Object)null) { break; } 5__9 = NextSeq(); 5__10 = Stopwatch.StartNew(); _pingInflight[5__9] = 5__10; if (!TryInvoke(serverPeer, "FiresGhetto.AutoTune.Ping", 5__9)) { _pingInflight.Remove(5__9); break; } 5__11 = 0f; goto IL_02b6; IL_0404: 5__8++; goto IL_0416; } if (_latLastRawMs.Count == 0) { _latLastAborted = true; _latLastTier = Tier.Low; _latLastMedianMs = 0; _latLastP95Ms = 0; _latLastJitterMs = 0; return false; } 5__5 = TrimWorst(_latLastRawMs, (_latLastRawMs.Count >= 5) ? 1 : 0); _latLastMedianMs = (int)ComputeMedian(5__5); _latLastP95Ms = (int)ComputeP95(5__5); _latLastJitterMs = ComputeIqrJitter(5__5); _latLastTier = ScoreNetTier(_latLastMedianMs, _latLastP95Ms, _latLastJitterMs); 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(); } } [CompilerGenerated] private sealed class d__45 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public ZNetPeer serverPeer; private float 5__1; private float 5__2; private float 5__3; private float 5__4; private int 5__5; private int 5__6; private string 5__7; private string 5__8; private Tier 5__9; private string 5__10; private bool 5__11; private int 5__12; private Tier 5__13; private Tier 5__14; private float 5__15; private bool 5__16; private Tier 5__17; private Tier 5__18; private Tier 5__19; private AutoTuneCache.CacheEntry 5__20; private FileNotFoundException 5__21; private TypeLoadException 5__22; private Exception 5__23; private List 5__24; private float 5__25; private float
5__26; private float 5__27; private float 5__28; private int 5__29; private float 5__30; private int 5__31; private List 5__32; private int 5__33; private int 5__34; private int 5__35; private Stopwatch 5__36; private bool 5__37; private float 5__38; private BwResult 5__39; private float 5__40; private float 5__41; private List.Enumerator <>s__42; private float 5__43; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__45(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__7 = null; 5__8 = null; 5__10 = null; 5__20 = null; 5__21 = null; 5__22 = null; 5__23 = null; 5__24 = null; 5__32 = null; 5__36 = null; <>s__42 = default(List.Enumerator); <>1__state = -2; } private bool MoveNext() { //IL_09ef: Unknown result type (might be due to invalid IL or missing references) //IL_09f9: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; _probeRunning = true; 5__1 = AutoTuneConfig.PlayerArrivalTimeoutSeconds?.Value ?? 180f; 5__2 = 0f; goto IL_0100; case 1: <>1__state = -1; 5__2 += Time.unscaledDeltaTime; goto IL_0100; case 2: <>1__state = -1; 5__4 += Time.unscaledDeltaTime; goto IL_0215; case 3: <>1__state = -1; if (_latLastAborted) { LoggerOptions.LogMessage("[AutoTune] Initial latency probe aborted (raw=[" + string.Join(",", _latLastRawMs) + "]) — defaulting to LOW tier, skipping bandwidth probe."); AutoTuneProbe.Finalize(5__10, 5__8, Tier.Low, _latLastMedianMs, serverPeer); return false; } 5__12 = _latLastMedianMs; 5__13 = _latLastTier; LoggerOptions.LogMessage(string.Format("[AutoTune] Latency: raw=[{0}] trimmed→ median={1}ms p95={2}ms iqrJitter={3}ms → {4}", string.Join(",", _latLastRawMs), 5__12, _latLastP95Ms, _latLastJitterMs, 5__13)); 5__14 = Tier.Medium; 5__24 = new List(300); 5__25 = 0f; goto IL_0692; case 4: <>1__state = -1;
5__26 = Time.unscaledDeltaTime; if (
5__26 > 0f &&
5__26 < 1f) { 5__24.Add(
5__26 * 1000f); } 5__25 +=
5__26; goto IL_0692; case 5: <>1__state = -1; 5__38 += Time.unscaledDeltaTime; goto IL_08d6; case 6: { <>1__state = -1; goto IL_0a09; } IL_08d6: if (_bwInflight.ContainsKey(5__35) && 5__38 < 5__30) { <>2__current = null; <>1__state = 5; return true; } if (_bwInflight.ContainsKey(5__35)) { _bwInflight.Remove(5__35); _bwRequested.Remove(5__35); 5__33++; } else if (_bwResults.TryGetValue(5__35, out 5__39)) { _bwResults.Remove(5__35); if (5__39.Ms > 0) { 5__40 = (float)5__39.Bytes / 1024f / ((float)5__39.Ms / 1000f); 5__32.Add(5__40); } } if (5__34 < 5__31 - 1) { <>2__current = (object)new WaitForSeconds(0.5f); <>1__state = 6; return true; } goto IL_0a09; IL_0100: if (!_playerArrivedInWorld && 5__2 < 5__1) { if (_probeAborted || (Object)(object)ZNet.instance == (Object)null) { ProbeAbort("aborted (disconnect/shutdown) while waiting for player arrival"); return false; } if (serverPeer == null || serverPeer.m_rpc == null) { ProbeAbort("peer dropped while waiting for player arrival"); return false; } <>2__current = null; <>1__state = 1; return true; } if (!_playerArrivedInWorld) { LoggerOptions.LogWarning($"[AutoTune] Game.m_playerInitialSpawn never fired within {5__1:0}s — proceeding with probe anyway, results may be unreliable."); } else { LoggerOptions.LogInfo($"[AutoTune] Player arrived after {5__2:0.0}s — settling before probe."); } 5__3 = AutoTuneConfig.ProbeStartDelaySeconds?.Value ?? 10f; 5__4 = 0f; goto IL_0215; IL_0a23: if (5__34 < 5__31 && serverPeer != null && serverPeer.m_rpc != null) { 5__35 = NextSeq(); 5__36 = Stopwatch.StartNew(); _bwInflight[5__35] = 5__36; _bwRequested[5__35] = 5__29; 5__37 = TryInvoke(serverPeer, "FiresGhetto.AutoTune.BwReq", 5__35, 5__29); if (5__37) { 5__38 = 0f; goto IL_08d6; } _bwInflight.Remove(5__35); _bwRequested.Remove(5__35); } if (5__32.Count > 0) { 5__41 = 5__32[0]; <>s__42 = 5__32.GetEnumerator(); try { while (<>s__42.MoveNext()) { 5__43 = <>s__42.Current; if (5__43 > 5__41) { 5__41 = 5__43; } } } finally { ((IDisposable)<>s__42).Dispose(); } <>s__42 = default(List.Enumerator); 5__15 = 5__41; 5__16 = true; LoggerOptions.LogMessage(string.Format("[AutoTune] Bandwidth: samples=[{0}] KB/s, peak={1:0} KB/s, timeouts={2}", string.Join(",", 5__32.ConvertAll((float v) => v.ToString("0"))), 5__15, 5__33)); } else if (5__33 > 0) { LoggerOptions.LogMessage($"[AutoTune] Bandwidth probe: all {5__33} samples timed out — treated as bandwidth-bad signal."); 5__15 = 0f; 5__16 = true; } 5__32 = null; break; IL_0692: if (5__25 < 5f) { <>2__current = null; <>1__state = 4; return true; } if (5__24.Count > 0) { 5__27 = MedianFloat(5__24); 5__28 = P95Float(5__24); 5__14 = ScoreFpsTier(5__27, 5__28); LoggerOptions.LogMessage($"[AutoTune] Frame time: median={5__27:0.0}ms p95={5__28:0.0}ms → {5__14}"); } 5__24 = null; 5__15 = -1f; 5__16 = false; if (5__13 == Tier.Low) { break; } 5__29 = AutoTuneConfig.ProbeBandwidthPayloadBytes?.Value ?? 32768; 5__30 = Mathf.Max(2f, (AutoTuneConfig.ProbePingTimeoutSeconds?.Value ?? 5f) * 1.5f); 5__31 = 3; 5__32 = new List(5__31); 5__33 = 0; 5__34 = 0; goto IL_0a23; IL_0a09: 5__36 = null; 5__34++; goto IL_0a23; IL_0215: if (5__4 < 5__3) { if (_probeAborted || (Object)(object)ZNet.instance == (Object)null) { ProbeAbort("aborted (disconnect/shutdown) during post-arrival settle"); return false; } if (serverPeer == null || serverPeer.m_rpc == null) { ProbeAbort("peer dropped during post-arrival settle"); return false; } <>2__current = null; <>1__state = 2; return true; } 5__5 = Mathf.Max(1, SystemInfo.processorCount); 5__6 = Mathf.Max(0, SystemInfo.systemMemorySize); 5__7 = SystemInfo.graphicsDeviceName ?? "unknown"; 5__8 = MakeHwHash(5__5, 5__6, 5__7); 5__9 = ScoreCpuTier(5__5, 5__6); 5__10 = MakeServerKey(serverPeer); 5__11 = AutoTuneConfig.RetuneOnEveryLogin?.Value ?? false; if (!5__11) { 5__20 = null; try { 5__20 = AutoTuneCache.TryGet(5__10, 5__8); } catch (FileNotFoundException ex) { 5__21 = ex; LoggerOptions.LogWarning("[AutoTune] Cache dep missing (" + (5__21.FileName ?? "Newtonsoft.Json") + "); running full probe instead of cached fast-path."); } catch (TypeLoadException ex2) { 5__22 = ex2; LoggerOptions.LogWarning("[AutoTune] Cache type load failed (" + 5__22.Message + "); running full probe."); } catch (Exception ex3) { 5__23 = ex3; LoggerOptions.LogWarning("[AutoTune] Cache lookup threw " + 5__23.GetType().Name + ": " + 5__23.Message + "; running full probe."); } if (5__20 != null) { LoggerOptions.LogInfo($"[AutoTune] Using cached tier for {5__10}: {5__20.Tier} (probed {(int)(DateTime.UtcNow - 5__20.TimestampUtc).TotalDays}d ago)"); AutoTuneState.SetClient(5__20.Tier, 5__20.PingMedianMs); ApplyClientTier(5__20.Tier); SendTierReport(serverPeer, 5__20.Tier, 5__20.PingMedianMs); _probeCompletedThisSession = true; _probeRunning = false; _sessionServerKey = 5__10; _sessionHwHash = 5__8; _sessionMachineTier = 5__9; _hasMachineTier = true; if ((Object)(object)FiresGhettoNetworkMod.Instance != (Object)null) { _rollingMonitorHandle = ((MonoBehaviour)FiresGhettoNetworkMod.Instance).StartCoroutine(RunRollingMonitor(serverPeer)); } return false; } 5__20 = null; } LoggerOptions.LogMessage($"[AutoTune] Starting probe — cores={5__5}, RAM={5__6}MB, GPU={5__7}"); _sessionServerKey = 5__10; _sessionHwHash = 5__8; <>2__current = DoLatencyProbe(serverPeer); <>1__state = 3; return true; } 5__17 = MinTier(5__9, 5__14); 5__18 = 5__13; 5__19 = ComputeFinalTier(5__17, 5__18, 5__15, 5__16); _sessionMachineTier = 5__17; _hasMachineTier = true; LoggerOptions.LogMessage(string.Format("[AutoTune] Final tier: machine={0} (cpu={1} fps={2}) latency={3} bw={4} → {5}", 5__17, 5__9, 5__14, 5__18, 5__16 ? (5__15.ToString("0") + "KB/s") : "n/a", 5__19)); AutoTuneProbe.Finalize(5__10, 5__8, 5__19, 5__12, serverPeer); if ((Object)(object)FiresGhettoNetworkMod.Instance != (Object)null) { _rollingMonitorHandle = ((MonoBehaviour)FiresGhettoNetworkMod.Instance).StartCoroutine(RunRollingMonitor(serverPeer)); } 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(); } } [CompilerGenerated] private sealed class d__49 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public ZNetPeer serverPeer; private float 5__1; private int 5__2; private float 5__3; private float 5__4; private Tier 5__5; private Tier 5__6; private Tier 5__7; private int 5__8; private Exception 5__9; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__49(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__9 = null; <>1__state = -2; } private bool MoveNext() { //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: { <>1__state = -1; ConfigEntry enableRollingMonitor = AutoTuneConfig.EnableRollingMonitor; if (enableRollingMonitor != null && !enableRollingMonitor.Value) { return false; } if (_rollingMonitorActive) { return false; } _rollingMonitorActive = true; 5__1 = AutoTuneConfig.RollingMonitorIntervalMinutes?.Value ?? 5f; 5__2 = AutoTuneConfig.RollingMonitorWindow?.Value ?? 5; 5__3 = 5__1 * 60f; _rollingTiers.Clear(); _rollingTiers.Enqueue(AutoTuneState.ClientTier); _pendingRollingTier = AutoTuneState.ClientTier; _pendingRollingObservations = 0; LoggerOptions.LogInfo($"[AutoTune] Rolling monitor started — re-probe every {5__1:0}min, window={5__2}"); goto IL_0581; } case 1: <>1__state = -1; 5__4 += 1f; if (_probeAborted) { _rollingMonitorActive = false; return false; } if ((Object)(object)ZNet.instance == (Object)null) { _rollingMonitorActive = false; return false; } if (serverPeer == null || serverPeer.m_rpc == null) { _rollingMonitorActive = false; return false; } if (!_probeCompletedThisSession) { _rollingMonitorActive = false; return false; } goto IL_01ca; case 2: { <>1__state = -1; if (_probeAborted || (Object)(object)ZNet.instance == (Object)null) { _rollingMonitorActive = false; return false; } if (_latLastAborted) { LoggerOptions.LogMessage(string.Format("[AutoTune] Rolling re-probe aborted (raw=[{0}]); leaving tier at {1}.", string.Join(",", _latLastRawMs), AutoTuneState.ClientTier)); } else { 5__5 = ComputeFinalTier(_sessionMachineTier, _latLastTier, -1f, bwProbeCompleted: false); _rollingTiers.Enqueue(5__5); while (_rollingTiers.Count > 5__2) { _rollingTiers.Dequeue(); } 5__6 = ComputeMode(_rollingTiers); 5__7 = AutoTuneState.ClientTier; if (5__6 != 5__7) { if (_pendingRollingTier == 5__6) { _pendingRollingObservations++; } else { _pendingRollingTier = 5__6; _pendingRollingObservations = 1; } 5__8 = ((5__6 > 5__7) ? 2 : 3); if (_pendingRollingObservations >= 5__8) { LoggerOptions.LogMessage(string.Format("[AutoTune] Rolling tier change: {0} → {1} ({2} consecutive obs; this probe: latency={3} median={4}ms; window=[{5}])", 5__7, 5__6, _pendingRollingObservations, _latLastTier, _latLastMedianMs, string.Join(",", _rollingTiers))); AutoTuneState.SetClient(5__6, _latLastMedianMs); ApplyClientTier(5__6); try { AutoTuneCache.Save(_sessionServerKey, 5__6, _latLastMedianMs, _sessionHwHash); } catch (Exception ex) { 5__9 = ex; LoggerOptions.LogWarning("[AutoTune] Cache save skipped (" + 5__9.GetType().Name + ": " + 5__9.Message + ")."); } SendTierReport(serverPeer, 5__6, _latLastMedianMs); _pendingRollingObservations = 0; } else { LoggerOptions.LogInfo(string.Format("[AutoTune] Rolling tier change pending: {0} → {1} (obs {2}/{3}; this probe: latency={4} median={5}ms; window=[{6}])", 5__7, 5__6, _pendingRollingObservations, 5__8, _latLastTier, _latLastMedianMs, string.Join(",", _rollingTiers))); } } else { _pendingRollingTier = 5__7; _pendingRollingObservations = 0; LoggerOptions.LogInfo(string.Format("[AutoTune] Rolling re-probe stable at {0} (this probe: latency={1} median={2}ms iqrJitter={3}ms; window=[{4}])", 5__6, _latLastTier, _latLastMedianMs, _latLastJitterMs, string.Join(",", _rollingTiers))); } } goto IL_0581; } IL_0581: if (_probeCompletedThisSession) { if (_probeAborted || (Object)(object)ZNet.instance == (Object)null) { _rollingMonitorActive = false; return false; } 5__4 = 0f; goto IL_01ca; } _rollingMonitorActive = false; return false; IL_01ca: if (5__4 < 5__3) { <>2__current = (object)new WaitForSeconds(1f); <>1__state = 1; return true; } if (_probeAborted || (Object)(object)ZNet.instance == (Object)null) { _rollingMonitorActive = false; return false; } <>2__current = DoLatencyProbe(serverPeer); <>1__state = 2; 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(); } } public const string RPC_PING = "FiresGhetto.AutoTune.Ping"; public const string RPC_PONG = "FiresGhetto.AutoTune.Pong"; public const string RPC_BW_REQ = "FiresGhetto.AutoTune.BwReq"; public const string RPC_BW_RESP = "FiresGhetto.AutoTune.BwResp"; public const string RPC_TIER_REPORT = "FiresGhetto.AutoTune.TierReport"; private static readonly Dictionary _pingInflight = new Dictionary(); private static readonly Dictionary _bwInflight = new Dictionary(); private static readonly Dictionary _bwRequested = new Dictionary(); private static readonly Dictionary _pingResultsMs = new Dictionary(); private static readonly Dictionary _bwResults = new Dictionary(); private static int _nextSeq = 1; private static bool _probeRunning; private static bool _probeCompletedThisSession; private static bool _playerArrivedInWorld; private static Tier _latLastTier; private static int _latLastMedianMs; private static int _latLastP95Ms; private static int _latLastJitterMs; private static List _latLastRawMs = new List(); private static bool _latLastAborted; private static Tier _sessionMachineTier = Tier.Medium; private static bool _hasMachineTier; private static string _sessionServerKey = string.Empty; private static string _sessionHwHash = string.Empty; private static readonly Queue _rollingTiers = new Queue(); private static bool _rollingMonitorActive; private static Tier _pendingRollingTier; private static int _pendingRollingObservations; private static Coroutine _probeCoroutineHandle; private static Coroutine _rollingMonitorHandle; private static bool _probeAborted; private static EventInfo _playerInitialSpawnEvent; private static Action _playerInitialSpawnHandler; private static bool _playerInitialSpawnEventResolved; public static void OnPeerConnected(ZNetPeer peer) { if (peer != null && peer.m_rpc != null) { try { peer.m_rpc.Register("FiresGhetto.AutoTune.Ping", (Action)OnRpcPing); peer.m_rpc.Register("FiresGhetto.AutoTune.BwReq", (Action)OnRpcBwReq); peer.m_rpc.Register("FiresGhetto.AutoTune.Pong", (Action)OnRpcPong); peer.m_rpc.Register("FiresGhetto.AutoTune.BwResp", (Action)OnRpcBwResp); peer.m_rpc.Register("FiresGhetto.AutoTune.TierReport", (Action)ServerAutoTune.OnTierReport); } catch (Exception ex) { LoggerOptions.LogWarning("[AutoTune] Failed to register probe RPCs on peer: " + ex.Message); return; } if ((!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer()) && !_probeCompletedThisSession && !_probeRunning && !((Object)(object)FiresGhettoNetworkMod.Instance == (Object)null) && AutoTuneConfig.EnableClientAutoTune != null && AutoTuneConfig.EnableClientAutoTune.Value) { _playerArrivedInWorld = false; SubscribePlayerArrival(); _probeAborted = false; _probeCoroutineHandle = ((MonoBehaviour)FiresGhettoNetworkMod.Instance).StartCoroutine(RunClientProbe(peer)); } } } private static EventInfo ResolvePlayerInitialSpawnEvent() { if (_playerInitialSpawnEventResolved) { return _playerInitialSpawnEvent; } _playerInitialSpawnEventResolved = true; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { Type type; try { type = assembly.GetType("Game", throwOnError: false); } catch { continue; } if (!(type == null)) { EventInfo @event = type.GetEvent("m_playerInitialSpawn", BindingFlags.Static | BindingFlags.Public); if (@event != null) { _playerInitialSpawnEvent = @event; return @event; } } } LoggerOptions.LogWarning("[AutoTune] Could not resolve Game.m_playerInitialSpawn via reflection — arrival wait will fall back to its safety timeout."); return null; } private static void SubscribePlayerArrival() { EventInfo eventInfo = ResolvePlayerInitialSpawnEvent(); if (eventInfo == null) { return; } if (_playerInitialSpawnHandler == null) { _playerInitialSpawnHandler = OnPlayerInitialSpawn; } try { eventInfo.RemoveEventHandler(null, _playerInitialSpawnHandler); eventInfo.AddEventHandler(null, _playerInitialSpawnHandler); } catch (Exception ex) { LoggerOptions.LogWarning("[AutoTune] Failed to subscribe to Game.m_playerInitialSpawn: " + ex.Message); } } private static void UnsubscribePlayerArrival() { EventInfo playerInitialSpawnEvent = _playerInitialSpawnEvent; if (playerInitialSpawnEvent == null || _playerInitialSpawnHandler == null) { return; } try { playerInitialSpawnEvent.RemoveEventHandler(null, _playerInitialSpawnHandler); } catch { } } private static void OnPlayerInitialSpawn() { _playerArrivedInWorld = true; UnsubscribePlayerArrival(); LoggerOptions.LogInfo("[AutoTune] Game.m_playerInitialSpawn fired — player is in the world."); } public static void OnPeerDisconnected() { _probeAborted = true; FiresGhettoNetworkMod instance = FiresGhettoNetworkMod.Instance; if ((Object)(object)instance != (Object)null) { if (_probeCoroutineHandle != null) { try { ((MonoBehaviour)instance).StopCoroutine(_probeCoroutineHandle); } catch { } _probeCoroutineHandle = null; } if (_rollingMonitorHandle != null) { try { ((MonoBehaviour)instance).StopCoroutine(_rollingMonitorHandle); } catch { } _rollingMonitorHandle = null; } } _probeRunning = false; _probeCompletedThisSession = false; _rollingMonitorActive = false; _rollingTiers.Clear(); _pendingRollingTier = Tier.Low; _pendingRollingObservations = 0; _pingInflight.Clear(); _bwInflight.Clear(); _bwRequested.Clear(); _pingResultsMs.Clear(); _bwResults.Clear(); _playerArrivedInWorld = false; UnsubscribePlayerArrival(); } private static void OnRpcPing(ZRpc rpc, int seq) { try { rpc.Invoke("FiresGhetto.AutoTune.Pong", new object[1] { seq }); } catch (Exception ex) { LoggerOptions.LogWarning("[AutoTune] Ping echo failed: " + ex.Message); } } private static void OnRpcBwReq(ZRpc rpc, int seq, int requestedBytes) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown int num = Mathf.Clamp(requestedBytes, 1024, 262144); try { ZPackage val = new ZPackage(); byte[] array = new byte[num]; for (int i = 0; i < num; i++) { array[i] = (byte)(i * 1103515245 + 12345 >> 8); } val.Write(array); rpc.Invoke("FiresGhetto.AutoTune.BwResp", new object[2] { seq, val }); } catch (Exception ex) { LoggerOptions.LogWarning("[AutoTune] Bandwidth echo failed: " + ex.Message); } } private static void OnRpcPong(ZRpc rpc, int seq) { if (_pingInflight.TryGetValue(seq, out var value)) { value.Stop(); _pingResultsMs[seq] = value.ElapsedMilliseconds; _pingInflight.Remove(seq); } } private static void OnRpcBwResp(ZRpc rpc, int seq, ZPackage pkg) { if (_bwInflight.TryGetValue(seq, out var value)) { value.Stop(); _bwRequested.TryGetValue(seq, out var value2); int num = 0; try { byte[] array = ((pkg != null) ? pkg.GetArray() : null); num = ((array != null) ? array.Length : value2); } catch { num = value2; } _bwResults[seq] = new BwResult { Bytes = num, Ms = value.ElapsedMilliseconds }; _bwInflight.Remove(seq); _bwRequested.Remove(seq); } } [IteratorStateMachine(typeof(d__45))] private static IEnumerator RunClientProbe(ZNetPeer serverPeer) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__45(0) { serverPeer = serverPeer }; } private static Tier ComputeFinalTier(Tier machineTier, Tier latencyTier, float bwKbPerSec, bool bwProbeCompleted) { if (machineTier == Tier.High) { return MinTier(machineTier, latencyTier); } Tier tier = MinTier(machineTier, latencyTier); if (!bwProbeCompleted || latencyTier == Tier.Low) { return tier; } Tier tier2 = ScoreBwTier(bwKbPerSec); if (tier2 >= tier) { return tier; } return (Tier)Math.Max(0, (int)(tier - 1)); } private static Tier ScoreBwTier(float kbPerSec) { if (kbPerSec >= 500f) { return Tier.High; } if (kbPerSec >= 200f) { return Tier.Medium; } return Tier.Low; } [IteratorStateMachine(typeof(d__48))] private static IEnumerator DoLatencyProbe(ZNetPeer serverPeer) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__48(0) { serverPeer = serverPeer }; } [IteratorStateMachine(typeof(d__49))] private static IEnumerator RunRollingMonitor(ZNetPeer serverPeer) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__49(0) { serverPeer = serverPeer }; } private static Tier ComputeMode(IEnumerable tiers) { int num = 0; int num2 = 0; int num3 = 0; using (IEnumerator enumerator = tiers.GetEnumerator()) { while (enumerator.MoveNext()) { switch (enumerator.Current) { case Tier.Low: num++; break; case Tier.Medium: num2++; break; case Tier.High: num3++; break; } } } int num4 = Math.Max(num, Math.Max(num2, num3)); bool flag = num == num4; bool flag2 = num2 == num4; bool flag3 = num3 == num4; if (flag && !flag2 && !flag3) { return Tier.Low; } if (flag2 && !flag && !flag3) { return Tier.Medium; } if (flag3 && !flag && !flag2) { return Tier.High; } Tier tier = ((!AutoTuneState.HasClientResult) ? Tier.Medium : AutoTuneState.ClientTier); if (tier == Tier.Low && flag) { return Tier.Low; } if (tier == Tier.Medium && flag2) { return Tier.Medium; } if (tier == Tier.High && flag3) { return Tier.High; } if (flag) { return Tier.Low; } if (flag2) { return Tier.Medium; } return Tier.High; } private static void Finalize(string serverKey, string hwHash, Tier tier, int pingMedianMs, ZNetPeer serverPeer) { AutoTuneState.SetClient(tier, pingMedianMs); try { AutoTuneCache.Save(serverKey, tier, pingMedianMs, hwHash); } catch (Exception ex) { LoggerOptions.LogWarning("[AutoTune] Cache save skipped (" + ex.GetType().Name + ": " + ex.Message + "); tier still applied in-memory."); } ApplyClientTier(tier); SendTierReport(serverPeer, tier, pingMedianMs); _probeCompletedThisSession = true; _probeRunning = false; } private static void ProbeAbort(string reason) { LoggerOptions.LogWarning("[AutoTune] Probe aborted: " + reason + " — defaulting to LOW tier."); AutoTuneState.SetClient(Tier.Low, 0); ApplyClientTier(Tier.Low); _probeCompletedThisSession = true; _probeRunning = false; } private static void ApplyClientTier(Tier tier) { try { NetworkingRatesGroup.ApplySendRates(); } catch (Exception ex) { LoggerOptions.LogWarning("[AutoTune] ApplySendRates failed: " + ex.Message); } try { NetworkingRatesGroup.ApplySendBufferSize(); } catch (Exception ex2) { LoggerOptions.LogWarning("[AutoTune] ApplySendBufferSize failed: " + ex2.Message); } try { NetworkingRatesGroup.ApplyRecvBufferSize(); } catch (Exception ex3) { LoggerOptions.LogWarning("[AutoTune] ApplyRecvBufferSize failed: " + ex3.Message); } try { NetworkingRatesGroup.ApplyRecvMaxMessageSize(); } catch (Exception ex4) { LoggerOptions.LogWarning("[AutoTune] ApplyRecvMaxMessageSize failed: " + ex4.Message); } LoggerOptions.LogMessage($"[AutoTune] Applied client tier {tier}"); } private static void SendTierReport(ZNetPeer serverPeer, Tier tier, int pingMedianMs) { if (serverPeer == null || serverPeer.m_rpc == null) { return; } try { serverPeer.m_rpc.Invoke("FiresGhetto.AutoTune.TierReport", new object[2] { (int)tier, pingMedianMs }); } catch (Exception ex) { LoggerOptions.LogWarning("[AutoTune] TierReport invoke failed: " + ex.Message); } } private static Tier ScoreCpuTier(int cores, int ramMb) { if (cores >= 8 && ramMb >= 16384) { return Tier.High; } if (cores >= 4 && ramMb >= 8192) { return Tier.Medium; } return Tier.Low; } private static Tier ScoreNetTier(int medianMs, int p95Ms, int jitterMs) { if (medianMs <= 60 && p95Ms <= 100 && jitterMs <= 20) { return Tier.High; } if (medianMs <= 120 && p95Ms <= 200 && jitterMs <= 50) { return Tier.Medium; } return Tier.Low; } private static Tier ScoreFpsTier(float medianMs, float p95Ms) { if (medianMs <= 16.7f && p95Ms <= 25f) { return Tier.High; } if (medianMs <= 33.4f && p95Ms <= 50f) { return Tier.Medium; } return Tier.Low; } private static Tier MinTier(Tier a, Tier b) { return (Tier)Mathf.Min((int)a, (int)b); } private static double ComputeMedian(List xs) { if (xs.Count == 0) { return 0.0; } List list = new List(xs); list.Sort(); int num = list.Count / 2; return (list.Count % 2 == 0) ? ((double)(list[num - 1] + list[num]) * 0.5) : ((double)list[num]); } private static double ComputeP95(List xs) { if (xs.Count == 0) { return 0.0; } List list = new List(xs); list.Sort(); int index = Mathf.Clamp((int)Math.Ceiling((double)list.Count * 0.95) - 1, 0, list.Count - 1); return list[index]; } private static int ComputeJitter(List xs) { if (xs.Count < 2) { return 0; } long num = xs[0]; long num2 = xs[0]; foreach (long x in xs) { if (x < num) { num = x; } if (x > num2) { num2 = x; } } return (int)(num2 - num); } private static int ComputeIqrJitter(List xs) { if (xs.Count < 4) { return ComputeJitter(xs); } List list = new List(xs); list.Sort(); int index = list.Count / 4; int num = list.Count * 3 / 4; if (num >= list.Count) { num = list.Count - 1; } return (int)(list[num] - list[index]); } private static List TrimWorst(List xs, int dropCount) { if (dropCount <= 0 || xs.Count <= dropCount) { return new List(xs); } List list = new List(xs); list.Sort(); return list.GetRange(0, list.Count - dropCount); } private static float MedianFloat(List xs) { if (xs.Count == 0) { return 0f; } List list = new List(xs); list.Sort(); int num = list.Count / 2; return (list.Count % 2 == 0) ? ((list[num - 1] + list[num]) * 0.5f) : list[num]; } private static float P95Float(List xs) { if (xs.Count == 0) { return 0f; } List list = new List(xs); list.Sort(); int index = Mathf.Clamp((int)Math.Ceiling((double)list.Count * 0.95) - 1, 0, list.Count - 1); return list[index]; } private static bool TryInvoke(ZNetPeer peer, string name, params object[] args) { try { peer.m_rpc.Invoke(name, args); return true; } catch (Exception ex) { LoggerOptions.LogWarning("[AutoTune] RPC '" + name + "' invoke failed: " + ex.Message); return false; } } private static int NextSeq() { int nextSeq = _nextSeq; _nextSeq++; if (_nextSeq <= 0) { _nextSeq = 1; } return nextSeq; } private static string MakeHwHash(int cores, int ramMb, string gpu) { int num = 17; num = num * 31 + cores; num = num * 31 + ramMb; return (num * 31 + (gpu ?? "").GetHashCode()).ToString("X8"); } private static string MakeServerKey(ZNetPeer serverPeer) { try { if (serverPeer != null && serverPeer.m_socket != null) { string endPointString = serverPeer.m_socket.GetEndPointString(); if (!string.IsNullOrEmpty(endPointString)) { return endPointString; } } } catch { } return "unknown-server"; } } [HarmonyPatch] public static class AutoTuneProbeHooks { [HarmonyPatch(typeof(ZNet), "OnNewConnection")] [HarmonyPostfix] public static void ZNet_OnNewConnection_Postfix(ZNetPeer peer) { AutoTuneProbe.OnPeerConnected(peer); } [HarmonyPatch(typeof(ZNet), "Disconnect")] [HarmonyPostfix] public static void ZNet_Disconnect_Postfix(ZNetPeer peer) { AutoTuneProbe.OnPeerDisconnected(); try { ServerAutoTune.ClearPeerReportedTier(peer?.m_uid ?? 0); } catch { } } [HarmonyPatch(typeof(ZNet), "Shutdown")] [HarmonyPostfix] public static void ZNet_Shutdown_Postfix() { AutoTuneProbe.OnPeerDisconnected(); } [HarmonyPatch(typeof(ZNet), "OnDestroy")] [HarmonyPostfix] public static void ZNet_OnDestroy_Postfix() { AutoTuneProbe.OnPeerDisconnected(); } } public enum Tier { Low, Medium, High } public struct TierPreset { public int SteamSendRateMinBytes; public int SteamSendRateMaxBytes; public int SteamSendBufferBytes; public int SteamRecvBufferBytes; public int SteamRecvMaxMessageBytes; public int ZoneLoadBatchSize; public bool EnablePrediction; public float SmoothingMaxInterval; public float SmoothingMinInterval; public int InstantiationBudgetMs; public int MaxInstancesPerFrame; public bool SafetyFallbackEnabled; public int SafetyFallbackThreshold; public UpdateRateOptions UpdateRate; public QueueSizeOptions QueueSize; public float ZDOThrottleDistance; public float AILODNearDistance; public float AILODFarDistance; public float AILODThrottleFactor; public float RpcAoIRadius; public int ExtendedZoneRadius; } public static class TierPresets { public static TierPreset For(Tier tier) { switch (tier) { case Tier.High: { TierPreset result = default(TierPreset); result.SteamSendRateMinBytes = 153600; result.SteamSendRateMaxBytes = 1048576; result.SteamSendBufferBytes = 2097152; result.SteamRecvBufferBytes = 8388608; result.SteamRecvMaxMessageBytes = 4194304; result.ZoneLoadBatchSize = 4; result.EnablePrediction = true; result.SmoothingMaxInterval = 0.15f; result.SmoothingMinInterval = 0f; result.InstantiationBudgetMs = 5; result.MaxInstancesPerFrame = 200; result.SafetyFallbackEnabled = true; result.SafetyFallbackThreshold = 5000; result.UpdateRate = UpdateRateOptions._150; result.QueueSize = QueueSizeOptions._48KB; result.ZDOThrottleDistance = 700f; result.AILODNearDistance = 150f; result.AILODFarDistance = 500f; result.AILODThrottleFactor = 0.5f; result.RpcAoIRadius = 384f; result.ExtendedZoneRadius = 2; return result; } case Tier.Medium: { TierPreset result = default(TierPreset); result.SteamSendRateMinBytes = 153600; result.SteamSendRateMaxBytes = 524288; result.SteamSendBufferBytes = 1048576; result.SteamRecvBufferBytes = 4194304; result.SteamRecvMaxMessageBytes = 4194304; result.ZoneLoadBatchSize = 2; result.EnablePrediction = false; result.SmoothingMaxInterval = 0.2f; result.SmoothingMinInterval = 0f; result.InstantiationBudgetMs = 3; result.MaxInstancesPerFrame = 100; result.SafetyFallbackEnabled = true; result.SafetyFallbackThreshold = 5000; result.UpdateRate = UpdateRateOptions._100; result.QueueSize = QueueSizeOptions._32KB; result.ZDOThrottleDistance = 500f; result.AILODNearDistance = 100f; result.AILODFarDistance = 300f; result.AILODThrottleFactor = 0.5f; result.RpcAoIRadius = 256f; result.ExtendedZoneRadius = 1; return result; } default: { TierPreset result = default(TierPreset); result.SteamSendRateMinBytes = 153600; result.SteamSendRateMaxBytes = 393216; result.SteamSendBufferBytes = 524288; result.SteamRecvBufferBytes = 2097152; result.SteamRecvMaxMessageBytes = 4194304; result.ZoneLoadBatchSize = 1; result.EnablePrediction = false; result.SmoothingMaxInterval = 0.3f; result.SmoothingMinInterval = 0f; result.InstantiationBudgetMs = 2; result.MaxInstancesPerFrame = 50; result.SafetyFallbackEnabled = true; result.SafetyFallbackThreshold = 8000; result.UpdateRate = UpdateRateOptions._75; result.QueueSize = QueueSizeOptions._vanilla; result.ZDOThrottleDistance = 350f; result.AILODNearDistance = 80f; result.AILODFarDistance = 200f; result.AILODThrottleFactor = 0.4f; result.RpcAoIRadius = 192f; result.ExtendedZoneRadius = 0; return result; } } } } public static class AutoTuneState { public static bool HasClientResult { get; private set; } public static Tier ClientTier { get; private set; } = Tier.Medium; public static int ClientPingMedianMs { get; private set; } public static bool HasServerResult { get; private set; } public static Tier ServerTier { get; private set; } = Tier.Medium; public static void SetClient(Tier tier, int pingMedianMs) { ClientTier = tier; ClientPingMedianMs = pingMedianMs; HasClientResult = true; } public static void ClearClient() { HasClientResult = false; ClientTier = Tier.Medium; ClientPingMedianMs = 0; } public static void SetServer(Tier tier) { ServerTier = tier; HasServerResult = true; } } public static class EffectiveConfig { public static int SteamSendRateMin() { if (IsDedicatedServerRuntime() && UseServerAutoTune()) { return TierPresets.For(AutoTuneState.ServerTier).SteamSendRateMinBytes; } if (UseClientAutoTune()) { return TierPresets.For(AutoTuneState.ClientTier).SteamSendRateMinBytes; } return SendRateMinFromEnum(FiresGhettoNetworkMod.ConfigSendRateMin.Value); } public static int SteamSendRateMax() { if (IsDedicatedServerRuntime() && UseServerAutoTune()) { return TierPresets.For(AutoTuneState.ServerTier).SteamSendRateMaxBytes; } if (UseClientAutoTune()) { return TierPresets.For(AutoTuneState.ClientTier).SteamSendRateMaxBytes; } return SendRateMaxFromEnum(FiresGhettoNetworkMod.ConfigSendRateMax.Value); } private static bool IsDedicatedServerRuntime() { if (ServerClientUtils.IsDedicatedServerDetected) { return true; } try { return (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsDedicated(); } catch { return false; } } public static int SteamRecvBufferBytes() { if (IsDedicatedServerRuntime() && UseServerAutoTune()) { return TierPresets.For(AutoTuneState.ServerTier).SteamRecvBufferBytes; } if (UseClientAutoTune()) { return TierPresets.For(AutoTuneState.ClientTier).SteamRecvBufferBytes; } return Math.Max(2097152, FiresGhettoNetworkMod.ConfigZPackageReceiveBufferSize?.Value ?? 2097152); } public static int SteamRecvMaxMessageBytes() { if (IsDedicatedServerRuntime() && UseServerAutoTune()) { return TierPresets.For(AutoTuneState.ServerTier).SteamRecvMaxMessageBytes; } if (UseClientAutoTune()) { return TierPresets.For(AutoTuneState.ClientTier).SteamRecvMaxMessageBytes; } return 4194304; } public static int SteamSendBufferBytes() { if (IsDedicatedServerRuntime() && UseServerAutoTune()) { return TierPresets.For(AutoTuneState.ServerTier).SteamSendBufferBytes; } if (UseClientAutoTune()) { return TierPresets.For(AutoTuneState.ClientTier).SteamSendBufferBytes; } return 1048576; } public static int ZoneLoadBatchSize() { if (UseClientAutoTune()) { return TierPresets.For(AutoTuneState.ClientTier).ZoneLoadBatchSize; } return Math.Max(1, FiresGhettoNetworkMod.ConfigZoneLoadBatchSize?.Value ?? 2); } public static int InstantiationBudgetMs() { if (UseClientAutoTune()) { return TierPresets.For(AutoTuneState.ClientTier).InstantiationBudgetMs; } int val = FiresGhettoNetworkMod.ConfigInstantiationBudgetMs?.Value ?? 3; return Math.Max(1, val); } public static int MaxInstancesPerFrame() { if (UseClientAutoTune()) { return TierPresets.For(AutoTuneState.ClientTier).MaxInstancesPerFrame; } int val = FiresGhettoNetworkMod.ConfigMaxInstancesPerFrame?.Value ?? 100; return Math.Max(10, val); } public static bool SafetyFallbackEnabled() { if (UseClientAutoTune()) { return TierPresets.For(AutoTuneState.ClientTier).SafetyFallbackEnabled; } return FiresGhettoNetworkMod.ConfigSafetyFallbackEnabled?.Value ?? true; } public static int SafetyFallbackThreshold() { if (UseClientAutoTune()) { return TierPresets.For(AutoTuneState.ClientTier).SafetyFallbackThreshold; } int val = FiresGhettoNetworkMod.ConfigSafetyFallbackThreshold?.Value ?? 5000; return Math.Max(100, val); } public static bool TimeSliceInstantiationEnabled() { return FiresGhettoNetworkMod.ConfigEnableTimeSliceInstantiation?.Value ?? true; } public static bool EnablePlayerPrediction() { if (UseClientAutoTune()) { bool enablePrediction = TierPresets.For(AutoTuneState.ClientTier).EnablePrediction; bool flag = AutoTuneState.ClientPingMedianMs >= 80; return enablePrediction && flag; } return PlayerPositionSyncPatches.ConfigEnablePlayerPrediction?.Value ?? false; } public static float SmoothingMaxInterval() { if (UseClientAutoTune()) { return TierPresets.For(AutoTuneState.ClientTier).SmoothingMaxInterval; } return PlayerPositionSyncPatches.ConfigSmoothingMaxInterval?.Value ?? 0.2f; } public static float SmoothingMinInterval() { if (UseClientAutoTune()) { return TierPresets.For(AutoTuneState.ClientTier).SmoothingMinInterval; } return PlayerPositionSyncPatches.ConfigSmoothingMinInterval?.Value ?? 0f; } public static UpdateRateOptions UpdateRate() { if (UseServerAutoTune()) { return TierPresets.For(AutoTuneState.ServerTier).UpdateRate; } return FiresGhettoNetworkMod.ConfigUpdateRate.Value; } public static QueueSizeOptions QueueSize() { if (UseServerAutoTune()) { return TierPresets.For(AutoTuneState.ServerTier).QueueSize; } return FiresGhettoNetworkMod.ConfigQueueSize.Value; } public static float ZDOThrottleDistance() { if (UseServerAutoTune()) { return TierPresets.For(AutoTuneState.ServerTier).ZDOThrottleDistance; } return FiresGhettoNetworkMod.ConfigZDOThrottleDistance.Value; } public static float AILODNearDistance() { if (UseServerAutoTune()) { return TierPresets.For(AutoTuneState.ServerTier).AILODNearDistance; } return FiresGhettoNetworkMod.ConfigAILODNearDistance.Value; } public static float AILODFarDistance() { if (UseServerAutoTune()) { return TierPresets.For(AutoTuneState.ServerTier).AILODFarDistance; } return FiresGhettoNetworkMod.ConfigAILODFarDistance.Value; } public static float AILODThrottleFactor() { if (UseServerAutoTune()) { return TierPresets.For(AutoTuneState.ServerTier).AILODThrottleFactor; } return FiresGhettoNetworkMod.ConfigAILODThrottleFactor.Value; } public static float RpcAoIRadius() { if (UseServerAutoTune()) { return TierPresets.For(AutoTuneState.ServerTier).RpcAoIRadius; } return FiresGhettoNetworkMod.ConfigRpcAoIRadius.Value; } public static int ExtendedZoneRadius() { if (UseServerAutoTune()) { return TierPresets.For(AutoTuneState.ServerTier).ExtendedZoneRadius; } return FiresGhettoNetworkMod.ConfigExtendedZoneRadius.Value; } private static bool UseClientAutoTune() { return AutoTuneConfig.EnableClientAutoTune != null && AutoTuneConfig.EnableClientAutoTune.Value && AutoTuneState.HasClientResult; } private static bool UseServerAutoTune() { return AutoTuneConfig.EnableServerAutoTune != null && AutoTuneConfig.EnableServerAutoTune.Value && AutoTuneState.HasServerResult; } private static int SendRateMinFromEnum(SendRateMinOptions opt) { return opt switch { SendRateMinOptions._1024KB => 1048576, SendRateMinOptions._768KB => 786432, SendRateMinOptions._512KB => 524288, SendRateMinOptions._256KB => 262144, _ => 153600, }; } private static int SendRateMaxFromEnum(SendRateMaxOptions opt) { return opt switch { SendRateMaxOptions._1024KB => 1048576, SendRateMaxOptions._768KB => 786432, SendRateMaxOptions._512KB => 524288, SendRateMaxOptions._256KB => 262144, _ => 153600, }; } } public static class ServerAutoTune { private struct ClientReport { public Tier Tier; public int PingMedianMs; public DateTime ReceivedUtc; } private const int ClientReportWindow = 20; private static readonly Queue _clientReports = new Queue(); private static DateTime _lastSummaryLogged = DateTime.MinValue; private static readonly TimeSpan SummaryInterval = TimeSpan.FromMinutes(15.0); private static readonly HashSet _peersWithTierReported = new HashSet(); public static bool HasPeerReportedTier(long peerUid) { return peerUid != 0L && _peersWithTierReported.Contains(peerUid); } public static void ClearPeerReportedTier(long peerUid) { if (peerUid != 0) { _peersWithTierReported.Remove(peerUid); } } public static void InitServerSide() { if (ServerClientUtils.IsDedicatedServerDetected) { int num = Mathf.Max(1, SystemInfo.processorCount); int num2 = Mathf.Max(0, SystemInfo.systemMemorySize); Tier tier = ScoreServerTier(num, num2); bool flag = AutoTuneConfig.EnableServerAutoTune?.Value ?? false; bool flag2 = AutoTuneConfig.LogServerSuggestions?.Value ?? true; if (flag) { AutoTuneState.SetServer(tier); LoggerOptions.LogMessage($"[AutoTune] SERVER auto-tune APPLIED: tier={tier} (cores={num}, RAM={num2}MB)."); } else if (flag2) { LogSelfTuneSuggestion(tier, num, num2); } } } private static Tier ScoreServerTier(int cores, int ramMb) { if (cores >= 8 && ramMb >= 16384) { return Tier.High; } if (cores >= 4 && ramMb >= 8192) { return Tier.Medium; } return Tier.Low; } private static void LogSelfTuneSuggestion(Tier tier, int cores, int ramMb) { TierPreset tierPreset = TierPresets.For(tier); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"[AutoTune] Server self-tune: {tier} (cores={cores}, RAM={ramMb}MB). 'Enable Server Auto-Tune' is OFF — settings unchanged. Suggested values:"); AppendDelta(stringBuilder, "Update Rate", FiresGhettoNetworkMod.ConfigUpdateRate.Value.ToString(), tierPreset.UpdateRate.ToString()); AppendDelta(stringBuilder, "Queue Size", FiresGhettoNetworkMod.ConfigQueueSize.Value.ToString(), tierPreset.QueueSize.ToString()); AppendDelta(stringBuilder, "ZDO Throttle Distance", $"{FiresGhettoNetworkMod.ConfigZDOThrottleDistance.Value:0}m", $"{tierPreset.ZDOThrottleDistance:0}m"); AppendDelta(stringBuilder, "AI LOD Near Distance", $"{FiresGhettoNetworkMod.ConfigAILODNearDistance.Value:0}m", $"{tierPreset.AILODNearDistance:0}m"); AppendDelta(stringBuilder, "AI LOD Far Distance", $"{FiresGhettoNetworkMod.ConfigAILODFarDistance.Value:0}m", $"{tierPreset.AILODFarDistance:0}m"); AppendDelta(stringBuilder, "AI LOD Throttle Factor", $"{FiresGhettoNetworkMod.ConfigAILODThrottleFactor.Value:0.00}", $"{tierPreset.AILODThrottleFactor:0.00}"); AppendDelta(stringBuilder, "RPC AoI Radius", $"{FiresGhettoNetworkMod.ConfigRpcAoIRadius.Value:0}m", $"{tierPreset.RpcAoIRadius:0}m"); AppendDelta(stringBuilder, "Extended Zone Radius", FiresGhettoNetworkMod.ConfigExtendedZoneRadius.Value.ToString(), tierPreset.ExtendedZoneRadius.ToString()); stringBuilder.Append("To apply: set 'Enable Server Auto-Tune' = true OR copy individual values into the config above."); LoggerOptions.LogMessage(stringBuilder.ToString()); } private static void AppendDelta(StringBuilder sb, string label, string current, string suggested) { if (current == suggested) { sb.AppendLine($" {label,-26} {current} (already matches)"); } else { sb.AppendLine($" {label,-26} {current} → {suggested}"); } } public static void OnTierReport(ZRpc rpc, int tierInt, int pingMedianMs) { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer()) { Tier tier = (Enum.IsDefined(typeof(Tier), tierInt) ? ((Tier)tierInt) : Tier.Low); _clientReports.Enqueue(new ClientReport { Tier = tier, PingMedianMs = pingMedianMs, ReceivedUtc = DateTime.UtcNow }); while (_clientReports.Count > 20) { _clientReports.Dequeue(); } long num = 0L; try { num = ZNet.instance.GetPeer(rpc)?.m_uid ?? 0; } catch { } if (num != 0) { _peersWithTierReported.Add(num); } LoggerOptions.LogInfo($"[AutoTune] Client reported tier={tier} ping={pingMedianMs}ms peer={num} (rolling window: {_clientReports.Count}/{20})"); if (DateTime.UtcNow - _lastSummaryLogged >= SummaryInterval) { LogClientSummary(); _lastSummaryLogged = DateTime.UtcNow; } } } private static void LogClientSummary() { ConfigEntry logServerSuggestions = AutoTuneConfig.LogServerSuggestions; if ((logServerSuggestions != null && !logServerSuggestions.Value) || _clientReports.Count == 0) { return; } int num = 0; int num2 = 0; int num3 = 0; long num4 = 0L; foreach (ClientReport clientReport in _clientReports) { switch (clientReport.Tier) { case Tier.Low: num++; break; case Tier.Medium: num2++; break; case Tier.High: num3++; break; } num4 += clientReport.PingMedianMs; } Tier tier = MedianTier(num, num2, num3); int num5 = (int)(num4 / Math.Max(1, _clientReports.Count)); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"[AutoTune] Client tier distribution (last {_clientReports.Count}): HIGH={num3} MED={num2} LOW={num}, avgPing={num5}ms"); stringBuilder.AppendLine($" Median client tier: {tier}"); ConfigEntry enableServerAutoTune = AutoTuneConfig.EnableServerAutoTune; if (enableServerAutoTune == null || !enableServerAutoTune.Value) { TierPreset tierPreset = TierPresets.For(tier); stringBuilder.AppendLine(" If you'd like the server tuned for the median client, suggested values:"); AppendDelta(stringBuilder, "Update Rate", FiresGhettoNetworkMod.ConfigUpdateRate.Value.ToString(), tierPreset.UpdateRate.ToString()); AppendDelta(stringBuilder, "Queue Size", FiresGhettoNetworkMod.ConfigQueueSize.Value.ToString(), tierPreset.QueueSize.ToString()); AppendDelta(stringBuilder, "ZDO Throttle Distance", $"{FiresGhettoNetworkMod.ConfigZDOThrottleDistance.Value:0}m", $"{tierPreset.ZDOThrottleDistance:0}m"); AppendDelta(stringBuilder, "AI LOD Far Distance", $"{FiresGhettoNetworkMod.ConfigAILODFarDistance.Value:0}m", $"{tierPreset.AILODFarDistance:0}m"); AppendDelta(stringBuilder, "RPC AoI Radius", $"{FiresGhettoNetworkMod.ConfigRpcAoIRadius.Value:0}m", $"{tierPreset.RpcAoIRadius:0}m"); AppendDelta(stringBuilder, "Extended Zone Radius", FiresGhettoNetworkMod.ConfigExtendedZoneRadius.Value.ToString(), tierPreset.ExtendedZoneRadius.ToString()); } LoggerOptions.LogMessage(stringBuilder.ToString()); } private static Tier MedianTier(int low, int med, int high) { int num = low + med + high; if (num == 0) { return Tier.Medium; } int num2 = num / 2; int num3 = 0; num3 += low; if (num3 > num2) { return Tier.Low; } num3 += med; if (num3 > num2) { return Tier.Medium; } return Tier.High; } } [HarmonyPatch] public static class ZoneLoadPatches { private const int CapMin = 5; private const int CapMax = 200; private const int ChunkSize = 1; private const int SafetyBudgetMultiplier = 3; private const int SafetyBudgetMaxMs = 16; [HarmonyReversePatch(/*Could not decode attribute arguments.*/)] [HarmonyPatch(typeof(ZNetScene), "CreateObjectsSorted")] public static void CallCreateObjectsSorted(ZNetScene self, List currentNearObjects, int maxCreatedPerFrame, ref int created) { throw new NotImplementedException("Harmony reverse patch failed for ZNetScene.CreateObjectsSorted"); } [HarmonyReversePatch(/*Could not decode attribute arguments.*/)] [HarmonyPatch(typeof(ZNetScene), "CreateDistantObjects")] public static void CallCreateDistantObjects(ZNetScene self, List objects, int maxCreatedPerFrame, ref int created) { throw new NotImplementedException("Harmony reverse patch failed for ZNetScene.CreateDistantObjects"); } [HarmonyPatch(typeof(ZNetScene), "CreateObjects")] [HarmonyPrefix] public static bool CreateObjects_TimeBudgetPrefix(ZNetScene __instance, List currentNearObjects, List currentDistantObjects) { try { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { return true; } } catch { return true; } if (!EffectiveConfig.TimeSliceInstantiationEnabled()) { return true; } int num = currentNearObjects?.Count ?? 0; int num2 = currentDistantObjects?.Count ?? 0; if (num == 0 && num2 == 0) { return false; } int num3 = EffectiveConfig.InstantiationBudgetMs(); int num4 = EffectiveConfig.MaxInstancesPerFrame(); if (EffectiveConfig.SafetyFallbackEnabled()) { int num5 = num + num2; if (num5 > EffectiveConfig.SafetyFallbackThreshold()) { int num6 = num3 * 3; if (num6 > 16) { num6 = 16; } num3 = num6; } } long num7 = num3 * Stopwatch.Frequency / 1000; Stopwatch stopwatch = Stopwatch.StartNew(); int created = 0; try { while (currentNearObjects != null && currentNearObjects.Count > 0 && stopwatch.ElapsedTicks < num7 && created < num4) { int num8 = created; int num9 = Math.Min(1, num4 - created); if (num9 <= 0) { break; } CallCreateObjectsSorted(__instance, currentNearObjects, num9, ref created); if (created == num8) { break; } } while (currentDistantObjects != null && currentDistantObjects.Count > 0 && stopwatch.ElapsedTicks < num7 && created < num4) { int num10 = created; int num11 = Math.Min(1, num4 - created); if (num11 <= 0) { break; } CallCreateDistantObjects(__instance, currentDistantObjects, num11, ref created); if (created == num10) { break; } } } catch (Exception ex) { LoggerOptions.LogWarning("[AutoTune] Time-slice CreateObjects threw: " + ex.Message); } return false; } public static int ScaleBatchCap(int vanillaCap) { try { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { return vanillaCap; } int num = EffectiveConfig.ZoneLoadBatchSize(); if (num <= 1) { return vanillaCap; } int num2 = vanillaCap * num; if (num2 > vanillaCap * 8) { num2 = vanillaCap * 8; } return num2; } catch { return vanillaCap; } } [HarmonyPatch(typeof(ZNetScene), "CreateObjects")] [HarmonyTranspiler] public static IEnumerable CreateObjects_BatchCapTranspiler(IEnumerable instructions) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown List list = new List(instructions); MethodInfo methodInfo = AccessTools.Method(typeof(ZoneLoadPatches), "ScaleBatchCap", (Type[])null, (Type[])null); if (methodInfo == null) { return list; } int num = 0; for (int i = 0; i < list.Count - 1; i++) { if (IsSmallIntLoad(list[i], out var value) && value >= 5 && value <= 200 && IsStLocZero(list[i + 1])) { list[i].opcode = OpCodes.Ldc_I4; list[i].operand = value; list.Insert(i + 1, new CodeInstruction(OpCodes.Call, (object)methodInfo)); i++; num++; } } if (num == 0) { LoggerOptions.LogWarning("[AutoTune] ZoneLoadBatch: no per-frame cap constant found in ZNetScene.CreateObjects — game IL may have changed; ZoneLoadBatchSize will be a no-op until a follow-up patch addresses the new IL shape."); } else { LoggerOptions.LogInfo($"[AutoTune] ZoneLoadBatch: patched {num} per-frame cap constant(s) (in-game cap + loading-screen cap) via runtime helper."); } return list; } private static bool IsStLocZero(CodeInstruction inst) { if (inst.opcode == OpCodes.Stloc_0) { return true; } if (inst.opcode == OpCodes.Stloc_S || inst.opcode == OpCodes.Stloc) { if (inst.operand is byte b) { return b == 0; } if (inst.operand is int num) { return num == 0; } if (inst.operand is LocalBuilder localBuilder) { return localBuilder.LocalIndex == 0; } if (inst.operand is LocalVariableInfo localVariableInfo) { return localVariableInfo.LocalIndex == 0; } } return false; } private static bool IsSmallIntLoad(CodeInstruction inst, out int value) { if (inst.opcode == OpCodes.Ldc_I4_0) { value = 0; return true; } if (inst.opcode == OpCodes.Ldc_I4_1) { value = 1; return true; } if (inst.opcode == OpCodes.Ldc_I4_2) { value = 2; return true; } if (inst.opcode == OpCodes.Ldc_I4_3) { value = 3; return true; } if (inst.opcode == OpCodes.Ldc_I4_4) { value = 4; return true; } if (inst.opcode == OpCodes.Ldc_I4_5) { value = 5; return true; } if (inst.opcode == OpCodes.Ldc_I4_6) { value = 6; return true; } if (inst.opcode == OpCodes.Ldc_I4_7) { value = 7; return true; } if (inst.opcode == OpCodes.Ldc_I4_8) { value = 8; return true; } if (inst.opcode == OpCodes.Ldc_I4 || inst.opcode == OpCodes.Ldc_I4_S) { if (inst.operand is int num) { value = num; return true; } if (inst.operand is sbyte b) { value = b; return true; } if (inst.operand is byte b2) { value = b2; return true; } if (inst.operand is short num2) { value = num2; return true; } } value = 0; return false; } } }