Decompiled source of PEAKNetworkingLibrary v1.0.7
off_grid.NetworkingLibrary.dll
Decompiled 2 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using NetworkingLibrary; using NetworkingLibrary.Features; using NetworkingLibrary.Modules; using NetworkingLibrary.Services; using Photon.Pun; using Photon.Realtime; using Steamworks; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("DAa")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Networking library for mods using Steam instead of PUN.")] [assembly: AssemblyFileVersion("1.0.7.0")] [assembly: AssemblyInformationalVersion("1.0.7")] [assembly: AssemblyProduct("NetworkingLibrary")] [assembly: AssemblyTitle("off_grid.NetworkingLibrary")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.7.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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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; } } } public class SteamCallbackPump : MonoBehaviour { private void Update() { try { SteamAPI.RunCallbacks(); } catch (Exception arg) { try { Net.Logger.LogError((object)$"SteamAPI.RunCallbacks error: {arg}"); } catch { } } } } public class UnityMainThreadDispatcher : MonoBehaviour { [CompilerGenerated] private sealed class <EnqueueDelayed>d__4 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public float d; public Action a; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <EnqueueDelayed>d__4(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(d); <>1__state = 1; return true; case 1: <>1__state = -1; lock (queue) { queue.Enqueue(a); } 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(); } } private static UnityMainThreadDispatcher? instance; private static readonly Queue<Action> queue = new Queue<Action>(); public static UnityMainThreadDispatcher Instance() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown if ((Object)(object)instance == (Object)null) { GameObject val = GameObject.Find("UnityMainThreadDispatcher"); if ((Object)(object)val == (Object)null) { val = new GameObject("UnityMainThreadDispatcher"); Object.DontDestroyOnLoad((Object)(object)val); instance = val.AddComponent<UnityMainThreadDispatcher>(); } else { instance = val.GetComponent<UnityMainThreadDispatcher>() ?? val.AddComponent<UnityMainThreadDispatcher>(); } } return instance; } public void Enqueue(Action a, float delaySeconds = 0f) { if (delaySeconds <= 0f) { lock (queue) { queue.Enqueue(a); return; } } ((MonoBehaviour)this).StartCoroutine(EnqueueDelayed(a, delaySeconds)); } [IteratorStateMachine(typeof(<EnqueueDelayed>d__4))] private IEnumerator EnqueueDelayed(Action a, float d) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <EnqueueDelayed>d__4(0) { a = a, d = d }; } private void Update() { while (true) { Action action = null; lock (queue) { if (queue.Count <= 0) { break; } action = queue.Dequeue(); } try { action?.Invoke(); } catch (Exception arg) { Debug.LogError((object)$"Dispatcher action error: {arg}"); } } } } namespace NetworkingLibrary { [BepInPlugin("off_grid.NetworkingLibrary", "NetworkingLibrary", "1.0.7")] public class Net : BaseUnityPlugin { internal static Harmony Harmony = new Harmony("off_grid.NetworkingLibrary"); public ConfigFile config; public static Net Instance { get; private set; } = null; internal static ManualLogSource Logger { get; private set; } = null; public static INetworkingService? Service { get; private set; } private void OnDestroy() { Service?.Shutdown(); } private void Awake() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown Logger = ((BaseUnityPlugin)this).Logger; Instance = this; FileManager.InitializeConfig(); Harmony.PatchAll(); Service = NetworkingServiceFactory.CreateDefaultService(); Service.Initialize(); GameObject val = new GameObject("NetworkingLibrary.Poller"); ((Object)val.AddComponent<NetworkingPoller>()).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)val); Logger.LogInfo((object)"NetworkingLibrary v1.0.7 has fully loaded!"); } } public static class MyPluginInfo { public const string PLUGIN_GUID = "off_grid.NetworkingLibrary"; public const string PLUGIN_NAME = "NetworkingLibrary"; public const string PLUGIN_VERSION = "1.0.7"; } } namespace NetworkingLibrary.Services { public enum ReliableType { Unreliable, Reliable, UnreliableNoDelay } public interface INetworkingService { bool IsInitialized { get; } bool InLobby { get; } ulong HostSteamId64 { get; } string HostIdString { get; } bool IsHost { get; } Func<Message, ulong, bool>? IncomingValidator { get; set; } event Action? LobbyCreated; event Action? LobbyEntered; event Action? LobbyLeft; event Action<ulong>? PlayerEntered; event Action<ulong>? PlayerLeft; event Action<string[]>? LobbyDataChanged; event Action<ulong, string[]>? PlayerDataChanged; ulong GetLocalSteam64(); ulong[] GetLobbyMemberSteamIds(); void Initialize(); void Shutdown(); void CreateLobby(int maxPlayers = 8); void JoinLobby(ulong lobbySteamId64); void LeaveLobby(); void InviteToLobby(ulong steamId64); IDisposable RegisterNetworkObject(object instance, uint modId, int mask = 0); IDisposable RegisterNetworkType(Type type, uint modId, int mask = 0); void DeregisterNetworkObject(object instance, uint modId, int mask = 0); void DeregisterNetworkType(Type type, uint modId, int mask = 0); void RPC(uint modId, string methodName, ReliableType reliable, params object[] parameters); void RPCTarget(uint modId, string methodName, ulong targetSteamId64, ReliableType reliable, params object[] parameters); void RPCToHost(uint modId, string methodName, ReliableType reliable, params object[] parameters); void RegisterLobbyDataKey(string key); void SetLobbyData(string key, object value); T GetLobbyData<T>(string key); void RegisterPlayerDataKey(string key); void SetPlayerData(string key, object value); T GetPlayerData<T>(ulong steamId64, string key); void PollReceive(); void RegisterModSigner(uint modId, Func<byte[], byte[]> signerDelegate); void RegisterModPublicKey(uint modId, RSAParameters pub); } public class NetworkingPoller : MonoBehaviour { private void Update() { try { Net.Service?.PollReceive(); } catch (Exception arg) { Net.Logger.LogError((object)$"NetworkingPollerDebug PollReceive error: {arg}"); } } private void Awake() { Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); } private void OnDestroy() { } } public static class NetworkingServiceFactory { public static INetworkingService CreateDefaultService() { if (SteamAPI.IsSteamRunning()) { Net.Logger.LogInfo((object)"Steam type present. Creating SteamNetworkingService."); return new SteamNetworkingService(); } Net.Logger.LogInfo((object)"Steam not available. Creating OfflineNetworkingService."); return new OfflineNetworkingService(); } } public class OfflineNetworkingService : INetworkingService { private class Token : IDisposable { private Action on; public Token(Action on) { this.on = on; } public void Dispose() { on(); } } private class SlidingWindowRateLimiter { private readonly int limit; private readonly TimeSpan window; private readonly Queue<DateTime> q = new Queue<DateTime>(); public SlidingWindowRateLimiter(int limit, TimeSpan window) { this.limit = limit; this.window = window; } public bool IncomingAllowed() { DateTime utcNow = DateTime.UtcNow; while (q.Count > 0 && utcNow - q.Peek() > window) { q.Dequeue(); } if (q.Count >= limit) { return false; } q.Enqueue(utcNow); return true; } } private class MessageHandler { public object Target; public MethodInfo Method; public ParameterInfo[] Parameters; public bool TakesInfo; public int Mask; } private readonly Dictionary<uint, Dictionary<string, List<MessageHandler>>> rpcs = new Dictionary<uint, Dictionary<string, List<MessageHandler>>>(); private readonly Dictionary<string, string> lobbyData = new Dictionary<string, string>(); private readonly Dictionary<ulong, Dictionary<string, string>> perPlayerData = new Dictionary<ulong, Dictionary<string, string>>(); private readonly HashSet<string> lobbyKeys = new HashSet<string>(); private readonly HashSet<string> playerKeys = new HashSet<string>(); private readonly SlidingWindowRateLimiter rateLimiter = new SlidingWindowRateLimiter(100, TimeSpan.FromSeconds(1.0)); private readonly Dictionary<ulong, byte[]> perPeerSymmetricKey = new Dictionary<ulong, byte[]>(); private byte[]? globalSharedSecret; private HMACSHA256? globalHmac; private long _nextMessageId; private bool offlineIsHost; public bool IsInitialized { get; private set; } public bool InLobby { get; private set; } public ulong HostSteamId64 { get; private set; } = 1uL; public string HostIdString => HostSteamId64.ToString(); public Func<Message, ulong, bool>? IncomingValidator { get; set; } public ulong LocalSteamId { get; private set; } = 1000uL; public bool IsHost => offlineIsHost; public event Action? LobbyCreated; public event Action? LobbyEntered; public event Action? LobbyLeft; public event Action<ulong>? PlayerEntered; public event Action<ulong>? PlayerLeft; public event Action<string[]>? LobbyDataChanged; public event Action<ulong, string[]>? PlayerDataChanged; public ulong GetLocalSteam64() { return 0uL; } public ulong[] GetLobbyMemberSteamIds() { return Array.Empty<ulong>(); } public void RegisterModSigner(uint modId, Func<byte[], byte[]> signerDelegate) { Net.Logger.LogWarning((object)"This feature is currently not setup in the offline system"); } public void RegisterModPublicKey(uint modId, RSAParameters pub) { Net.Logger.LogWarning((object)"This feature is currently not setup in the offline system"); } private ulong NextMessageId() { return (ulong)Interlocked.Increment(ref _nextMessageId); } public void Initialize() { if (!IsInitialized) { RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create(); byte[] array = new byte[32]; randomNumberGenerator.GetBytes(array); perPeerSymmetricKey[LocalSteamId] = array; IsInitialized = true; } } public void Shutdown() { IsInitialized = false; rpcs.Clear(); perPeerSymmetricKey.Clear(); globalHmac?.Dispose(); globalHmac = null; } public void CreateLobby(int maxPlayers = 8) { InLobby = true; HostSteamId64 = LocalSteamId; perPlayerData[LocalSteamId] = new Dictionary<string, string>(); this.LobbyCreated?.Invoke(); this.LobbyEntered?.Invoke(); offlineIsHost = true; } public void JoinLobby(ulong lobbySteamId64) { InLobby = true; HostSteamId64 = lobbySteamId64; if (!perPlayerData.ContainsKey(LocalSteamId)) { perPlayerData[LocalSteamId] = new Dictionary<string, string>(); } this.LobbyEntered?.Invoke(); this.PlayerEntered?.Invoke(LocalSteamId); } public void LeaveLobby() { InLobby = false; this.LobbyLeft?.Invoke(); offlineIsHost = false; } public void InviteToLobby(ulong steamId64) { this.PlayerEntered?.Invoke(steamId64); } public IDisposable RegisterNetworkObject(object instance, uint modId, int mask = 0) { object instance2 = instance; if (instance2 == null) { throw new ArgumentNullException("instance"); } Type type = instance2.GetType(); int num = 0; MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { CustomRPCAttribute[] array = methodInfo.GetCustomAttributes(inherit: false).OfType<CustomRPCAttribute>().ToArray(); if (array.Length != 0) { if (!rpcs.ContainsKey(modId)) { rpcs[modId] = new Dictionary<string, List<MessageHandler>>(); } if (!rpcs[modId].ContainsKey(methodInfo.Name)) { rpcs[modId][methodInfo.Name] = new List<MessageHandler>(); } rpcs[modId][methodInfo.Name].Add(new MessageHandler { Target = instance2, Method = methodInfo, Parameters = methodInfo.GetParameters(), TakesInfo = (methodInfo.GetParameters().Length != 0 && methodInfo.GetParameters().Last().ParameterType.Name == "RPCInfo"), Mask = mask }); num++; } } return new Token(delegate { DeregisterNetworkObject(instance2, modId, mask); }); } public IDisposable RegisterNetworkType(Type type, uint modId, int mask = 0) { Net.Logger.LogWarning((object)"This feature is currently not setup in the offline system"); return new Token(delegate { DeregisterNetworkObject(null, modId, mask); }); } public void DeregisterNetworkObject(object instance, uint modId, int mask = 0) { if (!rpcs.TryGetValue(modId, out Dictionary<string, List<MessageHandler>> value)) { return; } MethodInfo[] methods = instance.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { CustomRPCAttribute[] array = methodInfo.GetCustomAttributes(inherit: false).OfType<CustomRPCAttribute>().ToArray(); if (array.Length == 0 || !value.TryGetValue(methodInfo.Name, out var value2)) { continue; } for (int num = value2.Count - 1; num >= 0; num--) { if (value2[num].Target == instance && value2[num].Mask == mask) { value2.RemoveAt(num); } } } } public void DeregisterNetworkType(Type type, uint modId, int mask = 0) { Net.Logger.LogWarning((object)"This feature is currently not setup in the offline system"); } public void RPC(uint modId, string methodName, ReliableType reliable, params object[] parameters) { Message message = BuildMessage(modId, methodName, 0, parameters); if (message != null) { DispatchIncoming(message, LocalSteamId); } } public void RPCTarget(uint modId, string methodName, ulong targetSteamId64, ReliableType reliable, params object[] parameters) { Message message = BuildMessage(modId, methodName, 0, parameters); if (message != null && targetSteamId64 == LocalSteamId) { DispatchIncoming(message, LocalSteamId); } } public void RPCToHost(uint modId, string methodName, ReliableType reliable, params object[] parameters) { RPCTarget(modId, methodName, HostSteamId64, reliable, parameters); } public void RegisterLobbyDataKey(string key) { lobbyKeys.Add(key); } public void SetLobbyData(string key, object value) { if (!InLobby) { Debug.LogError((object)"Cannot set lobby data when not in lobby."); return; } if (!lobbyKeys.Contains(key)) { Debug.LogWarning((object)("Accessing unregistered lobby key " + key)); } lobbyData[key] = Convert.ToString(value, CultureInfo.InvariantCulture); this.LobbyDataChanged?.Invoke(new string[1] { key }); } public T GetLobbyData<T>(string key) { if (!InLobby) { Debug.LogError((object)"Cannot get lobby data when not in lobby."); return default(T); } if (!lobbyKeys.Contains(key)) { Debug.LogWarning((object)("Accessing unregistered lobby key " + key)); } if (!lobbyData.TryGetValue(key, out string value)) { return default(T); } try { return (T)Convert.ChangeType(value, typeof(T), CultureInfo.InvariantCulture); } catch { Debug.LogError((object)("Could not parse lobby data [" + key + "," + value + "]")); return default(T); } } public void RegisterPlayerDataKey(string key) { playerKeys.Add(key); } public void SetPlayerData(string key, object value) { if (!InLobby) { Debug.LogError((object)"Cannot set player data when not in lobby."); return; } if (!playerKeys.Contains(key)) { Debug.LogWarning((object)("Accessing unregistered player key " + key)); } perPlayerData[LocalSteamId][key] = Convert.ToString(value, CultureInfo.InvariantCulture); this.PlayerDataChanged?.Invoke(LocalSteamId, new string[1] { key }); } public T GetPlayerData<T>(ulong steamId64, string key) { if (!InLobby) { Debug.LogError((object)"Cannot get player data when not in lobby."); return default(T); } if (!playerKeys.Contains(key)) { Debug.LogWarning((object)("Accessing unregistered player key " + key)); } if (!perPlayerData.TryGetValue(steamId64, out Dictionary<string, string> value)) { return default(T); } if (!value.TryGetValue(key, out var value2)) { return default(T); } try { return (T)Convert.ChangeType(value2, typeof(T), CultureInfo.InvariantCulture); } catch { Debug.LogError((object)("Could not parse player data [" + key + "," + value2 + "]")); return default(T); } } public void PollReceive() { } private Message? BuildMessage(uint modId, string methodName, int mask, object[] parameters) { try { Message message = new Message(modId, methodName, mask); if (rpcs.TryGetValue(modId, out Dictionary<string, List<MessageHandler>> value) && value.TryGetValue(methodName, out var value2) && value2.Count > 0) { MessageHandler messageHandler = value2[0]; ParameterInfo[] parameters2 = messageHandler.Parameters; int num = (messageHandler.TakesInfo ? (parameters2.Length - 1) : parameters2.Length); if (num != parameters.Length) { throw new Exception($"Parameter count mismatch: expected {num}, got {parameters.Length}"); } for (int i = 0; i < num; i++) { Type parameterType = parameters2[i].ParameterType; object obj = parameters[i] ?? throw new Exception($"Null parameter {i}"); if (!parameterType.IsAssignableFrom(obj.GetType())) { throw new Exception($"Type mismatch {parameterType} vs {obj.GetType()}"); } message.WriteObject(parameterType, obj); } } else { foreach (object obj2 in parameters) { if (obj2 == null) { throw new Exception("Null parameter"); } message.WriteObject(obj2.GetType(), obj2); } } return message; } catch (Exception arg) { Debug.LogError((object)$"BuildMessage failed: {arg}"); return null; } } private void DispatchIncoming(Message message, ulong from) { if ((IncomingValidator != null && !IncomingValidator(message, from)) || !rateLimiter.IncomingAllowed()) { return; } if (!rpcs.TryGetValue(message.ModID, out Dictionary<string, List<MessageHandler>> value)) { Debug.LogWarning((object)$"No mod {message.ModID}"); return; } if (!value.TryGetValue(message.MethodName, out var value2)) { Debug.LogWarning((object)("No method " + message.MethodName)); return; } MessageHandler[] array = value2.ToArray(); foreach (MessageHandler messageHandler in array) { if (messageHandler.Mask == message.Mask) { ParameterInfo[] parameters = messageHandler.Parameters; int num = (messageHandler.TakesInfo ? (parameters.Length - 1) : parameters.Length); object[] array2 = new object[parameters.Length]; for (int j = 0; j < num; j++) { array2[j] = message.ReadObject(parameters[j].ParameterType); } if (messageHandler.TakesInfo) { Type parameterType = parameters[^1].ParameterType; array2[parameters.Length - 1] = CreateRpcInfoInstance(parameterType, from); } try { messageHandler.Method.Invoke(messageHandler.Target, array2); } catch (Exception arg) { Debug.LogError((object)$"Invoke RPC error: {arg}"); } } } } private object CreateRpcInfoInstance(Type infoType, ulong from) { try { ConstructorInfo constructor = infoType.GetConstructor(new Type[1] { typeof(ulong) }); if (constructor != null) { return constructor.Invoke(new object[1] { from }); } object obj = Activator.CreateInstance(infoType); FieldInfo field = infoType.GetField("SenderSteamID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(obj, from); } return obj; } catch { return null; } } } public class SteamNetworkingService : INetworkingService { private class FragmentBuffer { public int Total; public DateTime FirstSeen = DateTime.UtcNow; public Dictionary<int, byte[]> Fragments = new Dictionary<int, byte[]>(); } private sealed class RegistrationToken : IDisposable { private readonly SteamNetworkingService svc; private readonly object? instance; private readonly Type? registeredType; private readonly uint modId; private readonly int mask; private bool disposed; public RegistrationToken(SteamNetworkingService svc, object? instance, Type? registeredType, uint modId, int mask) { this.svc = svc; this.instance = instance; this.registeredType = registeredType; this.modId = modId; this.mask = mask; disposed = false; } public void Dispose() { if (!disposed) { disposed = true; if (instance != null) { svc.DeregisterNetworkObject(instance, modId, mask); } else if (registeredType != null) { svc.DeregisterNetworkType(registeredType, modId, mask); } } } } private class HandshakeState { public string? PeerPub; public string LocalNonce = string.Empty; public byte[]? Sym; public bool Completed; } private class SlidingWindowRateLimiter { private readonly int limit; private readonly TimeSpan window; private readonly Queue<DateTime> q = new Queue<DateTime>(); public SlidingWindowRateLimiter(int limit, TimeSpan window) { this.limit = limit; this.window = window; } public bool Allowed() { DateTime utcNow = DateTime.UtcNow; while (q.Count > 0 && utcNow - q.Peek() > window) { q.Dequeue(); } if (q.Count >= limit) { return false; } q.Enqueue(utcNow); return true; } public bool IncomingAllowed() { return Allowed(); } } private enum Priority { High, Normal, Low } private class QueuedSend { public byte[] Framed; public CSteamID Target; public ReliableType Reliable; public DateTime Enqueued; } private class UnackedMessage { public byte[] Framed; public CSteamID Target; public ReliableType Reliable; public DateTime LastSent; public int Attempts; public ulong msgId => BitConverter.ToUInt64(Framed, 1); } private class MessageHandler { public object Target; public MethodInfo Method; public ParameterInfo[] Parameters; public bool TakesInfo; public int Mask; } private const int CHANNEL = 120; private const int MAX_IN_MESSAGES = 500; private static IntPtr[] inMessages = new IntPtr[500]; private readonly object rpcLock = new object(); private CSteamID[] players = Array.Empty<CSteamID>(); private static readonly List<string> lobbyDataKeys = new List<string>(); private static readonly List<string> playerDataKeys = new List<string>(); private static readonly Dictionary<CSteamID, Dictionary<string, string>> lastPlayerData = new Dictionary<CSteamID, Dictionary<string, string>>(); private static readonly Dictionary<string, string> lastLobbyData = new Dictionary<string, string>(); private Callback<LobbyEnter_t>? cbLobbyEnter; private Callback<LobbyCreated_t>? cbLobbyCreated; private Callback<LobbyChatUpdate_t>? cbLobbyChatUpdate; private Callback<LobbyDataUpdate_t>? cbLobbyDataUpdate; private readonly Dictionary<uint, Dictionary<string, List<MessageHandler>>> rpcs = new Dictionary<uint, Dictionary<string, List<MessageHandler>>>(); private readonly Queue<QueuedSend> highQueue = new Queue<QueuedSend>(); private readonly Queue<QueuedSend> normalQueue = new Queue<QueuedSend>(); private readonly Queue<QueuedSend> lowQueue = new Queue<QueuedSend>(); private readonly object queueLock = new object(); private readonly Dictionary<(ulong target, ulong msgId), UnackedMessage> unacked = new Dictionary<(ulong, ulong), UnackedMessage>(); private readonly object unackedLock = new object(); private TimeSpan ackTimeout = TimeSpan.FromSeconds(1.2); private int maxRetransmitAttempts = 5; private long _nextMessageId; private readonly Dictionary<uint, ulong> outgoingSequencePerMod = new Dictionary<uint, ulong>(); private readonly Dictionary<ulong, Dictionary<uint, ulong>> lastSeenSequence = new Dictionary<ulong, Dictionary<uint, ulong>>(); private readonly Dictionary<ulong, SlidingWindowRateLimiter> rateLimiters = new Dictionary<ulong, SlidingWindowRateLimiter>(); private readonly Dictionary<ulong, byte[]> perPeerSymmetricKey = new Dictionary<ulong, byte[]>(); private byte[]? globalSharedSecret; private HMACSHA256? globalHmac; private readonly Dictionary<uint, Func<byte[], byte[]>> modSigners = new Dictionary<uint, Func<byte[], byte[]>>(); private readonly Dictionary<uint, RSAParameters> modPublicKeys = new Dictionary<uint, RSAParameters>(); private readonly Dictionary<ulong, HandshakeState> handshakeStates = new Dictionary<ulong, HandshakeState>(); private const byte FRAG_FLAG = 1; private const byte COMPRESSED_FLAG = 2; private const byte HMAC_FLAG = 4; private const byte SIGN_FLAG = 8; private const byte ACK_FLAG = 16; private RSACryptoServiceProvider? LocalRsa; private readonly Dictionary<(ulong sender, ulong msgId), FragmentBuffer> fragmentBuffers = new Dictionary<(ulong, ulong), FragmentBuffer>(); private readonly object fragmentLock = new object(); private readonly TimeSpan FragmentTimeout = TimeSpan.FromSeconds(30.0); public bool IsInitialized { get; private set; } public bool InLobby { get; private set; } public ulong HostSteamId64 { get { //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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) if (Lobby == CSteamID.Nil) { return 0uL; } CSteamID lobbyOwner = SteamMatchmaking.GetLobbyOwner(Lobby); if (lobbyOwner == CSteamID.Nil) { return 0uL; } return lobbyOwner.m_SteamID; } } public string HostIdString { get { //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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (Lobby == CSteamID.Nil) { return string.Empty; } CSteamID lobbyOwner = SteamMatchmaking.GetLobbyOwner(Lobby); if (!(lobbyOwner == CSteamID.Nil)) { return ((object)(CSteamID)(ref lobbyOwner)).ToString(); } return string.Empty; } } public CSteamID Lobby { get; private set; } = CSteamID.Nil; public bool IsHost { get { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) try { if (!InLobby) { return false; } CSteamID lobbyOwner = SteamMatchmaking.GetLobbyOwner(Lobby); if (lobbyOwner == CSteamID.Nil) { return false; } return lobbyOwner == SteamUser.GetSteamID(); } catch { return false; } } } public Func<Message, ulong, bool>? IncomingValidator { get; set; } public event Action? LobbyCreated; public event Action? LobbyEntered; public event Action? LobbyLeft; public event Action<ulong>? PlayerEntered; public event Action<ulong>? PlayerLeft; public event Action<string[]>? LobbyDataChanged; public event Action<ulong, string[]>? PlayerDataChanged; public ulong GetLocalSteam64() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) try { return SteamUser.GetSteamID().m_SteamID; } catch { return 0uL; } } public ulong[] GetLobbyMemberSteamIds() { //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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (!InLobby || Lobby == CSteamID.Nil) { return Array.Empty<ulong>(); } try { int numLobbyMembers = SteamMatchmaking.GetNumLobbyMembers(Lobby); if (numLobbyMembers <= 0) { return Array.Empty<ulong>(); } ulong[] array = new ulong[numLobbyMembers]; for (int i = 0; i < numLobbyMembers; i++) { CSteamID lobbyMemberByIndex = SteamMatchmaking.GetLobbyMemberByIndex(Lobby, i); array[i] = ((lobbyMemberByIndex == CSteamID.Nil) ? 0 : lobbyMemberByIndex.m_SteamID); } return array; } catch (Exception arg) { Net.Logger.LogError((object)$"GetLobbyMemberSteamIds error: {arg}"); return Array.Empty<ulong>(); } } private ulong NextMessageId() { return (ulong)Interlocked.Increment(ref _nextMessageId); } public void Initialize() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown if (IsInitialized) { return; } if (Application.isPlaying) { try { GameObject val = GameObject.Find("SteamCallbackPump"); if ((Object)(object)val == (Object)null) { val = new GameObject("SteamCallbackPump"); Object.DontDestroyOnLoad((Object)(object)val); val.AddComponent<SteamCallbackPump>(); Net.Logger.LogInfo((object)"Created SteamCallbackPump GameObject."); } } catch (Exception arg) { Net.Logger.LogError((object)$"Failed to create SteamCallbackPump: {arg}"); } } try { Message.MaxSize = 524288; } catch { } cbLobbyEnter = Callback<LobbyEnter_t>.Create((DispatchDelegate<LobbyEnter_t>)OnLobbyEnter); cbLobbyCreated = Callback<LobbyCreated_t>.Create((DispatchDelegate<LobbyCreated_t>)OnLobbyCreated); cbLobbyChatUpdate = Callback<LobbyChatUpdate_t>.Create((DispatchDelegate<LobbyChatUpdate_t>)OnLobbyChatUpdate); cbLobbyDataUpdate = Callback<LobbyDataUpdate_t>.Create((DispatchDelegate<LobbyDataUpdate_t>)OnLobbyDataUpdate); LocalRsa = new RSACryptoServiceProvider(2048); IsInitialized = true; Net.Logger.LogInfo((object)"SteamNetworkingService initialized"); } public void Shutdown() { cbLobbyEnter = null; cbLobbyCreated = null; cbLobbyChatUpdate = null; cbLobbyDataUpdate = null; rpcs.Clear(); unacked.Clear(); handshakeStates.Clear(); perPeerSymmetricKey.Clear(); globalHmac?.Dispose(); globalHmac = null; LocalRsa?.Dispose(); LocalRsa = null; IsInitialized = false; lock (fragmentLock) { fragmentBuffers.Clear(); } Net.Logger.LogInfo((object)"SteamNetworkingService shutdown"); } public void CreateLobby(int maxPlayers = 8) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) SteamMatchmaking.CreateLobby((ELobbyType)0, maxPlayers); } public void JoinLobby(ulong lobbySteamId64) { //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) SteamMatchmaking.JoinLobby(new CSteamID(lobbySteamId64)); } public void LeaveLobby() { OnLobbyLeftInternal(); } public void InviteToLobby(ulong steamId64) { //IL_0001: 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) SteamMatchmaking.InviteUserToLobby(Lobby, new CSteamID(steamId64)); } private void OnLobbyEnter(LobbyEnter_t param) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) Net.Logger.LogDebug((object)$"LobbyEnter {param.m_ulSteamIDLobby}"); Lobby = new CSteamID(param.m_ulSteamIDLobby); InLobby = true; RefreshPlayerList(); this.LobbyEntered?.Invoke(); } private void OnLobbyCreated(LobbyCreated_t param) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0029: 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) Net.Logger.LogDebug((object)$"LobbyCreated: {param.m_eResult}"); if ((int)param.m_eResult == 1) { Lobby = new CSteamID(param.m_ulSteamIDLobby); InLobby = true; RefreshPlayerList(); this.LobbyCreated?.Invoke(); } else { Net.Logger.LogError((object)$"Lobby creation failed: {param.m_eResult}"); } } private void OnLobbyChatUpdate(LobbyChatUpdate_t param) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: 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_003b: 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_004a: Unknown result type (might be due to invalid IL or missing references) try { RefreshPlayerList(); CSteamID val = default(CSteamID); ((CSteamID)(ref val))..ctor(param.m_ulSteamIDUserChanged); EChatMemberStateChange val2 = (EChatMemberStateChange)param.m_rgfChatMemberStateChange; if ((val2 & 1) != 0) { this.PlayerEntered?.Invoke(val.m_SteamID); } EChatMemberStateChange val3 = (EChatMemberStateChange)30; if ((val2 & val3) != 0) { this.PlayerLeft?.Invoke(val.m_SteamID); } } catch (Exception arg) { Net.Logger.LogError((object)$"OnLobbyChatUpdate error: {arg}"); } } internal void OnLobbyLeftInternal() { RefreshPlayerList(); lastLobbyData.Clear(); lastPlayerData.Clear(); InLobby = false; this.LobbyLeft?.Invoke(); } private void RefreshPlayerList() { //IL_0001: 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_000d: 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_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) try { _ = Lobby; if (Lobby == CSteamID.Nil) { players = Array.Empty<CSteamID>(); return; } int numLobbyMembers = SteamMatchmaking.GetNumLobbyMembers(Lobby); players = (CSteamID[])(object)new CSteamID[numLobbyMembers]; for (int i = 0; i < players.Length; i++) { players[i] = SteamMatchmaking.GetLobbyMemberByIndex(Lobby, i); } Net.Logger.LogDebug((object)$"RefreshPlayerList: total members = {players.Length}"); } catch (Exception arg) { Net.Logger.LogError((object)$"RefreshPlayerList error: {arg}"); players = Array.Empty<CSteamID>(); } } private void OnLobbyDataUpdate(LobbyDataUpdate_t param) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) if (!InLobby || param.m_ulSteamIDLobby != Lobby.m_SteamID) { return; } if (param.m_ulSteamIDLobby == param.m_ulSteamIDMember) { List<string> list = new List<string>(); foreach (string lobbyDataKey in lobbyDataKeys) { string lobbyData = SteamMatchmaking.GetLobbyData(Lobby, lobbyDataKey); if (!lastLobbyData.TryGetValue(lobbyDataKey, out string value) || value != lobbyData) { list.Add(lobbyDataKey); lastLobbyData[lobbyDataKey] = lobbyData; } } if (list.Count > 0) { this.LobbyDataChanged?.Invoke(list.ToArray()); } return; } CSteamID val = default(CSteamID); ((CSteamID)(ref val))..ctor(param.m_ulSteamIDMember); if (!lastPlayerData.ContainsKey(val)) { lastPlayerData[val] = new Dictionary<string, string>(); } List<string> list2 = new List<string>(); foreach (string playerDataKey in playerDataKeys) { string lobbyMemberData = SteamMatchmaking.GetLobbyMemberData(Lobby, val, playerDataKey); if (!lastPlayerData[val].TryGetValue(playerDataKey, out string value2) || value2 != lobbyMemberData) { list2.Add(playerDataKey); lastPlayerData[val][playerDataKey] = lobbyMemberData; } } if (list2.Count > 0) { this.PlayerDataChanged?.Invoke(val.m_SteamID, list2.ToArray()); } } public void RegisterLobbyDataKey(string key) { if (lobbyDataKeys.Contains(key)) { Net.Logger.LogWarning((object)("Lobby key " + key + " already registered")); } else { lobbyDataKeys.Add(key); } } public void SetLobbyData(string key, object value) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (!InLobby) { Net.Logger.LogError((object)"Cannot set lobby data when not in lobby."); return; } if (!lobbyDataKeys.Contains(key)) { Net.Logger.LogWarning((object)("Accessing unregistered lobby key '" + key + "'.")); } SteamMatchmaking.SetLobbyData(Lobby, key, Convert.ToString(value, CultureInfo.InvariantCulture)); } public T GetLobbyData<T>(string key) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) if (!InLobby) { Net.Logger.LogError((object)"Cannot get lobby data when not in lobby."); return default(T); } if (!lobbyDataKeys.Contains(key)) { Net.Logger.LogWarning((object)("Accessing unregistered lobby key '" + key + "'.")); } string lobbyData = SteamMatchmaking.GetLobbyData(Lobby, key); if (string.IsNullOrEmpty(lobbyData)) { return default(T); } try { return (T)Convert.ChangeType(lobbyData, typeof(T), CultureInfo.InvariantCulture); } catch { Net.Logger.LogError((object)("Could not parse lobby data [" + key + "," + lobbyData + "] as " + typeof(T).Name)); return default(T); } } public void RegisterPlayerDataKey(string key) { if (playerDataKeys.Contains(key)) { Net.Logger.LogWarning((object)("Player key " + key + " already registered")); } else { playerDataKeys.Add(key); } } public void SetPlayerData(string key, object value) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (!InLobby) { Net.Logger.LogError((object)"Cannot set player data when not in lobby."); return; } if (!playerDataKeys.Contains(key)) { Net.Logger.LogWarning((object)("Accessing unregistered player key '" + key + "'.")); } SteamMatchmaking.SetLobbyMemberData(Lobby, key, Convert.ToString(value, CultureInfo.InvariantCulture)); } public T GetPlayerData<T>(ulong steamId64, string key) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) if (!InLobby) { Net.Logger.LogError((object)"Cannot get player data when not in lobby."); return default(T); } if (!playerDataKeys.Contains(key)) { Net.Logger.LogWarning((object)("Accessing unregistered player key '" + key + "'.")); } CSteamID val = default(CSteamID); ((CSteamID)(ref val))..ctor(steamId64); string lobbyMemberData = SteamMatchmaking.GetLobbyMemberData(Lobby, val, key); if (string.IsNullOrEmpty(lobbyMemberData)) { return default(T); } try { return (T)Convert.ChangeType(lobbyMemberData, typeof(T), CultureInfo.InvariantCulture); } catch { Net.Logger.LogError((object)("Could not parse player data [" + key + "," + lobbyMemberData + "] as " + typeof(T).Name)); return default(T); } } public IDisposable RegisterNetworkObject(object instance, uint modId, int mask = 0) { if (instance == null) { throw new ArgumentNullException("instance"); } return RegisterNetworkTypeInternal(instance.GetType(), instance, modId, mask); } public IDisposable RegisterNetworkType(Type type, uint modId, int mask = 0) { if (type == null) { throw new ArgumentNullException("type"); } return RegisterNetworkTypeInternal(type, null, modId, mask); } private IDisposable RegisterNetworkTypeInternal(Type type, object? instance, uint modId, int mask) { int num = 0; lock (rpcLock) { MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo[] array = methods; foreach (MethodInfo methodInfo in array) { CustomRPCAttribute[] array2 = methodInfo.GetCustomAttributes(inherit: false).OfType<CustomRPCAttribute>().ToArray(); if (array2.Length != 0) { if (!rpcs.ContainsKey(modId)) { rpcs[modId] = new Dictionary<string, List<MessageHandler>>(); } if (!rpcs[modId].ContainsKey(methodInfo.Name)) { rpcs[modId][methodInfo.Name] = new List<MessageHandler>(); } MessageHandler item = new MessageHandler { Target = (methodInfo.IsStatic ? null : instance), Method = methodInfo, Parameters = methodInfo.GetParameters(), TakesInfo = (methodInfo.GetParameters().Length != 0 && methodInfo.GetParameters().Last().ParameterType.Name == "RPCInfo"), Mask = mask }; rpcs[modId][methodInfo.Name].Add(item); num++; } } } if (instance != null) { Net.Logger.LogInfo((object)$"Registered {num} RPCs for mod {modId} on {instance} ({instance.GetType().FullName})"); } else { Net.Logger.LogInfo((object)$"Registered {num} static RPCs for mod {modId} on type {type.FullName}"); } return new RegistrationToken(this, instance, type, modId, mask); } public void DeregisterNetworkObject(object instance, uint modId, int mask = 0) { if (instance == null) { throw new ArgumentNullException("instance"); } DeregisterNetworkObjectInternal(instance.GetType(), instance, modId, mask); } public void DeregisterNetworkType(Type type, uint modId, int mask = 0) { if (type == null) { throw new ArgumentNullException("type"); } DeregisterNetworkObjectInternal(type, null, modId, mask); } private void DeregisterNetworkObjectInternal(Type type, object? instanceOrNull, uint modId, int mask) { lock (rpcLock) { if (!rpcs.TryGetValue(modId, out Dictionary<string, List<MessageHandler>> value)) { Net.Logger.LogWarning((object)$"No RPCs for mod {modId}"); return; } int num = 0; string[] array = value.Keys.ToArray(); string[] array2 = array; foreach (string key in array2) { if (!value.TryGetValue(key, out var value2)) { continue; } for (int num2 = value2.Count - 1; num2 >= 0; num2--) { MessageHandler messageHandler = value2[num2]; if (instanceOrNull != null && messageHandler.Target != null && messageHandler.Target == instanceOrNull && messageHandler.Mask == mask) { value2.RemoveAt(num2); num++; } else if (instanceOrNull == null && messageHandler.Target == null && messageHandler.Method.DeclaringType == type && messageHandler.Mask == mask) { value2.RemoveAt(num2); num++; } } if (value2.Count == 0) { value.Remove(key); } } if (value.Count == 0) { rpcs.Remove(modId); } Net.Logger.LogInfo((object)$"Deregistered {num} RPCs for mod {modId} (type/instance {type.FullName})"); } } public void RPC(uint modId, string methodName, ReliableType reliable, params object[] parameters) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if (!InLobby) { Net.Logger.LogError((object)"RPC called while not in lobby"); return; } Message message = BuildMessage(modId, methodName, 0, parameters); if (message == null) { return; } CSteamID[] array = players; foreach (CSteamID val in array) { if (!(val == SteamUser.GetSteamID())) { EnqueueOrSend(BuildFramedBytesWithMeta(message, modId, reliable), val, reliable, DeterminePriority(modId, methodName)); } } InvokeLocalMessage(new Message(message.ToArray()), SteamUser.GetSteamID()); } public void RPCTarget(uint modId, string methodName, ulong targetSteamId64, ReliableType reliable, params object[] parameters) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) RPCTarget(modId, methodName, new CSteamID(targetSteamId64), reliable, parameters); } public void RPCTarget(uint modId, string methodName, CSteamID target, ReliableType reliable, params object[] parameters) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (!InLobby) { Net.Logger.LogError((object)"Cannot RPC target when not in lobby"); return; } Message message = BuildMessage(modId, methodName, 0, parameters); if (message != null) { byte[] framed = BuildFramedBytesWithMeta(message, modId, reliable); EnqueueOrSend(framed, target, reliable, DeterminePriority(modId, methodName)); } } public void RPCToHost(uint modId, string methodName, ReliableType reliable, params object[] parameters) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) if (!InLobby) { Net.Logger.LogError((object)"Not in lobby"); return; } CSteamID lobbyOwner = SteamMatchmaking.GetLobbyOwner(Lobby); if (lobbyOwner == CSteamID.Nil) { Net.Logger.LogError((object)"No host set"); } else { RPCTarget(modId, methodName, lobbyOwner, reliable, parameters); } } private Priority DeterminePriority(uint modId, string methodName) { string text = methodName.ToLowerInvariant(); if (text.Contains("admin") || text.Contains("control") || text.Contains("critical") || text.Contains("sync")) { return Priority.High; } return Priority.Normal; } private void EnqueueOrSend(byte[] framed, CSteamID target, ReliableType reliable, Priority p) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) SlidingWindowRateLimiter orCreateRateLimiter = GetOrCreateRateLimiter(target.m_SteamID); if (!orCreateRateLimiter.Allowed()) { return; } if (p == Priority.High) { SendWithPossibleAck(framed, target, reliable); return; } lock (queueLock) { Queue<QueuedSend> queue = ((p == Priority.Normal) ? normalQueue : lowQueue); queue.Enqueue(new QueuedSend { Framed = framed, Target = target, Reliable = reliable, Enqueued = DateTime.UtcNow }); } } private void FlushQueues(int maxPerFrame = 8) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) int num = 0; while (num < maxPerFrame) { QueuedSend queuedSend = null; lock (queueLock) { if (normalQueue.Count > 0) { queuedSend = normalQueue.Dequeue(); } else { if (lowQueue.Count <= 0) { break; } queuedSend = lowQueue.Dequeue(); } } if (queuedSend != null) { SendWithPossibleAck(queuedSend.Framed, queuedSend.Target, queuedSend.Reliable); num++; } } } private byte[] BuildFramedBytesWithMeta(Message msg, uint modId, ReliableType reliable) { byte[] array = msg.ToArray(); bool flag = array.Length > 1024; if (flag) { array = msg.CompressPayload(); } byte b = 0; if (flag) { b = (byte)(b | 2u); } if (globalHmac != null) { b = (byte)(b | 4u); } if (modSigners.ContainsKey(modId)) { b = (byte)(b | 8u); } ulong value2; lock (outgoingSequencePerMod) { if (!outgoingSequencePerMod.TryGetValue(modId, out var value)) { value = 0uL; } value2 = ++value; outgoingSequencePerMod[modId] = value; } ulong value3 = NextMessageId(); using MemoryStream memoryStream = new MemoryStream(); memoryStream.WriteByte(b); memoryStream.Write(BitConverter.GetBytes(value3), 0, 8); memoryStream.Write(BitConverter.GetBytes(value2), 0, 8); memoryStream.Write(BitConverter.GetBytes(1), 0, 4); memoryStream.Write(BitConverter.GetBytes(0), 0, 4); memoryStream.Write(array, 0, array.Length); byte[] array2 = memoryStream.ToArray(); if (modSigners.TryGetValue(modId, out Func<byte[], byte[]> value4)) { byte[] array3 = value4(array2); using MemoryStream memoryStream2 = new MemoryStream(); memoryStream2.Write(array2, 0, array2.Length); ushort value5 = (ushort)array3.Length; memoryStream2.Write(BitConverter.GetBytes(value5), 0, 2); memoryStream2.Write(array3, 0, array3.Length); array2 = memoryStream2.ToArray(); } if (globalHmac != null) { byte[] array4 = globalHmac.ComputeHash(array2); using MemoryStream memoryStream3 = new MemoryStream(); memoryStream3.Write(array2, 0, array2.Length); memoryStream3.Write(array4, 0, array4.Length); array2 = memoryStream3.ToArray(); } if (reliable == ReliableType.Reliable) { array2[0] = (byte)(array2[0] | 0x10u); } return array2; } private void SendWithPossibleAck(byte[] framed, CSteamID target, ReliableType reliable) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: 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) if (perPeerSymmetricKey.TryGetValue(target.m_SteamID, out byte[] value)) { using HMACSHA256 hMACSHA = new HMACSHA256(value); byte[] array = hMACSHA.ComputeHash(framed); using MemoryStream memoryStream = new MemoryStream(); memoryStream.Write(framed, 0, framed.Length); memoryStream.Write(array, 0, array.Length); framed = memoryStream.ToArray(); framed[0] = (byte)(framed[0] | 4u); } if ((framed[0] & 0x10u) != 0) { ulong item = BitConverter.ToUInt64(framed, 1); (ulong, ulong) key = (target.m_SteamID, item); lock (unackedLock) { unacked[key] = new UnackedMessage { Framed = framed, Target = target, Reliable = reliable, LastSent = DateTime.UtcNow, Attempts = 1 }; } } SendBytes(framed, target, reliable); } private void SendBytes(byte[] data, CSteamID target, ReliableType reliable) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Invalid comparison between Unknown and I4 //IL_00c0: 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) if (data.Length > Message.MaxSize) { Net.Logger.LogError((object)$"Send length {data.Length} exceeds Message.MaxSize {Message.MaxSize}"); return; } if (target == SteamUser.GetSteamID()) { Message message = new Message(data); InvokeLocalMessage(message, SteamUser.GetSteamID()); return; } SteamNetworkingIdentity val = default(SteamNetworkingIdentity); ((SteamNetworkingIdentity)(ref val)).SetSteamID(target); GCHandle gCHandle = GCHandle.Alloc(data, GCHandleType.Pinned); try { IntPtr intPtr = gCHandle.AddrOfPinnedObject(); int num = 32; switch (reliable) { case ReliableType.Unreliable: num |= 0; break; case ReliableType.Reliable: num |= 8; break; case ReliableType.UnreliableNoDelay: num |= 5; break; } EResult val2 = SteamNetworkingMessages.SendMessageToUser(ref val, intPtr, (uint)data.Length, num, 120); if ((int)val2 != 1) { Net.Logger.LogError((object)$"SendMessageToUser failed: {val2} to {target}"); } } catch (Exception arg) { Net.Logger.LogError((object)$"SendBytes exception: {arg}"); } finally { if (gCHandle.IsAllocated) { gCHandle.Free(); } } } public void PollReceive() { try { SteamAPI.RunCallbacks(); } catch (Exception arg) { Net.Logger.LogError((object)$"SteamAPI.RunCallbacks error: {arg}"); } FlushQueues(); RetransmitUnacked(); ReceiveMessages(); } private void RetransmitUnacked() { //IL_00f6: Unknown result type (might be due to invalid IL or missing references) List<UnackedMessage> list = new List<UnackedMessage>(); lock (unackedLock) { DateTime utcNow = DateTime.UtcNow; (ulong, ulong)[] array = unacked.Keys.ToArray(); (ulong, ulong)[] array2 = array; foreach ((ulong, ulong) key in array2) { UnackedMessage unackedMessage = unacked[key]; if (utcNow - unackedMessage.LastSent > ackTimeout) { if (unackedMessage.Attempts >= maxRetransmitAttempts) { unacked.Remove(key); continue; } unackedMessage.Attempts++; unackedMessage.LastSent = utcNow; unacked[key] = unackedMessage; list.Add(unackedMessage); } } } foreach (UnackedMessage item in list) { try { SendBytes(item.Framed, item.Target, item.Reliable); } catch (Exception arg) { Net.Logger.LogError((object)$"RetransmitUnacked: retransmit SendBytes exception: {arg}"); } } } private void ReceiveMessages() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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_0097: Unknown result type (might be due to invalid IL or missing references) try { int num = SteamNetworkingMessages.ReceiveMessagesOnChannel(120, inMessages, 500); if (num <= 0) { return; } for (int i = 0; i < num; i++) { IntPtr intPtr = inMessages[i]; SteamNetworkingMessage_t val = Marshal.PtrToStructure<SteamNetworkingMessage_t>(intPtr); int cbSize = val.m_cbSize; if (cbSize <= 0) { SteamNetworkingMessage_t.Release(intPtr); continue; } if (cbSize > Message.MaxSize) { SteamNetworkingMessage_t.Release(intPtr); continue; } CSteamID steamID = ((SteamNetworkingIdentity)(ref val.m_identityPeer)).GetSteamID(); if (steamID == CSteamID.Nil) { SteamNetworkingMessage_t.Release(intPtr); continue; } byte[] array = new byte[cbSize]; Marshal.Copy(val.m_pData, array, 0, cbSize); try { ProcessIncomingFrame(array, steamID); } catch (Exception arg) { Net.Logger.LogError((object)$"ProcessIncomingFrame exception: {arg}"); } SteamNetworkingMessage_t.Release(intPtr); } } catch (Exception arg2) { Net.Logger.LogError((object)$"ReceiveMessages outer exception: {arg2}"); } } private void ProcessIncomingFrame(byte[] frame, CSteamID sender) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_0478: Unknown result type (might be due to invalid IL or missing references) //IL_045a: Unknown result type (might be due to invalid IL or missing references) //IL_046b: Unknown result type (might be due to invalid IL or missing references) //IL_04af: Unknown result type (might be due to invalid IL or missing references) //IL_04d5: Unknown result type (might be due to invalid IL or missing references) using MemoryStream memoryStream = new MemoryStream(frame); int num = memoryStream.ReadByte(); if (num < 0) { return; } bool flag = (num & 2) != 0; bool flag2 = (num & 4) != 0; bool flag3 = (num & 8) != 0; bool flag4 = (num & 0x10) != 0; try { ulong num2 = ReadU64(memoryStream); ulong seq = ReadU64(memoryStream); int num3 = ReadI32(memoryStream); int key = ReadI32(memoryStream); int num4 = (int)(memoryStream.Length - memoryStream.Position); if (num4 <= 0) { return; } byte[] array = new byte[num4]; memoryStream.Read(array, 0, num4); byte[] array2; if (num3 > 1) { (ulong, ulong) key2 = (sender.m_SteamID, num2); lock (fragmentLock) { if (!fragmentBuffers.TryGetValue(key2, out FragmentBuffer value)) { value = new FragmentBuffer { Total = num3, FirstSeen = DateTime.UtcNow }; fragmentBuffers[key2] = value; } value.Fragments[key] = array; List<(ulong, ulong)> list = (from kv in fragmentBuffers where DateTime.UtcNow - kv.Value.FirstSeen > FragmentTimeout select kv.Key).ToList(); foreach (var item in list) { fragmentBuffers.Remove(item); } if (value.Fragments.Count != value.Total) { return; } using MemoryStream memoryStream2 = new MemoryStream(); for (int i = 0; i < value.Total; i++) { if (!value.Fragments.TryGetValue(i, out byte[] value2)) { fragmentBuffers.Remove(key2); return; } memoryStream2.Write(value2, 0, value2.Length); } array2 = memoryStream2.ToArray(); fragmentBuffers.Remove(key2); } } else { array2 = array; } byte[] array3 = array2; byte[] array4 = array3; if (flag2) { if (array3.Length < 32) { return; } int num5 = array3.Length - 32; byte[] array5 = new byte[num5]; Array.Copy(array3, 0, array5, 0, num5); byte[] array6 = new byte[32]; Array.Copy(array3, num5, array6, 0, 32); if (perPeerSymmetricKey.TryGetValue(sender.m_SteamID, out byte[] value3)) { using HMACSHA256 hMACSHA = new HMACSHA256(value3); byte[] array7 = hMACSHA.ComputeHash(array5); if (!((ReadOnlySpan<byte>)array7).SequenceEqual((ReadOnlySpan<byte>)array6)) { return; } array4 = array5; } else { if (globalHmac == null) { return; } byte[] array8 = globalHmac.ComputeHash(array5); if (!((ReadOnlySpan<byte>)array8).SequenceEqual((ReadOnlySpan<byte>)array6)) { return; } array4 = array5; } } if (flag3) { if (array4.Length < 5 || array4.Length < 5) { return; } uint key3 = BitConverter.ToUInt32(array4, 1); if (!modPublicKeys.TryGetValue(key3, out var value4)) { return; } byte[]? modulus = value4.Modulus; int num6 = ((modulus != null) ? modulus.Length : 0); if (num6 <= 0 || array4.Length < num6 + 2) { return; } int num7 = array4.Length - num6 - 2; if (num7 < 0) { return; } ushort num8 = BitConverter.ToUInt16(array4, num7); if (num8 != num6) { return; } byte[] array9 = new byte[num6]; Array.Copy(array4, num7 + 2, array9, 0, num6); byte[] array10 = new byte[num7]; Array.Copy(array4, 0, array10, 0, num7); try { using RSACryptoServiceProvider rSACryptoServiceProvider = new RSACryptoServiceProvider(); rSACryptoServiceProvider.ImportParameters(value4); if (!rSACryptoServiceProvider.VerifyData(array10, CryptoConfig.MapNameToOID("SHA256"), array9)) { return; } array4 = array10; } catch (Exception arg) { Net.Logger.LogError((object)$"ProcessIncomingFrame: Signature verification error: {arg}"); return; } } if (flag) { try { array4 = Message.DecompressPayload(array4); } catch (Exception arg2) { Net.Logger.LogError((object)$"ProcessIncomingFrame: Decompression failed: {arg2}"); return; } } Message message = new Message(array4); if (message.ModID == 0) { HandleInternalMessage(message, sender, num2, seq, flag4); if (flag4) { SendAckToSender(sender, num2); } return; } ulong steamID = sender.m_SteamID; SlidingWindowRateLimiter orCreateRateLimiter = GetOrCreateRateLimiter(steamID); if (orCreateRateLimiter.IncomingAllowed() && CheckAndUpdateSequence(steamID, message.ModID, seq)) { if (flag4) { SendAckToSender(sender, num2); } if (IncomingValidator == null || IncomingValidator(message, steamID)) { DispatchIncoming(message, sender); } } } catch (Exception arg3) { Net.Logger.LogError((object)$"ProcessIncomingFrame top-level exception: {arg3}"); } } private static ulong ReadU64(Stream s) { byte[] array = new byte[8]; s.Read(array, 0, 8); return BitConverter.ToUInt64(array, 0); } private static int ReadI32(Stream s) { byte[] array = new byte[4]; s.Read(array, 0, 4); return BitConverter.ToInt32(array, 0); } private void SendAckToSender(CSteamID sender, ulong msgId) { //IL_0021: 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) Message message = new Message(0u, "NETWORK_INTERNAL_ACK", 0); message.WriteULong(msgId); byte[] data = BuildFramedBytesWithMeta(message, 0u, ReliableType.Reliable); SendBytes(data, sender, ReliableType.Reliable); (ulong, ulong) key = (sender.m_SteamID, msgId); lock (unackedLock) { if (unacked.ContainsKey(key)) { unacked.Remove(key); } } } private void HandleInternalMessage(Message message, CSteamID sender, ulong msgId, ulong seq, bool requiresAck) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_007b: 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_00bf: Unknown result type (might be due to invalid IL or missing references) try { switch (message.MethodName) { case "NETWORK_INTERNAL_HANDSHAKE_PUBKEY": { string peerPubKeySerialized = message.ReadString(); string peerNonce = message.ReadString(); StartHandshakeReply(sender, peerPubKeySerialized, peerNonce); break; } case "NETWORK_INTERNAL_HANDSHAKE_SECRET": { byte[] encSecret = (byte[])message.ReadObject(typeof(byte[])); string initiatorNonce2 = message.ReadString(); CompleteHandshakeReceiver(sender, encSecret, initiatorNonce2); break; } case "NETWORK_INTERNAL_HANDSHAKE_CONFIRM": { string initiatorNonce = message.ReadString(); byte[] confirmHmac = (byte[])message.ReadObject(typeof(byte[])); CompleteHandshakeInitiator(sender, initiatorNonce, confirmHmac); break; } case "NETWORK_INTERNAL_ACK": { ulong item = message.ReadULong(); (ulong, ulong) key = (sender.m_SteamID, item); lock (unackedLock) { if (unacked.ContainsKey(key)) { unacked.Remove(key); } break; } } default: Net.Logger.LogWarning((object)("Unknown internal method " + message.MethodName)); break; } } catch (Exception arg) { Net.Logger.LogError((object)$"HandleInternalMessage error: {arg}"); } } private bool CheckAndUpdateSequence(ulong sender64, uint modId, ulong seq) { lock (lastSeenSequence) { if (!lastSeenSequence.TryGetValue(sender64, out Dictionary<uint, ulong> value)) { Dictionary<uint, ulong> dictionary2 = (lastSeenSequence[sender64] = new Dictionary<uint, ulong>()); value = dictionary2; } if (!value.TryGetValue(modId, out var value2)) { value2 = 0uL; } if (seq <= value2) { return false; } value[modId] = seq; return true; } } private void DispatchIncoming(Message message, CSteamID sender) { //IL_014f: Unknown result type (might be due to invalid IL or missing references) Message message2 = message; if (!rpcs.TryGetValue(message2.ModID, out Dictionary<string, List<MessageHandler>> value)) { Net.Logger.LogWarning((object)$"Dropping message for unknown mod {message2.ModID}"); return; } if (!value.TryGetValue(message2.MethodName, out var value2)) { Net.Logger.LogWarning((object)$"Dropping message for method {message2.MethodName} not registered for {message2.ModID}"); return; } bool flag = false; List<MessageHandler> list = value2.Where((MessageHandler h) => h.Mask == message2.Mask).ToList(); foreach (MessageHandler item in list) { Message message3 = new Message(message2.ToArray()); try { ParameterInfo[] parameters = item.Parameters; int num = (item.TakesInfo ? (parameters.Length - 1) : parameters.Length); object[] array = new object[parameters.Length]; for (int i = 0; i < num; i++) { Type parameterType = parameters[i].ParameterType; array[i] = message3.ReadObject(parameterType); } if (item.TakesInfo) { Type parameterType2 = parameters[^1].ParameterType; array[parameters.Length - 1] = CreateRpcInfoInstance(parameterType2, sender); } item.Method.Invoke(item.Target, array); flag = true; } catch { continue; } break; } if (!flag) { Net.Logger.LogWarning((object)$"No handler matched for {message2.ModID}:{message2.MethodName}"); } if (!flag) { Net.Logger.LogWarning((object)$"No handler matched for {message2.ModID}:{message2.MethodName}"); } } private object CreateRpcInfoInstance(Type infoType, CSteamID sender) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) try { ConstructorInfo constructor = infoType.GetConstructor(new Type[3] { typeof(ulong), typeof(string), typeof(bool) }); if (constructor != null) { bool flag = false; try { flag = sender == SteamUser.GetSteamID(); } catch { flag = false; } return constructor.Invoke(new object[3] { sender.m_SteamID, ((object)(CSteamID)(ref sender)).ToString(), flag }); } ConstructorInfo constructor2 = infoType.GetConstructor(new Type[1] { typeof(CSteamID) }); if (constructor2 != null) { return constructor2.Invoke(new object[1] { sender }); } ConstructorInfo constructor3 = infoType.GetConstructor(new Type[1] { typeof(ulong) }); if (constructor3 != null) { return constructor3.Invoke(new object[1] { sender.m_SteamID }); } ConstructorInfo constructor4 = infoType.GetConstructor(Type.EmptyTypes); if (constructor4 != null) { object obj2 = constructor4.Invoke(null); FieldInfo field = infoType.GetField("SenderSteamID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(obj2, sender); } FieldInfo field2 = infoType.GetField("Sender", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field2 != null) { field2.SetValue(obj2, sender); } return obj2; } } catch { } return null; } public void StartHandshake(CSteamID target) { //IL_0037: 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) if (LocalRsa != null) { string v = SerializeRsaPublicKey(LocalRsa); RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create(); byte[] array = new byte[16]; randomNumberGenerator.GetBytes(array); string text = Convert.ToBase64String(array); handshakeStates[target.m_SteamID] = new HandshakeState { PeerPub = null, LocalNonce = text, Completed = false }; Message message = new Message(0u, "NETWORK_INTERNAL_HANDSHAKE_PUBKEY", 0); message.WriteString(v); message.WriteString(text); byte[] data = BuildFramedBytesWithMeta(message, 0u, ReliableType.Reliable); SendBytes(data, target, ReliableType.Reliable); } } private void StartHandshakeReply(CSteamID sender, string peerPubKeySerialized, string peerNonce) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) if (LocalRsa != null) { HandshakeState handshakeState = (handshakeStates.ContainsKey(sender.m_SteamID) ? handshakeStates[sender.m_SteamID] : new HandshakeState()); handshakeState.PeerPub = peerPubKeySerialized; if (string.IsNullOrEmpty(handshakeState.LocalNonce)) { RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create(); byte[] array = new byte[16]; randomNumberGenerator.GetBytes(array); handshakeState.LocalNonce = Convert.ToBase64String(array); handshakeStates[sender.m_SteamID] = handshakeState; string v = SerializeRsaPublicKey(LocalRsa); Message message = new Message(0u, "NETWORK_INTERNAL_HANDSHAKE_PUBKEY", 0); message.WriteString(v); message.WriteString(handshakeState.LocalNonce); byte[] data = BuildFramedBytesWithMeta(message, 0u, ReliableType.Reliable); SendBytes(data, sender, ReliableType.Reliable); } else if (!handshakeState.Completed && !string.IsNullOrEmpty(handshakeState.LocalNonce) && !string.IsNullOrEmpty(handshakeState.PeerPub)) { RandomNumberGenerator randomNumberGenerator2 = RandomNumberGenerator.Create(); byte[] array2 = new byte[32]; randomNumberGenerator2.GetBytes(array2); RSACryptoServiceProvider rSACryptoServiceProvider = new RSACryptoServiceProvider(); RSAParameters parameters = DeserializeRsaPublicKey(handshakeState.PeerPub); rSACryptoServiceProvider.ImportParameters(parameters); byte[] v2 = rSACryptoServiceProvider.Encrypt(array2, fOAEP: false); Message message2 = new Message(0u, "NETWORK_INTERNAL_HANDSHAKE_SECRET", 0); message2.WriteBytes(v2); message2.WriteString(handshakeState.LocalNonce); byte[] data2 = BuildFramedBytesWithMeta(message2, 0u, ReliableType.Reliable); SendBytes(data2, sender, ReliableType.Reliable); handshakeState.Sym = array2; handshakeState.Completed = true; handshakeStates[sender.m_SteamID] = handshakeState; perPeerSymmetricKey[sender.m_SteamID] = array2; } } } private void CompleteHandshakeReceiver(CSteamID sender, byte[] encSecret, string initiatorNonce) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) if (LocalRsa == null) { return; } try { byte[] array = LocalRsa.Decrypt(encSecret, fOAEP: false); HandshakeState handshakeState = (handshakeStates.ContainsKey(sender.m_SteamID) ? handshakeStates[sender.m_SteamID] : new HandshakeState()); handshakeState.Sym = array; handshakeState.Completed = true; handshakeStates[sender.m_SteamID] = handshakeState; perPeerSymmetricKey[sender.m_SteamID] = array; byte[] v = HmacSha256Raw(array, Encoding.UTF8.GetBytes(initiatorNonce)); Message message = new Message(0u, "NETWORK_INTERNAL_HANDSHAKE_CONFIRM", 0); message.WriteString(initiatorNonce); message.WriteBytes(v); byte[] data = BuildFramedBytesWithMeta(message, 0u, ReliableType.Reliable); SendBytes(data, sender, ReliableType.Reliable); } catch (Exception arg) { Net.Logger.LogError((object)$"Handshake decryption error: {arg}"); } } private void CompleteHandshakeInitiator(CSteamID sender, string initiatorNonce, byte[] confirmHmac) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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) if (!handshakeStates.TryGetValue(sender.m_SteamID, out HandshakeState value) || value.Sym == null) { Net.Logger.LogWarning((object)"Handshake confirm received but no state"); return; } byte[] array = HmacSha256Raw(value.Sym, Encoding.UTF8.GetBytes(initiatorNonce)); if (!((ReadOnlySpan<byte>)array).SequenceEqual((ReadOnlySpan<byte>)confirmHmac)) { Net.Logger.LogWarning((object)"Handshake confirm HMAC mismatch"); return; } value.Completed = true; handshakeStates[sender.m_SteamID] = value; perPeerSymmetricKey[sender.m_SteamID] = value.Sym; } private static byte[] HmacSha256Raw(byte[] key, byte[] payload) { using HMACSHA256 hMACSHA = new HMACSHA256(key); return hMACSHA.ComputeHash(payload); } private static string SerializeRsaPublicKey(RSACryptoServiceProvider rsa) { RSAParameters rSAParameters = rsa.ExportParameters(includePrivateParameters: false); string text = Convert.ToBase64String(rSAParameters.Modulus ?? Array.Empty<byte>()); string text2 = Convert.ToBase64String(rSAParameters.Exponent ?? Array.Empty<byte>()); return text + ":" + text2; } private static RSAParameters DeserializeRsaPublicKey(string s) { string[] array = s.Split(':'); byte[] modulus = Convert.FromBase64String(array[0]); byte[] exponent = Convert.FromBase64String(array[1]); RSAParameters result = default(RSAParameters); result.Modulus = modulus; result.Exponent = exponent; return result; } public void SetSharedSecret(byte[]? secret) { if (secret == null) { globalSharedSecret = null; globalHmac?.Dispose(); globalHmac = null; } else { globalSharedSecret = (byte[])secret.Clone(); globalHmac?.Dispose(); globalHmac = new HMACSHA256(globalSharedSecret); } } public void RegisterModSigner(uint modId, Func<byte[], byte[]> signerDelegate) { modSigners[modId] = signerDelegate; } public void RegisterModPublicKey(uint modId, RSAParameters pub) { modPublicKeys[modId] = pub; } private void InvokeLocalMessage(Message message, CSteamID localSender) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) ulong steamID = localSender.m_SteamID; if ((IncomingValidator != null && !IncomingValidator(message, steamID)) || !rpcs.TryGetValue(message.ModID, out Dictionary<string, List<MessageHandler>> value) || !value.TryGetValue(message.MethodName, out var value2)) { return; } foreach (MessageHandler item in value2) { if (item.Mask == message.Mask) { ParameterInfo[] parameters = item.Parameters; int num = (item.TakesInfo ? (parameters.Length - 1) : parameters.Length); object[] array = new object[parameters.Length]; for (int i = 0; i < num; i++) { array[i] = message.ReadObject(parameters[i].ParameterType); } if (item.TakesInfo) { Type parameterType = parameters[^1].ParameterType; array[parameters.Length - 1] = CreateRpcInfoInstance(parameterType, localSender); } try { item.Method.Invoke(item.Target, array); } catch (Exception arg) { Net.Logger.LogError((object)$"Local invoke error: {arg}"); } } } } private Message? BuildMessage(uint modId, string methodName, int mask, object[] parameters) { object[] parameters2 = parameters; try { Message message = new Message(modId, methodName, mask); if (rpcs.TryGetValue(modId, out Dictionary<string, List<MessageHandler>> value) && value.TryGetValue(methodName, out var value2) && value2.Count > 0) { MessageHandler messageHandler = null; foreach (MessageHandler item in value2) { if (item.Mask != mask) { continue; } ParameterInfo[] parameters3 = item.Parameters; int num = (item.TakesInfo ? (parameters3.Length - 1) : parameters3.Length); if (num != parameters2.Length) { continue; } bool flag = true; for (int i = 0; i < num; i++) { if (!parameters3[i].ParameterType.IsAssignableFrom(parameters2[i].GetType())) { flag = false; break; } } if (flag) { messageHandler = item; break; } } if (messageHandler == null) { messageHandler = value2.FirstOrDefault(delegate(MessageHandler h) { int num3 = (h.TakesInfo ? (h.Parameters.Length - 1) : h.Parameters.Length); return num3 == parameters2.Length && h.Mask == mask; }) ?? value2[0]; } ParameterInfo[] parameters4 = messageHandler.Parameters; int num2 = (messageHandler.TakesInfo ? (parameters4.Length - 1) : parameters4.Length); for (int j = 0; j < num2; j++) { Type parameterType = parameters4[j].ParameterType; object obj = parameters2[j] ?? throw new Exception($"Null parameter {j}"); if (!parameterType.IsAssignableFrom(obj.GetType())) { throw new Exception($"Parameter {j} type mismatch: expected {parameterType}, got {obj.GetType()}"); } message.WriteObject(parameterType, obj); } } if (message.Length() > Message.MaxSize * 16) { Net.Logger.LogError((object)"Message exceeds maximum allowed overall size."); return null; } return message; } catch (Exception arg) { Net.Logger.LogError((object)$"BuildMessage failed: {arg}"); return null; } } private SlidingWindowRateLimiter GetOrCreateRateLimiter(ulong steam64) { lock (rateLimiters) { if (!rateLimiters.TryGetValue(steam64, out SlidingWindowRateLimiter value)) { value = new SlidingWindowRateLimiter(100, TimeSpan.FromSeconds(1.0)); rateLimiters[steam64] = value; } return value; } } private static byte[] HmacSha256RawStatic(byte[] key, byte[] payload) { using HMACSHA256 hMACSHA = new HMACSHA256(key); return hMACSHA.ComputeHash(payload); } } } namespace NetworkingLibrary.Patches { internal class NetworkPatch { } } namespace NetworkingLibrary.Modules { [AttributeUsage(AttributeTargets.Method)] public class CustomRPCAttribute : Attribute { } public class Message : IDisposable { public const byte PROTOCOL_VERSION = 1; public static int MaxSize; public byte ProtocolVersion; public uint ModID; public string MethodName = string.Empty; public int Mask; private List<byte> buffer = new List<byte>(); internal byte[] readableBuffer = Array.Empty<byte>(); internal int readPos; private static readonly Dictionary<Type, Action<Message, object>> writeCasters; private static readonly Dictionary<Type, Func<Message, object>> readCasters; public bool Compressed { get; private set; } public Message(uint modId, string methodName, int mask) { ProtocolVersion = 1; ModID = modId; MethodName = methodName; Mask = mask; WriteByte(ProtocolVersion); WriteUInt(ModID); WriteString(MethodName); WriteInt(Mask); } public Message(byte[] data) { SetBytes(data); ProtocolVersion = ReadByte(); ModID = ReadUInt(); MethodName = ReadString(); Mask = ReadInt(); } public void SetBytes(byte[] data) { buffer.Clear(); buffer.AddRange(data); readableBuffer = buffer.ToArray(); readPos = 0; } public byte[] ToArray() { readableBuffer = buffer.ToArray(); return readableBuffer; } public int Length() { return buffer.Count; } public int UnreadLength() { return Length() - readPos; } public void Reset(bool zero = true) { if (zero) { buffer.Clear(); readableBuffer = Array.Empty<byte>(); readPos = 0; } else { readPos -= 4; } } public Message WriteByte(byte v) { buffer.Add(v); return this; } public Message WriteBytes(byte[] v) { WriteInt(v.Length); buffer.AddRange(v); return this; } public Message WriteInt(int v) { buffer.AddRange(BitConverter.GetBytes(v)); return this; } public Message WriteUInt(uint v) { buffer.AddRange(BitConverter.GetBytes(v)); return this; } public Message WriteLong(long v) { buffer.AddRange(BitConverter.GetBytes(v)); return this; } public Message WriteULong(ulong v) { buffer.AddRange(BitConverter.GetBytes(v)); return this; } public Message WriteFloat(float v) { buffer.AddRange(BitConverter.GetBytes(v)); return this; } public Message WriteBool(bool v) { buffer.AddRange(BitConverter.GetBytes(v)); return this; } public Message WriteString(string v) { byte[] bytes = Encoding.UTF8.GetBytes(v ?? ""); WriteInt(bytes.Length); buffer.AddRange(bytes); return this; } public Message WriteVector3(Vector3 v) { //IL_0001: 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_001b: Unknown result type (might be due to invalid IL or missing references) WriteFloat(v.x); WriteFloat(v.y); WriteFloat(v.z); return this; } public Message WriteQuaternion(Quaternion q) { //IL_0001: 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_001b: 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) WriteFloat(q.x); WriteFloat(q.y); WriteFloat(q.z); WriteFloat(q.w); return this; } public void WriteObject(Type type, object value) { if (type == null) { throw new ArgumentNullException("type"); } if (writeCasters.TryGetValue(type, out Action<Message, object> value2)) { value2(this, value); return; } Type underlyingType = Nullable.GetUnderlyingType(type); if (underlyingType != null) { bool flag = value != null; WriteBool(flag); if (flag) { WriteObject(underlyingType, value); } return; } if (type.IsEnum) { Type underlyingType2 = Enum.GetUnderlyingType(type); WriteObject(underlyingType2, Convert.ChangeType(value, underlyingType2)); return; } if (type.IsArray) { Type elementType = type.GetElementType(); Array array = (value as Array) ?? Array.CreateInstance(elementType, 0); WriteInt(array.Length); for (int i = 0; i < array.Length; i++) { WriteObject(elementType, array.GetValue(i)); } return; } if (typeof(IList).IsAssignableFrom(type)) { Type type2 = null; if (type.IsGenericType) { Type[] genericArguments = type.GetGenericArguments(); if (genericArguments.Length == 1) { type2 = genericArguments[0]; } } IList list = (IList)value; if (type2 != null) { WriteInt(list.Count); for (int j = 0; j < list.Count; j++) { WriteObject(type2, list[j]); } return; } throw new Exception("Cannot serialize non-generic IList (heterogeneous lists) without explicit serializer registration."); } throw new Exception("Unsupported type for WriteObject: " + type.FullName + ". Register a serializer using Message.RegisterSerializer."); } public static void RegisterSerializer<T>(Action<Message, T> writer, Func<Message, T> reader) { Action<Message, T> writer2 = writer; Func<Message, T> reader2 = reader; if (writer2 == null || reader2 == null) { throw new ArgumentNullException(); } writeCasters[typeof(T)] = delegate(Message m, object o) { writer2(m, (T)o); }; readCasters[typeof(T)] = (Message m) => reader2(m); } public static void UnregisterSerializer<T>() { writeCasters.Remove(typeof(T)); readCasters.Remove(typeof(T)); } static Message() { MaxSize = 65536; writeCasters = new Dictionary<Type, Action<Message, object>>(); readCasters = new Dictionary<Type, Func<Message, object>>(); writeCasters[typeof(byte)] = delegate(Message m, object o) { m.WriteByte((byte)o); }; writeCasters[typeof(byte[])] = delegate(Message m, object o) { m.WriteBytes((byte[])o); }; writeCasters[typeof(int)] = delegate(Message m, object o) { m.WriteInt((int)o); }; writeCasters[typeof(uint)] = delegate(Message m, object o) { m.WriteUInt((uint)o); }; writeCasters[typeof(long)] = delegate(Message m, object o) { m.WriteLong((long)o); }; writeCasters[typeof(ulong)] = delegate(Message m, object o) { m.WriteULong((ulong)o); }; writeCasters[typeof(float)] = delegate(Message m, object o) { m.WriteFloat((float)o); }; writeCasters[typeof(bool)] = delegate(Message m, object o) { m.WriteBool((bool)o); }; writeCasters[typeof(string)] = delegate(Message m, object o) { m.WriteString((string)o); }; writeCasters[typeof(Vector3)] = delegate(Message m, object o) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) m.WriteVector3((Vector3)o); }; writeCasters[typeof(Quaternion)] = delegate(Message m, object o) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) m.WriteQuaternion((Quaternion)o); }; writeCasters[typeof(CSteamID)] = delegate(Message m, object o) { m.WriteULong(((CSteamID)o).m_SteamID); }; writeCasters[typeof(int[])] = delegate(Message m, object o) { int[] array5 = ((int[])o) ?? Array.Empty<int>(); m.WriteInt(array5.Length); for (int l = 0; l < array5.Length; l++) { m.WriteInt(array5[l]); } }; writeCasters[typeof(string[])] = delegate(Message m, object o) { string[] array4 = ((string[])o) ?? Array.Empty<string>(); m.WriteInt(array4.Length); for (int k = 0; k < array4.Length; k++) { m.WriteString(array4[k]); } }; readCasters[typeof(byte)] = (Message m) => m.ReadByte(); readCasters[typeof(byte[])] = delegate(Message m) { int num3 = m.ReadInt(); if (num3 == 0) { return new byte[0]; } byte[] array3 = new byte[num3]; Array.Copy(m.readableBuffer, m.readPos, array3, 0, num3); m.readPos += num3; return array3; }; readCasters[typeof(int)] = (Message m) => m.ReadInt(); readCasters[typeof(uint)] = (Message m) => m.ReadUInt(); readCasters[typeof(long)] = (Message m) => m.ReadLong(); readCasters[typeof(ulong)] = (Message m) => m.ReadULong(); readCasters[typeof(float)] = (Message m) => m.ReadFloat(); readCasters[typeof(bool)] = (Message m) => m.ReadBool(); readCasters[typeof(string)] = (Message m) => m.ReadString(); readCasters[typeof(Vector3)] = (Message m) => m.ReadVector3(); readCasters[typeof(Quaternion)] = (Message m) => m.ReadQuaternion(); readCasters[typeof(CSteamID)] = (Message m) => (object)new CSteamID(m.ReadULong()); readCasters[typeof(int[])] = delegate(Message m) { int num2 = m.ReadInt(); if (num2 == 0) { return new int[0]; } int[] array2 = new int[num2]; for (int j = 0; j < num2; j++) { array2[j] = m.ReadInt(); } return array2; }; readCasters[typeof(string[])] = delegate(Message m) { int num = m.ReadInt(); if (num == 0) { return new string[0]; } string[] array = new string[num]; for (int i = 0; i < num; i++) { array[i] = m.ReadString(); } return array; }; } public byte ReadByte() { if (buffer.Count > readPos) { byte result = readableBuffer[readPos]; readPos++; return result; } throw new Exception("ReadByte out of range"); } public int ReadInt() { if (buffer.Count > readPos) { int result = BitConverter.ToInt32(readableBuffer, readPos); readPos += 4; return result; } throw new Exception("ReadInt out of range"); } public uint ReadUInt() { if (buffer.Count > readPos) { uint result = BitConverter.ToUInt32(readableBuffer, readPos); readPos += 4; return result; } throw new Exception("ReadUInt out of range"); } public long ReadLong() { if (buffer.Count > readPos) { long result = BitConverter.ToInt64(readableBuffer, readPos); readPos += 8; return result; } throw new Exception("ReadLong out of range"); } public ulong ReadULong() { if (buffer.Count > readPos) { ulong result = BitConverter.ToUInt64(readableBuffer, readPos); readPos += 8; return result; } throw new Exception("ReadULong out of range"); } public float ReadFloat() { if (buffer.Count > readPos) { float result = BitConverter.ToSingle(readableBuffer, readPos); readPos += 4; return result; } throw new Exception("ReadFloat out of range"); } public bool ReadBool() { if (buffer.Count > readPos) { bool result = BitConverter.ToBoolean(readableBuffer, readPos); readPos++; return result; } throw new Exception("ReadBool out of range"); } public string ReadString() { int num = ReadInt(); if (num == 0) { return string.Empty; } if (buffer.Count > readPos) { string @string = Encoding.UTF8.GetString(readableBuffer, readPos, num); readPos += num; return @string; } throw new Exception("ReadString out of range"); } public Vector3 ReadVector3() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) return new Vector3(ReadFloat(), ReadFloat(), ReadFloat()); } public Quaternion ReadQuaternion() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) return new Quaternion(ReadFloat(), ReadFloat(), ReadFloat(), ReadFloat()); } public object ReadObject(Type type) { if (type == null) { throw new ArgumentNullException("type"); } if (readCasters.TryGetValue(type, out Func<Message, object> value)) { return value(this); } Type underlyingType = Nullable.GetUnderlyingType(type); if (underlyingType != null) { if (!ReadBool()) { return null; } return ReadObject(underlyingType); } if (type.IsEnum) { Type underlyingType2 = Enum.GetUnderlyingType(type); object value2 = ReadObject(underlyingType2); return Enum.ToObject(type, value2); } if (type.IsArray) { Type elementType = type.GetElementType(); int num = ReadInt(); Array array = Array.CreateInstance(elementType, num); for (int i = 0; i < num; i++) { object value3 = ReadObject(elementType); array.SetValue(value3, i); } return array; }