Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of BetterNetworking Valheim v1.2.0
plugins\BetterNetworking_Valheim\DIT.BetterNetworking10.dll
Decompiled 2 days agousing System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Costura; using HarmonyLib; using Ionic.Zlib; using Microsoft.CodeAnalysis; using PlayFab.Party; using Steamworks; using UnityEngine; using ZstdSharp; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyMetadata("PluginVersion", "1.2.0")] [assembly: AssemblyCompany("DIT.BetterNetworking10")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.2.0.0")] [assembly: AssemblyInformationalVersion("1.2.0+b695dc4fc4ae2241d59884838224eee3c1046cbc")] [assembly: AssemblyProduct("DIT.BetterNetworking10")] [assembly: AssemblyTitle("DIT.BetterNetworking10")] [assembly: AssemblyVersion("1.2.0.0")] [module: RefSafetyRules(11)] internal class <Module> { static <Module>() { AssemblyLoader.Attach(); } } 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 DIT.BetterNetworking10 { [BepInPlugin("DIT.BetterNetworking10", "Better Networking 1.0 Safe", "1.2.0")] [BepInIncompatibility("CW_Jesse.BetterNetworking")] [BepInIncompatibility("org.bepinex.plugins.network")] [BepInIncompatibility("be.sebastienvercammen.valheim.netcompression")] [BepInIncompatibility("com.github.dalayeth.Networkfix")] [BepInIncompatibility("Steel.ValheimMod")] public sealed class BetterNetworking10 : BaseUnityPlugin { public const string PluginGuid = "DIT.BetterNetworking10"; public const string PluginName = "Better Networking 1.0 Safe"; public const string PluginVersion = "1.2.0"; internal static ConfigEntry<CompatibilityMode> Mode; internal static ConfigEntry<bool> EnableCompression; internal static ConfigEntry<bool> EnableQueueSize; internal static ConfigEntry<bool> EnableUpdateRate; internal static ConfigEntry<bool> EnableSendRate; internal static ConfigEntry<bool> EnableConnectionBuffer; internal static ConfigEntry<bool> EnablePortalGhostFix; internal static ConfigEntry<QueueSizeOption> QueueSize; internal static ConfigEntry<UpdateRateOption> UpdateRate; internal static ConfigEntry<SendRateOption> MinimumSendRate; internal static ConfigEntry<SendRateOption> MaximumSendRate; internal static ConfigEntry<ForceCrossplayOption> ForceCrossplay; internal static ConfigEntry<int> PlayerLimit; private Harmony _harmony; internal static BetterNetworking10 Instance { get; private set; } internal static BNLog Log { get; private set; } internal static bool SafeMode => Mode.Value == CompatibilityMode.VanillaSafe; internal static bool CompressionActive => EnableCompression.Value; internal static bool QueueActive => !SafeMode && EnableQueueSize.Value && QueueSize.Value != QueueSizeOption.Vanilla; internal static bool UpdateRateActive => !SafeMode && EnableUpdateRate.Value && UpdateRate.Value != UpdateRateOption.Percent100; internal static bool SendRateActive => !SafeMode && EnableSendRate.Value; internal static bool ConnectionBufferActive => !SafeMode && EnableConnectionBuffer.Value; internal static bool PortalGhostFixActive => EnablePortalGhostFix.Value; internal static int QueueBudgetBytes => QueueSize.Value switch { QueueSizeOption.KB80 => 81920, QueueSizeOption.KB64 => 65536, QueueSizeOption.KB48 => 49152, QueueSizeOption.KB32 => 32768, _ => 10240, }; internal static float UpdateRateMultiplier => UpdateRate.Value switch { UpdateRateOption.Percent75 => 0.75f, UpdateRateOption.Percent50 => 0.5f, _ => 1f, }; internal static bool IsDedicated => ((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.IsDedicated() : Application.isBatchMode; private void Awake() { //IL_0066: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: 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_00a2: 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_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Expected O, but got Unknown Instance = this; bool flag = !File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath); BindConfig(); Log = new BNLog(((BaseUnityPlugin)this).Logger, ((BaseUnityPlugin)this).Config); if (flag) { LegacyConfigMigration.TryImport(((BaseUnityPlugin)this).Config, Log); } GameVersion val = default(GameVersion); ((GameVersion)(ref val))..ctor(1, 0, 12); GameVersion val2 = default(GameVersion); ((GameVersion)(ref val2))..ctor(1, 0, 15); if (Version.CurrentVersion < val || Version.CurrentVersion > val2) { Log.Warning($"This build supports the audited Valheim range {val}-{val2} with network version 40; running {Version.CurrentVersion} / network {40u}. Check PATCH FAILED messages and retest networking after game updates."); } else { Log.Message($"Valheim compatibility verified: {Version.CurrentVersion}, network version {40u}"); } _harmony = new Harmony("DIT.BetterNetworking10"); PatchInstaller.Install(_harmony); PortalGhostRefresh.Initialize(); } private void Update() { PortalGhostRefresh.Tick(); } private void OnDestroy() { CompressionState.Clear(); ConnectionBufferPatch.Clear(); PortalGhostRefresh.Clear(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void BindConfig() { //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Expected O, but got Unknown Mode = ((BaseUnityPlugin)this).Config.Bind<CompatibilityMode>("00 - Compatibility", "Mode", CompatibilityMode.Balanced, "Balanced: the enabled feature switches below are applied. VanillaSafe: only compression is allowed; ZDO scheduling, queue, and Steam send-rate changes are disabled."); EnableCompression = ((BaseUnityPlugin)this).Config.Bind<bool>("01 - Features", "Compression", true, "Compress traffic only when both peers run this compatible build."); EnableQueueSize = ((BaseUnityPlugin)this).Config.Bind<bool>("01 - Features", "Queue Size", true, "Changes the ZDO send budget directly. Disabled in VanillaSafe mode."); EnableUpdateRate = ((BaseUnityPlugin)this).Config.Bind<bool>("01 - Features", "Update Rate", true, "Allows a lower ZDO update frequency. Disabled in VanillaSafe mode."); EnableSendRate = ((BaseUnityPlugin)this).Config.Bind<bool>("01 - Features", "Steam Send Rate", true, "Changes Steamworks send-rate limits. Has no effect with PlayFab/crossplay. Disabled in VanillaSafe mode."); EnableConnectionBuffer = ((BaseUnityPlugin)this).Config.Bind<bool>("01 - Features", "New Connection ZDO Buffer", false, "Experimental. Buffers early ZDOData packets per connection until ZDOMan registers the peer. OFF by default because stale player/ZDO state is being diagnosed."); EnablePortalGhostFix = ((BaseUnityPlugin)this).Config.Bind<bool>("01 - Features", "Portal Ghost Fix", false, "Optional server-side portal observer refresh. Re-sends a teleported player's ZDO and related networked status-effect ZDOs; vanilla clients are supported. Disable the standalone Sfantul mod when using this."); QueueSize = ((BaseUnityPlugin)this).Config.Bind<QueueSizeOption>("02 - Networking", "Queue Size", QueueSizeOption.KB32, "ZDO send budget based on Valheim's reported queue size. Vanilla leaves the game's 10 KB budget unchanged."); UpdateRate = ((BaseUnityPlugin)this).Config.Bind<UpdateRateOption>("02 - Networking", "Update Rate", UpdateRateOption.Percent100, "100 = vanilla 20 Hz timer; 75 = 15 Hz; 50 = 10 Hz."); MinimumSendRate = ((BaseUnityPlugin)this).Config.Bind<SendRateOption>("03 - Steamworks", "Minimum Send Rate", SendRateOption.KB256, "Steamworks minimum send rate in KB/s. Keep this below the real upload capacity."); MaximumSendRate = ((BaseUnityPlugin)this).Config.Bind<SendRateOption>("03 - Steamworks", "Maximum Send Rate", SendRateOption.KB1024, "Steamworks maximum send rate in KB/s."); ForceCrossplay = ((BaseUnityPlugin)this).Config.Bind<ForceCrossplayOption>("04 - Dedicated Server", "Force Crossplay", ForceCrossplayOption.Vanilla, "Vanilla follows the server command line; PlayFab forces crossplay; Steamworks disables crossplay."); PlayerLimit = ((BaseUnityPlugin)this).Config.Bind<int>("04 - Dedicated Server", "Player Limit", 10, new ConfigDescription("Dedicated server player limit. Requires restart.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 127), Array.Empty<object>())); } internal static int RateBytesPerSecond(SendRateOption option) { return (int)option * 1024; } } public enum CompatibilityMode { Balanced, VanillaSafe } public enum QueueSizeOption { Vanilla = 10, KB32 = 32, KB48 = 48, KB64 = 64, KB80 = 80 } public enum UpdateRateOption { Percent100 = 100, Percent75 = 75, Percent50 = 50 } public enum SendRateOption { KB150 = 150, KB256 = 256, KB512 = 512, KB768 = 768, KB1024 = 1024 } public enum ForceCrossplayOption { Vanilla, PlayFab, Steamworks } internal static class PatchInstaller { private static int _applied; private static int _skipped; private static int _failed; internal static void Install(Harmony harmony) { if (BetterNetworking10.CompressionActive) { try { CompressionCodec.Initialize(); CompressionFrame.SelfTest(); InstallCompression(harmony); } catch (Exception ex) { Skip("Compression", "self-test failed: " + ex.GetType().Name + ": " + ex.Message); BetterNetworking10.Log.Error("Compression was left disabled because its startup self-test failed"); } } else { Skip("Compression", "disabled by configuration"); } if (BetterNetworking10.QueueActive) { Patch(harmony, "ZDO queue budget", AccessTools.DeclaredMethod(typeof(ZDOMan), "SendZDOs", (Type[])null, (Type[])null), null, null, "Transpiler", typeof(QueueBudgetPatch)); } else { Skip("ZDO queue budget", BetterNetworking10.SafeMode ? "VanillaSafe mode" : "vanilla/disabled setting"); } if (BetterNetworking10.UpdateRateActive) { Patch(harmony, "ZDO update rate", AccessTools.DeclaredMethod(typeof(ZDOMan), "SendZDOToPeers2", (Type[])null, (Type[])null), "Prefix", null, null, typeof(UpdateRatePatch)); } else { Skip("ZDO update rate", BetterNetworking10.SafeMode ? "VanillaSafe mode" : "100%/disabled setting"); } if (BetterNetworking10.SendRateActive) { Patch(harmony, "Steam send rate", AccessTools.DeclaredMethod(typeof(ZSteamSocket), "RegisterGlobalCallbacks", (Type[])null, (Type[])null), null, "Postfix", null, typeof(SteamSendRatePatch)); } else { Skip("Steam send rate", BetterNetworking10.SafeMode ? "VanillaSafe mode" : "disabled by configuration"); } if (BetterNetworking10.ConnectionBufferActive) { InstallConnectionBuffer(harmony); } else { Skip("New connection ZDO buffer", BetterNetworking10.SafeMode ? "VanillaSafe mode" : "disabled by default/configuration"); } InstallDedicatedTweaks(harmony); BetterNetworking10.Log.Message($"Patch audit complete: {_applied} applied, {_skipped} skipped, {_failed} failed. Mode={BetterNetworking10.Mode.Value}"); } private static void InstallCompression(Harmony harmony) { Patch(harmony, "Compression connect", AccessTools.DeclaredMethod(typeof(ZNet), "OnNewConnection", (Type[])null, (Type[])null), null, "OnConnectPostfix", null, typeof(CompressionPatch)); Patch(harmony, "Compression disconnect pre", AccessTools.DeclaredMethod(typeof(ZNet), "Disconnect", (Type[])null, (Type[])null), "OnDisconnectPrefix", null, null, typeof(CompressionPatch)); Patch(harmony, "Compression disconnect post", AccessTools.DeclaredMethod(typeof(ZNet), "Disconnect", (Type[])null, (Type[])null), null, "OnDisconnectPostfix", null, typeof(CompressionPatch)); Patch(harmony, "Steam compress-once send", AccessTools.DeclaredMethod(typeof(ZSteamSocket), "Send", new Type[1] { typeof(ZPackage) }, (Type[])null), "SendPrefix", null, null, typeof(SteamCompressionPatch)); Patch(harmony, "Steam receive", AccessTools.DeclaredMethod(typeof(ZSteamSocket), "Recv", (Type[])null, (Type[])null), null, "ReceivePostfix", null, typeof(SteamCompressionPatch)); Patch(harmony, "PlayFab socket ctor()", AccessTools.DeclaredConstructor(typeof(ZPlayFabSocket), Type.EmptyTypes, false), null, "SocketOpenPostfix", null, typeof(PlayFabCompressionPatch)); Patch(harmony, "PlayFab socket ctor(string,callback)", AccessTools.DeclaredConstructor(typeof(ZPlayFabSocket), new Type[2] { typeof(string), typeof(Action<PlayFabMatchmakingServerData>) }, false), null, "SocketOpenPostfix", null, typeof(PlayFabCompressionPatch)); Patch(harmony, "PlayFab socket ctor(player)", AccessTools.DeclaredConstructor(typeof(ZPlayFabSocket), new Type[1] { typeof(PlayFabPlayer) }, false), null, "SocketOpenPostfix", null, typeof(PlayFabCompressionPatch)); Patch(harmony, "PlayFab socket dispose", AccessTools.DeclaredMethod(typeof(ZPlayFabSocket), "Dispose", (Type[])null, (Type[])null), null, "SocketClosePostfix", null, typeof(PlayFabCompressionPatch)); Patch(harmony, "PlayFab compress", AccessTools.DeclaredMethod(typeof(PlayFabZLibWorkQueue), "DoCompress", (Type[])null, (Type[])null), "CompressPrefix", null, null, typeof(PlayFabCompressionPatch)); Patch(harmony, "PlayFab decompress", AccessTools.DeclaredMethod(typeof(PlayFabZLibWorkQueue), "DoUncompress", (Type[])null, (Type[])null), "DecompressPrefix", null, null, typeof(PlayFabCompressionPatch)); Patch(harmony, "PlayFab direct compress", AccessTools.DeclaredMethod(typeof(PlayFabZLibWorkQueue), "CompressOnThisThread", (Type[])null, (Type[])null), "DirectCompressPrefix", null, null, typeof(PlayFabCompressionPatch)); Patch(harmony, "PlayFab direct decompress", AccessTools.DeclaredMethod(typeof(PlayFabZLibWorkQueue), "UncompressOnThisThread", (Type[])null, (Type[])null), "DirectDecompressPrefix", null, null, typeof(PlayFabCompressionPatch)); } private static void InstallConnectionBuffer(Harmony harmony) { Patch(harmony, "Connection buffer start", AccessTools.DeclaredMethod(typeof(ZNet), "OnNewConnection", (Type[])null, (Type[])null), null, "OnNewConnectionPostfix", null, typeof(ConnectionBufferPatch)); Patch(harmony, "Connection buffer replay", AccessTools.DeclaredMethod(typeof(ZDOMan), "AddPeer", (Type[])null, (Type[])null), null, "AddPeerPostfix", null, typeof(ConnectionBufferPatch)); Patch(harmony, "Connection buffer clear", AccessTools.DeclaredMethod(typeof(ZNet), "Shutdown", (Type[])null, (Type[])null), null, "ShutdownPostfix", null, typeof(ConnectionBufferPatch)); } private static void InstallDedicatedTweaks(Harmony harmony) { if (BetterNetworking10.ForceCrossplay.Value != ForceCrossplayOption.Vanilla) { Patch(harmony, "Dedicated force crossplay", AccessTools.DeclaredMethod(typeof(FejdStartup), "ParseServerArguments", (Type[])null, (Type[])null), null, "ParseArgumentsPostfix", null, typeof(DedicatedPatch)); } else { Skip("Dedicated force crossplay", "vanilla setting"); } if (BetterNetworking10.PlayerLimit.Value != 10) { Patch(harmony, "Dedicated player limit", AccessTools.DeclaredMethod(typeof(ZNet), "RPC_PeerInfo", (Type[])null, (Type[])null), null, null, "PlayerLimitTranspiler", typeof(DedicatedPatch)); } else { Skip("Dedicated player limit", "vanilla value 10"); } } private static void Patch(Harmony harmony, string label, MethodBase target, string prefix = null, string postfix = null, string transpiler = null, Type patchType = null) { //IL_0048: 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) //IL_0078: Unknown result type (might be due to invalid IL or missing references) if (target == null) { _failed++; BetterNetworking10.Log.Error("PATCH FAILED " + label + ": target method not found; feature left vanilla"); return; } try { HarmonyMethod val = ((prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.DeclaredMethod(patchType, prefix, (Type[])null, (Type[])null))); HarmonyMethod val2 = ((postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.DeclaredMethod(patchType, postfix, (Type[])null, (Type[])null))); HarmonyMethod val3 = ((transpiler == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.DeclaredMethod(patchType, transpiler, (Type[])null, (Type[])null))); if ((prefix != null && val.method == null) || (postfix != null && val2.method == null) || (transpiler != null && val3.method == null)) { throw new MissingMethodException("patch method not found"); } harmony.Patch(target, val, val2, val3, (HarmonyMethod)null, (HarmonyMethod)null); _applied++; BetterNetworking10.Log.Message("PATCH OK " + label + ": " + target.DeclaringType?.Name + "." + target.Name); } catch (Exception ex) { _failed++; BetterNetworking10.Log.Error("PATCH FAILED " + label + ": " + ex.GetType().Name + ": " + ex.Message + "; feature left vanilla"); } } private static void Skip(string label, string reason) { _skipped++; BetterNetworking10.Log.Message("PATCH SKIP " + label + ": " + reason); } } internal sealed class BNLog { internal enum LogLevel { Warning, Message, Info } private readonly ManualLogSource _logger; private readonly ConfigEntry<LogLevel> _level; internal bool IsInfo => _level.Value >= LogLevel.Info; internal BNLog(ManualLogSource logger, ConfigFile config) { _logger = logger; _level = config.Bind<LogLevel>("99 - Logging", "Log Level", LogLevel.Message, "Warning, Message, or Info (verbose packet compression logging). "); } internal void Error(object value) { _logger.LogError(value); } internal void Warning(object value) { _logger.LogWarning(value); } internal void Message(object value) { if (_level.Value >= LogLevel.Message) { _logger.LogMessage(value); } } internal void Info(object value) { if (_level.Value >= LogLevel.Info) { _logger.LogInfo(value); } } } internal static class CompressionCodec { private const string DictionaryResource = "DIT.BetterNetworking10.dict.small"; private static Compressor _compressor; private static Decompressor _decompressor; private static readonly object Sync = new object(); internal static void Initialize() { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("DIT.BetterNetworking10.dict.small"); if (stream == null) { throw new InvalidOperationException("Embedded Zstd dictionary is missing"); } byte[] array = new byte[stream.Length]; int num; for (int i = 0; i < array.Length; i += num) { num = stream.Read(array, i, array.Length - i); if (num == 0) { throw new EndOfStreamException("Could not read embedded Zstd dictionary"); } } _compressor = new Compressor(1); _compressor.LoadDictionary(array); _decompressor = new Decompressor(); _decompressor.LoadDictionary(array); } internal static byte[] Compress(byte[] input) { lock (Sync) { return _compressor.Wrap((ReadOnlySpan<byte>)input).ToArray(); } } internal static byte[] Decompress(byte[] input) { lock (Sync) { return _decompressor.Unwrap((ReadOnlySpan<byte>)input, int.MaxValue).ToArray(); } } } internal static class CompressionFrame { private static readonly byte[] Magic = new byte[4] { 66, 78, 49, 48 }; private const int HeaderSize = 5; private const byte RawPayload = 0; private const byte CompressedPayload = 1; internal static byte[] Encode(byte[] input) { return Encode(input, diagnostics: true); } private static byte[] Encode(byte[] input, bool diagnostics) { if (HasHeader(input)) { if (diagnostics) { BetterNetworking10.Log.Warning("Prevented duplicate Better Networking compression frame"); } return input; } byte[] array = CompressionCodec.Compress(input); bool flag = array.Length < input.Length; byte[] array2 = (flag ? array : input); byte[] array3 = new byte[5 + array2.Length]; Buffer.BlockCopy(Magic, 0, array3, 0, Magic.Length); array3[Magic.Length] = (byte)(flag ? 1 : 0); Buffer.BlockCopy(array2, 0, array3, 5, array2.Length); if (diagnostics && BetterNetworking10.Log.IsInfo && input.Length > 256) { if (flag) { BetterNetworking10.Log.Info($"Compression sent {input.Length} B -> {array3.Length} B ({100f * (float)array3.Length / (float)input.Length:0}%, framed)"); } else { BetterNetworking10.Log.Info($"Compression bypassed for {input.Length} B packet because it would grow ({array3.Length} B framed)"); } } return array3; } internal static bool TryDecode(byte[] input, out byte[] output) { output = null; if (!HasHeader(input)) { return false; } int num = input.Length - 5; byte[] array = new byte[num]; Buffer.BlockCopy(input, 5, array, 0, num); byte b = input[Magic.Length]; switch (b) { case 0: output = array; break; case 1: output = CompressionCodec.Decompress(array); break; default: throw new InvalidDataException($"Unknown Better Networking frame type {b}"); } if (BetterNetworking10.Log.IsInfo && output.Length > 256) { BetterNetworking10.Log.Info($"Compression received {input.Length} B -> {output.Length} B (framed)"); } return true; } internal static bool HasHeader(byte[] input) { if (input == null || input.Length < 5) { return false; } for (int i = 0; i < Magic.Length; i++) { if (input[i] != Magic[i]) { return false; } } return true; } internal static void SelfTest() { byte[] array = Enumerable.Repeat((byte)90, 1024).ToArray(); byte[] array2 = Encode(array); if (!TryDecode(array2, out var output) || !array.SequenceEqual(output)) { throw new InvalidDataException("Compression frame round-trip failed"); } if (array2 != Encode(array2, diagnostics: false)) { throw new InvalidDataException("Compression frame duplicate guard failed"); } } } internal static class CompressionState { internal sealed class PeerState { internal int Version; internal bool Enabled; internal volatile bool Sending; internal volatile bool Receiving; internal volatile bool WarnedUnframed; } internal const int ProtocolVersion = 8; private static readonly ConcurrentDictionary<ISocket, PeerState> Peers = new ConcurrentDictionary<ISocket, PeerState>(); internal static void Add(ISocket socket) { if (socket != null) { Peers[socket] = new PeerState(); } } internal static void Remove(ISocket socket) { if (socket != null) { Peers.TryRemove(socket, out var _); } } internal static bool TryGet(ISocket socket, out PeerState state) { state = null; return socket != null && Peers.TryGetValue(socket, out state); } internal static void Clear() { Peers.Clear(); } } internal static class PeerLookup { internal static ZNetPeer ByRpc(ZRpc rpc) { if (rpc == null || (Object)(object)ZNet.instance == (Object)null) { return null; } return ((IEnumerable<ZNetPeer>)ZNet.instance.GetPeers()).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer peer) => peer.m_rpc == rpc)); } internal static string Name(ZNetPeer peer) { if (peer == null) { return "[unknown]"; } if (peer.m_server) { return "[server]"; } string text = ((peer.m_socket == null) ? "?" : peer.m_socket.GetHostName()); return peer.m_playerName + "[" + text + "]"; } } internal static class CompressionPatch { private const string RpcVersion = "CW_Jesse.BetterNetworking.CompressionVersion"; private const string RpcEnabled = "CW_Jesse.BetterNetworking.CompressionEnabled"; private const string RpcStarted = "CW_Jesse.BetterNetworking.CompressedStarted"; internal static void OnConnectPostfix(ZNetPeer peer) { if (peer?.m_socket != null && peer.m_rpc != null) { CompressionState.Add(peer.m_socket); peer.m_rpc.Register<int>("CW_Jesse.BetterNetworking.CompressionVersion", (Action<ZRpc, int>)ReceiveVersion); peer.m_rpc.Register<bool>("CW_Jesse.BetterNetworking.CompressionEnabled", (Action<ZRpc, bool>)ReceiveEnabled); peer.m_rpc.Register<bool>("CW_Jesse.BetterNetworking.CompressedStarted", (Action<ZRpc, bool>)ReceiveStarted); peer.m_rpc.Invoke("CW_Jesse.BetterNetworking.CompressionVersion", new object[1] { 8 }); BetterNetworking10.Log.Message("Compression peer connected: " + PeerLookup.Name(peer)); } } internal static void OnDisconnectPrefix(ZNetPeer peer) { BetterNetworking10.Log.Message("Compression peer disconnected: " + PeerLookup.Name(peer)); } internal static void OnDisconnectPostfix(ZNetPeer peer) { CompressionState.Remove(peer?.m_socket); } private static void ReceiveVersion(ZRpc rpc, int version) { ZNetPeer val = PeerLookup.ByRpc(rpc); if (val != null && CompressionState.TryGet(val.m_socket, out var state)) { state.Version = version; if (version == 8) { BetterNetworking10.Log.Message($"Compression compatible with {PeerLookup.Name(val)} (protocol {version})"); SendEnabled(val); } else if (version > 0) { BetterNetworking10.Log.Warning($"Compression disabled with {PeerLookup.Name(val)}: protocol {version}, expected {8}"); } } } private static void ReceiveEnabled(ZRpc rpc, bool enabled) { ZNetPeer val = PeerLookup.ByRpc(rpc); if (val != null && CompressionState.TryGet(val.m_socket, out var state)) { state.Enabled = enabled; SendStarted(val, BetterNetworking10.CompressionActive && enabled && state.Version == 8); } } private static void ReceiveStarted(ZRpc rpc, bool started) { ZNetPeer val = PeerLookup.ByRpc(rpc); if (val != null && CompressionState.TryGet(val.m_socket, out var state)) { state.Receiving = started; state.WarnedUnframed = false; BetterNetworking10.Log.Message($"Compression from {PeerLookup.Name(val)}: {started}"); } } private static void SendEnabled(ZNetPeer peer) { peer.m_rpc.Invoke("CW_Jesse.BetterNetworking.CompressionEnabled", new object[1] { BetterNetworking10.CompressionActive }); if (CompressionState.TryGet(peer.m_socket, out var state)) { SendStarted(peer, BetterNetworking10.CompressionActive && state.Enabled && state.Version == 8); } } private static void SendStarted(ZNetPeer peer, bool started) { if (CompressionState.TryGet(peer.m_socket, out var state) && state.Sending != started) { peer.m_rpc.Invoke("CW_Jesse.BetterNetworking.CompressedStarted", new object[1] { started }); Flush(peer.m_socket); state.Sending = started; BetterNetworking10.Log.Message($"Compression to {PeerLookup.Name(peer)}: {started}"); } } private static void Flush(ISocket socket) { if (socket is ZSteamSocket) { socket.Flush(); return; } ZPlayFabSocket val = (ZPlayFabSocket)(object)((socket is ZPlayFabSocket) ? socket : null); if (val != null) { PlayFabCompressionPatch.Flush(val); } } } internal static class SteamCompressionPatch { internal static void SendPrefix(ZSteamSocket __instance, ref ZPackage pkg) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown if (pkg != null && CompressionState.TryGet((ISocket)(object)__instance, out var state) && state.Sending) { pkg = new ZPackage(CompressionFrame.Encode(pkg.GetArray())); } } internal static void ReceivePostfix(ZSteamSocket __instance, ref ZPackage __result) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown if (__result == null || !CompressionState.TryGet((ISocket)(object)__instance, out var state)) { return; } byte[] array = __result.GetArray(); try { if (CompressionFrame.TryDecode(array, out var output)) { __result = new ZPackage(output); state.WarnedUnframed = false; if (!state.Receiving) { state.Receiving = true; BetterNetworking10.Log.Warning("Received framed Steamworks packet before compression-start state; recovered automatically"); } } else if (state.Receiving && !state.WarnedUnframed) { state.WarnedUnframed = true; BetterNetworking10.Log.Warning("Received an unframed Steamworks packet while compression was active; packet preserved"); } } catch (Exception ex) { BetterNetworking10.Log.Error("Invalid Better Networking Steamworks frame dropped: " + ex.Message); __result = null; } } } internal static class PlayFabCompressionPatch { private static readonly ConcurrentDictionary<PlayFabZLibWorkQueue, ZPlayFabSocket> Sockets = new ConcurrentDictionary<PlayFabZLibWorkQueue, ZPlayFabSocket>(); private static readonly FieldInfo WorkQueueField = AccessTools.Field(typeof(ZPlayFabSocket), "m_zlibWorkQueue"); private static readonly FieldInfo WorkLockField = AccessTools.Field(typeof(PlayFabZLibWorkQueue), "m_lock"); private static readonly FieldInfo WorkersField = AccessTools.Field(typeof(PlayFabZLibWorkQueue), "s_workers"); private static readonly MethodInfo ExecuteMethod = AccessTools.Method(typeof(PlayFabZLibWorkQueue), "Execute", (Type[])null, (Type[])null); private static readonly MethodInfo LateUpdateMethod = AccessTools.Method(typeof(ZPlayFabSocket), "LateUpdate", (Type[])null, (Type[])null); internal static void SocketOpenPostfix(ZPlayFabSocket __instance, PlayFabZLibWorkQueue ___m_zlibWorkQueue) { if (__instance != null && ___m_zlibWorkQueue != null) { Sockets[___m_zlibWorkQueue] = __instance; } } internal static void SocketClosePostfix(PlayFabZLibWorkQueue ___m_zlibWorkQueue) { if (___m_zlibWorkQueue != null) { Sockets.TryRemove(___m_zlibWorkQueue, out var _); } } internal static bool CompressPrefix(PlayFabZLibWorkQueue __instance, Queue<byte[]> ___m_inCompress, Queue<byte[]> ___m_outCompress) { if (!Sockets.TryGetValue(__instance, out var value)) { return true; } if (!CompressionState.TryGet((ISocket)(object)value, out var state) || !state.Sending) { return true; } while (___m_inCompress.Count > 0) { byte[] input = ___m_inCompress.Dequeue(); try { ___m_outCompress.Enqueue(CompressionFrame.Encode(input)); } catch (Exception ex) { BetterNetworking10.Log.Error("PlayFab compression failed: " + ex.Message); } } return false; } internal static bool DecompressPrefix(PlayFabZLibWorkQueue __instance, Queue<byte[]> ___m_inDecompress, Queue<byte[]> ___m_outDecompress) { if (!Sockets.TryGetValue(__instance, out var value)) { return true; } CompressionState.TryGet((ISocket)(object)value, out var state); while (___m_inDecompress.Count > 0) { byte[] array = ___m_inDecompress.Dequeue(); bool flag = CompressionFrame.HasHeader(array); try { if (CompressionFrame.TryDecode(array, out var output)) { ___m_outDecompress.Enqueue(output); if (state != null && !state.Receiving) { state.Receiving = true; BetterNetworking10.Log.Warning("Received framed PlayFab packet before compression-start state; recovered automatically"); } continue; } } catch (Exception ex) { BetterNetworking10.Log.Error("Invalid Better Networking PlayFab frame dropped: " + ex.Message); continue; } if (!flag) { try { byte[] item = ZlibStream.UncompressBuffer(array); ___m_outDecompress.Enqueue(item); } catch { BetterNetworking10.Log.Warning("PlayFab packet was neither BN-framed nor vanilla-zlib; preserving raw data"); ___m_outDecompress.Enqueue(array); } } } return false; } internal static bool DirectCompressPrefix(PlayFabZLibWorkQueue __instance, byte[] payload, ref byte[] __result) { if (!Sockets.TryGetValue(__instance, out var value)) { return true; } if (!CompressionState.TryGet((ISocket)(object)value, out var state) || !state.Sending) { return true; } __result = CompressionFrame.Encode(payload); return false; } internal static bool DirectDecompressPrefix(PlayFabZLibWorkQueue __instance, byte[] payload, ref byte[] __result) { if (!Sockets.TryGetValue(__instance, out var value)) { return true; } if (!CompressionFrame.HasHeader(payload)) { return true; } try { if (!CompressionFrame.TryDecode(payload, out __result)) { return true; } if (CompressionState.TryGet((ISocket)(object)value, out var state) && !state.Receiving) { state.Receiving = true; BetterNetworking10.Log.Warning("Recovered an early/direct BN-compressed PlayFab packet"); } return false; } catch { return true; } } internal static void Flush(ZPlayFabSocket socket) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (socket == null || WorkQueueField == null || WorkLockField == null || WorkersField == null || ExecuteMethod == null || LateUpdateMethod == null) { return; } _ = (PlayFabZLibWorkQueue)WorkQueueField.GetValue(socket); object value = WorkLockField.GetValue(null); List<PlayFabZLibWorkQueue> list = (List<PlayFabZLibWorkQueue>)WorkersField.GetValue(null); lock (value) { PlayFabZLibWorkQueue[] array = list.ToArray(); foreach (PlayFabZLibWorkQueue obj in array) { ExecuteMethod.Invoke(obj, Array.Empty<object>()); } } LateUpdateMethod.Invoke(socket, null); } } internal static class ConnectionBufferPatch { private const int MaximumPacketsPerConnection = 256; private static readonly ConcurrentDictionary<ZRpc, List<ZPackage>> Buffers = new ConcurrentDictionary<ZRpc, List<ZPackage>>(); private static readonly MethodInfo ReceiveZdoData = AccessTools.DeclaredMethod(typeof(ZDOMan), "RPC_ZDOData", (Type[])null, (Type[])null); internal static void OnNewConnectionPostfix(ZNet __instance, ZNetPeer peer) { if ((Object)(object)__instance == (Object)null || peer?.m_rpc == null || __instance.IsServer()) { return; } List<ZPackage> buffer = Buffers.GetOrAdd(peer.m_rpc, (ZRpc _) => new List<ZPackage>()); peer.m_rpc.Register<ZPackage>("ZDOData", (Action<ZRpc, ZPackage>)delegate(ZRpc rpc, ZPackage package) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown lock (buffer) { if (buffer.Count >= 256) { BetterNetworking10.Log.Error("Connection ZDO buffer reached 256 packets; dropping additional early packets"); } else { buffer.Add(new ZPackage(package.GetArray())); } } }); } internal static void AddPeerPostfix(ZDOMan __instance, ZNetPeer netPeer) { if (__instance == null || netPeer?.m_rpc == null || ReceiveZdoData == null || !Buffers.TryRemove(netPeer.m_rpc, out var value)) { return; } lock (value) { BetterNetworking10.Log.Warning($"Replaying {value.Count} early ZDOData packet(s) for one connection"); foreach (ZPackage item in value) { ReceiveZdoData.Invoke(__instance, new object[2] { netPeer.m_rpc, item }); } } } internal static void ShutdownPostfix() { Clear(); } internal static void Clear() { Buffers.Clear(); } } internal static class DedicatedPatch { internal static void ParseArgumentsPostfix() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (BetterNetworking10.IsDedicated) { switch (BetterNetworking10.ForceCrossplay.Value) { case ForceCrossplayOption.PlayFab: ZNet.m_onlineBackend = (OnlineBackendType)1; ZPlayFabMatchmaking.LookupPublicIP(); break; case ForceCrossplayOption.Steamworks: ZNet.m_onlineBackend = (OnlineBackendType)0; break; } } } internal static int GetPlayerLimit() { return BetterNetworking10.PlayerLimit.Value; } internal static IEnumerable<CodeInstruction> PlayerLimitTranspiler(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = instructions.ToList(); MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(ZNet), "GetNrOfPlayers", (Type[])null, (Type[])null); MethodInfo operand = AccessTools.DeclaredMethod(typeof(DedicatedPatch), "GetPlayerLimit", (Type[])null, (Type[])null); int num = -1; for (int i = 1; i < list.Count; i++) { if ((CodeInstructionExtensions.Is(list[i], OpCodes.Ldc_I4_S, (object)(sbyte)10) || CodeInstructionExtensions.Is(list[i], OpCodes.Ldc_I4, (object)10)) && CodeInstructionExtensions.Calls(list[i - 1], methodInfo)) { if (num != -1) { BetterNetworking10.Log.Error("Player-limit transpiler found multiple guard constants; leaving RPC_PeerInfo unmodified"); return list; } num = i; } } if (num == -1) { BetterNetworking10.Log.Error("Player-limit transpiler could not identify the 1.0 server-full guard; leaving RPC_PeerInfo unmodified"); return list; } list[num].opcode = OpCodes.Call; list[num].operand = operand; BetterNetworking10.Log.Message($"Player-limit transpiler verified 1/1 IL match; limit={BetterNetworking10.PlayerLimit.Value}"); return list; } } internal static class LegacyConfigMigration { private const string LegacyFileName = "CW_Jesse.BetterNetworking.cfg"; internal static void TryImport(ConfigFile config, BNLog log) { string path = Path.Combine(Paths.ConfigPath, "CW_Jesse.BetterNetworking.cfg"); if (!File.Exists(path)) { return; } try { Dictionary<string, string> values = ReadValues(path); int num = 0; if (TryGet(values, "Networking", "Compression Enabled", out var value) && TryParseBoolean(value, out var result)) { BetterNetworking10.EnableCompression.Value = result; num++; } if (TryGet(values, "Networking", "Queue Size", out var value2) && TryParseQueueSize(value2, out var result2)) { BetterNetworking10.EnableQueueSize.Value = true; BetterNetworking10.QueueSize.Value = result2; num++; } if (TryGet(values, "Networking", "Update Rate", out var value3) && TryParseUpdateRate(value3, out var result3)) { BetterNetworking10.EnableUpdateRate.Value = true; BetterNetworking10.UpdateRate.Value = result3; num++; } if (TryGet(values, "Networking (Steamworks)", "Minimum Send Rate", out var value4) && TryParseSendRate(value4, out var result4)) { BetterNetworking10.EnableSendRate.Value = true; BetterNetworking10.MinimumSendRate.Value = result4; num++; } if (TryGet(values, "Networking (Steamworks)", "Maximum Send Rate", out var value5) && TryParseSendRate(value5, out var result5)) { BetterNetworking10.EnableSendRate.Value = true; BetterNetworking10.MaximumSendRate.Value = result5; num++; } if (TryGet(values, "Dedicated Server", "Force Crossplay", out var value6) && TryParseCrossplay(value6, out var result6)) { BetterNetworking10.ForceCrossplay.Value = result6; num++; } if (TryGet(values, "Dedicated Server", "Player Limit", out var value7) && int.TryParse(value7.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result7)) { BetterNetworking10.PlayerLimit.Value = Math.Max(1, Math.Min(127, result7)); num++; } if (TryGet(values, "Logging", "Log Level", out var value8) && TryParseLogLevel(value8, out var result8)) { config.Bind<BNLog.LogLevel>("99 - Logging", "Log Level", BNLog.LogLevel.Message, (ConfigDescription)null).Value = result8; num++; } BetterNetworking10.EnableConnectionBuffer.Value = false; config.Save(); log.Message(string.Format("Imported {0} setting(s) from legacy config {1}. The old file was left unchanged.", num, "CW_Jesse.BetterNetworking.cfg")); } catch (Exception ex) { log.Warning("Legacy config import skipped: " + ex.GetType().Name + ": " + ex.Message); } } private static Dictionary<string, string> ReadValues(string path) { Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); string section = string.Empty; string[] array = File.ReadAllLines(path); foreach (string text in array) { string text2 = text.Trim(); if (text2.Length == 0 || text2.StartsWith("#") || text2.StartsWith(";")) { continue; } if (text2.StartsWith("[") && text2.EndsWith("]")) { section = text2.Substring(1, text2.Length - 2).Trim(); continue; } int num = text2.IndexOf('='); if (num > 0) { string key = text2.Substring(0, num).Trim(); string value = text2.Substring(num + 1).Trim(); dictionary[MakeKey(section, key)] = value; } } return dictionary; } private static bool TryGet(Dictionary<string, string> values, string section, string key, out string value) { return values.TryGetValue(MakeKey(section, key), out value); } private static string MakeKey(string section, string key) { return section + "\n" + key; } private static string Normalize(string value) { return new string(value.Where(char.IsLetterOrDigit).Select(char.ToLowerInvariant).ToArray()); } private static bool TryParseBoolean(string value, out bool result) { string text = Normalize(value); if (text == "true" || text == "enabled") { result = true; return true; } if (text == "false" || text == "disabled") { result = false; return true; } result = false; return false; } private static bool TryParseQueueSize(string value, out QueueSizeOption result) { string text = Normalize(value); if (text.Contains("80")) { result = QueueSizeOption.KB80; } else if (text.Contains("64")) { result = QueueSizeOption.KB64; } else if (text.Contains("48")) { result = QueueSizeOption.KB48; } else if (text.Contains("32")) { result = QueueSizeOption.KB32; } else { if (!text.Contains("vanilla") && !text.Contains("10")) { result = (QueueSizeOption)0; return false; } result = QueueSizeOption.Vanilla; } return true; } private static bool TryParseUpdateRate(string value, out UpdateRateOption result) { string text = Normalize(value); if (text.Contains("100")) { result = UpdateRateOption.Percent100; } else if (text.Contains("75")) { result = UpdateRateOption.Percent75; } else { if (!text.Contains("50")) { result = (UpdateRateOption)0; return false; } result = UpdateRateOption.Percent50; } return true; } private static bool TryParseSendRate(string value, out SendRateOption result) { string text = Normalize(value); if (text.Contains("1024")) { result = SendRateOption.KB1024; } else if (text.Contains("768")) { result = SendRateOption.KB768; } else if (text.Contains("512")) { result = SendRateOption.KB512; } else if (text.Contains("256")) { result = SendRateOption.KB256; } else { if (!text.Contains("150")) { result = (SendRateOption)0; return false; } result = SendRateOption.KB150; } return true; } private static bool TryParseCrossplay(string value, out ForceCrossplayOption result) { string text = Normalize(value); if (text.Contains("playfab") || text.Contains("enabled")) { result = ForceCrossplayOption.PlayFab; } else if (text.Contains("steamworks") || text.Contains("disabled")) { result = ForceCrossplayOption.Steamworks; } else { if (!text.Contains("vanilla")) { result = ForceCrossplayOption.Vanilla; return false; } result = ForceCrossplayOption.Vanilla; } return true; } private static bool TryParseLogLevel(string value, out BNLog.LogLevel result) { string text = Normalize(value); if (text.Contains("info")) { result = BNLog.LogLevel.Info; } else if (text.Contains("message")) { result = BNLog.LogLevel.Message; } else { if (!text.Contains("warning")) { result = BNLog.LogLevel.Warning; return false; } result = BNLog.LogLevel.Warning; } return true; } } internal static class QueueBudgetPatch { internal static int GetBudget() { return BetterNetworking10.QueueBudgetBytes; } internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = instructions.ToList(); int num = list.Count((CodeInstruction i) => CodeInstructionExtensions.Is(i, OpCodes.Ldc_I4, (object)10240)); if (num != 2) { BetterNetworking10.Log.Error($"Queue transpiler expected exactly 2 vanilla 10240 constants but found {num}; leaving SendZDOs unmodified"); return list; } MethodInfo operand = AccessTools.DeclaredMethod(typeof(QueueBudgetPatch), "GetBudget", (Type[])null, (Type[])null); foreach (CodeInstruction item in list) { if (CodeInstructionExtensions.Is(item, OpCodes.Ldc_I4, (object)10240)) { item.opcode = OpCodes.Call; item.operand = operand; } } BetterNetworking10.Log.Message($"Queue transpiler verified 2/2 IL matches; budget={BetterNetworking10.QueueBudgetBytes / 1024} KB"); return list; } } internal static class UpdateRatePatch { internal static void Prefix(ref float dt) { dt *= BetterNetworking10.UpdateRateMultiplier; } } internal static class SteamSendRatePatch { internal static void Postfix() { int num = BetterNetworking10.RateBytesPerSecond(BetterNetworking10.MinimumSendRate.Value); int num2 = BetterNetworking10.RateBytesPerSecond(BetterNetworking10.MaximumSendRate.Value); if (num > num2) { int num3 = num; num = num2; num2 = num3; BetterNetworking10.Log.Warning("Steam send-rate minimum exceeded maximum; values were swapped at runtime"); } Set((ESteamNetworkingConfigValue)10, num); Set((ESteamNetworkingConfigValue)11, num2); } private static void Set(ESteamNetworkingConfigValue key, int value) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0018: 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) GCHandle gCHandle = GCHandle.Alloc(value, GCHandleType.Pinned); try { if (BetterNetworking10.IsDedicated) { SteamGameServerNetworkingUtils.SetConfigValue(key, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)1, gCHandle.AddrOfPinnedObject()); } else { SteamNetworkingUtils.SetConfigValue(key, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)1, gCHandle.AddrOfPinnedObject()); } BetterNetworking10.Log.Message($"Steamworks {key} set to {value / 1024} KB/s"); } catch (Exception ex) { BetterNetworking10.Log.Error($"Could not set Steamworks {key}: {ex.Message}"); } finally { gCHandle.Free(); } } } internal static class PortalGhostRefresh { private const string StandalonePluginGuid = "sfantul.portalobserverfix"; private const float CheckIntervalSeconds = 0.25f; private const float DelayedRefreshSeconds = 0.75f; private const float MinimumJumpDistance = 64f; private const float MinimumJumpDistanceSquared = 4096f; private static readonly Dictionary<ZDOID, Vector3> LastPositions = new Dictionary<ZDOID, Vector3>(); private static readonly Dictionary<ZDOID, float> DelayedRefreshes = new Dictionary<ZDOID, float>(); private static readonly HashSet<ZDOID> ActivePlayers = new HashSet<ZDOID>(); private static readonly List<ZDOID> TemporaryIds = new List<ZDOID>(); private static ZDOMan _manager; private static float _nextCheck; private static bool _externalModInstalled; private static bool _runtimeDisabled; internal static void Initialize() { _externalModInstalled = Chainloader.PluginInfos.ContainsKey("sfantul.portalobserverfix"); _runtimeDisabled = false; if (!BetterNetworking10.PortalGhostFixActive) { BetterNetworking10.Log.Message("FEATURE SKIP Portal ghost fix: disabled by configuration"); } else if (_externalModInstalled) { BetterNetworking10.Log.Warning("FEATURE SKIP Portal ghost fix: standalone Sfantul-PortalGhostFix detected; built-in refresh disabled to avoid duplicate sends"); } else { BetterNetworking10.Log.Message("FEATURE ON Portal ghost fix: server-side player and linked status-effect refresh; vanilla clients supported"); } } internal static void Tick() { //IL_00c9: 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_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: 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_00dc: 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_00ff: 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_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0141: 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_0126: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: 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_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0158: 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_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0186: 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_01b6: 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_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_0222: 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_0207: Unknown result type (might be due to invalid IL or missing references) //IL_0214: 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_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_02db: Unknown result type (might be due to invalid IL or missing references) if (!BetterNetworking10.PortalGhostFixActive || _externalModInstalled || _runtimeDisabled) { return; } ZNet instance = ZNet.instance; ZDOMan instance2 = ZDOMan.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer() || instance2 == null) { if (_manager != null) { ClearRuntimeState(); } return; } if (_manager != instance2) { ClearRuntimeState(); _manager = instance2; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < _nextCheck) { return; } _nextCheck = realtimeSinceStartup + 0.25f; try { ActivePlayers.Clear(); foreach (PlayerInfo player in instance.GetPlayerList()) { ZDOID characterID = player.m_characterID; if (characterID == ZDOID.None) { continue; } ActivePlayers.Add(characterID); ZDO zDO = instance2.GetZDO(characterID); if (zDO == null) { LastPositions.Remove(characterID); DelayedRefreshes.Remove(characterID); continue; } Vector3 position = zDO.GetPosition(); if (LastPositions.TryGetValue(characterID, out var value)) { Vector3 val = position - value; if (((Vector3)(ref val)).sqrMagnitude >= 4096f) { int num = ForceRefresh(instance2, characterID); DelayedRefreshes[characterID] = realtimeSinceStartup + 0.75f; BetterNetworking10.Log.Message($"Portal ghost refresh queued for {SafePlayerName(player.m_name)} ({characterID}) after {((Vector3)(ref val)).magnitude:F1} m movement; linked effects={num}"); } } if (DelayedRefreshes.TryGetValue(characterID, out var value2) && realtimeSinceStartup >= value2) { ForceRefresh(instance2, characterID); DelayedRefreshes.Remove(characterID); } LastPositions[characterID] = position; } TemporaryIds.Clear(); foreach (ZDOID key in LastPositions.Keys) { if (!ActivePlayers.Contains(key)) { TemporaryIds.Add(key); } } foreach (ZDOID temporaryId in TemporaryIds) { LastPositions.Remove(temporaryId); DelayedRefreshes.Remove(temporaryId); } } catch (Exception ex) { _runtimeDisabled = true; ClearRuntimeState(); BetterNetworking10.Log.Error("FEATURE FAILED Portal ghost fix: " + ex.GetType().Name + ": " + ex.Message + ". Disabled until restart; networking remains otherwise unchanged"); } } internal static void Clear() { ClearRuntimeState(); _externalModInstalled = false; _runtimeDisabled = false; } private static int ForceRefresh(ZDOMan manager, ZDOID characterId) { //IL_0002: 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_002a: 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_003a: 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) manager.ForceSendZDO(characterId); int num = 0; List<ZDOID> allZDOIDsWithHash = ZDOExtraData.GetAllZDOIDsWithHash((Type)4, ZDOVars.s_ownerZDOUserId); foreach (ZDOID item in allZDOIDsWithHash) { if (!(item == characterId)) { ZDO zDO = manager.GetZDO(item); if (zDO != null && zDO.GetLong(ZDOVars.s_ownerZDOUserId, 0L) == ((ZDOID)(ref characterId)).UserID && zDO.GetLong(ZDOVars.s_ownerZDOId, 0L) == ((ZDOID)(ref characterId)).ID) { manager.ForceSendZDO(item); num++; } } } return num; } private static void ClearRuntimeState() { LastPositions.Clear(); DelayedRefreshes.Clear(); ActivePlayers.Clear(); TemporaryIds.Clear(); _manager = null; _nextCheck = 0f; } private static string SafePlayerName(string name) { return string.IsNullOrEmpty(name) ? "unknown player" : name; } } internal static class VersionInfo { public const string Version = "1.2.0"; } } namespace Costura { [CompilerGenerated] internal static class AssemblyLoader { private static object nullCacheLock = new object(); private static Dictionary<string, bool> nullCache = new Dictionary<string, bool>(); private static Dictionary<string, string> assemblyNames = new Dictionary<string, string>(); private static Dictionary<string, string> symbolNames = new Dictionary<string, string>(); private static int isAttached; private static string CultureToString(CultureInfo culture) { if (culture == null) { return ""; } return culture.Name; } private static Assembly ReadExistingAssembly(AssemblyName name) { AppDomain currentDomain = AppDomain.CurrentDomain; Assembly[] assemblies = currentDomain.GetAssemblies(); Assembly[] array = assemblies; foreach (Assembly assembly in array) { AssemblyName name2 = assembly.GetName(); if (string.Equals(name2.Name, name.Name, StringComparison.InvariantCultureIgnoreCase) && string.Equals(CultureToString(name2.CultureInfo), CultureToString(name.CultureInfo), StringComparison.InvariantCultureIgnoreCase)) { return assembly; } } return null; } private static void CopyTo(Stream source, Stream destination) { byte[] array = new byte[81920]; int count; while ((count = source.Read(array, 0, array.Length)) != 0) { destination.Write(array, 0, count); } } private static Stream LoadStream(string fullName) { Assembly executingAssembly = Assembly.GetExecutingAssembly(); if (fullName.EndsWith(".compressed")) { using (Stream stream = executingAssembly.GetManifestResourceStream(fullName)) { using DeflateStream source = new DeflateStream(stream, CompressionMode.Decompress); MemoryStream memoryStream = new MemoryStream(); CopyTo(source, memoryStream); memoryStream.Position = 0L; return memoryStream; } } return executingAssembly.GetManifestResourceStream(fullName); } private static Stream LoadStream(Dictionary<string, string> resourceNames, string name) { if (resourceNames.TryGetValue(name, out var value)) { return LoadStream(value); } return null; } private static byte[] ReadStream(Stream stream) { byte[] array = new byte[stream.Length]; stream.Read(array, 0, array.Length); return array; } private static Assembly ReadFromEmbeddedResources(Dictionary<string, string> assemblyNames, Dictionary<string, string> symbolNames, AssemblyName requestedAssemblyName) { string text = requestedAssemblyName.Name.ToLowerInvariant(); if (requestedAssemblyName.CultureInfo != null && !string.IsNullOrEmpty(requestedAssemblyName.CultureInfo.Name)) { text = requestedAssemblyName.CultureInfo.Name + "." + text; } byte[] rawAssembly; using (Stream stream = LoadStream(assemblyNames, text)) { if (stream == null) { return null; } rawAssembly = ReadStream(stream); } using (Stream stream2 = LoadStream(symbolNames, text)) { if (stream2 != null) { byte[] rawSymbolStore = ReadStream(stream2); return Assembly.Load(rawAssembly, rawSymbolStore); } } return Assembly.Load(rawAssembly); } public static Assembly ResolveAssembly(object sender, ResolveEventArgs e) { lock (nullCacheLock) { if (nullCache.ContainsKey(e.Name)) { return null; } } AssemblyName assemblyName = new AssemblyName(e.Name); Assembly assembly = ReadExistingAssembly(assemblyName); if ((object)assembly != null) { return assembly; } assembly = ReadFromEmbeddedResources(assemblyNames, symbolNames, assemblyName); if ((object)assembly == null) { lock (nullCacheLock) { nullCache[e.Name] = true; } if ((assemblyName.Flags & AssemblyNameFlags.Retargetable) != AssemblyNameFlags.None) { assembly = Assembly.Load(assemblyName); } } return assembly; } static AssemblyLoader() { assemblyNames.Add("costura", "costura.costura.dll.compressed"); symbolNames.Add("costura", "costura.costura.pdb.compressed"); assemblyNames.Add("system.buffers", "costura.system.buffers.dll.compressed"); assemblyNames.Add("system.diagnostics.diagnosticsource", "costura.system.diagnostics.diagnosticsource.dll.compressed"); assemblyNames.Add("system.memory", "costura.system.memory.dll.compressed"); assemblyNames.Add("system.numerics.vectors", "costura.system.numerics.vectors.dll.compressed"); assemblyNames.Add("system.runtime.compilerservices.unsafe", "costura.system.runtime.compilerservices.unsafe.dll.compressed"); assemblyNames.Add("system.threading.tasks.extensions", "costura.system.threading.tasks.extensions.dll.compressed"); assemblyNames.Add("zstdsharp", "costura.zstdsharp.dll.compressed"); } public static void Attach() { if (Interlocked.Exchange(ref isAttached, 1) == 1) { return; } AppDomain currentDomain = AppDomain.CurrentDomain; currentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs e) { lock (nullCacheLock) { if (nullCache.ContainsKey(e.Name)) { return (Assembly)null; } } AssemblyName assemblyName = new AssemblyName(e.Name); Assembly assembly = ReadExistingAssembly(assemblyName); if ((object)assembly != null) { return assembly; } assembly = ReadFromEmbeddedResources(assemblyNames, symbolNames, assemblyName); if ((object)assembly == null) { lock (nullCacheLock) { nullCache[e.Name] = true; } if ((assemblyName.Flags & AssemblyNameFlags.Retargetable) != AssemblyNameFlags.None) { assembly = Assembly.Load(assemblyName); } } return assembly; }; } } } internal class DITBetterNetworking10_ProcessedByFody { internal const string FodyVersion = "6.9.3.0"; internal const string Costura = "5.7.0"; }