Decompiled source of kou fixdesync v1.0.0

kou_fixdesync.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Steamworks;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("TestHarness")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("kou_fixdesync")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("kou_fixdesync")]
[assembly: AssemblyTitle("kou_fixdesync")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace KouFixDesync
{
	[BepInPlugin("kou.fixdesync", "kou_fixdesync", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string GUID = "kou.fixdesync";

		public const string NAME = "kou_fixdesync";

		public const string VERSION = "1.0.0";

		internal static Plugin Instance;

		internal static ManualLogSource Log;

		internal ConfigEntry<bool> CfgWindowEnabled;

		internal ConfigEntry<int> CfgWindowBytes;

		internal ConfigEntry<int> CfgMinWindowBytes;

		internal ConfigEntry<bool> CfgAdaptiveWindow;

		internal ConfigEntry<int> CfgTargetRateKBps;

		internal ConfigEntry<bool> CfgCadenceEnabled;

		internal ConfigEntry<int> CfgSendIntervalMs;

		internal ConfigEntry<bool> CfgSteamRatesEnabled;

		internal ConfigEntry<int> CfgRateMinKBps;

		internal ConfigEntry<int> CfgRateMaxKBps;

		internal ConfigEntry<bool> CfgDistantSyncEnabled;

		internal ConfigEntry<int> CfgDistantThreshold;

		internal ConfigEntry<bool> CfgOwnershipEnabled;

		internal ConfigEntry<int> CfgOwnershipPingCeiling;

		internal ConfigEntry<int> CfgOwnershipPingFloor;

		internal ConfigEntry<int> CfgOwnershipCooldownSec;

		private void Awake()
		{
			//IL_0247: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			CfgWindowEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1.SendWindow", "Enabled", true, "Enlarge the per-peer ZDO send window (vanilla: 10240 bytes incl. unacked data - the main desync cause).");
			CfgWindowBytes = ((BaseUnityPlugin)this).Config.Bind<int>("1.SendWindow", "WindowBytes", 61440, "Send window size in bytes. Hard-clamped to 10240-262144 (Steamworks drops single messages >512KB).");
			CfgMinWindowBytes = ((BaseUnityPlugin)this).Config.Bind<int>("1.SendWindow", "MinWindowBytes", 2048, "Minimum free window required before sending a batch (vanilla: 2048). Clamped 1024-8192.");
			CfgAdaptiveWindow = ((BaseUnityPlugin)this).Config.Bind<bool>("1.SendWindow", "PingAdaptive", true, "Scale each peer's window with its measured round-trip time (window = TargetRate x RTT). High-ping players get bigger windows automatically.");
			CfgTargetRateKBps = ((BaseUnityPlugin)this).Config.Bind<int>("1.SendWindow", "TargetRateKBps", 300, "Target per-peer send rate in KB/s used for the ping-adaptive window. Clamped 60-2048.");
			CfgCadenceEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("2.SendCadence", "Enabled", true, "Service ALL peers every send tick instead of vanilla's one-peer-per-frame round robin.");
			CfgSendIntervalMs = ((BaseUnityPlugin)this).Config.Bind<int>("2.SendCadence", "SendIntervalMs", 50, "Send tick interval in milliseconds (vanilla: 50). Clamped 20-200.");
			CfgSteamRatesEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("3.SteamRates", "Enabled", true, "Raise Steam networking send rate limits (vanilla pins min=max=150 KB/s).");
			CfgRateMinKBps = ((BaseUnityPlugin)this).Config.Bind<int>("3.SteamRates", "SendRateMinKBps", 256, "Steam SendRateMin in KB/s. Clamped 150-4096.");
			CfgRateMaxKBps = ((BaseUnityPlugin)this).Config.Bind<int>("3.SteamRates", "SendRateMaxKBps", 1024, "Steam SendRateMax in KB/s. Keeping max > min lets Steam's congestion control work. Clamped to >= min, <= 8192.");
			CfgDistantSyncEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("4.DistantSync", "Enabled", true, "Sync distant-zone objects when fewer than DistantThreshold near objects are queued (vanilla threshold: 10).");
			CfgDistantThreshold = ((BaseUnityPlugin)this).Config.Bind<int>("4.DistantSync", "DistantThreshold", 30, "Queue-size threshold below which distant objects are added to the sync list. Clamped 10-200.");
			CfgOwnershipEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("5.SmartOwnership", "Enabled", true, "SERVER ONLY: assign mob/object simulation ownership to the lowest-ping player covering the area, and migrate it off high-ping players.");
			CfgOwnershipPingCeiling = ((BaseUnityPlugin)this).Config.Bind<int>("5.SmartOwnership", "PingCeilingMs", 150, "Migrate objects away from an owner whose ping exceeds this (ms).");
			CfgOwnershipPingFloor = ((BaseUnityPlugin)this).Config.Bind<int>("5.SmartOwnership", "PingFloorMs", 80, "Only migrate when a candidate with ping below this (ms) also covers the area.");
			CfgOwnershipCooldownSec = ((BaseUnityPlugin)this).Config.Bind<int>("5.SmartOwnership", "CooldownSec", 10, "Per-object cooldown between ownership migrations, to prevent flapping.");
			Settings.ApplyLocal();
			new Harmony("kou.fixdesync").PatchAll();
			Log.LogInfo((object)"kou_fixdesync 1.0.0 loaded, patches applied");
		}
	}
	internal static class Settings
	{
		public const int PkgVersion = 1;

		public const int AbsoluteMaxWindow = 262144;

		public static bool WindowEnabled = true;

		public static int WindowBytes = 61440;

		public static int MinWindowBytes = 2048;

		public static bool AdaptiveWindow = true;

		public static int TargetRateKBps = 300;

		public static bool CadenceEnabled = true;

		public static int SendIntervalMs = 50;

		public static bool SteamRatesEnabled = true;

		public static int RateMinKBps = 256;

		public static int RateMaxKBps = 1024;

		public static bool DistantSyncEnabled = true;

		public static int DistantThreshold = 30;

		public static bool OwnershipEnabled = true;

		public static int OwnershipPingCeiling = 150;

		public static int OwnershipPingFloor = 80;

		public static int OwnershipCooldownSec = 10;

		public static void ApplyLocal()
		{
			Plugin instance = Plugin.Instance;
			WindowEnabled = instance.CfgWindowEnabled.Value;
			WindowBytes = Clamp(instance.CfgWindowBytes.Value, 10240, 262144);
			MinWindowBytes = Clamp(instance.CfgMinWindowBytes.Value, 1024, 8192);
			AdaptiveWindow = instance.CfgAdaptiveWindow.Value;
			TargetRateKBps = Clamp(instance.CfgTargetRateKBps.Value, 60, 2048);
			CadenceEnabled = instance.CfgCadenceEnabled.Value;
			SendIntervalMs = Clamp(instance.CfgSendIntervalMs.Value, 20, 200);
			SteamRatesEnabled = instance.CfgSteamRatesEnabled.Value;
			RateMinKBps = Clamp(instance.CfgRateMinKBps.Value, 150, 4096);
			RateMaxKBps = Clamp(instance.CfgRateMaxKBps.Value, RateMinKBps, 8192);
			DistantSyncEnabled = instance.CfgDistantSyncEnabled.Value;
			DistantThreshold = Clamp(instance.CfgDistantThreshold.Value, 10, 200);
			OwnershipEnabled = instance.CfgOwnershipEnabled.Value;
			OwnershipPingCeiling = Clamp(instance.CfgOwnershipPingCeiling.Value, 50, 1000);
			OwnershipPingFloor = Clamp(instance.CfgOwnershipPingFloor.Value, 10, OwnershipPingCeiling);
			OwnershipCooldownSec = Clamp(instance.CfgOwnershipCooldownSec.Value, 1, 300);
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)"kou_fixdesync: local config applied");
			}
		}

		public static void WritePackage(ZPackage pkg)
		{
			pkg.Write(1);
			pkg.Write(WindowEnabled);
			pkg.Write(WindowBytes);
			pkg.Write(MinWindowBytes);
			pkg.Write(AdaptiveWindow);
			pkg.Write(TargetRateKBps);
			pkg.Write(CadenceEnabled);
			pkg.Write(SendIntervalMs);
			pkg.Write(SteamRatesEnabled);
			pkg.Write(RateMinKBps);
			pkg.Write(RateMaxKBps);
			pkg.Write(DistantSyncEnabled);
			pkg.Write(DistantThreshold);
		}

		public static void ReadPackage(ZPackage pkg)
		{
			int num = pkg.ReadInt();
			if (num != 1)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)$"kou_fixdesync: server config version {num} != {1}, keeping local values");
				}
				return;
			}
			WindowEnabled = pkg.ReadBool();
			WindowBytes = Clamp(pkg.ReadInt(), 10240, 262144);
			MinWindowBytes = Clamp(pkg.ReadInt(), 1024, 8192);
			AdaptiveWindow = pkg.ReadBool();
			TargetRateKBps = Clamp(pkg.ReadInt(), 60, 2048);
			CadenceEnabled = pkg.ReadBool();
			SendIntervalMs = Clamp(pkg.ReadInt(), 20, 200);
			SteamRatesEnabled = pkg.ReadBool();
			RateMinKBps = Clamp(pkg.ReadInt(), 150, 4096);
			RateMaxKBps = Clamp(pkg.ReadInt(), RateMinKBps, 8192);
			DistantSyncEnabled = pkg.ReadBool();
			DistantThreshold = Clamp(pkg.ReadInt(), 10, 200);
			ManualLogSource log2 = Plugin.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)($"kou_fixdesync: server-dictated config applied (window={WindowBytes} adaptive={AdaptiveWindow} " + $"cadence={CadenceEnabled}/{SendIntervalMs}ms rates={RateMinKBps}-{RateMaxKBps}KB/s distant={DistantThreshold})"));
			}
		}

		private static int Clamp(int v, int min, int max)
		{
			return Mathf.Clamp(v, min, max);
		}
	}
}
namespace KouFixDesync.Patches
{
	internal static class ConfigSyncPatch
	{
		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		private static class OnNewConnectionPatch
		{
			[HarmonyPostfix]
			private static void Postfix(ZNet __instance, ZNetPeer peer)
			{
				//IL_0033: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Expected O, but got Unknown
				peer.m_rpc.Register<ZPackage>("KouFixDesync_Cfg", (Action<ZRpc, ZPackage>)OnConfigReceived);
				if (__instance.IsServer())
				{
					ZPackage val = new ZPackage();
					Settings.WritePackage(val);
					peer.m_rpc.Invoke("KouFixDesync_Cfg", new object[1] { val });
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "Awake")]
		private static class AwakePatch
		{
			[HarmonyPostfix]
			private static void Postfix()
			{
				Settings.ApplyLocal();
			}
		}

		private const string RpcName = "KouFixDesync_Cfg";

		private static void OnConfigReceived(ZRpc rpc, ZPackage pkg)
		{
			if (!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer())
			{
				Settings.ReadPackage(pkg);
				SteamRatePatch.Apply();
			}
		}
	}
	[HarmonyPatch(typeof(ZDOMan), "CreateSyncList")]
	internal static class DistantSyncPatch
	{
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			int replaced = 0;
			foreach (CodeInstruction instruction in instructions)
			{
				if (replaced == 0 && instruction.opcode == OpCodes.Ldc_I4_S && Convert.ToInt32(instruction.operand) == 10)
				{
					replaced++;
					yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(DistantSyncPatch), "GetThreshold", (Type[])null, (Type[])null))
					{
						labels = instruction.labels,
						blocks = instruction.blocks
					};
				}
				else
				{
					yield return instruction;
				}
			}
			if (replaced != 1)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)$"kou_fixdesync: CreateSyncList transpiler matched {replaced} (expected 1) - game update may have changed the method");
				}
			}
		}

		public static int GetThreshold()
		{
			if (!Settings.DistantSyncEnabled)
			{
				return 10;
			}
			return Settings.DistantThreshold;
		}
	}
	[HarmonyPatch(typeof(ZDOMan), "ReleaseNearbyZDOS")]
	internal static class OwnershipPatch
	{
		private static readonly List<ZDO> _tempObjects = new List<ZDO>();

		private static readonly Dictionary<ZDOID, float> _lastMigration = new Dictionary<ZDOID, float>();

		private static float _lastCacheClean;

		[HarmonyPrefix]
		private static bool Prefix(ZDOMan __instance, Vector3 refPosition, long uid)
		{
			//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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: 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_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			if (!Settings.OwnershipEnabled || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return true;
			}
			Vector2i zone = ZoneSystem.GetZone(refPosition);
			_tempObjects.Clear();
			__instance.FindSectorObjects(zone, ZoneSystem.instance.m_activeArea, 0, _tempObjects, (List<ZDO>)null);
			int num = ZoneSystem.instance.m_activeArea - 1;
			List<ZNetPeer> peers = ZNet.instance.GetPeers();
			float time = Time.time;
			CleanMigrationCache(time);
			foreach (ZDO tempObject in _tempObjects)
			{
				if (!tempObject.Persistent)
				{
					continue;
				}
				Vector2i sector = tempObject.GetSector();
				long owner = tempObject.GetOwner();
				if (owner == uid)
				{
					if (!ZNetScene.InActiveArea(sector, zone, num))
					{
						tempObject.SetOwner(0L);
					}
					else if (uid != ZDOMan.GetSessionID())
					{
						TryMigrate(tempObject, sector, owner, peers, time);
					}
				}
				else if ((!tempObject.HasOwner() || !IsInPeerActiveArea(sector, owner)) && ZNetScene.InActiveArea(sector, zone, num))
				{
					tempObject.SetOwner(BestOwnerFor(sector, uid, peers));
				}
			}
			return false;
		}

		private static long BestOwnerFor(Vector2i sector, long fallbackUid, List<ZNetPeer> peers)
		{
			//IL_0019: 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)
			long result = fallbackUid;
			int num = int.MaxValue;
			foreach (ZNetPeer peer in peers)
			{
				if (ZNetScene.InActiveArea(sector, peer.GetRefPos()))
				{
					int pingMs = GetPingMs(peer);
					if (pingMs < num)
					{
						num = pingMs;
						result = peer.m_uid;
					}
				}
			}
			return result;
		}

		private static void TryMigrate(ZDO zdo, Vector2i sector, long owner, List<ZNetPeer> peers, float now)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: 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)
			ZNetPeer peer = ZNet.instance.GetPeer(owner);
			if (peer == null || GetPingMs(peer) <= Settings.OwnershipPingCeiling || (_lastMigration.TryGetValue(zdo.m_uid, out var value) && now - value < (float)Settings.OwnershipCooldownSec))
			{
				return;
			}
			ZNetPeer val = null;
			int num = int.MaxValue;
			foreach (ZNetPeer peer2 in peers)
			{
				if (peer2.m_uid != owner && ZNetScene.InActiveArea(sector, peer2.GetRefPos()))
				{
					int pingMs = GetPingMs(peer2);
					if (pingMs < num)
					{
						num = pingMs;
						val = peer2;
					}
				}
			}
			if (val != null && num < Settings.OwnershipPingFloor)
			{
				zdo.SetOwner(val.m_uid);
				_lastMigration[zdo.m_uid] = now;
			}
		}

		private static bool IsInPeerActiveArea(Vector2i sector, long uid)
		{
			//IL_0008: 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_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if (uid == ZDOMan.GetSessionID())
			{
				return ZNetScene.InActiveArea(sector, ZNet.instance.GetReferencePosition());
			}
			ZNetPeer peer = ZNet.instance.GetPeer(uid);
			if (peer == null)
			{
				return false;
			}
			return ZNetScene.InActiveArea(sector, peer.GetRefPos());
		}

		private static int GetPingMs(ZNetPeer peer)
		{
			if (peer.m_socket == null)
			{
				return int.MaxValue;
			}
			float num = default(float);
			float num2 = default(float);
			int result = default(int);
			float num3 = default(float);
			float num4 = default(float);
			peer.m_socket.GetConnectionQuality(ref num, ref num2, ref result, ref num3, ref num4);
			return result;
		}

		private static void CleanMigrationCache(float now)
		{
			if (!(now - _lastCacheClean < 60f) && _lastMigration.Count >= 512)
			{
				_lastCacheClean = now;
				_lastMigration.Clear();
			}
		}
	}
	[HarmonyPatch(typeof(ZDOMan), "SendZDOToPeers2")]
	internal static class SendCadencePatch
	{
		private static readonly FieldInfo PeersField = AccessTools.Field(typeof(ZDOMan), "m_peers");

		private static readonly FieldInfo NextSendPeerField = AccessTools.Field(typeof(ZDOMan), "m_nextSendPeer");

		private static readonly MethodInfo SendZDOsMethod = AccessTools.Method(typeof(ZDOMan), "SendZDOs", (Type[])null, (Type[])null);

		private static float _timer;

		private static readonly object[] _args = new object[2];

		[HarmonyPrefix]
		private static bool Prefix(ZDOMan __instance, float dt)
		{
			if (!Settings.CadenceEnabled)
			{
				return true;
			}
			IList list = (IList)PeersField.GetValue(__instance);
			if (list == null || list.Count == 0)
			{
				return false;
			}
			_timer += dt;
			if (_timer < (float)Settings.SendIntervalMs / 1000f)
			{
				return false;
			}
			_timer = 0f;
			_args[1] = false;
			for (int i = 0; i < list.Count; i++)
			{
				_args[0] = list[i];
				SendZDOsMethod.Invoke(__instance, _args);
			}
			NextSendPeerField.SetValue(__instance, -1);
			return false;
		}
	}
	[HarmonyPatch(typeof(ZDOMan), "SendZDOs")]
	internal static class SendWindowPatch
	{
		private struct PingCache
		{
			public float Time;

			public int Ping;
		}

		private static FieldInfo _zdoPeerPeerField;

		private static readonly Dictionary<object, PingCache> _pingCache = new Dictionary<object, PingCache>();

		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			int windows = 0;
			int mins = 0;
			foreach (CodeInstruction instruction in instructions)
			{
				if (instruction.opcode == OpCodes.Ldc_I4 && instruction.operand is int num && num == 10240)
				{
					windows++;
					yield return new CodeInstruction(OpCodes.Ldarg_1, (object)null)
					{
						labels = instruction.labels,
						blocks = instruction.blocks
					};
					yield return CodeInstruction.Call(typeof(SendWindowPatch), "GetWindow", (Type[])null, (Type[])null);
				}
				else if (instruction.opcode == OpCodes.Ldc_I4 && instruction.operand is int num2 && num2 == 2048)
				{
					mins++;
					yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(SendWindowPatch), "GetMinWindow", (Type[])null, (Type[])null))
					{
						labels = instruction.labels,
						blocks = instruction.blocks
					};
				}
				else
				{
					yield return instruction;
				}
			}
			if (windows != 2 || mins != 1)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)$"kou_fixdesync: SendZDOs transpiler matched {windows}x10240 (expected 2), {mins}x2048 (expected 1) - game update may have changed the method");
				}
			}
		}

		public static int GetWindow(object zdoPeer)
		{
			if (!Settings.WindowEnabled)
			{
				return 10240;
			}
			int num = Settings.WindowBytes;
			if (Settings.AdaptiveWindow)
			{
				int peerPingMs = GetPeerPingMs(zdoPeer);
				if (peerPingMs > 0)
				{
					int num2 = (int)((long)Settings.TargetRateKBps * 1024L * peerPingMs / 1000);
					if (num2 > num)
					{
						num = num2;
					}
				}
			}
			return Mathf.Min(num, 262144);
		}

		public static int GetMinWindow()
		{
			if (!Settings.WindowEnabled)
			{
				return 2048;
			}
			return Settings.MinWindowBytes;
		}

		private static int GetPeerPingMs(object zdoPeer)
		{
			if (zdoPeer == null)
			{
				return 0;
			}
			float time = Time.time;
			if (_pingCache.TryGetValue(zdoPeer, out var value) && time - value.Time < 1f)
			{
				return value.Ping;
			}
			int num = 0;
			if (_zdoPeerPeerField == null)
			{
				_zdoPeerPeerField = AccessTools.Field(zdoPeer.GetType(), "m_peer");
			}
			object? obj = _zdoPeerPeerField?.GetValue(zdoPeer);
			ZNetPeer val = (ZNetPeer)((obj is ZNetPeer) ? obj : null);
			if (val != null && val.m_socket != null)
			{
				float num2 = default(float);
				float num3 = default(float);
				float num4 = default(float);
				float num5 = default(float);
				val.m_socket.GetConnectionQuality(ref num2, ref num3, ref num, ref num4, ref num5);
			}
			if (_pingCache.Count > 64)
			{
				_pingCache.Clear();
			}
			_pingCache[zdoPeer] = new PingCache
			{
				Time = time,
				Ping = num
			};
			return num;
		}
	}
	[HarmonyPatch(typeof(ZSteamSocket), "RegisterGlobalCallbacks")]
	internal static class SteamRatePatch
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			Apply();
		}

		public static void Apply()
		{
			if (!Settings.SteamRatesEnabled)
			{
				return;
			}
			try
			{
				SetGlobalInt((ESteamNetworkingConfigValue)10, Settings.RateMinKBps * 1024);
				SetGlobalInt((ESteamNetworkingConfigValue)11, Settings.RateMaxKBps * 1024);
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)$"kou_fixdesync: Steam send rate set to {Settings.RateMinKBps}-{Settings.RateMaxKBps} KB/s");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("kou_fixdesync: could not set Steam send rates: " + ex.Message));
				}
			}
		}

		private static void SetGlobalInt(ESteamNetworkingConfigValue key, int value)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			GCHandle gCHandle = GCHandle.Alloc(value, GCHandleType.Pinned);
			try
			{
				SteamNetworkingUtils.SetConfigValue(key, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)1, gCHandle.AddrOfPinnedObject());
			}
			finally
			{
				gCHandle.Free();
			}
		}
	}
}