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 BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
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.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.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.0.1")]
[BepInProcess("valheim.exe")]
[BepInProcess("valheim_server.exe")]
public class FiresGhettoNetworkMod : BaseUnityPlugin
{
[CompilerGenerated]
private sealed class <RegisterDummyRpcWhenReady>d__32 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public FiresGhettoNetworkMod <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <RegisterDummyRpcWhenReady>d__32(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;
}
ZRoutedRpc.instance.Register("ForceUpdateZDO", (Action<long>)delegate
{
});
((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.0.1";
public static ConfigEntry<LogLevel> ConfigLogLevel;
public static ConfigEntry<bool> ConfigEnableCompression;
public static ConfigEntry<UpdateRateOptions> ConfigUpdateRate;
public static ConfigEntry<SendRateMinOptions> ConfigSendRateMin;
public static ConfigEntry<SendRateMaxOptions> ConfigSendRateMax;
public static ConfigEntry<QueueSizeOptions> ConfigQueueSize;
public static ConfigEntry<ForceCrossplayOptions> ConfigForceCrossplay;
public static ConfigEntry<int> ConfigPlayerLimit;
public static ConfigEntry<bool> ConfigEnableShipFixes;
public static ConfigEntry<bool> ConfigEnableServerSideShipSimulation;
public static ConfigEntry<int> ConfigExtendedZoneRadius;
public static ConfigEntry<bool> ConfigEnableZDOThrottling;
public static ConfigEntry<float> ConfigZDOThrottleDistance;
public static ConfigEntry<bool> ConfigEnableAILOD;
public static ConfigEntry<float> ConfigAILODNearDistance;
public static ConfigEntry<float> ConfigAILODFarDistance;
public static ConfigEntry<float> ConfigAILODThrottleFactor;
public static ConfigEntry<int> ConfigZoneLoadBatchSize;
public static ConfigEntry<int> ConfigZPackageReceiveBufferSize;
public static ConfigEntry<bool> ConfigEnableServerAuthority;
internal static Harmony Harmony { get; private set; }
private void Awake()
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_0010: Expected O, but got Unknown
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();
LoggerOptions.LogInfo(ServerClientUtils.IsDedicatedServerDetected ? "Running on DEDICATED SERVER — disabling unsafe client-only features." : "Running on CLIENT — all features enabled.");
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 });
PlayerPositionSyncPatches.Init(((BaseUnityPlugin)this).Config);
Harmony.PatchAll(typeof(CompressionGroup));
Harmony.PatchAll(typeof(NetworkingRatesGroup));
Harmony.PatchAll(typeof(DedicatedServerGroup));
Harmony.PatchAll(typeof(ShipFixesGroup));
Harmony.PatchAll(typeof(ZDOMemoryManager));
Harmony.PatchAll(typeof(PlayerPositionSyncPatches));
bool flag = (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer();
if (flag && ConfigEnableServerAuthority.Value)
{
Harmony.PatchAll(typeof(ServerAuthorityPatches));
Harmony.PatchAll(typeof(MonsterAIPatches));
Harmony.PatchAll(typeof(ZDOThrottlingPatches));
Harmony.PatchAll(typeof(AILODPatches));
LoggerOptions.LogInfo("Server-authority patches enabled (zone loading, ZDO ownership, AI, events, ZDO throttling, AI LOD, etc.)");
}
else if (!flag)
{
LoggerOptions.LogInfo("Server-authority patches ignored on client (server-only feature).");
}
else
{
LoggerOptions.LogInfo("Server-authority patches disabled via config.");
}
((BaseUnityPlugin)this).Logger.LogInfo((object)"FiresGhettoNetworkMod v1.0.1 loaded.");
}
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_0105: Unknown result type (might be due to invalid IL or missing references)
//IL_010f: Expected O, but got Unknown
//IL_0170: Unknown result type (might be due to invalid IL or missing references)
//IL_017a: Expected O, but got Unknown
//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
//IL_01ab: Expected O, but got Unknown
//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
//IL_0208: Expected O, but got Unknown
//IL_025b: Unknown result type (might be due to invalid IL or missing references)
//IL_0265: Expected O, but got Unknown
//IL_0298: Unknown result type (might be due to invalid IL or missing references)
//IL_02a2: Expected O, but got Unknown
//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
//IL_02df: Expected O, but got Unknown
//IL_030e: Unknown result type (might be due to invalid IL or missing references)
//IL_0318: Expected O, but got Unknown
ConfigLogLevel = ((BaseUnityPlugin)this).Config.Bind<LogLevel>("General", "Log Level", LogLevel.Message, "Controls verbosity in BepInEx log.");
ConfigEnableCompression = ((BaseUnityPlugin)this).Config.Bind<bool>("Networking", "Enable Compression", true, "Enable ZSTD network compression (highly recommended).");
ConfigUpdateRate = ((BaseUnityPlugin)this).Config.Bind<UpdateRateOptions>("Networking", "Update Rate", UpdateRateOptions._100, "Server update frequency. Lower = less bandwidth.");
ConfigSendRateMin = ((BaseUnityPlugin)this).Config.Bind<SendRateMinOptions>("Networking.Steamworks", "Send Rate Min", SendRateMinOptions._256KB, "Minimum send rate Steam will attempt.");
ConfigSendRateMax = ((BaseUnityPlugin)this).Config.Bind<SendRateMaxOptions>("Networking.Steamworks", "Send Rate Max", SendRateMaxOptions._1024KB, "Maximum send rate Steam will attempt.");
ConfigQueueSize = ((BaseUnityPlugin)this).Config.Bind<QueueSizeOptions>("Networking", "Queue Size", QueueSizeOptions._32KB, "Send queue size. Higher helps high-player servers.");
ConfigForceCrossplay = ((BaseUnityPlugin)this).Config.Bind<ForceCrossplayOptions>("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<int>("Dedicated Server", "Player Limit", 10, new ConfigDescription("Max players on dedicated server. Requires restart.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 127), Array.Empty<object>()));
ConfigEnableShipFixes = ((BaseUnityPlugin)this).Config.Bind<bool>("Ship Fixes", "Enable Universal Ship Fixes", true, "Apply permanent autopilot + jitter fixes to ALL ships.");
ConfigEnableServerSideShipSimulation = ((BaseUnityPlugin)this).Config.Bind<bool>("Ship Fixes", "Server-Side Ship Simulation", true, "Server authoritatively simulates ship physics.");
ConfigEnableServerAuthority = ((BaseUnityPlugin)this).Config.Bind<bool>("Server Authority", "Enable Server-Side Simulation", true, new ConfigDescription("Makes the server fully authoritative over zones, ZDO ownership, monster AI, events, etc. (does NOT override your existing ship fixes).\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<object>()));
ConfigExtendedZoneRadius = ((BaseUnityPlugin)this).Config.Bind<int>("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<int>(0, 3), Array.Empty<object>()));
ConfigEnableZDOThrottling = ((BaseUnityPlugin)this).Config.Bind<bool>("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<float>("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<float>(0f, 1000f), Array.Empty<object>()));
ConfigEnableAILOD = ((BaseUnityPlugin)this).Config.Bind<bool>("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<float>("Server Authority", "AI LOD Near Distance", 100f, new ConfigDescription("Full-speed AI within this range (meters).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(50f, 200f), Array.Empty<object>()));
ConfigAILODFarDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Server Authority", "AI LOD Far Distance", 300f, new ConfigDescription("Beyond this distance, AI is throttled (meters).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(200f, 600f), Array.Empty<object>()));
ConfigAILODThrottleFactor = ((BaseUnityPlugin)this).Config.Bind<float>("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<float>(0.25f, 0.75f), Array.Empty<object>()));
ZDOMemoryManager.ConfigMaxZDOs = ((BaseUnityPlugin)this).Config.Bind<int>("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<int>(0, 1000000), Array.Empty<object>()));
ConfigEntryBase[] array = (ConfigEntryBase[])(object)new ConfigEntryBase[19]
{
(ConfigEntryBase)ConfigLogLevel,
(ConfigEntryBase)ConfigEnableCompression,
(ConfigEntryBase)ConfigUpdateRate,
(ConfigEntryBase)ConfigSendRateMin,
(ConfigEntryBase)ConfigSendRateMax,
(ConfigEntryBase)ConfigQueueSize,
(ConfigEntryBase)ConfigForceCrossplay,
(ConfigEntryBase)ConfigPlayerLimit,
(ConfigEntryBase)ConfigEnableShipFixes,
(ConfigEntryBase)ConfigEnableServerSideShipSimulation,
(ConfigEntryBase)ConfigEnableServerAuthority,
(ConfigEntryBase)ConfigExtendedZoneRadius,
(ConfigEntryBase)ConfigEnableZDOThrottling,
(ConfigEntryBase)ConfigZDOThrottleDistance,
(ConfigEntryBase)ConfigEnableAILOD,
(ConfigEntryBase)ConfigAILODNearDistance,
(ConfigEntryBase)ConfigAILODFarDistance,
(ConfigEntryBase)ConfigAILODThrottleFactor,
(ConfigEntryBase)ZDOMemoryManager.ConfigMaxZDOs
};
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(<RegisterDummyRpcWhenReady>d__32))]
private IEnumerator RegisterDummyRpcWhenReady()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <RegisterDummyRpcWhenReady>d__32(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("100% - 20 updates/sec [default]")]
_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 [default]")]
_1024KB,
[Description("768 KB/s | 6 Mbit/s")]
_768KB,
[Description("512 KB/s | 4 Mbit/s")]
_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), "FixedUpdate")]
[HarmonyPrefix]
public static bool FixedUpdate_Prefix(Character __instance)
{
//IL_0087: Unknown result type (might be due to invalid IL or missing references)
//IL_0093: Unknown result type (might be due to invalid IL or missing references)
if (!ZNet.instance.IsServer() || !FiresGhettoNetworkMod.ConfigEnableAILOD.Value)
{
return true;
}
if (!__instance.IsPlayer())
{
Tameable component = ((Component)__instance).GetComponent<Tameable>();
if (component == null || !component.IsTamed())
{
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 && Time.time % (1f / FiresGhettoNetworkMod.ConfigAILODThrottleFactor.Value) > Time.fixedDeltaTime)
{
return false;
}
return true;
}
}
return true;
}
}
[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<ISocket, SocketStatus> peerStatus = new Dictionary<ISocket, SocketStatus>();
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<bool> 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<int>("FiresGhetto.CompressionVersion", (Action<ZRpc, int>)RPC_CompressionVersion);
peer.m_rpc.Register<bool>("FiresGhetto.CompressionEnabled", (Action<ZRpc, bool>)RPC_CompressionEnabled);
peer.m_rpc.Register<bool>("FiresGhetto.CompressedStarted", (Action<ZRpc, bool>)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<byte[]> ___m_sendQueue, ZSteamSocket __instance)
{
if (compressor == null || !CompressionStatus.GetSendCompressionStarted((ISocket)(object)__instance))
{
return true;
}
___m_sendQueue = new Queue<byte[]>(___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<ZNetPeer>)AccessTools.Field(typeof(ZRoutedRpc), "m_peers").GetValue(ZRoutedRpc.instance))?.FirstOrDefault((Func<ZNetPeer, bool>)((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.");
ServerClientUtils.Detect();
isDedicatedDetected = ServerClientUtils.IsDedicatedServerDetected;
if (isDedicatedDetected)
{
LoggerOptions.LogInfo("Running as dedicated server detected (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<CodeInstruction> OverridePlayerLimit(IEnumerable<CodeInstruction> instructions)
{
//IL_008d: Unknown result type (might be due to invalid IL or missing references)
//IL_0097: Expected O, but got Unknown
if (!isDedicatedDetected)
{
return instructions;
}
List<CodeInstruction> list = new List<CodeInstruction>(instructions);
for (int i = 0; i < list.Count; i++)
{
if (list[i].opcode == OpCodes.Ldc_I4_S && (sbyte)list[i].operand == 10)
{
LoggerOptions.LogInfo($"Overriding player limit: 10 → {FiresGhettoNetworkMod.ConfigPlayerLimit.Value}");
list[i] = new CodeInstruction(OpCodes.Ldc_I4_S, (object)(sbyte)FiresGhettoNetworkMod.ConfigPlayerLimit.Value);
}
}
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
{
[HarmonyPatch(typeof(RandEventSystem), "FixedUpdate")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> RandEventSystem_FixedUpdate_Transpiler(IEnumerable<CodeInstruction> instructions)
{
//IL_0003: Unknown result type (might be due to invalid IL or missing references)
//IL_0009: Expected O, but got Unknown
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Expected O, but got Unknown
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_0061: Expected O, but got Unknown
//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
//IL_00b3: Expected O, but got Unknown
//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
//IL_00d6: Expected O, but got Unknown
CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null);
val.MatchForward(false, (CodeMatch[])(object)new CodeMatch[2]
{
new CodeMatch((OpCode?)OpCodes.Ldsfld, (object)AccessTools.Field(typeof(Player), "m_localPlayer"), (string)null),
new CodeMatch((OpCode?)OpCodes.Callvirt, (object)AccessTools.Method(typeof(Object), "op_Implicit", (Type[])null, (Type[])null), (string)null)
});
if (val.IsInvalid)
{
LoggerOptions.LogWarning("RandEventSystem transpiler failed to find m_localPlayer check");
return instructions;
}
val.RemoveInstructions(2);
val.Insert((CodeInstruction[])(object)new CodeInstruction[2]
{
new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(Player), "GetAllPlayers", (Type[])null, (Type[])null)),
new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(MonsterAIPatches), "HasAnyPlayerNearby", (Type[])null, (Type[])null))
});
return val.InstructionEnumeration();
}
[HarmonyPatch(typeof(SpawnSystem), "UpdateSpawning")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> SpawnSystem_UpdateSpawning_Transpiler(IEnumerable<CodeInstruction> instructions)
{
//IL_0003: Unknown result type (might be due to invalid IL or missing references)
//IL_0009: Expected O, but got Unknown
//IL_0032: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Expected O, but got Unknown
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Expected O, but got Unknown
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Expected O, but got Unknown
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
//IL_0089: Expected O, but got Unknown
//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
//IL_00d7: Expected O, but got Unknown
//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
//IL_0102: Expected O, but got Unknown
CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null);
val.MatchForward(false, (CodeMatch[])(object)new CodeMatch[4]
{
new CodeMatch((OpCode?)OpCodes.Ldsfld, (object)AccessTools.Field(typeof(Player), "m_localPlayer"), (string)null),
new CodeMatch((OpCode?)OpCodes.Ldnull, (object)null, (string)null),
new CodeMatch((OpCode?)OpCodes.Call, (object)AccessTools.Method(typeof(Object), "op_Equality", (Type[])null, (Type[])null), (string)null),
new CodeMatch((OpCode?)OpCodes.Brfalse, (object)null, (string)null)
});
if (val.IsInvalid)
{
LoggerOptions.LogWarning("SpawnSystem transpiler failed to find m_localPlayer check");
return instructions;
}
val.RemoveInstructions(3);
val.SetInstructionAndAdvance(new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(Player), "GetAllPlayers", (Type[])null, (Type[])null)));
val.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1]
{
new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(MonsterAIPatches), "HasAnyPlayerNearby", (Type[])null, (Type[])null))
});
val.SetOpcodeAndAdvance(OpCodes.Brfalse);
return val.InstructionEnumeration();
}
private static bool HasAnyPlayerNearby(List<Player> allPlayers)
{
//IL_003a: 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_0055: 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_005f: Unknown result type (might be due to invalid IL or missing references)
if (allPlayers == null || allPlayers.Count == 0)
{
return false;
}
foreach (Player allPlayer in allPlayers)
{
if ((Object)(object)allPlayer != (Object)null && ZNetScene.InActiveArea(ZoneSystem.GetZone(((Component)allPlayer).transform.position), ZoneSystem.GetZone((float)ZoneSystem.instance.m_activeArea * 64f * Vector3.one)))
{
return true;
}
}
return false;
}
}
[HarmonyPatch]
public static class NetworkingRatesGroup
{
private const int VANILLA_QUEUE_SIZE = 10240;
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 sendRateValue = GetSendRateValue(FiresGhettoNetworkMod.ConfigSendRateMin.Value);
int sendRateValue2 = GetSendRateValue(FiresGhettoNetworkMod.ConfigSendRateMax.Value);
SetSteamConfig("k_ESteamNetworkingConfig_SendRateMin", sendRateValue);
SetSteamConfig("k_ESteamNetworkingConfig_SendRateMax", sendRateValue2);
LoggerOptions.LogMessage($"Steam send rates applied: Min {sendRateValue / 1024} KB/s, Max {sendRateValue2 / 1024} KB/s");
}
}
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 (FiresGhettoNetworkMod.ConfigUpdateRate.Value)
{
case UpdateRateOptions._75:
dt *= 0.75f;
break;
case UpdateRateOptions._50:
dt *= 0.5f;
break;
}
}
[HarmonyPatch(typeof(ZNet), "Start")]
[HarmonyPostfix]
private static void EnsureRatesOnStart()
{
ApplySendRates();
}
private static void SetSteamConfig(string enumMemberName, int value)
{
IntPtr intPtr = IntPtr.Zero;
try
{
IEnumerable<Type> source = AppDomain.CurrentDomain.GetAssemblies().SelectMany(delegate(Assembly a)
{
try
{
return a.GetTypes();
}
catch
{
return Array.Empty<Type>();
}
});
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();
}
[HarmonyPatch(typeof(ZSteamSocket), "GetSendQueueSize")]
[HarmonyPostfix]
private static void AdjustSteamQueueSize(ref int __result)
{
QueueSizeOptions value = FiresGhettoNetworkMod.ConfigQueueSize.Value;
if (1 == 0)
{
}
int num = value switch
{
QueueSizeOptions._80KB => 81920,
QueueSizeOptions._64KB => 65536,
QueueSizeOptions._48KB => 49152,
QueueSizeOptions._32KB => 32768,
_ => 10240,
};
if (1 == 0)
{
}
int num2 = num;
if (num2 != 10240)
{
int num3 = 10240 - num2;
__result += num3;
if (__result < 0)
{
__result = 0;
}
}
}
}
[HarmonyPatch]
public static class PlayerPositionSyncPatches
{
private class PlayerPredictionData
{
public Vector3 lastPos;
public Quaternion lastRot;
public Vector3 velocity;
public float lastUpdateTime;
public bool hasData = false;
}
public static ConfigEntry<bool> ConfigEnablePlayerPositionBoost;
public static ConfigEntry<float> ConfigPlayerPositionUpdateMultiplier;
public static ConfigEntry<bool> ConfigEnableClientInterpolation;
public static ConfigEntry<bool> ConfigEnablePlayerPrediction;
private static Dictionary<long, PlayerPredictionData> predictionData = new Dictionary<long, PlayerPredictionData>();
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
ConfigEnablePlayerPositionBoost = config.Bind<bool>("Player Sync", "Enable High-Frequency Position Updates", true, "Boosts server send rate for player positions to reduce floaty movement/desync between players.");
ConfigPlayerPositionUpdateMultiplier = config.Bind<float>("Player Sync", "Position Update Multiplier", 2.5f, new ConfigDescription("Multiplier for player position sync priority (1.0 = vanilla, 2.5 = recommended). Higher = smoother on mixed PCs.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f), Array.Empty<object>()));
ConfigEnableClientInterpolation = config.Bind<bool>("Player Sync", "Enable Client-Side Interpolation", true, "Smooths received player positions on clients to eliminate jitter on high-ping/low-end PCs.");
ConfigEnablePlayerPrediction = config.Bind<bool>("Player Sync", "Enable Client-Side Prediction", true, "Predicts other players' movement between network updates for ultra-smooth feel (especially in combat on high ping).\nCLIENT-ONLY — no server impact.");
}
[HarmonyPatch(typeof(ZDOMan), "ServerSortSendZDOS")]
[HarmonyPrefix]
public static void ServerSortSendZDOS_Prefix(List<ZDO> objects, Vector3 refPos)
{
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
if (!ConfigEnablePlayerPositionBoost.Value || !ZNet.instance.IsServer())
{
return;
}
float value = ConfigPlayerPositionUpdateMultiplier.Value;
foreach (ZDO @object in objects)
{
@object.m_tempSortValue = Vector3.Distance(@object.GetPosition(), refPos);
if (IsPlayerZDO(@object))
{
@object.m_tempSortValue -= 150f * value;
}
}
}
private static bool IsPlayerZDO(ZDO zdo)
{
if (zdo == null)
{
return false;
}
return zdo.GetString(StringExtensionMethods.GetStableHashCode("playerName"), "").Length > 0;
}
[HarmonyPatch(typeof(ZNetView), "Deserialize")]
[HarmonyPostfix]
public static void Deserialize_Postfix(ZNetView __instance, ZPackage pkg)
{
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Invalid comparison between Unknown and I4
//IL_009c: 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_00a3: Unknown result type (might be due to invalid IL or missing references)
//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
//IL_013e: Unknown result type (might be due to invalid IL or missing references)
//IL_013f: Unknown result type (might be due to invalid IL or missing references)
//IL_0146: Unknown result type (might be due to invalid IL or missing references)
//IL_0147: 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_0125: Unknown result type (might be due to invalid IL or missing references)
//IL_012a: Unknown result type (might be due to invalid IL or missing references)
//IL_0131: Unknown result type (might be due to invalid IL or missing references)
//IL_0136: Unknown result type (might be due to invalid IL or missing references)
if ((!ConfigEnableClientInterpolation.Value && !ConfigEnablePlayerPrediction.Value) || (Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || (int)ZNet.GetConnectionStatus() != 2)
{
return;
}
ZDO val = ((__instance != null) ? __instance.GetZDO() : null);
if (val == null || !IsPlayerZDO(val))
{
return;
}
long owner = val.GetOwner();
if (owner == ZNet.GetUID())
{
return;
}
Vector3 position = val.GetPosition();
Quaternion rotation = val.GetRotation();
if (!predictionData.TryGetValue(owner, out var value))
{
value = new PlayerPredictionData
{
lastPos = position,
lastRot = rotation,
lastUpdateTime = Time.time,
hasData = true
};
predictionData[owner] = value;
return;
}
float num = Time.time - value.lastUpdateTime;
if (num > 0f && value.hasData)
{
value.velocity = (position - value.lastPos) / num;
}
value.lastPos = position;
value.lastRot = rotation;
value.lastUpdateTime = Time.time;
value.hasData = true;
}
[HarmonyPatch(typeof(Player), "LateUpdate")]
[HarmonyPostfix]
public static void Player_LateUpdate_Postfix(Player __instance)
{
//IL_003c: Unknown result type (might be due to invalid IL or missing references)
//IL_0042: Invalid comparison between Unknown and I4
//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
//IL_00f3: 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_010b: Unknown result type (might be due to invalid IL or missing references)
//IL_0110: Unknown result type (might be due to invalid IL or missing references)
//IL_0117: Unknown result type (might be due to invalid IL or missing references)
//IL_014e: 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_015e: Unknown result type (might be due to invalid IL or missing references)
//IL_0168: Unknown result type (might be due to invalid IL or missing references)
//IL_016d: Unknown result type (might be due to invalid IL or missing references)
//IL_0172: Unknown result type (might be due to invalid IL or missing references)
//IL_0180: 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_0189: Unknown result type (might be due to invalid IL or missing references)
//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer || (Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || (int)ZNet.GetConnectionStatus() != 2)
{
return;
}
ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
if ((Object)(object)component == (Object)null || !component.IsValid())
{
return;
}
ZDO zDO = component.GetZDO();
if (zDO == null || !IsPlayerZDO(zDO))
{
return;
}
long owner = zDO.GetOwner();
if (predictionData.TryGetValue(owner, out var value) && value.hasData)
{
if (ConfigEnablePlayerPrediction.Value)
{
float num = Time.deltaTime * 1.5f;
Vector3 val = value.lastPos + value.velocity * num;
((Component)__instance).transform.position = Vector3.Lerp(((Component)__instance).transform.position, val, 0.8f);
}
if (ConfigEnableClientInterpolation.Value)
{
float num2 = Time.deltaTime * 12f;
num2 = Mathf.Clamp01(num2);
Vector3 val2 = value.lastPos + value.velocity * Time.deltaTime * 0.5f;
((Component)__instance).transform.position = Vector3.Lerp(((Component)__instance).transform.position, val2, num2);
((Component)__instance).transform.rotation = Quaternion.Slerp(((Component)__instance).transform.rotation, value.lastRot, num2);
}
}
}
[HarmonyPatch(typeof(Tameable), "Awake")]
[HarmonyPrefix]
public static bool Tameable_Awake_Prefix()
{
return (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer();
}
[HarmonyPatch(typeof(Tameable), "SetText")]
[HarmonyPrefix]
public static bool Tameable_SetText_Prefix()
{
return (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer();
}
}
[HarmonyPatch]
public static class ServerAuthorityPatches
{
[HarmonyPatch(typeof(ZNetScene), "CreateDestroyObjects")]
[HarmonyPrefix]
public static bool CreateDestroyObjects_Prefix(ZNetScene __instance)
{
//IL_007d: Unknown result type (might be due to invalid IL or missing references)
//IL_0082: Unknown result type (might be due to invalid IL or missing references)
//IL_0084: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
//IL_008b: Unknown result type (might be due to invalid IL or missing references)
//IL_0092: Unknown result type (might be due to invalid IL or missing references)
if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer())
{
return true;
}
int value = FiresGhettoNetworkMod.ConfigExtendedZoneRadius.Value;
int num = ZoneSystem.instance.m_activeArea + value;
int num2 = ZoneSystem.instance.m_activeDistantArea + value;
List<ZDO> list = new List<ZDO>();
List<ZDO> list2 = new List<ZDO>();
foreach (ZNetPeer connectedPeer in ZNet.instance.GetConnectedPeers())
{
Vector3 refPos = connectedPeer.GetRefPos();
Vector2i zone = ZoneSystem.GetZone(refPos);
ZDOMan.instance.FindSectorObjects(zone, num, num2, list, list2);
}
List<ZDO> list3 = list.Distinct().ToList();
List<ZDO> list4 = list2.Distinct().ToList();
Traverse val = Traverse.Create((object)__instance);
val.Method("CreateObjects", new object[2] { list3, list4 }).GetValue();
val.Method("RemoveObjects", new object[2] { list3, list4 }).GetValue();
return false;
}
[HarmonyPatch(typeof(ZoneSystem), "IsActiveAreaLoaded")]
[HarmonyPrefix]
public static bool IsActiveAreaLoaded_Prefix(ref bool __result, ZoneSystem __instance, Dictionary<Vector2i, object> ___m_zones)
{
//IL_005f: Unknown result type (might be due to invalid IL or missing references)
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_006b: Unknown result type (might be due to invalid IL or missing references)
//IL_00cd: 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_00b0: Unknown result type (might be due to invalid IL or missing references)
//IL_008c: Unknown result type (might be due to invalid IL or missing references)
if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer())
{
return true;
}
int value = FiresGhettoNetworkMod.ConfigExtendedZoneRadius.Value;
int num = __instance.m_activeArea + value;
foreach (ZNetPeer peer in ZNet.instance.GetPeers())
{
Vector2i zone = ZoneSystem.GetZone(peer.GetRefPos());
for (int i = zone.y - num; i <= zone.y + num; i++)
{
for (int j = zone.x - num; j <= zone.x + num; j++)
{
if (!___m_zones.ContainsKey(new Vector2i(j, i)))
{
__result = false;
return false;
}
}
}
}
__result = true;
return false;
}
[HarmonyPatch(typeof(ZoneSystem), "Update")]
[HarmonyPrefix]
public static bool ZoneSystem_Update_Prefix(ZoneSystem __instance, ref float ___m_updateTimer)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Invalid comparison between Unknown and I4
//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
if ((int)ZNet.GetConnectionStatus() != 2)
{
return false;
}
___m_updateTimer += Time.deltaTime;
if ((double)___m_updateTimer > 0.10000000149011612)
{
___m_updateTimer = 0f;
Traverse.Create((object)__instance).Method("UpdateTTL", new object[1] { 0.1f }).GetValue();
if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer())
{
foreach (ZNetPeer peer in ZNet.instance.GetPeers())
{
Traverse.Create((object)__instance).Method("CreateLocalZones", new object[1] { peer.GetRefPos() }).GetValue();
}
}
}
return false;
}
[HarmonyPatch(typeof(ZDOMan), "ReleaseNearbyZDOS")]
[HarmonyPrefix]
public static bool ReleaseNearbyZDOS_Prefix(ZDOMan __instance, ref Vector3 refPosition, ref long uid)
{
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_004e: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
//IL_0155: Unknown result type (might be due to invalid IL or missing references)
if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer())
{
return true;
}
int value = FiresGhettoNetworkMod.ConfigExtendedZoneRadius.Value;
int num = ZoneSystem.instance.m_activeArea + value;
Vector2i zone = ZoneSystem.GetZone(refPosition);
List<ZDO> value2 = Traverse.Create((object)__instance).Field("m_tempNearObjects").GetValue<List<ZDO>>();
value2.Clear();
__instance.FindSectorObjects(zone, num, 0, value2, (List<ZDO>)null);
foreach (ZDO item in value2)
{
if (!item.Persistent)
{
continue;
}
bool flag = false;
foreach (ZNetPeer peer in ZNet.instance.GetPeers())
{
if (ZNetScene.InActiveArea(item.GetSector(), ZoneSystem.GetZone(peer.GetRefPos())))
{
flag = true;
break;
}
}
long owner = item.GetOwner();
if (owner == uid || owner == ZNet.GetUID())
{
if (!flag)
{
item.SetOwner(0L);
}
continue;
}
bool flag2 = owner == 0L || !Traverse.Create((object)__instance).Method("IsInPeerActiveArea", new object[2]
{
item.GetSector(),
owner
}).GetValue<bool>();
if (flag2 && flag)
{
item.SetOwner(ZNet.GetUID());
}
}
return false;
}
[HarmonyPatch(typeof(ZNetScene), "OutsideActiveArea", new Type[] { typeof(Vector3) })]
[HarmonyPrefix]
public static bool OutsideActiveArea_Prefix(ref bool __result, Vector3 point)
{
//IL_005e: 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_0066: Unknown result type (might be due to invalid IL or missing references)
if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer())
{
return true;
}
int value = FiresGhettoNetworkMod.ConfigExtendedZoneRadius.Value;
int num = ZoneSystem.instance.m_activeArea + value;
__result = true;
foreach (ZNetPeer peer in ZNet.instance.GetPeers())
{
if (!ZNetScene.OutsideActiveArea(point, ZoneSystem.GetZone(peer.GetRefPos()), num))
{
__result = false;
break;
}
}
return false;
}
[HarmonyPatch(typeof(Tameable), "Awake")]
[HarmonyPrefix]
public static bool Tameable_Awake_Prefix(Tameable __instance)
{
return (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer();
}
[HarmonyPatch(typeof(Tameable), "Update")]
[HarmonyPrefix]
public static bool Tameable_Update_Prefix(Tameable __instance)
{
return (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer();
}
[HarmonyPatch(typeof(AudioMan), "Update")]
[HarmonyPrefix]
public static bool AudioMan_Update_Prefix()
{
return !ZNet.instance.IsServer();
}
}
public static class ServerClientUtils
{
public static bool IsDedicatedServerDetected { get; private set; }
public static void Detect()
{
IsDedicatedServerDetected = false;
try
{
if (Application.isBatchMode)
{
IsDedicatedServerDetected = true;
LoggerOptions.LogInfo("Detected dedicated server via Application.isBatchMode.");
return;
}
Type type = AccessTools.TypeByName("ZNet") ?? Type.GetType("ZNet, Assembly-CSharp");
if (type == null)
{
LoggerOptions.LogInfo("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;
LoggerOptions.LogInfo("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;
LoggerOptions.LogInfo("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;
LoggerOptions.LogInfo("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;
LoggerOptions.LogInfo("Detected dedicated server via ZNet.instance.IsDedicated.");
return;
}
}
}
LoggerOptions.LogInfo("No dedicated-server indicator found; assuming client/listen-server.");
}
catch (Exception ex)
{
LoggerOptions.LogWarning("Server detection failed: " + ex.Message);
IsDedicatedServerDetected = false;
}
}
}
[HarmonyPatch]
public static class ShipFixesGroup
{
[HarmonyPatch(typeof(Player), "TryPlacePiece")]
public static class Player_TryPlacePiece_Patch
{
public static void Postfix(Player __instance, Piece piece, bool __result)
{
if (!__result || (Object)(object)piece == (Object)null)
{
return;
}
ShipControlls component = ((Component)piece).GetComponent<ShipControlls>();
if ((Object)(object)component != (Object)null)
{
((Behaviour)component).enabled = true;
Debug.Log((object)("FiresGhettoNetworkMod: Enabled ShipControlls on placed ship: " + ((Object)piece).name));
}
string cleanName = ((Object)piece).name.Replace("(Clone)", "").Trim();
if (ShipPrefabNames.Any((string name) => cleanName.StartsWith(name)))
{
ZNetView component2 = ((Component)piece).GetComponent<ZNetView>();
if ((Object)(object)component2 != (Object)null && component2.GetZDO() != null)
{
component2.GetZDO().Set(ZDOVars.s_user, -1L);
Debug.Log((object)("FiresGhettoNetworkMod: " + cleanName + " placed — PERMANENT AUTOPILOT ENABLED"));
}
}
}
}
[HarmonyPatch(typeof(ShipControlls), "RPC_RequestControl")]
[HarmonyPatch(typeof(ShipControlls), "RPC_ReleaseControl")]
public static class ShipControlls_BlockUserWrite_Patch
{
private static bool Prefix(ShipControlls __instance)
{
return true;
}
private static void Postfix(ShipControlls __instance)
{
if (!FiresGhettoNetworkMod.ConfigEnableShipFixes.Value || (Object)(object)__instance?.m_ship == (Object)null)
{
return;
}
string cleanName = ((Object)__instance.m_ship).name.Replace("(Clone)", "").Trim();
if (!ShipPrefabNames.Any((string n) => cleanName.StartsWith(n)))
{
return;
}
ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
if (!((Object)(object)component == (Object)null) && component.IsValid())
{
long @long = component.GetZDO().GetLong(ZDOVars.s_user, 0L);
if (@long != -1)
{
component.GetZDO().Set(ZDOVars.s_user, -1L);
component.InvokeRPC("ForceUpdateZDO", Array.Empty<object>());
LoggerOptions.LogInfo("Reverted user on ship " + cleanName + " to -1 to preserve dummy physics.");
}
}
}
}
[HarmonyPatch(typeof(ShipControlls), "HaveValidUser")]
public static class ShipControlls_DummyPhysics_Patch
{
private static bool Prefix(ShipControlls __instance, ref bool __result)
{
if ((Object)(object)__instance.m_ship == (Object)null)
{
return true;
}
string cleanName = ((Object)__instance.m_ship).name.Replace("(Clone)", "").Trim();
if (!ShipPrefabNames.Any((string n) => cleanName.StartsWith(n)))
{
return true;
}
ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
if ((Object)(object)component == (Object)null || !component.IsValid())
{
return true;
}
long @long = component.GetZDO().GetLong(ZDOVars.s_user, 0L);
if (@long == -1)
{
__result = true;
return false;
}
return true;
}
}
public static readonly List<string> ShipPrefabNames = new List<string>
{
"Vandrarskapp01", "VAPrototype", "VAPrototype1", "VAPinnacle", "VAPinnacle1", "VAPinnacle2", "RowBoat", "VikingShip_Ashlands", "VikingShip", "Raft",
"Karve", "Trailership"
};
}
[HarmonyPatch]
public static class ZDOMemoryManager
{
public static ConfigEntry<int> 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<int>("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<int>(0, 2000000), Array.Empty<object>()));
}
[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<ZDOID, ZDO> dictionary = (Dictionary<ZDOID, ZDO>)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<ZDOID, ZDO>)fieldInfo.GetValue(__instance);
LoggerOptions.LogInfo($"Cleanup complete: {count} → {dictionary?.Count ?? 0} ZDOs");
}
}
}
[HarmonyPatch]
public static class ZDOThrottlingPatches
{
[HarmonyPatch(typeof(ZDOMan), "SendZDOToPeers2")]
[HarmonyPrefix]
public static void ThrottleDistantZDOs(ref float dt, List<ZDO> objects, Vector3 refPos)
{
//IL_0063: 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)
if (!ZNet.instance.IsServer() || !FiresGhettoNetworkMod.ConfigEnableZDOThrottling.Value)
{
return;
}
float value = FiresGhettoNetworkMod.ConfigZDOThrottleDistance.Value;
if (value <= 0f)
{
return;
}
for (int num = objects.Count - 1; num >= 0; num--)
{
ZDO val = objects[num];
if (val != null)
{
float num2 = Vector3.Distance(val.GetPosition(), refPos);
if (num2 > value)
{
dt *= 0.5f;
}
}
}
}
}
}