using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading.Tasks; using Microsoft.CodeAnalysis; using SteamNetworkLib.Core; using SteamNetworkLib.Events; using SteamNetworkLib.Exceptions; using SteamNetworkLib.Models; using SteamNetworkLib.Sync; using SteamNetworkLib.Utilities; using Steamworks; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("SteamNetworkLib")] [assembly: AssemblyConfiguration("Mono")] [assembly: AssemblyFileVersion("1.5.0.0")] [assembly: AssemblyInformationalVersion("1.5.0+267a26b4fb822606e5964a8741e62d740660a77f")] [assembly: AssemblyProduct("SteamNetworkLib")] [assembly: AssemblyTitle("SteamNetworkLib")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.5.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.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; } } } namespace SteamNetworkLib { public class SteamNetworkClient : IDisposable { private bool _disposed = false; private bool _isInitialized = false; private bool _versionCheckEnabled = true; private readonly NetworkRules _rules; private readonly List _syncVars = new List(); private readonly Dictionary _virtualMembers = new Dictionary(); private readonly Dictionary _virtualLobbyData = new Dictionary(); private readonly Dictionary> _virtualMemberData = new Dictionary>(); private DedicatedServerMessagingBridge? _dedicatedBridge; private NetworkSessionMode _sessionMode = NetworkSessionMode.None; private string _virtualSessionId = string.Empty; private CSteamID _virtualOwnerId = CSteamID.Nil; private CSteamID _virtualLocalPlayerId = CSteamID.Nil; private CSteamID _virtualServerSteamId = CSteamID.Nil; private DateTime _lastDedicatedBridgeAttachAttemptUtc = DateTime.MinValue; private DateTime _lastDedicatedRegisterAttemptUtc = DateTime.MinValue; private DateTime _lastDedicatedSnapshotUtc = DateTime.MinValue; private bool _dedicatedJoinEventRaised; private static readonly TimeSpan DedicatedRegisterRetryDelay = TimeSpan.FromSeconds(2.0); private static readonly JsonSyncSerializer DedicatedJsonSerializer = new JsonSyncSerializer(); private const string STEAMNETWORKLIB_VERSION_KEY = "__snl_version"; public static string LibraryVersion { get; } = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "Unknown"; public bool VersionCheckEnabled { get { return _versionCheckEnabled; } set { _versionCheckEnabled = value; } } public bool IsInitialized => _isInitialized; public SteamLobbyManager? LobbyManager { get; private set; } public SteamLobbyData? LobbyData { get; private set; } public SteamMemberData? MemberData { get; private set; } public SteamP2PManager? P2PManager { get; private set; } public bool IsInLobby { get { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (_sessionMode == NetworkSessionMode.DedicatedRelay) { return _virtualLocalPlayerId != CSteamID.Nil; } return LobbyManager?.IsInLobby ?? false; } } public bool IsHost { get { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0022: 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) if (_sessionMode == NetworkSessionMode.DedicatedRelay) { return _virtualOwnerId != CSteamID.Nil && _virtualOwnerId == _virtualLocalPlayerId; } return LobbyManager?.IsHost ?? false; } } public CSteamID LocalPlayerId { get { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0029: 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) //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (_sessionMode == NetworkSessionMode.DedicatedRelay) { return _virtualLocalPlayerId; } return LobbyManager?.LocalPlayerID ?? CSteamID.Nil; } } public ulong LocalPlayerId64 => LocalPlayerId.m_SteamID; public ulong HostPlayerId64 => CurrentLobby?.OwnerId.m_SteamID ?? 0; public LobbyInfo? CurrentLobby { get { //IL_0013: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0063: 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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) if (_sessionMode == NetworkSessionMode.DedicatedRelay) { if (_virtualLocalPlayerId == CSteamID.Nil) { return null; } CSteamID lobbyId = ((_virtualServerSteamId != CSteamID.Nil) ? _virtualServerSteamId : ((_virtualOwnerId != CSteamID.Nil) ? _virtualOwnerId : _virtualLocalPlayerId)); return new LobbyInfo { LobbyId = lobbyId, OwnerId = _virtualOwnerId, MemberCount = _virtualMembers.Count, MaxMembers = Math.Max(1, _virtualMembers.Count), Name = "Dedicated Server Session", CreatedAt = DateTime.UtcNow }; } return LobbyManager?.CurrentLobby; } } public NetworkRules NetworkRules => _rules; public NetworkSessionMode SessionMode => _sessionMode; public event EventHandler? OnVersionMismatch; public event EventHandler? OnLobbyJoined; public event EventHandler? OnLobbyCreated; public event EventHandler? OnLobbyLeft; public event EventHandler? OnMemberJoined; public event EventHandler? OnMemberLeft; public event EventHandler? OnLobbyDataChanged; public event EventHandler? OnMemberDataChanged; public event EventHandler? OnP2PMessageReceived; public void UpdateNetworkRules(NetworkRules rules) { if (rules != null && P2PManager != null) { P2PManager.UpdateRules(rules); } } public SteamNetworkClient() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_006f: Unknown result type (might be due to invalid IL or missing references) _rules = new NetworkRules(); } public SteamNetworkClient(NetworkRules rules) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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_006f: Unknown result type (might be due to invalid IL or missing references) _rules = rules ?? new NetworkRules(); } public bool Initialize() { if (_isInitialized) { return true; } try { if (!SteamNetworkUtils.IsSteamInitialized()) { throw new SteamNetworkException("Steam is not initialized. Make sure Steam is running and SteamAPI.Init() was called.", SteamNetworkErrorKind.SteamUnavailable, "Initialize", isRetryable: true); } LobbyManager = new SteamLobbyManager(); LobbyData = new SteamLobbyData(LobbyManager); MemberData = new SteamMemberData(LobbyManager); P2PManager = new SteamP2PManager(LobbyManager, _rules); SubscribeToEvents(); AttachDedicatedBridgeIfAvailable(); UpdateSessionMode(forceApplyOverrides: true); _isInitialized = true; return true; } catch (Exception ex) { throw new SteamNetworkException("Failed to initialize SteamNetworkClient: " + ex.Message, ex, (!(ex is SteamNetworkException ex2)) ? SteamNetworkErrorKind.SteamUnavailable : ex2.ErrorKind, "Initialize", isRetryable: true); } } public bool TryInitialize() { SteamNetworkException error; return TryInitialize(out error); } public bool TryInitialize(out SteamNetworkException? error) { try { Initialize(); error = null; return true; } catch (SteamNetworkException ex) { error = ex; return false; } catch (Exception ex2) { error = new SteamNetworkException("Failed to initialize SteamNetworkClient: " + ex2.Message, ex2, SteamNetworkErrorKind.SteamUnavailable, "TryInitialize", isRetryable: true); return false; } } public async Task CreateLobbyAsync(ELobbyType lobbyType = 1, int maxMembers = 4) { //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) EnsureInitialized(); ClearVirtualSessionState(emitLobbyLeft: true); UpdateSessionMode(forceApplyOverrides: true); return await LobbyManager.CreateLobbyAsync(lobbyType, maxMembers); } public async Task JoinLobbyAsync(CSteamID lobbyId) { //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) EnsureInitialized(); ClearVirtualSessionState(emitLobbyLeft: true); UpdateSessionMode(forceApplyOverrides: true); return await LobbyManager.JoinLobbyAsync(lobbyId); } public void LeaveLobby() { EnsureInitialized(); if (_sessionMode == NetworkSessionMode.DedicatedRelay) { ClearVirtualSessionState(emitLobbyLeft: true); UpdateSessionMode(forceApplyOverrides: true); } else { LobbyManager.LeaveLobby(); } } public List GetLobbyMembers() { EnsureInitialized(); if (_sessionMode == NetworkSessionMode.DedicatedRelay) { return _virtualMembers.Values.Select(CloneMember).ToList(); } return LobbyManager.GetLobbyMembers(); } public List GetRemoteMembers() { return (from member in GetLobbyMembers() where !member.IsLocalPlayer select member).ToList(); } public SteamNetworkLib.Models.MemberInfo? GetHostMember() { return GetLobbyMembers().FirstOrDefault((SteamNetworkLib.Models.MemberInfo member) => member.IsOwner); } public bool TryGetHostMember(out SteamNetworkLib.Models.MemberInfo? host) { host = GetHostMember(); return host != null; } public SteamNetworkLib.Models.MemberInfo? GetLocalMember() { return GetLobbyMembers().FirstOrDefault((SteamNetworkLib.Models.MemberInfo member) => member.IsLocalPlayer); } public bool TryGetLocalMember(out SteamNetworkLib.Models.MemberInfo? localMember) { localMember = GetLocalMember(); return localMember != null; } public SteamNetworkLib.Models.MemberInfo? GetMember(ulong steamId64) { return GetLobbyMembers().FirstOrDefault((SteamNetworkLib.Models.MemberInfo member) => member.SteamId.m_SteamID == steamId64); } public bool TryGetMember(ulong steamId64, out SteamNetworkLib.Models.MemberInfo? member) { member = GetMember(steamId64); return member != null; } public void InviteFriend(CSteamID friendId) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) EnsureInitialized(); if (_sessionMode == NetworkSessionMode.DedicatedRelay) { throw new LobbyException("Cannot invite friends while connected to a dedicated-server session"); } LobbyManager.InviteFriend(friendId); } public void OpenInviteDialog() { EnsureInitialized(); if (_sessionMode == NetworkSessionMode.DedicatedRelay) { throw new LobbyException("Cannot open invite dialog while connected to a dedicated-server session"); } LobbyManager.OpenInviteDialog(); } public void SetLobbyData(string key, string value) { EnsureInitialized(); if (_sessionMode == NetworkSessionMode.DedicatedRelay) { ValidateDataKey(key); if (IsHost) { SendDedicatedLobbyDataUpdate(key, value ?? string.Empty); } } else { LobbyData.SetData(key, value); } } public string? GetLobbyData(string key) { EnsureInitialized(); if (_sessionMode == NetworkSessionMode.DedicatedRelay) { ValidateDataKey(key); string value; return _virtualLobbyData.TryGetValue(key, out value) ? value : null; } return LobbyData.GetData(key); } public void SetMyData(string key, string value) { EnsureInitialized(); if (_sessionMode == NetworkSessionMode.DedicatedRelay) { ValidateDataKey(key); SendDedicatedMemberDataUpdate(key, value ?? string.Empty); } else { MemberData.SetMemberData(key, value); } } public string? GetMyData(string key) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) EnsureInitialized(); if (_sessionMode == NetworkSessionMode.DedicatedRelay) { ValidateDataKey(key); return GetVirtualMemberData(LocalPlayerId, key); } return MemberData.GetMemberData(key); } public string? GetPlayerData(CSteamID playerId, string key) { //IL_002e: 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) EnsureInitialized(); if (_sessionMode == NetworkSessionMode.DedicatedRelay) { ValidateDataKey(key); return GetVirtualMemberData(playerId, key); } return MemberData.GetMemberData(playerId, key); } public Dictionary GetDataForAllPlayers(string key) { //IL_0042: 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) EnsureInitialized(); if (_sessionMode == NetworkSessionMode.DedicatedRelay) { ValidateDataKey(key); Dictionary dictionary = new Dictionary(); foreach (SteamNetworkLib.Models.MemberInfo value in _virtualMembers.Values) { string virtualMemberData = GetVirtualMemberData(value.SteamId, key); if (!string.IsNullOrEmpty(virtualMemberData)) { dictionary[value.SteamId] = virtualMemberData; } } return dictionary; } return MemberData.GetMemberDataForAllPlayers(key); } public void SetMyDataBatch(Dictionary data) { EnsureInitialized(); if (_sessionMode == NetworkSessionMode.DedicatedRelay) { if (data == null || data.Count == 0) { return; } { foreach (KeyValuePair datum in data) { SetMyData(datum.Key, datum.Value); } return; } } MemberData.SetMemberDataBatch(data); } public async Task SendMessageToPlayerAsync(CSteamID playerId, P2PMessage message) { //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) EnsureInitialized(); return await P2PManager.SendMessageAsync(playerId, message, 0, (EP2PSend)2); } public async Task SendLargeDataToPlayerAsync(CSteamID playerId, string transferName, byte[] data, int channel = 0, int? chunkSize = null) { //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) EnsureInitialized(); return await P2PManager.SendLargeDataAsync(playerId, transferName, data, channel, chunkSize); } public async Task BroadcastMessageAsync(P2PMessage message) { EnsureInitialized(); List members = GetLobbyMembers(); List> sendTasks = new List>(); CSteamID localPlayerId = LocalPlayerId; foreach (SteamNetworkLib.Models.MemberInfo member in members) { if (member.SteamId != localPlayerId) { sendTasks.Add(P2PManager.SendMessageAsync(member.SteamId, message, 0, (EP2PSend)2)); } } if (sendTasks.Count > 0) { await Task.WhenAll(sendTasks); } } public void BroadcastMessage(P2PMessage message) { EnsureInitialized(); BroadcastMessageAsync(message); } public async Task SendTextMessageAsync(CSteamID playerId, string text) { //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) EnsureInitialized(); TextMessage message = new TextMessage { Content = text }; return await P2PManager.SendMessageAsync(playerId, message, 0, (EP2PSend)2); } public async Task BroadcastTextMessageAsync(string text) { EnsureInitialized(); TextMessage message = new TextMessage { Content = text }; await BroadcastMessageAsync(message); } public void BroadcastTextMessage(string text) { EnsureInitialized(); BroadcastTextMessageAsync(text); } public async Task SendDataSyncAsync(CSteamID playerId, string key, string value, string dataType = "string") { //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) EnsureInitialized(); DataSyncMessage message = new DataSyncMessage { Key = key, Value = value, DataType = dataType }; return await P2PManager.SendMessageAsync(playerId, message, 0, (EP2PSend)2); } public void RegisterMessageHandler(Action handler) where T : P2PMessage, new() { EnsureInitialized(); P2PManager.RegisterMessageHandler(handler); } public void ProcessIncomingMessages() { if (_isInitialized) { AttachDedicatedBridgeIfAvailable(); UpdateSessionMode(forceApplyOverrides: false); TryRegisterDedicatedSession(force: false); P2PManager.ProcessIncomingPackets(); } } public async Task SyncModDataWithAllPlayersAsync(string dataKey, string dataValue, string dataType = "string") { EnsureInitialized(); SetMyData(dataKey, dataValue); DataSyncMessage syncMessage = new DataSyncMessage { Key = dataKey, Value = dataValue, DataType = dataType }; await BroadcastMessageAsync(syncMessage); } public void SyncModDataWithAllPlayers(string dataKey, string dataValue, string dataType = "string") { EnsureInitialized(); SetMyData(dataKey, dataValue); DataSyncMessage message = new DataSyncMessage { Key = dataKey, Value = dataValue, DataType = dataType }; BroadcastMessage(message); } public bool IsModDataCompatible(string dataKey) { EnsureInitialized(); Dictionary dataForAllPlayers = GetDataForAllPlayers(dataKey); if (dataForAllPlayers.Count <= 1) { return true; } string[] array = dataForAllPlayers.Values.Distinct().ToArray(); return array.Length == 1; } internal void SetLibraryVersionData() { EnsureInitialized(); SetMyData("__snl_version", LibraryVersion); } public bool CheckLibraryVersionCompatibility() { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) EnsureInitialized(); if (!_versionCheckEnabled) { return true; } Dictionary dataForAllPlayers = GetDataForAllPlayers("__snl_version"); if (dataForAllPlayers.Count == 0) { return true; } List list = dataForAllPlayers.Values.Distinct().ToList(); if (list.Count <= 1) { return true; } string libraryVersion = LibraryVersion; List list2 = new List(); foreach (KeyValuePair item in dataForAllPlayers) { if (item.Value != libraryVersion) { list2.Add(item.Key); } } try { this.OnVersionMismatch?.Invoke(this, new VersionMismatchEventArgs(libraryVersion, dataForAllPlayers, list2)); } catch (Exception ex) { Console.WriteLine("Error in version mismatch event handler: " + ex.Message); } string text = string.Join(", ", dataForAllPlayers.Select((KeyValuePair kvp) => $"{kvp.Key}:{kvp.Value}")); Console.WriteLine("[SteamNetworkLib] WARNING: Version mismatch detected! Local version: " + libraryVersion + ", Player versions: " + text); return false; } public Dictionary GetPlayerLibraryVersions() { EnsureInitialized(); return GetDataForAllPlayers("__snl_version"); } public HostSyncVar CreateHostSyncVar(string key, T defaultValue, NetworkSyncOptions? options = null, ISyncValidator? validator = null) { EnsureInitialized(); HostSyncVar hostSyncVar = new HostSyncVar(this, key, defaultValue, options, validator); _syncVars.Add(hostSyncVar); return hostSyncVar; } public ClientSyncVar CreateClientSyncVar(string key, T defaultValue, NetworkSyncOptions? options = null, ISyncValidator? validator = null) { EnsureInitialized(); ClientSyncVar clientSyncVar = new ClientSyncVar(this, key, defaultValue, options, validator); _syncVars.Add(clientSyncVar); return clientSyncVar; } private void SubscribeToEvents() { LobbyManager.OnLobbyJoined += delegate(object s, LobbyJoinedEventArgs e) { UpdateSessionMode(forceApplyOverrides: true); this.OnLobbyJoined?.Invoke(this, e); }; LobbyManager.OnLobbyCreated += delegate(object s, LobbyCreatedEventArgs e) { UpdateSessionMode(forceApplyOverrides: true); this.OnLobbyCreated?.Invoke(this, e); }; LobbyManager.OnLobbyLeft += delegate(object s, LobbyLeftEventArgs e) { DisposeSyncVars(); UpdateSessionMode(forceApplyOverrides: true); this.OnLobbyLeft?.Invoke(this, e); }; LobbyManager.OnMemberJoined += delegate(object s, MemberJoinedEventArgs e) { this.OnMemberJoined?.Invoke(this, e); }; LobbyManager.OnMemberLeft += delegate(object s, MemberLeftEventArgs e) { this.OnMemberLeft?.Invoke(this, e); }; LobbyData.OnLobbyDataChanged += delegate(object s, LobbyDataChangedEventArgs e) { this.OnLobbyDataChanged?.Invoke(this, e); }; LobbyManager.OnLobbyDataChanged += delegate(object s, LobbyDataChangedEventArgs e) { this.OnLobbyDataChanged?.Invoke(this, e); }; MemberData.OnMemberDataChanged += delegate(object s, MemberDataChangedEventArgs e) { this.OnMemberDataChanged?.Invoke(this, e); }; P2PManager.OnMessageReceived += delegate(object s, P2PMessageReceivedEventArgs e) { this.OnP2PMessageReceived?.Invoke(this, e); }; if (!_versionCheckEnabled) { return; } LobbyManager.OnLobbyJoined += delegate { SafeExecute(delegate { SetLibraryVersionData(); }, "setting version data on lobby join"); }; LobbyManager.OnLobbyCreated += delegate { SafeExecute(delegate { SetLibraryVersionData(); }, "setting version data on lobby creation"); }; LobbyManager.OnMemberJoined += delegate { SafeExecute(async delegate { await Task.Delay(500); CheckLibraryVersionCompatibility(); }, "checking version compatibility"); }; OnMemberDataChanged += delegate(object s, MemberDataChangedEventArgs e) { if (e.Key == "__snl_version") { SafeExecute(delegate { CheckLibraryVersionCompatibility(); }, "checking version compatibility on data change"); } }; } private void AttachDedicatedBridgeIfAvailable() { if (_dedicatedBridge == null && !(DateTime.UtcNow - _lastDedicatedBridgeAttachAttemptUtc < DedicatedRegisterRetryDelay)) { _lastDedicatedBridgeAttachAttemptUtc = DateTime.UtcNow; _dedicatedBridge = DedicatedServerMessagingBridge.TryCreate(); if (_dedicatedBridge != null) { _dedicatedBridge.MessageReceived += OnDedicatedBridgeMessageReceived; TryRegisterDedicatedSession(force: true); } } } private void OnDedicatedBridgeMessageReceived(string command, string data) { if (!string.IsNullOrEmpty(command)) { if (string.Equals(command, "snl_dedicated_snapshot", StringComparison.Ordinal)) { HandleDedicatedSnapshot(data); } else if (string.Equals(command, "snl_dedicated_member_joined", StringComparison.Ordinal)) { HandleDedicatedMemberJoined(data); } else if (string.Equals(command, "snl_dedicated_member_left", StringComparison.Ordinal)) { HandleDedicatedMemberLeft(data); } else if (string.Equals(command, "snl_dedicated_lobby_data_changed", StringComparison.Ordinal)) { HandleDedicatedLobbyDataChanged(data); } else if (string.Equals(command, "snl_dedicated_member_data_changed", StringComparison.Ordinal)) { HandleDedicatedMemberDataChanged(data); } else if (string.Equals(command, "snl_dedicated_p2p_message", StringComparison.Ordinal)) { HandleDedicatedP2PMessage(data); } else if ((string.Equals(command, "auth_result", StringComparison.Ordinal) || string.Equals(command, "server_data", StringComparison.Ordinal)) && !IsDedicatedSessionFresh()) { TryRegisterDedicatedSession(force: true); } } } private void TryRegisterDedicatedSession(bool force) { if (_dedicatedBridge != null && !IsDedicatedSessionFresh()) { SteamLobbyManager? lobbyManager = LobbyManager; if ((lobbyManager == null || !lobbyManager.IsInLobby) && (force || _dedicatedBridge.IsDedicatedContextLikely || _sessionMode == NetworkSessionMode.DedicatedRelay) && (force || !(DateTime.UtcNow - _lastDedicatedRegisterAttemptUtc < DedicatedRegisterRetryDelay))) { DedicatedCompatibilityProtocol.RegisterRequest value = new DedicatedCompatibilityProtocol.RegisterRequest { LibraryVersion = LibraryVersion }; string payload = DedicatedJsonSerializer.Serialize(value); _lastDedicatedRegisterAttemptUtc = DateTime.UtcNow; _dedicatedBridge.TrySendToServer("snl_dedicated_register", payload); } } } private void HandleDedicatedSnapshot(string data) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_0328: Unknown result type (might be due to invalid IL or missing references) //IL_032e: Unknown result type (might be due to invalid IL or missing references) DedicatedCompatibilityProtocol.SnapshotPayload snapshotPayload; try { snapshotPayload = DedicatedJsonSerializer.Deserialize(data ?? string.Empty); } catch { return; } if (snapshotPayload == null) { return; } Dictionary dictionary = new Dictionary(_virtualMembers); _virtualMembers.Clear(); _virtualLobbyData.Clear(); _virtualMemberData.Clear(); _virtualSessionId = snapshotPayload.SessionId ?? string.Empty; _virtualLocalPlayerId = ParseSteamIdOrNil(snapshotPayload.LocalSteamId); _virtualOwnerId = ParseSteamIdOrNil(snapshotPayload.OwnerSteamId); _virtualServerSteamId = ParseSteamIdOrNil(snapshotPayload.ServerSteamId); _lastDedicatedSnapshotUtc = DateTime.UtcNow; if (snapshotPayload.LobbyData != null) { foreach (KeyValuePair lobbyDatum in snapshotPayload.LobbyData) { _virtualLobbyData[lobbyDatum.Key] = lobbyDatum.Value ?? string.Empty; } } if (snapshotPayload.MemberData != null) { foreach (KeyValuePair> memberDatum in snapshotPayload.MemberData) { if (!TryParseSteamId(memberDatum.Key, out var steamId)) { continue; } Dictionary dictionary2 = new Dictionary(); if (memberDatum.Value != null) { foreach (KeyValuePair item in memberDatum.Value) { dictionary2[item.Key] = item.Value ?? string.Empty; } } _virtualMemberData[steamId.m_SteamID] = dictionary2; } } if (snapshotPayload.Members != null) { foreach (DedicatedCompatibilityProtocol.MemberSnapshot member in snapshotPayload.Members) { UpsertVirtualMember(member, raiseJoinedEvent: false); } } RefreshVirtualOwnerFlags(); UpdateSessionMode(forceApplyOverrides: true); RaiseDedicatedLobbyJoinedIfNeeded(); foreach (KeyValuePair virtualMember in _virtualMembers) { if (!dictionary.ContainsKey(virtualMember.Key) && virtualMember.Value.SteamId != LocalPlayerId) { this.OnMemberJoined?.Invoke(this, new MemberJoinedEventArgs(CloneMember(virtualMember.Value))); } } foreach (KeyValuePair item2 in dictionary) { if (!_virtualMembers.ContainsKey(item2.Key) && item2.Value.SteamId != LocalPlayerId) { this.OnMemberLeft?.Invoke(this, new MemberLeftEventArgs(CloneMember(item2.Value), "Left dedicated session")); } } } private void HandleDedicatedMemberJoined(string data) { //IL_0033: 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) DedicatedCompatibilityProtocol.MemberJoinedPayload memberJoinedPayload = DedicatedJsonSerializer.Deserialize(data ?? string.Empty); if (memberJoinedPayload?.Member != null) { _virtualOwnerId = ParseSteamIdOrNil(memberJoinedPayload.OwnerSteamId); _lastDedicatedSnapshotUtc = DateTime.UtcNow; UpdateSessionMode(forceApplyOverrides: false); UpsertVirtualMember(memberJoinedPayload.Member, raiseJoinedEvent: true); RefreshVirtualOwnerFlags(); } } private void HandleDedicatedMemberLeft(string data) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: 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_0072: 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_0091: 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) DedicatedCompatibilityProtocol.MemberLeftPayload memberLeftPayload = DedicatedJsonSerializer.Deserialize(data ?? string.Empty); if (memberLeftPayload == null || !TryParseSteamId(memberLeftPayload.SteamId, out var steamId)) { return; } _virtualOwnerId = ParseSteamIdOrNil(memberLeftPayload.OwnerSteamId); _lastDedicatedSnapshotUtc = DateTime.UtcNow; if (_virtualMembers.TryGetValue(steamId.m_SteamID, out SteamNetworkLib.Models.MemberInfo value)) { _virtualMembers.Remove(steamId.m_SteamID); _virtualMemberData.Remove(steamId.m_SteamID); if (value.SteamId != LocalPlayerId) { this.OnMemberLeft?.Invoke(this, new MemberLeftEventArgs(CloneMember(value), "Left dedicated session")); } } RefreshVirtualOwnerFlags(); UpdateSessionMode(forceApplyOverrides: false); } private void HandleDedicatedLobbyDataChanged(string data) { //IL_0092: 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) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) DedicatedCompatibilityProtocol.LobbyDataChangedPayload lobbyDataChangedPayload = DedicatedJsonSerializer.Deserialize(data ?? string.Empty); if (lobbyDataChangedPayload != null && !string.IsNullOrEmpty(lobbyDataChangedPayload.Key)) { string value; string oldValue = (_virtualLobbyData.TryGetValue(lobbyDataChangedPayload.Key, out value) ? value : null); if (lobbyDataChangedPayload.NewValue == null) { _virtualLobbyData.Remove(lobbyDataChangedPayload.Key); } else { _virtualLobbyData[lobbyDataChangedPayload.Key] = lobbyDataChangedPayload.NewValue; } CSteamID changedBy = ParseSteamIdOrNil(lobbyDataChangedPayload.ChangedBySteamId); this.OnLobbyDataChanged?.Invoke(this, new LobbyDataChangedEventArgs(lobbyDataChangedPayload.Key, oldValue, lobbyDataChangedPayload.NewValue, changedBy)); } } private void HandleDedicatedMemberDataChanged(string data) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) DedicatedCompatibilityProtocol.MemberDataChangedPayload memberDataChangedPayload = DedicatedJsonSerializer.Deserialize(data ?? string.Empty); if (memberDataChangedPayload != null && !string.IsNullOrEmpty(memberDataChangedPayload.MemberSteamId) && !string.IsNullOrEmpty(memberDataChangedPayload.Key) && TryParseSteamId(memberDataChangedPayload.MemberSteamId, out var steamId)) { Dictionary orCreateVirtualMemberMap = GetOrCreateVirtualMemberMap(steamId.m_SteamID); string value; string oldValue = (orCreateVirtualMemberMap.TryGetValue(memberDataChangedPayload.Key, out value) ? value : null); if (memberDataChangedPayload.NewValue == null) { orCreateVirtualMemberMap.Remove(memberDataChangedPayload.Key); } else { orCreateVirtualMemberMap[memberDataChangedPayload.Key] = memberDataChangedPayload.NewValue; } this.OnMemberDataChanged?.Invoke(this, new MemberDataChangedEventArgs(steamId, memberDataChangedPayload.Key, oldValue, memberDataChangedPayload.NewValue)); } } private void HandleDedicatedP2PMessage(string data) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) DedicatedCompatibilityProtocol.P2PMessagePayload p2PMessagePayload = DedicatedJsonSerializer.Deserialize(data ?? string.Empty); if (p2PMessagePayload != null && !string.IsNullOrEmpty(p2PMessagePayload.SenderSteamId) && !string.IsNullOrEmpty(p2PMessagePayload.DataBase64) && TryParseSteamId(p2PMessagePayload.SenderSteamId, out var steamId)) { byte[] data2; try { data2 = Convert.FromBase64String(p2PMessagePayload.DataBase64); } catch { return; } P2PManager?.ProcessExternalPacket(steamId, data2, p2PMessagePayload.Channel); } } private void SendDedicatedLobbyDataUpdate(string key, string value) { if (_dedicatedBridge != null) { DedicatedCompatibilityProtocol.SetLobbyDataRequest value2 = new DedicatedCompatibilityProtocol.SetLobbyDataRequest { Key = key, Value = value }; _dedicatedBridge.TrySendToServer("snl_dedicated_set_lobby_data", DedicatedJsonSerializer.Serialize(value2)); } } private void SendDedicatedMemberDataUpdate(string key, string value) { if (_dedicatedBridge != null) { DedicatedCompatibilityProtocol.SetMemberDataRequest value2 = new DedicatedCompatibilityProtocol.SetMemberDataRequest { Key = key, Value = value }; _dedicatedBridge.TrySendToServer("snl_dedicated_set_member_data", DedicatedJsonSerializer.Serialize(value2)); } } private Task SendPacketViaDedicatedAsync(CSteamID targetId, byte[] packetData, int channel, EP2PSend sendType) { //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) if (_sessionMode != NetworkSessionMode.DedicatedRelay || _dedicatedBridge == null) { return Task.FromResult(result: false); } DedicatedCompatibilityProtocol.P2PSendRequest value = new DedicatedCompatibilityProtocol.P2PSendRequest { TargetSteamId = ((targetId == CSteamID.Nil) ? string.Empty : targetId.m_SteamID.ToString(CultureInfo.InvariantCulture)), DataBase64 = Convert.ToBase64String(packetData), Channel = channel }; bool result = _dedicatedBridge.TrySendToServer("snl_dedicated_p2p_send", DedicatedJsonSerializer.Serialize(value)); return Task.FromResult(result); } private void UpdateSessionMode(bool forceApplyOverrides) { NetworkSessionMode networkSessionMode = DetermineSessionMode(); if (_sessionMode != networkSessionMode || forceApplyOverrides) { if (_sessionMode == NetworkSessionMode.DedicatedRelay && networkSessionMode != NetworkSessionMode.DedicatedRelay) { bool emitLobbyLeft = networkSessionMode == NetworkSessionMode.None; ClearVirtualSessionState(emitLobbyLeft); } _sessionMode = networkSessionMode; ConfigureP2POverridesForCurrentMode(); } } private NetworkSessionMode DetermineSessionMode() { //IL_001d: 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) SteamLobbyManager? lobbyManager = LobbyManager; if (lobbyManager != null && lobbyManager.IsInLobby) { return NetworkSessionMode.LobbyP2P; } if (_virtualLocalPlayerId != CSteamID.Nil && IsDedicatedSessionFresh()) { return NetworkSessionMode.DedicatedRelay; } return NetworkSessionMode.None; } private bool IsDedicatedSessionFresh() { //IL_0014: 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) return _lastDedicatedSnapshotUtc != DateTime.MinValue && _virtualLocalPlayerId != CSteamID.Nil; } private void ConfigureP2POverridesForCurrentMode() { if (P2PManager == null) { return; } if (_sessionMode == NetworkSessionMode.DedicatedRelay) { P2PManager.ConfigureOverrides(SendPacketViaDedicatedAsync, () => _virtualMembers.Values.Select(CloneMember).ToList(), () => LocalPlayerId, () => IsInLobby); } else { P2PManager.ConfigureOverrides(null, null, null, null); } } private void RaiseDedicatedLobbyJoinedIfNeeded() { if (_sessionMode == NetworkSessionMode.DedicatedRelay && !_dedicatedJoinEventRaised) { LobbyInfo currentLobby = CurrentLobby; if (currentLobby != null) { _dedicatedJoinEventRaised = true; this.OnLobbyJoined?.Invoke(this, new LobbyJoinedEventArgs(currentLobby, (EResult)1)); } } } private void ClearVirtualSessionState(bool emitLobbyLeft) { //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: 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_0018: 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_0025: 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_004c: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (emitLobbyLeft && _dedicatedJoinEventRaised) { CSteamID lobbyId = ((_virtualServerSteamId != CSteamID.Nil) ? _virtualServerSteamId : ((_virtualOwnerId != CSteamID.Nil) ? _virtualOwnerId : _virtualLocalPlayerId)); this.OnLobbyLeft?.Invoke(this, new LobbyLeftEventArgs(lobbyId, "Dedicated session ended")); } _virtualMembers.Clear(); _virtualLobbyData.Clear(); _virtualMemberData.Clear(); _virtualSessionId = string.Empty; _virtualOwnerId = CSteamID.Nil; _virtualLocalPlayerId = CSteamID.Nil; _virtualServerSteamId = CSteamID.Nil; _lastDedicatedSnapshotUtc = DateTime.MinValue; _dedicatedJoinEventRaised = false; } private void UpsertVirtualMember(DedicatedCompatibilityProtocol.MemberSnapshot snapshot, bool raiseJoinedEvent) { //IL_0024: 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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008a: 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_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) if (snapshot != null && TryParseSteamId(snapshot.SteamId, out var steamId)) { bool isLocalPlayer = ((_virtualLocalPlayerId != CSteamID.Nil) ? (steamId == _virtualLocalPlayerId) : snapshot.IsLocalPlayer); SteamNetworkLib.Models.MemberInfo memberInfo = new SteamNetworkLib.Models.MemberInfo { SteamId = steamId, DisplayName = (snapshot.DisplayName ?? string.Empty), IsOwner = ((_virtualOwnerId != CSteamID.Nil) ? (steamId == _virtualOwnerId) : snapshot.IsOwner), IsLocalPlayer = isLocalPlayer, JoinedAt = FromUnixMillisecondsOrNow(snapshot.JoinedAtUnixMs) }; bool flag = _virtualMembers.ContainsKey(steamId.m_SteamID); _virtualMembers[steamId.m_SteamID] = memberInfo; if (!flag && raiseJoinedEvent && memberInfo.SteamId != LocalPlayerId) { this.OnMemberJoined?.Invoke(this, new MemberJoinedEventArgs(CloneMember(memberInfo))); } } } private string? GetVirtualMemberData(CSteamID playerId, string key) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) if (!_virtualMemberData.TryGetValue(playerId.m_SteamID, out Dictionary value)) { return null; } if (!value.TryGetValue(key, out var value2) || string.IsNullOrEmpty(value2)) { return null; } return value2; } private Dictionary GetOrCreateVirtualMemberMap(ulong playerSteamId) { if (!_virtualMemberData.TryGetValue(playerSteamId, out Dictionary value)) { value = new Dictionary(); _virtualMemberData[playerSteamId] = value; } return value; } private void RefreshVirtualOwnerFlags() { //IL_0021: 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) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) foreach (KeyValuePair virtualMember in _virtualMembers) { virtualMember.Value.IsOwner = _virtualOwnerId != CSteamID.Nil && virtualMember.Value.SteamId == _virtualOwnerId; } } private static SteamNetworkLib.Models.MemberInfo CloneMember(SteamNetworkLib.Models.MemberInfo source) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) return new SteamNetworkLib.Models.MemberInfo { SteamId = source.SteamId, DisplayName = source.DisplayName, IsOwner = source.IsOwner, IsLocalPlayer = source.IsLocalPlayer, JoinedAt = source.JoinedAt }; } private static DateTime FromUnixMillisecondsOrNow(long unixMs) { if (unixMs <= 0) { return DateTime.UtcNow; } try { return DateTimeOffset.FromUnixTimeMilliseconds(unixMs).UtcDateTime; } catch { return DateTime.UtcNow; } } private static void ValidateDataKey(string key) { if (string.IsNullOrEmpty(key)) { throw new ArgumentException("Data key cannot be null or empty", "key"); } if (key.Length > 255) { throw new ArgumentException("Data key cannot exceed 255 characters", "key"); } if (key.StartsWith("__steam_", StringComparison.Ordinal)) { throw new ArgumentException("Data key cannot start with '__steam_' (reserved by Steam)", "key"); } } private static bool TryParseSteamId(string? raw, out CSteamID steamId) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) steamId = CSteamID.Nil; if (!ulong.TryParse(raw, NumberStyles.None, CultureInfo.InvariantCulture, out var result) || result == 0) { return false; } steamId = new CSteamID(result); return true; } private static CSteamID ParseSteamIdOrNil(string? raw) { //IL_0012: 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_0013: 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) CSteamID steamId; return TryParseSteamId(raw, out steamId) ? steamId : CSteamID.Nil; } private void SafeExecute(Action action, string operation) { try { action(); } catch (Exception ex) { Console.WriteLine("[SteamNetworkLib] Warning: Failed " + operation + ": " + ex.Message); } } private void SafeExecute(Func action, string operation) { Func action2 = action; string operation2 = operation; Task.Run(async delegate { try { await action2(); } catch (Exception ex2) { Exception ex = ex2; Console.WriteLine("[SteamNetworkLib] Warning: Failed " + operation2 + ": " + ex.Message); } }); } private void DisposeSyncVars() { foreach (IDisposable syncVar in _syncVars) { try { syncVar?.Dispose(); } catch (Exception ex) { Console.WriteLine("[SteamNetworkLib] Warning: Failed to dispose sync var: " + ex.Message); } } _syncVars.Clear(); } private void UnsubscribeFromEvents() { this.OnLobbyJoined = null; this.OnLobbyCreated = null; this.OnLobbyLeft = null; this.OnMemberJoined = null; this.OnMemberLeft = null; this.OnLobbyDataChanged = null; this.OnMemberDataChanged = null; this.OnP2PMessageReceived = null; this.OnVersionMismatch = null; } private void EnsureInitialized() { if (!_isInitialized) { throw new InvalidOperationException("SteamNetworkClient is not initialized. Call Initialize() first."); } } public void Dispose() { if (!_disposed) { try { DisposeSyncVars(); UnsubscribeFromEvents(); P2PManager?.Dispose(); MemberData?.Dispose(); LobbyData?.Dispose(); LobbyManager?.Dispose(); _dedicatedBridge?.Dispose(); _dedicatedBridge = null; ClearVirtualSessionState(emitLobbyLeft: false); } catch (Exception ex) { Console.WriteLine("Error disposing SteamNetworkClient: " + ex.Message); } _disposed = true; GC.SuppressFinalize(this); } } } } namespace SteamNetworkLib.Utilities { public static class AudioStreamingCompatibility { public static bool IsSupported => true; public static string RuntimeType => "Mono"; public static void ThrowIfNotSupported() { if (!IsSupported) { throw new PlatformNotSupportedException("Audio streaming is not supported on " + RuntimeType + " runtime. Audio streaming requires Mono runtime due to OpusSharp limitations with IL2CPP. IL2CPP has memory marshalling issues that cause corruption when passing audio data between managed and native code. Please use a Mono-based game/environment for audio streaming features."); } } public static string GetCompatibilityMessage() { if (IsSupported) { return "Audio streaming is supported on " + RuntimeType + " runtime."; } return "Audio streaming is NOT supported on " + RuntimeType + " runtime. Audio streaming requires Mono runtime due to OpusSharp limitations. IL2CPP has memory marshalling issues that cause corruption when passing audio data between managed and native code through the OpusSharp library. The core SteamNetworkLib features (lobbies, P2P messaging, file transfer, etc.) work perfectly on both Mono and IL2CPP - only audio streaming is affected."; } public static void LogCompatibilityWarning() { if (!IsSupported) { Console.WriteLine("[SteamNetworkLib] WARNING: " + GetCompatibilityMessage()); } } } public static class MessageSerializer { public const string MESSAGE_HEADER = "SNLM"; public static byte[] SerializeMessage(P2PMessage message) { try { byte[] array = message.Serialize(); byte[] bytes = Encoding.UTF8.GetBytes(message.MessageType); byte[] bytes2 = Encoding.UTF8.GetBytes("SNLM"); int num = bytes2.Length + 1 + bytes.Length + array.Length; byte[] array2 = new byte[num]; int num2 = 0; Array.Copy(bytes2, 0, array2, num2, bytes2.Length); num2 += bytes2.Length; array2[num2] = (byte)bytes.Length; num2++; Array.Copy(bytes, 0, array2, num2, bytes.Length); num2 += bytes.Length; Array.Copy(array, 0, array2, num2, array.Length); return array2; } catch (Exception ex) when (!(ex is P2PException)) { string message2 = "Failed to serialize message: " + ex.Message; string messageType = message.MessageType; throw new P2PException(message2, ex, SteamNetworkErrorKind.SerializationFailed, "SerializeMessage", null, null, messageType); } } public static (string MessageType, byte[] MessageData) DeserializeMessage(byte[] data) { try { if (data.Length < 6) { string message = $"Invalid message data: too short ({data.Length} bytes)"; int? packetSize = data.Length; throw new P2PException(message, SteamNetworkErrorKind.MessageFormatInvalid, "DeserializeMessage", null, null, null, packetSize); } byte[] bytes = Encoding.UTF8.GetBytes("SNLM"); int num = 0; bool flag = true; for (int i = 0; i < bytes.Length; i++) { if (data[num + i] != bytes[i]) { flag = false; break; } } if (!flag) { byte[] array = new byte[bytes.Length]; Array.Copy(data, num, array, 0, bytes.Length); string message2 = "Invalid message header: expected 'SNLM', got '" + Encoding.UTF8.GetString(array) + "'"; int? packetSize = data.Length; throw new P2PException(message2, SteamNetworkErrorKind.MessageFormatInvalid, "DeserializeMessage", null, null, null, packetSize); } num += bytes.Length; byte b = data[num]; num++; if (b == 0 || num + b > data.Length) { string message3 = $"Invalid message format: type length {b} exceeds data bounds (data length: {data.Length}, offset: {num})"; int? packetSize = data.Length; throw new P2PException(message3, SteamNetworkErrorKind.MessageFormatInvalid, "DeserializeMessage", null, null, null, packetSize); } string @string = Encoding.UTF8.GetString(data, num, b); num += b; int num2 = data.Length - num; byte[] array2 = new byte[num2]; Array.Copy(data, num, array2, 0, num2); return (@string, array2); } catch (Exception ex) when (!(ex is P2PException)) { string message4 = "Failed to deserialize message: " + ex.Message; int? packetSize = data.Length; throw new P2PException(message4, ex, SteamNetworkErrorKind.MessageFormatInvalid, "DeserializeMessage", null, null, null, packetSize); } } public static T CreateMessage(byte[] data) where T : P2PMessage, new() { try { (string MessageType, byte[] MessageData) tuple = DeserializeMessage(data); string item = tuple.MessageType; byte[] item2 = tuple.MessageData; T val = new T(); if (val.MessageType != item) { string message = "Message type mismatch: expected '" + val.MessageType + "', got '" + item + "'"; string messageType = item; throw new P2PException(message, SteamNetworkErrorKind.MessageTypeMismatch, "CreateMessage", null, null, messageType); } val.Deserialize(item2); return val; } catch (Exception ex) when (!(ex is P2PException)) { string message2 = "Failed to create message of type " + typeof(T).Name + ": " + ex.Message; string messageType = typeof(T).Name; throw new P2PException(message2, ex, SteamNetworkErrorKind.SerializationFailed, "CreateMessage", null, null, messageType); } } public static bool IsValidMessage(byte[] data) { try { if (data.Length < 6) { return false; } byte[] bytes = Encoding.UTF8.GetBytes("SNLM"); bool flag = true; for (int i = 0; i < bytes.Length; i++) { if (data[i] != bytes[i]) { flag = false; break; } } if (!flag) { return false; } byte b = data[bytes.Length]; return bytes.Length + 1 + b < data.Length; } catch (Exception ex) { Console.WriteLine("[SteamNetworkLib] MessageSerializer.IsValidMessage ERROR: " + ex.Message); Console.WriteLine("[SteamNetworkLib] MessageSerializer.IsValidMessage Stack Trace: " + ex.StackTrace); return false; } } public static string? GetMessageType(byte[] data) { try { if (!IsValidMessage(data)) { return null; } byte[] bytes = Encoding.UTF8.GetBytes("SNLM"); int num = bytes.Length; byte b = data[num]; num++; if (num + b > data.Length) { return null; } return Encoding.UTF8.GetString(data, num, b); } catch (Exception ex) { Console.WriteLine("[SteamNetworkLib] MessageSerializer.GetMessageType ERROR: " + ex.Message); Console.WriteLine("[SteamNetworkLib] MessageSerializer.GetMessageType Stack Trace: " + ex.StackTrace); return null; } } } public static class SteamNetworkUtils { public static bool IsSteamInitialized() { //IL_0013: 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_001a: 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) try { if (!SteamAPI.IsSteamRunning()) { return false; } CSteamID steamID = SteamUser.GetSteamID(); return steamID != CSteamID.Nil && steamID.m_SteamID != 0; } catch { return false; } } public static string GetLocalPlayerName() { if (!IsSteamInitialized()) { return "Unknown Player"; } return SteamFriends.GetPersonaName(); } public static string GetPlayerName(CSteamID steamId) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (!IsSteamInitialized()) { return "Unknown Player"; } return SteamFriends.GetFriendPersonaName(steamId); } public static bool IsValidSteamID(CSteamID steamId) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0083: 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_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (steamId == CSteamID.Nil || steamId.m_SteamID == 0) { return false; } try { if (((CSteamID)(ref steamId)).IsValid()) { return true; } return steamId.m_SteamID != 0L && steamId.m_SteamID != 1; } catch (Exception ex) { Console.WriteLine("[SteamNetworkLib] SteamNetworkUtils.IsValidSteamID ERROR: " + ex.Message); Console.WriteLine("[SteamNetworkLib] SteamNetworkUtils.IsValidSteamID Stack Trace: " + ex.StackTrace); return steamId.m_SteamID != 0L && steamId.m_SteamID != 1; } } public static CSteamID? ParseSteamID(string steamIdString) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(steamIdString)) { return null; } if (ulong.TryParse(steamIdString, out var result)) { CSteamID val = default(CSteamID); ((CSteamID)(ref val))..ctor(result); return IsValidSteamID(val) ? new CSteamID?(val) : null; } return null; } public static bool IsFriend(CSteamID steamId) { //IL_0008: 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_001d: 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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 if (!IsSteamInitialized() || !IsValidSteamID(steamId)) { return false; } EFriendRelationship friendRelationship = SteamFriends.GetFriendRelationship(steamId); return (int)friendRelationship == 3; } public static string FormatLobbyType(ELobbyType lobbyType) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected I4, but got Unknown if (1 == 0) { } string result = (int)lobbyType switch { 0 => "Private", 1 => "Friends Only", 2 => "Public", 3 => "Invisible", _ => "Unknown", }; if (1 == 0) { } return result; } public static string FormatSteamResult(EResult result) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected I4, but got Unknown //IL_031b: Unknown result type (might be due to invalid IL or missing references) if (1 == 0) { } string result2 = (result - 1) switch { 0 => "Success", 1 => "Generic failure", 2 => "No connection to Steam", 4 => "Invalid password", 5 => "Logged in elsewhere", 6 => "Invalid protocol version", 7 => "Invalid parameter", 8 => "File not found", 9 => "Service busy", 10 => "Invalid state", 11 => "Invalid name", 12 => "Invalid email", 13 => "Duplicate name", 14 => "Access denied", 15 => "Timeout", 16 => "Banned", 17 => "Account not found", 18 => "Invalid Steam ID", 19 => "Service unavailable", 20 => "Not logged on", 21 => "Request pending", 22 => "Encryption failure", 23 => "Insufficient privilege", 24 => "Limit exceeded", 25 => "Access revoked", 26 => "License expired", 27 => "Already redeemed", 28 => "Duplicate request", 29 => "Already owned", 30 => "IP not found", 31 => "Persist failed", 32 => "Locking failed", 33 => "Logon session replaced", 34 => "Connect failed", 35 => "Handshake failed", 36 => "IO failure", 37 => "Remote disconnect", 38 => "Shopping cart not found", 39 => "Blocked", 40 => "Ignored", 41 => "No match", 42 => "Account disabled", 43 => "Service read only", 44 => "Account not featured", 45 => "Administrator OK", 46 => "Content version", 47 => "Try another CM", 48 => "Password required to kick session", 49 => "Already logged in elsewhere", 50 => "Suspended", 51 => "Cancelled", 52 => "Data corruption", 53 => "Disk full", 54 => "Remote call failed", _ => $"Unknown result: {result}", }; if (1 == 0) { } return result2; } } } namespace SteamNetworkLib.Sync { public class ClientSyncVar : IDisposable { private readonly SteamNetworkClient _client; private readonly string _key; private readonly string _fullKey; private readonly NetworkSyncOptions _options; private readonly ISyncSerializer _serializer; private readonly ISyncValidator? _validator; private readonly T _defaultValue; private T _localValue; private readonly Dictionary _playerValues = new Dictionary(); private bool _disposed; private bool _isSubscribed; private DateTime _lastSyncTime; private T? _pendingValue; private bool _hasPendingValue; public T Value { get { return _localValue; } set { SetValue(value); } } public string Key => _key; public string FullKey => _fullKey; public bool IsDirty => _hasPendingValue; public event Action? OnValueChanged; public event Action? OnMyValueChanged; public event Action? OnSyncError; internal ClientSyncVar(SteamNetworkClient client, string key, T defaultValue, NetworkSyncOptions? options = null, ISyncValidator? validator = null) { _client = client ?? throw new ArgumentNullException("client"); if (string.IsNullOrEmpty(key)) { throw new ArgumentException("Sync key cannot be null or empty", "key"); } _key = key; _options = options ?? new NetworkSyncOptions(); _fullKey = _options.GetFullKey(key); _defaultValue = defaultValue; _localValue = defaultValue; _serializer = _options.Serializer ?? new JsonSyncSerializer(); _validator = validator; _lastSyncTime = DateTime.MinValue; if (!_serializer.CanSerialize(typeof(T))) { throw new SyncSerializationException("Type '" + typeof(T).Name + "' cannot be serialized. Ensure it has a parameterless constructor and all public properties are of supported types (primitives, strings, List, Dictionary, or other serializable custom types).", typeof(T), _fullKey); } SubscribeToChanges(); TryLoadExistingValues(); } private void SetValue(T newValue) { if (!_client.IsInLobby) { if (_options.WarnOnIgnoredWrites) { Console.WriteLine("[SteamNetworkLib] ClientSyncVar '" + _key + "': Write ignored - not in a lobby."); } } else { if (object.Equals(_localValue, newValue)) { return; } if (_validator != null && !_validator.IsValid(newValue)) { string text = _validator.GetErrorMessage(newValue) ?? "Validation failed"; string text2 = "[SteamNetworkLib] ClientSyncVar '" + _key + "': " + text; if (_options.ThrowOnValidationError) { throw new SyncValidationException(text2, _fullKey, newValue); } this.OnSyncError?.Invoke(new SyncValidationException(text2, _fullKey, newValue)); Console.WriteLine(text2); return; } if (_options.MaxSyncsPerSecond > 0) { TimeSpan timeSpan = DateTime.UtcNow - _lastSyncTime; TimeSpan timeSpan2 = TimeSpan.FromSeconds(1.0 / (double)_options.MaxSyncsPerSecond); if (timeSpan < timeSpan2) { _pendingValue = newValue; _hasPendingValue = true; return; } } if (!_options.AutoSync) { _pendingValue = newValue; _hasPendingValue = true; } else { PerformSync(newValue); } } } private void PerformSync(T newValue) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) T localValue = _localValue; try { string value = _serializer.Serialize(newValue); _client.SetMyData(_fullKey, value); _localValue = newValue; _playerValues[_client.LocalPlayerId.m_SteamID] = newValue; _lastSyncTime = DateTime.UtcNow; _hasPendingValue = false; this.OnValueChanged?.Invoke(_client.LocalPlayerId, localValue, newValue); this.OnMyValueChanged?.Invoke(localValue, newValue); } catch (Exception ex) { this.OnSyncError?.Invoke(ex); if (_options.WarnOnIgnoredWrites) { Console.WriteLine("[SteamNetworkLib] ClientSyncVar '" + _key + "': Sync error - " + ex.Message); } } } public void FlushPending() { if (_hasPendingValue && _pendingValue != null) { PerformSync(_pendingValue); } } public T GetValue(CSteamID playerId) { //IL_0007: 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) //IL_006b: Unknown result type (might be due to invalid IL or missing references) if (_playerValues.TryGetValue(playerId.m_SteamID, out var value)) { return value; } if (_client.IsInLobby) { try { string playerData = _client.GetPlayerData(playerId, _fullKey); if (!string.IsNullOrEmpty(playerData)) { T val = _serializer.Deserialize(playerData); _playerValues[playerId.m_SteamID] = val; return val; } } catch (Exception obj) { this.OnSyncError?.Invoke(obj); } } return _defaultValue; } public Dictionary GetAllValues() { //IL_0042: 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) Dictionary dictionary = new Dictionary(); if (!_client.IsInLobby) { return dictionary; } List lobbyMembers = _client.GetLobbyMembers(); foreach (SteamNetworkLib.Models.MemberInfo item in lobbyMembers) { dictionary[item.SteamId] = GetValue(item.SteamId); } return dictionary; } public void Refresh() { _playerValues.Clear(); TryLoadExistingValues(); } private void TryLoadExistingValues() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) if (!_client.IsInLobby) { return; } try { List lobbyMembers = _client.GetLobbyMembers(); foreach (SteamNetworkLib.Models.MemberInfo item in lobbyMembers) { string playerData = _client.GetPlayerData(item.SteamId, _fullKey); if (!string.IsNullOrEmpty(playerData)) { T val = _serializer.Deserialize(playerData); _playerValues[item.SteamId.m_SteamID] = val; if (item.SteamId == _client.LocalPlayerId) { _localValue = val; } } } } catch (Exception obj) { this.OnSyncError?.Invoke(obj); } } private void SubscribeToChanges() { if (!_isSubscribed) { _client.OnMemberDataChanged += HandleMemberDataChanged; _client.OnLobbyJoined += HandleLobbyJoined; _client.OnMemberJoined += HandleMemberJoined; _client.OnMemberLeft += HandleMemberLeft; _isSubscribed = true; } } private void UnsubscribeFromChanges() { if (_isSubscribed) { _client.OnMemberDataChanged -= HandleMemberDataChanged; _client.OnLobbyJoined -= HandleLobbyJoined; _client.OnMemberJoined -= HandleMemberJoined; _client.OnMemberLeft -= HandleMemberLeft; _isSubscribed = false; } } private void HandleMemberDataChanged(object? sender, MemberDataChangedEventArgs e) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: 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_0106: Unknown result type (might be due to invalid IL or missing references) if (e.Key != _fullKey) { return; } try { T value; T val = (_playerValues.TryGetValue(e.MemberId.m_SteamID, out value) ? value : _defaultValue); T val2; if (string.IsNullOrEmpty(e.NewValue)) { val2 = _defaultValue; _playerValues.Remove(e.MemberId.m_SteamID); } else { val2 = _serializer.Deserialize(e.NewValue); _playerValues[e.MemberId.m_SteamID] = val2; } if (e.MemberId == _client.LocalPlayerId) { _localValue = val2; } if (!object.Equals(val, val2)) { this.OnValueChanged?.Invoke(e.MemberId, val, val2); if (e.MemberId == _client.LocalPlayerId) { this.OnMyValueChanged?.Invoke(val, val2); } } } catch (Exception obj) { this.OnSyncError?.Invoke(obj); } } private void HandleLobbyJoined(object? sender, LobbyJoinedEventArgs e) { _playerValues.Clear(); TryLoadExistingValues(); } private void HandleMemberJoined(object? sender, MemberJoinedEventArgs e) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) try { string playerData = _client.GetPlayerData(e.Member.SteamId, _fullKey); if (!string.IsNullOrEmpty(playerData)) { T value = _serializer.Deserialize(playerData); _playerValues[e.Member.SteamId.m_SteamID] = value; } } catch (Exception obj) { this.OnSyncError?.Invoke(obj); } } private void HandleMemberLeft(object? sender, MemberLeftEventArgs e) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) _playerValues.Remove(e.Member.SteamId.m_SteamID); } public void Dispose() { if (!_disposed) { UnsubscribeFromChanges(); this.OnValueChanged = null; this.OnMyValueChanged = null; this.OnSyncError = null; _playerValues.Clear(); _disposed = true; } } } public class HostSyncVar : IDisposable { private readonly SteamNetworkClient _client; private readonly string _key; private readonly string _fullKey; private readonly NetworkSyncOptions _options; private readonly ISyncSerializer _serializer; private readonly ISyncValidator? _validator; private T _value; private T _defaultValue; private bool _disposed; private bool _isSubscribed; private DateTime _lastSyncTime; private T? _pendingValue; private bool _hasPendingValue; public T Value { get { return _value; } set { SetValue(value); } } public bool CanWrite => _client.IsHost; public string Key => _key; public string FullKey => _fullKey; public bool IsDirty => _hasPendingValue; public event Action? OnValueChanged; public event Action? OnWriteIgnored; public event Action? OnSyncError; internal HostSyncVar(SteamNetworkClient client, string key, T defaultValue, NetworkSyncOptions? options = null, ISyncValidator? validator = null) { _client = client ?? throw new ArgumentNullException("client"); if (string.IsNullOrEmpty(key)) { throw new ArgumentException("Sync key cannot be null or empty", "key"); } _key = key; _options = options ?? new NetworkSyncOptions(); _fullKey = _options.GetFullKey(key); _defaultValue = defaultValue; _value = defaultValue; _serializer = _options.Serializer ?? new JsonSyncSerializer(); _validator = validator; _lastSyncTime = DateTime.MinValue; if (!_serializer.CanSerialize(typeof(T))) { throw new SyncSerializationException("Type '" + typeof(T).Name + "' cannot be serialized. Ensure it has a parameterless constructor and all public properties are of supported types (primitives, strings, List, Dictionary, or other serializable custom types).", typeof(T), _fullKey); } SubscribeToChanges(); TryLoadExistingValue(); } private void SetValue(T newValue) { if (!_client.IsHost) { this.OnWriteIgnored?.Invoke(newValue); if (_options.WarnOnIgnoredWrites) { Console.WriteLine("[SteamNetworkLib] HostSyncVar '" + _key + "': Write ignored - only host can modify this value."); } } else { if (object.Equals(_value, newValue)) { return; } if (_validator != null && !_validator.IsValid(newValue)) { string text = _validator.GetErrorMessage(newValue) ?? "Validation failed"; string text2 = "[SteamNetworkLib] HostSyncVar '" + _key + "': " + text; if (_options.ThrowOnValidationError) { throw new SyncValidationException(text2, _fullKey, newValue); } this.OnSyncError?.Invoke(new SyncValidationException(text2, _fullKey, newValue)); Console.WriteLine(text2); return; } if (_options.MaxSyncsPerSecond > 0) { TimeSpan timeSpan = DateTime.UtcNow - _lastSyncTime; TimeSpan timeSpan2 = TimeSpan.FromSeconds(1.0 / (double)_options.MaxSyncsPerSecond); if (timeSpan < timeSpan2) { _pendingValue = newValue; _hasPendingValue = true; return; } } if (!_options.AutoSync) { _pendingValue = newValue; _hasPendingValue = true; } else { PerformSync(newValue); } } } private void PerformSync(T newValue) { T value = _value; try { string value2 = _serializer.Serialize(newValue); _client.SetLobbyData(_fullKey, value2); _value = newValue; _lastSyncTime = DateTime.UtcNow; _hasPendingValue = false; this.OnValueChanged?.Invoke(value, newValue); } catch (Exception ex) { this.OnSyncError?.Invoke(ex); if (_options.WarnOnIgnoredWrites) { Console.WriteLine("[SteamNetworkLib] HostSyncVar '" + _key + "': Sync error - " + ex.Message); } } } public void FlushPending() { if (_hasPendingValue && _pendingValue != null) { PerformSync(_pendingValue); } } public void Refresh() { TryLoadExistingValue(); } private void TryLoadExistingValue() { if (!_client.IsInLobby) { return; } try { string lobbyData = _client.GetLobbyData(_fullKey); if (!string.IsNullOrEmpty(lobbyData)) { T value = _value; T val = _serializer.Deserialize(lobbyData); if (!object.Equals(value, val)) { _value = val; this.OnValueChanged?.Invoke(value, val); } } } catch (Exception obj) { this.OnSyncError?.Invoke(obj); } } private void SubscribeToChanges() { if (!_isSubscribed) { _client.OnLobbyDataChanged += HandleLobbyDataChanged; _client.OnLobbyJoined += HandleLobbyJoined; _isSubscribed = true; } } private void UnsubscribeFromChanges() { if (_isSubscribed) { _client.OnLobbyDataChanged -= HandleLobbyDataChanged; _client.OnLobbyJoined -= HandleLobbyJoined; _isSubscribed = false; } } private void HandleLobbyDataChanged(object? sender, LobbyDataChangedEventArgs e) { if (e.Key != _fullKey) { return; } try { if (string.IsNullOrEmpty(e.NewValue)) { T value = _value; _value = _defaultValue; if (!object.Equals(value, _value)) { this.OnValueChanged?.Invoke(value, _value); } } else { T value2 = _value; _value = _serializer.Deserialize(e.NewValue); if (!object.Equals(value2, _value)) { this.OnValueChanged?.Invoke(value2, _value); } } } catch (Exception obj) { this.OnSyncError?.Invoke(obj); } } private void HandleLobbyJoined(object? sender, LobbyJoinedEventArgs e) { TryLoadExistingValue(); } public void Dispose() { if (!_disposed) { UnsubscribeFromChanges(); this.OnValueChanged = null; this.OnWriteIgnored = null; this.OnSyncError = null; _disposed = true; } } } public interface ISyncSerializer { string Serialize(T value); T Deserialize(string data); bool CanSerialize(Type type); } public interface ISyncValidator { bool IsValid(T value); string? GetErrorMessage(T value); } public class PredicateValidator : ISyncValidator { private readonly Func _predicate; private readonly string _errorMessage; public PredicateValidator(Func predicate, string errorMessage) { _predicate = predicate ?? throw new ArgumentNullException("predicate"); _errorMessage = errorMessage ?? "Validation failed"; } public bool IsValid(T value) { return _predicate(value); } public string? GetErrorMessage(T value) { return _errorMessage; } } public class RangeValidator : ISyncValidator where T : IComparable { private readonly T _min; private readonly T _max; private readonly bool _inclusive; public RangeValidator(T min, T max, bool inclusive = true) { if (min.CompareTo(max) > 0) { throw new ArgumentException("Minimum value must be less than or equal to maximum value", "min"); } _min = min; _max = max; _inclusive = inclusive; } public bool IsValid(T value) { if (_inclusive) { return value.CompareTo(_min) >= 0 && value.CompareTo(_max) <= 0; } return value.CompareTo(_min) > 0 && value.CompareTo(_max) < 0; } public string? GetErrorMessage(T value) { string text = (_inclusive ? "[]" : "()"); return $"Value {value} must be in range {text[0]}{_min}, {_max}{text[1]}"; } } public class CompositeValidator : ISyncValidator { private readonly ISyncValidator[] _validators; public CompositeValidator(params ISyncValidator[] validators) { if (validators == null) { throw new ArgumentNullException("validators"); } _validators = validators; } public bool IsValid(T value) { ISyncValidator[] validators = _validators; foreach (ISyncValidator syncValidator in validators) { if (!syncValidator.IsValid(value)) { return false; } } return true; } public string? GetErrorMessage(T value) { ISyncValidator[] validators = _validators; foreach (ISyncValidator syncValidator in validators) { if (!syncValidator.IsValid(value)) { return syncValidator.GetErrorMessage(value); } } return null; } } public class JsonSyncSerializer : ISyncSerializer { public string Serialize(T value) { try { return SerializeValue(value, typeof(T)); } catch (SyncSerializationException) { throw; } catch (Exception ex2) { throw new SyncSerializationException("Failed to serialize type '" + typeof(T).Name + "': " + ex2.Message, typeof(T), string.Empty, ex2); } } public T Deserialize(string data) { try { object obj = DeserializeValue(data, typeof(T)); return (T)obj; } catch (SyncSerializationException) { throw; } catch (Exception ex2) { throw new SyncSerializationException("Failed to deserialize to type '" + typeof(T).Name + "': " + ex2.Message, typeof(T), string.Empty, ex2); } } public bool CanSerialize(Type type) { return CanSerializeType(type, new HashSet()); } private bool CanSerializeType(Type type, HashSet visited) { if (visited.Contains(type)) { return true; } visited.Add(type); Type underlyingType = Nullable.GetUnderlyingType(type); if (underlyingType != null) { return CanSerializeType(underlyingType, visited); } if (type == typeof(string) || type == typeof(int) || type == typeof(long) || type == typeof(float) || type == typeof(double) || type == typeof(bool) || type == typeof(byte) || type == typeof(short) || type == typeof(uint) || type == typeof(ulong) || type == typeof(decimal) || type == typeof(DateTime) || type == typeof(Guid)) { return true; } if (type.IsEnum) { return true; } if (type.IsArray) { Type elementType = type.GetElementType(); return elementType != null && CanSerializeType(elementType, visited); } if (type.IsGenericType) { Type genericTypeDefinition = type.GetGenericTypeDefinition(); Type[] genericArguments = type.GetGenericArguments(); if (genericTypeDefinition == typeof(List<>)) { return CanSerializeType(genericArguments[0], visited); } if (genericTypeDefinition == typeof(Dictionary<, >)) { return genericArguments[0] == typeof(string) && CanSerializeType(genericArguments[1], visited); } } if (type.IsClass || type.IsValueType) { if (type.IsClass) { ConstructorInfo constructor = type.GetConstructor(Type.EmptyTypes); if (constructor == null) { return false; } } PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); PropertyInfo[] array = properties; foreach (PropertyInfo propertyInfo in array) { if (propertyInfo.CanRead && propertyInfo.CanWrite && !CanSerializeType(propertyInfo.PropertyType, visited)) { return false; } } return true; } return false; } private string SerializeValue(object? value, Type type) { if (value == null) { return "null"; } Type underlyingType = Nullable.GetUnderlyingType(type); if (underlyingType != null) { type = underlyingType; } if (type == typeof(string)) { return EscapeString((string)value); } if (type == typeof(bool)) { return ((bool)value) ? "true" : "false"; } if (type == typeof(int) || type == typeof(long) || type == typeof(byte) || type == typeof(short) || type == typeof(uint) || type == typeof(ulong)) { return value.ToString(); } if (type == typeof(float)) { return ((float)value).ToString(CultureInfo.InvariantCulture); } if (type == typeof(double)) { return ((double)value).ToString(CultureInfo.InvariantCulture); } if (type == typeof(decimal)) { return ((decimal)value).ToString(CultureInfo.InvariantCulture); } if (type == typeof(DateTime)) { return EscapeString(((DateTime)value).ToString("O", CultureInfo.InvariantCulture)); } if (type == typeof(Guid)) { return EscapeString(((Guid)value).ToString("D")); } if (type.IsEnum) { return Convert.ToInt64(value).ToString(); } if (type.IsArray) { Array array = (Array)value; Type elementType = type.GetElementType(); StringBuilder stringBuilder = new StringBuilder("["); for (int i = 0; i < array.Length; i++) { if (i > 0) { stringBuilder.Append(","); } stringBuilder.Append(SerializeValue(array.GetValue(i), elementType)); } stringBuilder.Append("]"); return stringBuilder.ToString(); } if (type.IsGenericType) { Type genericTypeDefinition = type.GetGenericTypeDefinition(); Type[] genericArguments = type.GetGenericArguments(); if (genericTypeDefinition == typeof(List<>)) { IList list = (IList)value; Type type2 = genericArguments[0]; StringBuilder stringBuilder2 = new StringBuilder("["); for (int j = 0; j < list.Count; j++) { if (j > 0) { stringBuilder2.Append(","); } stringBuilder2.Append(SerializeValue(list[j], type2)); } stringBuilder2.Append("]"); return stringBuilder2.ToString(); } if (genericTypeDefinition == typeof(Dictionary<, >) && genericArguments[0] == typeof(string)) { IDictionary dictionary = (IDictionary)value; Type type3 = genericArguments[1]; StringBuilder stringBuilder3 = new StringBuilder("{"); bool flag = true; foreach (DictionaryEntry item in dictionary) { if (!flag) { stringBuilder3.Append(","); } flag = false; stringBuilder3.Append(EscapeString((string)item.Key)); stringBuilder3.Append(":"); stringBuilder3.Append(SerializeValue(item.Value, type3)); } stringBuilder3.Append("}"); return stringBuilder3.ToString(); } } if (type.IsClass || type.IsValueType) { StringBuilder stringBuilder4 = new StringBuilder("{"); PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); bool flag2 = true; PropertyInfo[] array2 = properties; foreach (PropertyInfo propertyInfo in array2) { if (propertyInfo.CanRead && propertyInfo.CanWrite) { if (!flag2) { stringBuilder4.Append(","); } flag2 = false; stringBuilder4.Append(EscapeString(propertyInfo.Name)); stringBuilder4.Append(":"); stringBuilder4.Append(SerializeValue(propertyInfo.GetValue(value), propertyInfo.PropertyType)); } } stringBuilder4.Append("}"); return stringBuilder4.ToString(); } throw new SyncSerializationException("Cannot serialize type: " + type.Name, type); } private object? DeserializeValue(string json, Type type) { json = json.Trim(); if (json == "null") { return null; } Type underlyingType = Nullable.GetUnderlyingType(type); if (underlyingType != null) { type = underlyingType; } if (type == typeof(string)) { return UnescapeString(json); } if (type == typeof(bool)) { return json == "true"; } if (type == typeof(int)) { return int.Parse(json, CultureInfo.InvariantCulture); } if (type == typeof(long)) { return long.Parse(json, CultureInfo.InvariantCulture); } if (type == typeof(float)) { return float.Parse(json, CultureInfo.InvariantCulture); } if (type == typeof(double)) { return double.Parse(json, CultureInfo.InvariantCulture); } if (type == typeof(byte)) { return byte.Parse(json, CultureInfo.InvariantCulture); } if (type == typeof(short)) { return short.Parse(json, CultureInfo.InvariantCulture); } if (type == typeof(uint)) { return uint.Parse(json, CultureInfo.InvariantCulture); } if (type == typeof(ulong)) { return ulong.Parse(json, CultureInfo.InvariantCulture); } if (type == typeof(decimal)) { return decimal.Parse(json, CultureInfo.InvariantCulture); } if (type == typeof(DateTime)) { return DateTime.Parse(UnescapeString(json), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); } if (type == typeof(Guid)) { return Guid.Parse(UnescapeString(json)); } if (type.IsEnum) { long value = long.Parse(json, CultureInfo.InvariantCulture); return Enum.ToObject(type, value); } if (type.IsArray) { Type elementType = type.GetElementType(); List list = ParseJsonArray(json); Array array = Array.CreateInstance(elementType, list.Count); for (int i = 0; i < list.Count; i++) { array.SetValue(DeserializeValue(list[i], elementType), i); } return array; } if (type.IsGenericType) { Type genericTypeDefinition = type.GetGenericTypeDefinition(); Type[] genericArguments = type.GetGenericArguments(); if (genericTypeDefinition == typeof(List<>)) { Type type2 = genericArguments[0]; IList list2 = (IList)Activator.CreateInstance(type); List list3 = ParseJsonArray(json); foreach (string item in list3) { list2.Add(DeserializeValue(item, type2)); } return list2; } if (genericTypeDefinition == typeof(Dictionary<, >) && genericArguments[0] == typeof(string)) { Type type3 = genericArguments[1]; IDictionary dictionary = (IDictionary)Activator.CreateInstance(type); Dictionary dictionary2 = ParseJsonObject(json); foreach (KeyValuePair item2 in dictionary2) { dictionary[item2.Key] = DeserializeValue(item2.Value, type3); } return dictionary; } } if (type.IsClass || type.IsValueType) { object obj = Activator.CreateInstance(type); Dictionary dictionary3 = ParseJsonObject(json); PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public); PropertyInfo[] array2 = properties; foreach (PropertyInfo propertyInfo in array2) { if (propertyInfo.CanRead && propertyInfo.CanWrite && dictionary3.ContainsKey(propertyInfo.Name)) { object value2 = DeserializeValue(dictionary3[propertyInfo.Name], propertyInfo.PropertyType); propertyInfo.SetValue(obj, value2); } } return obj; } throw new SyncSerializationException("Cannot deserialize type: " + type.Name, type); } private string EscapeString(string value) { StringBuilder stringBuilder = new StringBuilder("\""); foreach (char c in value) { switch (c) { case '"': stringBuilder.Append("\\\""); break; case '\\': stringBuilder.Append("\\\\"); break; case '\n': stringBuilder.Append("\\n"); break; case '\r': stringBuilder.Append("\\r"); break; case '\t': stringBuilder.Append("\\t"); break; default: stringBuilder.Append(c); break; } } stringBuilder.Append("\""); return stringBuilder.ToString(); } private string UnescapeString(string json) { if (json.Length < 2 || json[0] != '"' || json[json.Length - 1] != '"') { throw new SyncSerializationException("Invalid JSON string format"); } StringBuilder stringBuilder = new StringBuilder(); bool flag = false; for (int i = 1; i < json.Length - 1; i++) { char c = json[i]; if (flag) { switch (c) { case '"': stringBuilder.Append('"'); break; case '\\': stringBuilder.Append('\\'); break; case 'n': stringBuilder.Append('\n'); break; case 'r': stringBuilder.Append('\r'); break; case 't': stringBuilder.Append('\t'); break; default: stringBuilder.Append(c); break; } flag = false; } else if (c == '\\') { flag = true; } else { stringBuilder.Append(c); } } return stringBuilder.ToString(); } private List ParseJsonArray(string json) { List list = new List(); if (json.Length < 2 || json[0] != '[' || json[json.Length - 1] != ']') { throw new SyncSerializationException("Invalid JSON array format"); } int num = 0; bool flag = false; bool flag2 = false; StringBuilder stringBuilder = new StringBuilder(); for (int i = 1; i < json.Length - 1; i++) { char c = json[i]; if (flag2) { stringBuilder.Append(c); flag2 = false; continue; } switch (c) { case '\\': stringBuilder.Append(c); flag2 = true; continue; case '"': flag = !flag; stringBuilder.Append(c); continue; } if (!flag) { if (c == '[' || c == '{') { num++; } else if (c == ']' || c == '}') { num--; } else if (c == ',' && num == 0) { string text = stringBuilder.ToString().Trim(); if (text.Length > 0) { list.Add(text); } stringBuilder.Clear(); continue; } } stringBuilder.Append(c); } string text2 = stringBuilder.ToString().Trim(); if (text2.Length > 0) { list.Add(text2); } return list; } private Dictionary ParseJsonObject(string json) { Dictionary dictionary = new Dictionary(); if (json.Length < 2 || json[0] != '{' || json[json.Length - 1] != '}') { throw new SyncSerializationException("Invalid JSON object format"); } int num = 0; bool flag = false; bool flag2 = false; bool flag3 = true; StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder2 = new StringBuilder(); for (int i = 1; i < json.Length - 1; i++) { char c = json[i]; if (flag2) { if (flag3) { stringBuilder.Append(c); } else { stringBuilder2.Append(c); } flag2 = false; continue; } switch (c) { case '\\': if (flag3) { stringBuilder.Append(c); } else { stringBuilder2.Append(c); } flag2 = true; continue; case '"': flag = !flag; if (flag3) { stringBuilder.Append(c); } else { stringBuilder2.Append(c); } continue; } if (!flag) { if (c == '[' || c == '{') { num++; } else if (c == ']' || c == '}') { num--; } else { if (c == ':' && num == 0 && flag3) { flag3 = false; continue; } if (c == ',' && num == 0) { string text = UnescapeString(stringBuilder.ToString().Trim()); string value = stringBuilder2.ToString().Trim(); if (text.Length > 0) { dictionary[text] = value; } stringBuilder.Clear(); stringBuilder2.Clear(); flag3 = true; continue; } } } if (flag3) { stringBuilder.Append(c); } else { stringBuilder2.Append(c); } } string text2 = stringBuilder.ToString().Trim(); string value2 = stringBuilder2.ToString().Trim(); if (text2.Length > 0) { dictionary[UnescapeString(text2)] = value2; } return dictionary; } } public class NetworkSyncOptions { internal const string ReservedPrefix = "__snl_sync_"; public bool AutoSync { get; set; } = true; public bool SyncOnPlayerJoin { get; set; } = true; public int MaxSyncsPerSecond { get; set; } = 0; public bool ThrowOnValidationError { get; set; } = false; public bool WarnOnIgnoredWrites { get; set; } = false; public ISyncSerializer? Serializer { get; set; } public string? KeyPrefix { get; set; } internal string GetFullKey(string key) { if (string.IsNullOrEmpty(KeyPrefix)) { return key; } return KeyPrefix + key; } } [Obsolete("Use SteamNetworkLib.Exceptions.SyncException instead.")] public class SyncException : SteamNetworkLib.Exceptions.SyncException { public SyncException(string message) : base(message) { } public SyncException(string message, Exception innerException) : base(message, innerException) { } public SyncException(string message, string syncKey) : base(message, syncKey) { } public SyncException(string message, string syncKey, Exception innerException) : base(message, syncKey, innerException) { } } public class SyncSerializationException : SteamNetworkLib.Exceptions.SyncException { public Type? TargetType { get; } public SyncSerializationException(string message) : base(message, null, SteamNetworkErrorKind.SerializationFailed) { } public SyncSerializationException(string message, Exception innerException) : base(message, null, innerException, SteamNetworkErrorKind.SerializationFailed) { } public SyncSerializationException(string message, Type targetType) : base(message, null, SteamNetworkErrorKind.SerializationFailed) { TargetType = targetType; } public SyncSerializationException(string message, Type targetType, string syncKey) : base(message, syncKey, SteamNetworkErrorKind.SerializationFailed) { TargetType = targetType; } public SyncSerializationException(string message, Type targetType, string syncKey, Exception innerException) : base(message, syncKey, innerException, SteamNetworkErrorKind.SerializationFailed) { TargetType = targetType; } } public class SyncValidationException : SteamNetworkLib.Exceptions.SyncException { public object? InvalidValue { get; } public SyncValidationException(string message) : base(message, null, SteamNetworkErrorKind.ValidationFailed) { } public SyncValidationException(string message, string syncKey, object? invalidValue) : base(message, syncKey, SteamNetworkErrorKind.ValidationFailed) { InvalidValue = invalidValue; } } } namespace SteamNetworkLib.Streaming { public abstract class StreamChannel : IDisposable where T : class { protected readonly SortedDictionary> _jitterBuffer = new SortedDictionary>(); protected uint _expectedSequence = 0u; protected uint _highestSequenceReceived = 0u; protected bool _isStreaming = false; protected bool _isDisposed = false; protected DateTime _lastFrameTime = DateTime.UtcNow; protected readonly object _bufferLock = new object(); public string StreamId { get; } public int BufferMs { get; set; } = 200; public int MaxBufferMs { get; set; } = 500; public bool EnablePacketLossDetection { get; set; } = true; public bool EnableJitterBuffer { get; set; } = true; public int BufferedFrameCount => _jitterBuffer.Count; public uint TotalFramesReceived { get; private set; } public uint TotalFramesDropped { get; private set; } public uint TotalFramesLate { get; private set; } public double AverageJitter { get; private set; } public event Action? OnFrameReady; public event Action? OnFrameDropped; public event Action>? OnStreamStarted; public event Action>? OnStreamEnded; public event Action? OnFrameLate; protected StreamChannel(string streamId) { StreamId = streamId ?? throw new ArgumentNullException("streamId"); } public virtual void ProcessStreamMessage(StreamMessage message, CSteamID senderId) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) if (_isDisposed || message.StreamId != StreamId) { return; } DateTime utcNow = DateTime.UtcNow; try { if (message.IsStreamStart && !_isStreaming) { Console.WriteLine($"[StreamChannel] Stream {StreamId} started from {senderId}"); StartStream(); } else if (message.IsStreamEnd && _isStreaming) { Console.WriteLine($"[StreamChannel] Stream {StreamId} ended from {senderId}"); EndStream(); return; } TotalFramesReceived++; _highestSequenceReceived = Math.Max(_highestSequenceReceived, message.SequenceNumber); if (_expectedSequence == 0 && message.SequenceNumber != 0) { _expectedSequence = message.SequenceNumber; Console.WriteLine($"[StreamChannel] Setting initial expected sequence to {_expectedSequence}"); } if (message.SequenceNumber < _expectedSequence) { Console.WriteLine($"[StreamChannel] Ignoring old/duplicate frame {message.SequenceNumber} (expected: {_expectedSequence})"); return; } if (message.SequenceNumber > _expectedSequence && TotalFramesReceived > 10) { uint num = message.SequenceNumber - _expectedSequence; Console.WriteLine($"[StreamChannel] Sequence gap detected: got {message.SequenceNumber}, expected {_expectedSequence} (gap: {num})"); } long num2 = (utcNow.Ticks - message.CaptureTimestamp) / 10000; DateTime utcNow2 = DateTime.UtcNow; T val = DeserializeFrame(message.StreamData, message); double totalMilliseconds = (DateTime.UtcNow - utcNow2).TotalMilliseconds; if (val == null) { Console.WriteLine($"[StreamChannel] Failed to deserialize frame {message.SequenceNumber}"); return; } StreamFrame streamFrame = new StreamFrame { SequenceNumber = message.SequenceNumber, Data = val, CaptureTimestamp = message.CaptureTimestamp, ReceivedTimestamp = utcNow.Ticks, FrameDurationMs = message.FrameDurationMs, IsRetransmit = message.IsRetransmit }; if (TotalFramesReceived % 50 == 0) { Console.WriteLine($"[StreamChannel] Frame {message.SequenceNumber}: End-to-end latency: {num2}ms, Deserialize time: {totalMilliseconds:F2}ms, Buffer level: {_jitterBuffer.Count}"); } if (EnableJitterBuffer) { AddToJitterBuffer(streamFrame); return; } this.OnFrameReady?.Invoke(streamFrame.Data); _expectedSequence = message.SequenceNumber + 1; } catch (Exception ex) { Console.WriteLine("[StreamChannel] Error processing stream message: " + ex.Message); } } public virtual void Update() { if (_isDisposed || !EnableJitterBuffer) { return; } lock (_bufferLock) { ProcessJitterBuffer(); CleanupOldFrames(); DetectLostFrames(); } } protected abstract T? DeserializeFrame(byte[] data, StreamMessage message); protected virtual T? HandleMissingFrame(uint sequenceNumber) { return null; } private void AddToJitterBuffer(StreamFrame frame) { lock (_bufferLock) { if (frame.SequenceNumber >= _expectedSequence) { _jitterBuffer[frame.SequenceNumber] = frame; int num = Math.Max(5, MaxBufferMs / frame.FrameDurationMs); while (_jitterBuffer.Count > num) { KeyValuePair> keyValuePair = _jitterBuffer.First(); _jitterBuffer.Remove(keyValuePair.Key); TotalFramesDropped++; this.OnFrameDropped?.Invoke(keyValuePair.Key); Console.WriteLine($"[StreamBuffer] Dropped old frame {keyValuePair.Key} - buffer too large ({_jitterBuffer.Count}/{num})"); } } } } private void ProcessJitterBuffer() { DateTime utcNow = DateTime.UtcNow; int num = ((_jitterBuffer.Count > 0) ? _jitterBuffer.Values.First().FrameDurationMs : 40); int num2 = Math.Max(2, BufferMs / num); if (_jitterBuffer.Count < num2 && _expectedSequence < 10) { if (_expectedSequence == 0 && _jitterBuffer.Count > 0) { Console.WriteLine($"[StreamBuffer] Building initial buffer: {_jitterBuffer.Count}/{num2} frames ({num}ms frames)"); } return; } if (_jitterBuffer.Count == 0 && _isStreaming) { Console.WriteLine("[StreamBuffer] Buffer underrun! Waiting for frames to rebuild buffer..."); return; } int num3 = 0; while (_jitterBuffer.ContainsKey(_expectedSequence) && num3 < 3) { StreamFrame streamFrame = _jitterBuffer[_expectedSequence]; _jitterBuffer.Remove(_expectedSequence); DateTime dateTime = _lastFrameTime.AddMilliseconds(streamFrame.FrameDurationMs); DateTime utcNow2 = DateTime.UtcNow; if (utcNow2 > dateTime.AddMilliseconds(BufferMs)) { TotalFramesLate++; TimeSpan arg = utcNow2 - dateTime; this.OnFrameLate?.Invoke(_expectedSequence, arg); if (arg.TotalMilliseconds > 60.0) { Console.WriteLine($"[StreamBuffer] Frame {_expectedSequence} is {arg.TotalMilliseconds:F1}ms late!"); } } if (_expectedSequence % 100 == 0) { int count = _jitterBuffer.Count; double totalMilliseconds = (DateTime.UtcNow - utcNow).TotalMilliseconds; Console.WriteLine($"[StreamBuffer] Processed frame {_expectedSequence}, buffer level: {count}, processing time: {totalMilliseconds:F2}ms"); } this.OnFrameReady?.Invoke(streamFrame.Data); _lastFrameTime = utcNow2; _expectedSequence++; num3++; } if (!EnablePacketLossDetection || _jitterBuffer.ContainsKey(_expectedSequence)) { return; } uint num4 = _jitterBuffer.Keys.FirstOrDefault((uint seq) => seq > _expectedSequence); if (num4 == 0) { return; } uint num5 = num4 - _expectedSequence; if (num5 <= 3) { T val = HandleMissingFrame(_expectedSequence); if (val != null) { this.OnFrameReady?.Invoke(val); Console.WriteLine($"[StreamBuffer] Used PLC for missing frame {_expectedSequence}"); } else { TotalFramesDropped++; this.OnFrameDropped?.Invoke(_expectedSequence); } _expectedSequence++; } else { Console.WriteLine($"[StreamBuffer] Large gap detected ({num5} frames), skipping to frame {num4}"); for (uint num6 = _expectedSequence; num6 < num4; num6++) { TotalFramesDropped++; this.OnFrameDropped?.Invoke(num6); } _expectedSequence = num4; } } private void CleanupOldFrames() { DateTime cutoffTime = DateTime.UtcNow.AddMilliseconds(-MaxBufferMs); long ticks = cutoffTime.Ticks; List list = (from kvp in _jitterBuffer where new DateTime(kvp.Value.ReceivedTimestamp) < cutoffTime select kvp.Key).ToList(); foreach (uint item in list) { _jitterBuffer.Remove(item); TotalFramesDropped++; this.OnFrameDropped?.Invoke(item); } } private void DetectLostFrames() { if (_jitterBuffer.Count <= 0) { return; } uint num = _jitterBuffer.Keys.Min(); uint num2 = num - _expectedSequence; int num3 = Math.Max(5, BufferMs / 40); if (num2 > num3) { Console.WriteLine($"[StreamBuffer] Large gap detected: {num2} frames (max: {num3}), jumping to frame {num}"); for (uint num4 = _expectedSequence; num4 < num; num4++) { TotalFramesDropped++; this.OnFrameDropped?.Invoke(num4); } _expectedSequence = num; } } private void StartStream() { _isStreaming = true; _expectedSequence = 0u; _highestSequenceReceived = 0u; _lastFrameTime = DateTime.UtcNow; this.OnStreamStarted?.Invoke(this); } private void EndStream() { _isStreaming = false; this.OnStreamEnded?.Invoke(this); } public virtual void Reset() { lock (_bufferLock) { _jitterBuffer.Clear(); _expectedSequence = 0u; _highestSequenceReceived = 0u; TotalFramesReceived = 0u; TotalFramesDropped = 0u; TotalFramesLate = 0u; _isStreaming = false; } } public virtual void Dispose() { if (!_isDisposed) { lock (_bufferLock) { _jitterBuffer.Clear(); } _isDisposed = true; } } } public class StreamFrame where T : class { public uint SequenceNumber { get; set; } public T Data { get; set; } = null; public long CaptureTimestamp { get; set; } public long ReceivedTimestamp { get; set; } public int FrameDurationMs { get; set; } = 20; public bool IsRetransmit { get; set; } } public abstract class StreamSender : IDisposable where T : class { protected uint _currentSequence = 0u; protected bool _isDisposed = false; protected SteamNetworkClient? _networkClient; public string StreamId { get; } public string StreamType { get; protected set; } = "data"; public string PayloadType { get; protected set; } = "data"; public int FrameDurationMs { get; protected set; } = 20; public bool IsStreaming { get; private set; } public uint TotalFramesSent { get; private set; } public uint TotalBytesSent { get; private set; } public event Action>? OnStreamStarted; public event Action>? OnStreamEnded; public event Action? OnFrameSent; protected StreamSender(string streamId, SteamNetworkClient? networkClient) { StreamId = streamId ?? throw new ArgumentNullException("streamId"); _networkClient = networkClient; } public virtual void StartStream() { if (!IsStreaming && !_isDisposed) { IsStreaming = true; _currentSequence = 0u; StreamMessage message = CreateStreamMessage(new byte[0], isStart: true, isEnd: false); SendStreamMessage(message); this.OnStreamStarted?.Invoke(this); } } public virtual void StopStream() { if (IsStreaming && !_isDisposed) { StreamMessage message = CreateStreamMessage(new byte[0], isStart: false, isEnd: true); SendStreamMessage(message); IsStreaming = false; this.OnStreamEnded?.Invoke(this); } } protected virtual void SendFrame(T frameData) { if (!IsStreaming || _isDisposed || frameData == null) { return; } try { byte[] array = SerializeFrame(frameData); if (array != null && array.Length != 0) { StreamMessage message = CreateStreamMessage(array, isStart: false, isEnd: false); SendStreamMessage(message); TotalFramesSent++; TotalBytesSent += (uint)array.Length; this.OnFrameSent?.Invoke(_currentSequence - 1, array.Length); } } catch (Exception ex) { Console.WriteLine("Error sending frame: " + ex.Message); } } protected abstract byte[]? SerializeFrame(T frameData); protected virtual StreamMessage CreateStreamMessage(byte[] data, bool isStart, bool isEnd) { //IL_008c: Unknown result type (might be due to invalid IL or missing references) return new StreamMessage { StreamType = StreamType, StreamId = StreamId, SequenceNumber = _currentSequence++, CaptureTimestamp = DateTime.UtcNow.Ticks, IsStreamStart = isStart, IsStreamEnd = isEnd, StreamData = data, PayloadType = PayloadType, FrameDurationMs = FrameDurationMs, Priority = 128, RecommendedSendType = GetRecommendedSendType() }; } protected virtual EP2PSend GetRecommendedSendType() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) return (EP2PSend)1; } protected virtual void SendStreamMessage(StreamMessage message) { if (_networkClient == null) { Console.WriteLine("[StreamSender] Warning: No network client available for sending stream message"); return; } try { DateTime utcNow = DateTime.UtcNow; _networkClient.BroadcastMessage(message); double totalMilliseconds = (DateTime.UtcNow - utcNow).TotalMilliseconds; if (totalMilliseconds > 10.0) { Console.WriteLine($"[StreamSender] Network broadcast took {totalMilliseconds:F2}ms (slow!) for frame {message.SequenceNumber}"); } else if (message.SequenceNumber % 100 == 0) { Console.WriteLine($"[StreamSender] Frame {message.SequenceNumber}: Network send took {totalMilliseconds:F2}ms"); } } catch (Exception ex) { Console.WriteLine("[StreamSender] Error sending stream message: " + ex.Message); } } protected virtual async Task SendFrameToTarget(T frameData, CSteamID targetId) { //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) if (!IsStreaming || _isDisposed || frameData == null || _networkClient == null) { return false; } try { byte[] encodedData = SerializeFrame(frameData); if (encodedData == null || encodedData.Length == 0) { return false; } StreamMessage message = CreateStreamMessage(encodedData, isStart: false, isEnd: false); return await _networkClient.SendMessageToPlayerAsync(targetId, message); } catch (Exception ex) { Console.WriteLine("Error sending frame to target: " + ex.Message); return false; } } public virtual void Reset() { _currentSequence = 0u; TotalFramesSent = 0u; TotalBytesSent = 0u; IsStreaming = false; } public virtual void Dispose() { if (!_isDisposed) { if (IsStreaming) { StopStream(); } _isDisposed = true; } } } } namespace SteamNetworkLib.Models { public class DataSyncMessage : P2PMessage { public override string MessageType => "DATA_SYNC"; public string Key { get; set; } = string.Empty; public string Value { get; set; } = string.Empty; public string DataType { get; set; } = "string"; public override byte[] Serialize() { string text = Key.Replace("\"", "\\\""); string text2 = Value.Replace("\"", "\\\""); string text3 = DataType.Replace("\"", "\\\""); string s = "{" + CreateJsonBase("\"Key\":\"" + text + "\",\"Value\":\"" + text2 + "\",\"DataType\":\"" + text3 + "\"") + "}"; return Encoding.UTF8.GetBytes(s); } public override void Deserialize(byte[] data) { try { string @string = Encoding.UTF8.GetString(data); ParseJsonBase(@string); Key = ExtractJsonValue(@string, "Key"); Value = ExtractJsonValue(@string, "Value"); DataType = ExtractJsonValue(@string, "DataType"); } catch (Exception ex) { Console.WriteLine("[SteamNetworkLib] DataSyncMessage.Deserialize ERROR: " + ex.Message); Console.WriteLine("[SteamNetworkLib] DataSyncMessage.Deserialize Stack Trace: " + ex.StackTrace); try { string string2 = Encoding.UTF8.GetString(data); Console.WriteLine("[SteamNetworkLib] Raw JSON: " + string2); } catch { } throw; } } } public class EventMessage : P2PMessage { public override string MessageType => "EVENT"; public string EventType { get; set; } = "user"; public string EventName { get; set; } = string.Empty; public string EventData { get; set; } = string.Empty; public int Priority { get; set; } = 1; public bool ShouldPersist { get; set; } public string TargetAudience { get; set; } = "all"; public string TargetPlayerIds { get; set; } = string.Empty; public bool RequiresAck { get; set; } public string EventId { get; set; } = Guid.NewGuid().ToString(); public DateTime? ExpiresAt { get; set; } public string Tags { get; set; } = string.Empty; public override byte[] Serialize() { string text = ExpiresAt?.ToString("O") ?? ""; string s = "{" + CreateJsonBase($"\"EventType\":\"{EventType}\",\"EventName\":\"{EventName}\",\"EventData\":\"{EventData}\",\"Priority\":{Priority},\"ShouldPersist\":{ShouldPersist.ToString().ToLower()},\"TargetAudience\":\"{TargetAudience}\",\"TargetPlayerIds\":\"{TargetPlayerIds}\",\"RequiresAck\":{RequiresAck.ToString().ToLower()},\"EventId\":\"{EventId}\",\"ExpiresAt\":\"{text}\",\"Tags\":\"{Tags}\"") + "}"; return Encoding.UTF8.GetBytes(s); } public override void Deserialize(byte[] data) { string @string = Encoding.UTF8.GetString(data); ParseJsonBase(@string); EventType = ExtractJsonValue(@string, "EventType"); EventName = ExtractJsonValue(@string, "EventName"); EventData = ExtractJsonValue(@string, "EventData"); if (int.TryParse(ExtractJsonValue(@string, "Priority"), out var result)) { Priority = result; } ShouldPersist = ExtractJsonValue(@string, "ShouldPersist").ToLower() == "true"; TargetAudience = ExtractJsonValue(@string, "TargetAudience"); TargetPlayerIds = ExtractJsonValue(@string, "TargetPlayerIds"); RequiresAck = ExtractJsonValue(@string, "RequiresAck").ToLower() == "true"; EventId = ExtractJsonValue(@string, "EventId"); string text = ExtractJsonValue(@string, "ExpiresAt"); if (!string.IsNullOrEmpty(text) && DateTime.TryParse(text, out var result2)) { ExpiresAt = result2; } Tags = ExtractJsonValue(@string, "Tags"); } } public class FileTransferMessage : P2PMessage { public override string MessageType => "FILE_TRANSFER"; public string TransferId { get; set; } = string.Empty; public string FileName { get; set; } = string.Empty; public int FileSize { get; set; } public int ChunkIndex { get; set; } public int TotalChunks { get; set; } public bool IsFileData { get; set; } public byte[] ChunkData { get; set; } = new byte[0]; public override byte[] Serialize() { string text = EscapeJsonValue(TransferId); string text2 = EscapeJsonValue(FileName); string s = "{" + CreateJsonBase($"\"TransferId\":\"{text}\",\"FileName\":\"{text2}\",\"FileSize\":{FileSize},\"ChunkIndex\":{ChunkIndex},\"TotalChunks\":{TotalChunks},\"IsFileData\":{IsFileData.ToString().ToLower()}") + "}"; byte[] bytes = Encoding.UTF8.GetBytes(s); byte[] array = new byte[4 + bytes.Length + ChunkData.Length]; int num = 0; BitConverter.GetBytes(bytes.Length).CopyTo(array, num); num += 4; bytes.CopyTo(array, num); num += bytes.Length; ChunkData.CopyTo(array, num); return array; } public override void Deserialize(byte[] data) { if (data.Length < 4) { return; } int num = BitConverter.ToInt32(data, 0); if (num > 0 && num <= data.Length - 4) { string @string = Encoding.UTF8.GetString(data, 4, num); ParseJsonBase(@string); TransferId = ExtractJsonValue(@string, "TransferId"); FileName = ExtractJsonValue(@string, "FileName"); if (int.TryParse(ExtractJsonValue(@string, "FileSize"), out var result)) { FileSize = result; } if (int.TryParse(ExtractJsonValue(@string, "ChunkIndex"), out var result2)) { ChunkIndex = result2; } if (int.TryParse(ExtractJsonValue(@string, "TotalChunks"), out var result3)) { TotalChunks = result3; } IsFileData = ExtractJsonValue(@string, "IsFileData").ToLower() == "true"; int num2 = data.Length - 4 - num; if (num2 > 0) { ChunkData = new byte[num2]; Array.Copy(data, 4 + num, ChunkData, 0, num2); } else { ChunkData = new byte[0]; } } } private static string EscapeJsonValue(string value) { return value.Replace("\\", "\\\\").Replace("\"", "\\\""); } } public class HeartbeatMessage : P2PMessage { public override string MessageType => "HEARTBEAT"; public string HeartbeatId { get; set; } = Guid.NewGuid().ToString(); public bool IsResponse { get; set; } public long HighPrecisionTimestamp { get; set; } public uint SequenceNumber { get; set; } public float PacketLossPercent { get; set; } public float AverageLatencyMs { get; set; } public int BandwidthUsage { get; set; } public string PlayerStatus { get; set; } = "online"; public string ConnectionInfo { get; set; } = string.Empty; public override byte[] Serialize() { if (HighPrecisionTimestamp == 0) { HighPrecisionTimestamp = DateTime.UtcNow.Ticks; } string s = "{" + CreateJsonBase($"\"HeartbeatId\":\"{HeartbeatId}\",\"IsResponse\":{IsResponse.ToString().ToLower()},\"HighPrecisionTimestamp\":{HighPrecisionTimestamp},\"SequenceNumber\":{SequenceNumber},\"PacketLossPercent\":{PacketLossPercent},\"AverageLatencyMs\":{AverageLatencyMs},\"BandwidthUsage\":{BandwidthUsage},\"PlayerStatus\":\"{PlayerStatus}\",\"ConnectionInfo\":\"{ConnectionInfo}\"") + "}"; return Encoding.UTF8.GetBytes(s); } public override void Deserialize(byte[] data) { try { string @string = Encoding.UTF8.GetString(data); ParseJsonBase(@string); HeartbeatId = ExtractJsonValue(@string, "HeartbeatId"); IsResponse = ExtractJsonValue(@string, "IsResponse").ToLower() == "true"; if (long.TryParse(ExtractJsonValue(@string, "HighPrecisionTimestamp"), out var result)) { HighPrecisionTimestamp = result; } if (uint.TryParse(ExtractJsonValue(@string, "SequenceNumber"), out var result2)) { SequenceNumber = result2; } if (float.TryParse(ExtractJsonValue(@string, "PacketLossPercent"), out var result3)) { PacketLossPercent = result3; } if (float.TryParse(ExtractJsonValue(@string, "AverageLatencyMs"), out var result4)) { AverageLatencyMs = result4; } if (int.TryParse(ExtractJsonValue(@string, "BandwidthUsage"), out var result5)) { BandwidthUsage = result5; } PlayerStatus = ExtractJsonValue(@string, "PlayerStatus"); ConnectionInfo = ExtractJsonValue(@string, "ConnectionInfo"); } catch (Exception ex) { Console.WriteLine("[SteamNetworkLib] HeartbeatMessage.Deserialize ERROR: " + ex.Message); Console.WriteLine("[SteamNetworkLib] HeartbeatMessage.Deserialize Stack Trace: " + ex.StackTrace); throw; } } } public class LobbyInfo { public CSteamID LobbyId { get; set; } public ulong LobbyId64 { get { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return LobbyId.m_SteamID; } set { //IL_0002: Unknown result type (might be due to invalid IL or missing references) LobbyId = new CSteamID(value); } } public CSteamID OwnerId { get; set; } public ulong OwnerId64 { get { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return OwnerId.m_SteamID; } set { //IL_0002: Unknown result type (might be due to invalid IL or missing references) OwnerId = new CSteamID(value); } } public int MemberCount { get; set; } public int MaxMembers { get; set; } public string? Name { get; set; } public DateTime CreatedAt { get; set; } public LobbyInfo() { CreatedAt = DateTime.Now; } } public class MemberInfo { public CSteamID SteamId { get; set; } public ulong SteamId64 { get { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return SteamId.m_SteamID; } set { //IL_0002: Unknown result type (might be due to invalid IL or missing references) SteamId = new CSteamID(value); } } public string SteamIdString => SteamId.m_SteamID.ToString(); public string DisplayName { get; set; } = string.Empty; public bool IsOwner { get; set; } public bool IsLocalPlayer { get; set; } public DateTime JoinedAt { get; set; } public MemberInfo() { JoinedAt = DateTime.Now; } } public abstract class P2PMessage { public abstract string MessageType { get; } public CSteamID SenderId { get; set; } public DateTime Timestamp { get; set; } public abstract byte[] Serialize(); public abstract void Deserialize(byte[] data); protected P2PMessage() { Timestamp = DateTime.UtcNow; } protected string CreateJsonBase(string additionalData = "") { //IL_0007: Unknown result type (might be due to invalid IL or missing references) string text = $"\"SenderId\":{SenderId.m_SteamID},\"Timestamp\":\"{Timestamp:O}\""; return string.IsNullOrEmpty(additionalData) ? text : (text + "," + additionalData); } protected void ParseJsonBase(string json) { //IL_009f: 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) try { string s = ExtractJsonValue(json, "SenderId"); if (ulong.TryParse(s, out var result)) { SenderId = new CSteamID(result); } string s2 = ExtractJsonValue(json, "Timestamp"); if (DateTime.TryParse(s2, out var result2)) { Timestamp = result2; } else { Timestamp = DateTime.UtcNow; } } catch (Exception ex) { Console.WriteLine("[SteamNetworkLib] P2PMessage.ParseJsonBase ERROR: " + ex.Message); Console.WriteLine("[SteamNetworkLib] P2PMessage.ParseJsonBase Stack Trace: " + ex.StackTrace); SenderId = new CSteamID(76561197960265728uL); Timestamp = DateTime.UtcNow; } } protected string ExtractJsonValue(string json, string key) { try { int num = FindJsonValueStart(json, key); if (num < 0) { return string.Empty; } if (json[num] == '"') { num++; int num2 = -1; bool flag = false; for (int i = num; i < json.Length; i++) { if (json[i] == '\\' && !flag) { flag = true; continue; } if (json[i] == '"' && !flag) { num2 = i; break; } flag = false; } if (num2 == -1) { return string.Empty; } string text = json.Substring(num, num2 - num); return text.Replace("\\\"", "\"").Replace("\\\\", "\\").Replace("\\/", "/") .Replace("\\b", "\b") .Replace("\\f", "\f") .Replace("\\n", "\n") .Replace("\\r", "\r") .Replace("\\t", "\t"); } int j; for (j = num; j < json.Length && json[j] != ',' && json[j] != '}' && json[j] != ']' && !char.IsWhiteSpace(json[j]); j++) { } return json.Substring(num, j - num); } catch (Exception ex) { Console.WriteLine("[SteamNetworkLib] P2PMessage.ExtractJsonValue ERROR: " + ex.Message); Console.WriteLine("[SteamNetworkLib] P2PMessage.ExtractJsonValue Stack Trace: " + ex.StackTrace); return string.Empty; } } protected string ExtractJsonRawValue(string json, string key) { try { int num = FindJsonValueStart(json, key); if (num < 0 || num >= json.Length) { return string.Empty; } int num2 = FindJsonValueEnd(json, num); if (num2 <= num) { return string.Empty; } return json.Substring(num, num2 - num).Trim(); } catch (Exception ex) { Console.WriteLine("[SteamNetworkLib] P2PMessage.ExtractJsonRawValue ERROR: " + ex.Message); Console.WriteLine("[SteamNetworkLib] P2PMessage.ExtractJsonRawValue Stack Trace: " + ex.StackTrace); return string.Empty; } } private static int FindJsonValueStart(string json, string key) { string text = "\"" + key + "\":"; int num = json.IndexOf(text); if (num == -1) { return -1; } for (num += text.Length; num < json.Length && char.IsWhiteSpace(json[num]); num++) { } return (num >= json.Length) ? (-1) : num; } private static int FindJsonValueEnd(string json, int startIndex) { bool flag = false; bool flag2 = false; int num = 0; for (int i = startIndex; i < json.Length; i++) { char c = json[i]; if (flag2) { flag2 = false; continue; } switch (c) { case '\\': flag2 = true; continue; case '"': flag = !flag; continue; } if (flag) { continue; } if (c == '{' || c == '[') { num++; } else if (c == '}' || c == ']') { if (num == 0) { return i; } num--; } else if (c == ',' && num == 0) { return i; } } return json.Length; } } public class StreamMessage : P2PMessage { public override string MessageType => "STREAM"; public string StreamType { get; set; } = "audio"; public string StreamId { get; set; } = string.Empty; public uint SequenceNumber { get; set; } public long CaptureTimestamp { get; set; } public int SampleRate { get; set; } = 44100; public int Channels { get; set; } = 1; public int BitsPerSample { get; set; } = 16; public int FrameSamples { get; set; } = 960; public string Codec { get; set; } = "none"; public int Quality { get; set; } = 75; public bool IsStreamStart { get; set; } public bool IsStreamEnd { get; set; } public EP2PSend RecommendedSendType { get; set; } = (EP2PSend)1; public byte[] StreamData { get; set; } = new byte[0]; public string Metadata { get; set; } = string.Empty; public uint? AckForSequence { get; set; } public bool IsRetransmit { get; set; } = false; public bool IsFecFrame { get; set; } = false; public string PayloadType { get; set; } = "audio"; public int FrameDurationMs { get; set; } = 20; public byte Priority { get; set; } = 128; public override byte[] Serialize() { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected I4, but got Unknown string s = "{" + CreateJsonBase(string.Format("\"StreamType\":\"{0}\",\"StreamId\":\"{1}\",\"SequenceNumber\":{2},\"CaptureTimestamp\":{3},\"SampleRate\":{4},\"Channels\":{5},\"BitsPerSample\":{6},\"FrameSamples\":{7},\"Codec\":\"{8}\",\"Quality\":{9},\"IsStreamStart\":{10},\"IsStreamEnd\":{11},\"RecommendedSendType\":{12},\"Metadata\":\"{13}\",\"AckForSequence\":{14},\"IsRetransmit\":{15},\"IsFecFrame\":{16},\"PayloadType\":\"{17}\",\"FrameDurationMs\":{18},\"Priority\":{19}", StreamType, StreamId, SequenceNumber, CaptureTimestamp, SampleRate, Channels, BitsPerSample, FrameSamples, Codec, Quality, IsStreamStart.ToString().ToLower(), IsStreamEnd.ToString().ToLower(), (int)RecommendedSendType, Metadata, AckForSequence?.ToString() ?? "null", IsRetransmit.ToString().ToLower(), IsFecFrame.ToString().ToLower(), PayloadType, FrameDurationMs, Priority)) + "}"; byte[] bytes = Encoding.UTF8.GetBytes(s); byte[] array = new byte[4 + bytes.Length + StreamData.Length]; int num = 0; BitConverter.GetBytes(bytes.Length).CopyTo(array, num); num += 4; bytes.CopyTo(array, num); num += bytes.Length; StreamData.CopyTo(array, num); return array; } public override void Deserialize(byte[] data) { if (data.Length < 4) { return; } int num = BitConverter.ToInt32(data, 0); if (num > 0 && num <= data.Length - 4) { string @string = Encoding.UTF8.GetString(data, 4, num); ParseJsonBase(@string); StreamType = ExtractJsonValue(@string, "StreamType"); StreamId = ExtractJsonValue(@string, "StreamId"); if (uint.TryParse(ExtractJsonValue(@string, "SequenceNumber"), out var result)) { SequenceNumber = result; } if (long.TryParse(ExtractJsonValue(@string, "CaptureTimestamp"), out var result2)) { CaptureTimestamp = result2; } if (int.TryParse(ExtractJsonValue(@string, "SampleRate"), out var result3)) { SampleRate = result3; } if (int.TryParse(ExtractJsonValue(@string, "Channels"), out var result4)) { Channels = result4; } if (int.TryParse(ExtractJsonValue(@string, "BitsPerSample"), out var result5)) { BitsPerSample = result5; } if (int.TryParse(ExtractJsonValue(@string, "FrameSamples"), out var result6)) { FrameSamples = result6; } Codec = ExtractJsonValue(@string, "Codec"); if (int.TryParse(ExtractJsonValue(@string, "Quality"), out var result7)) { Quality = result7; } IsStreamStart = ExtractJsonValue(@string, "IsStreamStart").ToLower() == "true"; IsStreamEnd = ExtractJsonValue(@string, "IsStreamEnd").ToLower() == "true"; if (int.TryParse(ExtractJsonValue(@string, "RecommendedSendType"), out var result8)) { RecommendedSendType = (EP2PSend)result8; } Metadata = ExtractJsonValue(@string, "Metadata"); string text = ExtractJsonValue(@string, "AckForSequence"); if (!string.IsNullOrEmpty(text) && text != "null" && uint.TryParse(text, out var result9)) { AckForSequence = result9; } IsRetransmit = ExtractJsonValue(@string, "IsRetransmit").ToLower() == "true"; IsFecFrame = ExtractJsonValue(@string, "IsFecFrame").ToLower() == "true"; PayloadType = ExtractJsonValue(@string, "PayloadType"); if (int.TryParse(ExtractJsonValue(@string, "FrameDurationMs"), out var result10)) { FrameDurationMs = result10; } if (byte.TryParse(ExtractJsonValue(@string, "Priority"), out var result11)) { Priority = result11; } int num2 = data.Length - 4 - num; if (num2 > 0) { StreamData = new byte[num2]; Array.Copy(data, 4 + num, StreamData, 0, num2); } else { StreamData = new byte[0]; } } } } public class TextMessage : P2PMessage { public override string MessageType => "TEXT"; public string Content { get; set; } = string.Empty; public override byte[] Serialize() { string text = Content.Replace("\"", "\\\""); string s = "{" + CreateJsonBase("\"Content\":\"" + text + "\"") + "}"; return Encoding.UTF8.GetBytes(s); } public override void Deserialize(byte[] data) { try { string @string = Encoding.UTF8.GetString(data); ParseJsonBase(@string); Content = ExtractJsonValue(@string, "Content"); } catch (Exception ex) { Console.WriteLine("[SteamNetworkLib] TextMessage.Deserialize ERROR: " + ex.Message); Console.WriteLine("[SteamNetworkLib] TextMessage.Deserialize Stack Trace: " + ex.StackTrace); throw; } } } public abstract class TypedP2PMessage : P2PMessage { private static readonly JsonSyncSerializer Serializer = new JsonSyncSerializer(); public TPayload Payload { get; set; } = default(TPayload); protected TypedP2PMessage() { } protected TypedP2PMessage(TPayload payload) { Payload = payload; } public override byte[] Serialize() { try { string text = Serializer.Serialize(Payload); string s = "{" + CreateJsonBase("\"Payload\":" + text) + "}"; return Encoding.UTF8.GetBytes(s); } catch (Exception ex) { throw new InvalidOperationException("Failed to serialize typed P2P payload '" + typeof(TPayload).Name + "' for message '" + MessageType + "': " + ex.Message, ex); } } public override void Deserialize(byte[] data) { try { string @string = Encoding.UTF8.GetString(data); ParseJsonBase(@string); string text = ExtractJsonRawValue(@string, "Payload"); if (string.IsNullOrWhiteSpace(text)) { Payload = default(TPayload); } else { Payload = Serializer.Deserialize(text); } } catch (Exception ex) { throw new InvalidOperationException("Failed to deserialize typed P2P payload '" + typeof(TPayload).Name + "' for message '" + MessageType + "': " + ex.Message, ex); } } } } namespace SteamNetworkLib.Exceptions { public class LobbyException : SteamNetworkException { public EResult? SteamResult { get; } public CSteamID? LobbyId { get; } public ulong LobbyId64 => LobbyId?.m_SteamID ?? 0; public CSteamID? MemberId { get; } public ulong MemberId64 => MemberId?.m_SteamID ?? 0; public string? DataKey { get; } public bool RequiresHost { get; } public LobbyException() { } public LobbyException(string message) : base(message) { } public LobbyException(string message, Exception inner) : base(message, inner) { } public LobbyException(string message, EResult steamResult) : base(message) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) SteamResult = steamResult; } public LobbyException(string message, CSteamID lobbyId) : base(message) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) LobbyId = lobbyId; } public LobbyException(string message, EResult steamResult, CSteamID lobbyId) : base(message) { //IL_000a: 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) SteamResult = steamResult; LobbyId = lobbyId; } public LobbyException(string message, SteamNetworkErrorKind errorKind, string? operation = null, CSteamID? lobbyId = null, CSteamID? memberId = null, string? dataKey = null, bool requiresHost = false, EResult? steamResult = null, bool isRetryable = false) : base(message, errorKind, operation, isRetryable) { LobbyId = lobbyId; MemberId = memberId; DataKey = dataKey; RequiresHost = requiresHost; SteamResult = steamResult; } public LobbyException(string message, Exception inner, SteamNetworkErrorKind errorKind, string? operation = null, CSteamID? lobbyId = null, CSteamID? memberId = null, string? dataKey = null, bool requiresHost = false, EResult? steamResult = null, bool isRetryable = false) : base(message, inner, errorKind, operation, isRetryable) { LobbyId = lobbyId; MemberId = memberId; DataKey = dataKey; RequiresHost = requiresHost; SteamResult = steamResult; } } public class P2PException : SteamNetworkException { public CSteamID? TargetId { get; } public ulong TargetId64 => TargetId?.m_SteamID ?? 0; public EP2PSessionError? SessionError { get; } public int Channel { get; } public bool HasChannel { get; } public string? MessageType { get; } public int? PacketSize { get; } public int? MaxPacketSize { get; } public P2PException() { } public P2PException(string message) : base(message) { } public P2PException(string message, Exception inner) : base(message, inner) { } public P2PException(string message, CSteamID targetId) : base(message) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) TargetId = targetId; } public P2PException(string message, EP2PSessionError sessionError) : base(message) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) SessionError = sessionError; } public P2PException(string message, CSteamID targetId, int channel) : base(message) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) TargetId = targetId; Channel = channel; HasChannel = true; } public P2PException(string message, CSteamID targetId, EP2PSessionError sessionError) : base(message) { //IL_000a: 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) TargetId = targetId; SessionError = sessionError; } public P2PException(string message, SteamNetworkErrorKind errorKind, string? operation = null, CSteamID? targetId = null, int? channel = null, string? messageType = null, int? packetSize = null, int? maxPacketSize = null, EP2PSessionError? sessionError = null, bool isRetryable = false) : base(message, errorKind, operation, isRetryable) { TargetId = targetId; Channel = channel.GetValueOrDefault(); HasChannel = channel.HasValue; MessageType = messageType; PacketSize = packetSize; MaxPacketSize = maxPacketSize; SessionError = sessionError; } public P2PException(string message, Exception inner, SteamNetworkErrorKind errorKind, string? operation = null, CSteamID? targetId = null, int? channel = null, string? messageType = null, int? packetSize = null, int? maxPacketSize = null, EP2PSessionError? sessionError = null, bool isRetryable = false) : base(message, inner, errorKind, operation, isRetryable) { TargetId = targetId; Channel = channel.GetValueOrDefault(); HasChannel = channel.HasValue; MessageType = messageType; PacketSize = packetSize; MaxPacketSize = maxPacketSize; SessionError = sessionError; } } public enum SteamNetworkErrorKind { Unknown, SteamUnavailable, NotInitialized, NotInLobby, InvalidSteamId, PermissionDenied, PacketTooLarge, SerializationFailed, ValidationFailed, MessageFormatInvalid, MessageTypeMismatch, SessionFailed, LobbyDataFailed, MemberDataFailed } public class SteamNetworkException : Exception { public SteamNetworkErrorKind ErrorKind { get; } public string? Operation { get; } public bool IsRetryable { get; } public SteamNetworkException() { } public SteamNetworkException(string message) : base(message) { } public SteamNetworkException(string message, Exception inner) : base(message, inner) { } public SteamNetworkException(string message, SteamNetworkErrorKind errorKind, string? operation = null, bool isRetryable = false) : base(message) { ErrorKind = errorKind; Operation = operation; IsRetryable = isRetryable; } public SteamNetworkException(string message, Exception inner, SteamNetworkErrorKind errorKind, string? operation = null, bool isRetryable = false) : base(message, inner) { ErrorKind = errorKind; Operation = operation; IsRetryable = isRetryable; } } public class SyncException : SteamNetworkException { public string? SyncKey { get; } public SyncException(string message) : base(message, SteamNetworkErrorKind.Unknown) { } public SyncException(string message, Exception innerException) : base(message, innerException, SteamNetworkErrorKind.Unknown) { } public SyncException(string message, string syncKey) : base(message, SteamNetworkErrorKind.Unknown) { SyncKey = syncKey; } public SyncException(string message, string syncKey, Exception innerException) : base(message, innerException, SteamNetworkErrorKind.Unknown) { SyncKey = syncKey; } public SyncException(string message, string? syncKey, SteamNetworkErrorKind errorKind, string? operation = null, bool isRetryable = false) : base(message, errorKind, operation, isRetryable) { SyncKey = syncKey; } public SyncException(string message, string? syncKey, Exception innerException, SteamNetworkErrorKind errorKind, string? operation = null, bool isRetryable = false) : base(message, innerException, errorKind, operation, isRetryable) { SyncKey = syncKey; } } } namespace SteamNetworkLib.Events { public class LobbyJoinedEventArgs : EventArgs { public LobbyInfo Lobby { get; } public EResult Result { get; } public LobbyJoinedEventArgs(LobbyInfo lobby, EResult result = 1) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) Lobby = lobby; Result = result; } } public class LobbyCreatedEventArgs : EventArgs { public LobbyInfo Lobby { get; } public EResult Result { get; } public LobbyCreatedEventArgs(LobbyInfo lobby, EResult result = 1) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) Lobby = lobby; Result = result; } } public class LobbyLeftEventArgs : EventArgs { public CSteamID LobbyId { get; } public string Reason { get; } public LobbyLeftEventArgs(CSteamID lobbyId, string reason = "") { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) LobbyId = lobbyId; Reason = reason; } } public class MemberJoinedEventArgs : EventArgs { public SteamNetworkLib.Models.MemberInfo Member { get; } public MemberJoinedEventArgs(SteamNetworkLib.Models.MemberInfo member) { Member = member; } } public class MemberLeftEventArgs : EventArgs { public SteamNetworkLib.Models.MemberInfo Member { get; } public string Reason { get; } public MemberLeftEventArgs(SteamNetworkLib.Models.MemberInfo member, string reason = "") { Member = member; Reason = reason; } } public class LobbyDataChangedEventArgs : EventArgs { public string Key { get; } public string? OldValue { get; } public string? NewValue { get; } public CSteamID ChangedBy { get; } public LobbyDataChangedEventArgs(string key, string? oldValue, string? newValue, CSteamID changedBy) { //IL_001e: 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) Key = key; OldValue = oldValue; NewValue = newValue; ChangedBy = changedBy; } } public class MemberDataChangedEventArgs : EventArgs { public CSteamID MemberId { get; } public string Key { get; } public string? OldValue { get; } public string? NewValue { get; } public MemberDataChangedEventArgs(CSteamID memberId, string key, string? oldValue, string? newValue) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) MemberId = memberId; Key = key; OldValue = oldValue; NewValue = newValue; } } public class VersionMismatchEventArgs : EventArgs { public string LocalVersion { get; } public Dictionary PlayerVersions { get; } public List IncompatiblePlayers { get; } public VersionMismatchEventArgs(string localVersion, Dictionary playerVersions, List incompatiblePlayers) { LocalVersion = localVersion; PlayerVersions = playerVersions; IncompatiblePlayers = incompatiblePlayers; } } public class P2PPacketReceivedEventArgs : EventArgs { public CSteamID SenderId { get; } public byte[] Data { get; } public int Channel { get; } public uint PacketSize { get; } public DateTime ReceivedAt { get; } public P2PPacketReceivedEventArgs(CSteamID senderId, byte[] data, int channel, uint packetSize) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) SenderId = senderId; Data = data; Channel = channel; PacketSize = packetSize; ReceivedAt = DateTime.UtcNow; } } public class P2PMessageReceivedEventArgs : EventArgs { public P2PMessage Message { get; } public CSteamID SenderId { get; } public int Channel { get; } public DateTime ReceivedAt { get; } public P2PMessageReceivedEventArgs(P2PMessage message, CSteamID senderId, int channel) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) Message = message; SenderId = senderId; Channel = channel; ReceivedAt = DateTime.UtcNow; } } public class P2PSessionRequestEventArgs : EventArgs { public CSteamID RequesterId { get; } public string RequesterName { get; } public bool ShouldAccept { get; set; } public P2PSessionRequestEventArgs(CSteamID requesterId, string requesterName) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) RequesterId = requesterId; RequesterName = requesterName; ShouldAccept = true; } } public class P2PSessionConnectFailEventArgs : EventArgs { public CSteamID TargetId { get; } public EP2PSessionError Error { get; } public P2PSessionConnectFailEventArgs(CSteamID targetId, EP2PSessionError error) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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_0011: Unknown result type (might be due to invalid IL or missing references) TargetId = targetId; Error = error; } } public class P2PPacketSentEventArgs : EventArgs { public CSteamID TargetId { get; } public bool Success { get; } public int DataSize { get; } public int Channel { get; } public EP2PSend SendType { get; } public P2PPacketSentEventArgs(CSteamID targetId, bool success, int dataSize, int channel, EP2PSend sendType) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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) //IL_0028: Unknown result type (might be due to invalid IL or missing references) TargetId = targetId; Success = success; DataSize = dataSize; Channel = channel; SendType = sendType; } } } namespace SteamNetworkLib.Core { internal static class DedicatedCompatibilityProtocol { [Serializable] internal sealed class RegisterRequest { public string LibraryVersion { get; set; } = string.Empty; } [Serializable] internal sealed class SnapshotPayload { public string SessionId { get; set; } = string.Empty; public string LocalSteamId { get; set; } = string.Empty; public string OwnerSteamId { get; set; } = string.Empty; public string ServerSteamId { get; set; } = string.Empty; public List Members { get; set; } = new List(); public Dictionary LobbyData { get; set; } = new Dictionary(); public Dictionary> MemberData { get; set; } = new Dictionary>(); } [Serializable] internal sealed class MemberSnapshot { public string SteamId { get; set; } = string.Empty; public string DisplayName { get; set; } = string.Empty; public bool IsOwner { get; set; } public bool IsLocalPlayer { get; set; } public long JoinedAtUnixMs { get; set; } } [Serializable] internal sealed class MemberJoinedPayload { public MemberSnapshot Member { get; set; } = new MemberSnapshot(); public string OwnerSteamId { get; set; } = string.Empty; } [Serializable] internal sealed class MemberLeftPayload { public string SteamId { get; set; } = string.Empty; public string OwnerSteamId { get; set; } = string.Empty; } [Serializable] internal sealed class LobbyDataChangedPayload { public string Key { get; set; } = string.Empty; public string OldValue { get; set; } = string.Empty; public string NewValue { get; set; } = string.Empty; public string ChangedBySteamId { get; set; } = string.Empty; } [Serializable] internal sealed class MemberDataChangedPayload { public string MemberSteamId { get; set; } = string.Empty; public string Key { get; set; } = string.Empty; public string OldValue { get; set; } = string.Empty; public string NewValue { get; set; } = string.Empty; } [Serializable] internal sealed class SetLobbyDataRequest { public string Key { get; set; } = string.Empty; public string Value { get; set; } = string.Empty; } [Serializable] internal sealed class SetMemberDataRequest { public string Key { get; set; } = string.Empty; public string Value { get; set; } = string.Empty; } [Serializable] internal sealed class P2PSendRequest { public string TargetSteamId { get; set; } = string.Empty; public string DataBase64 { get; set; } = string.Empty; public int Channel { get; set; } } [Serializable] internal sealed class P2PMessagePayload { public string SenderSteamId { get; set; } = string.Empty; public string DataBase64 { get; set; } = string.Empty; public int Channel { get; set; } } public const string RegisterCommand = "snl_dedicated_register"; public const string SnapshotCommand = "snl_dedicated_snapshot"; public const string MemberJoinedCommand = "snl_dedicated_member_joined"; public const string MemberLeftCommand = "snl_dedicated_member_left"; public const string LobbyDataChangedCommand = "snl_dedicated_lobby_data_changed"; public const string MemberDataChangedCommand = "snl_dedicated_member_data_changed"; public const string SetLobbyDataCommand = "snl_dedicated_set_lobby_data"; public const string SetMemberDataCommand = "snl_dedicated_set_member_data"; public const string P2PSendCommand = "snl_dedicated_p2p_send"; public const string P2PMessageCommand = "snl_dedicated_p2p_message"; } internal sealed class DedicatedServerMessagingBridge : IDisposable { private static readonly string[] DedicatedSignalCommands = new string[9] { "auth_challenge", "auth_result", "server_data", "snl_dedicated_snapshot", "snl_dedicated_member_joined", "snl_dedicated_member_left", "snl_dedicated_lobby_data_changed", "snl_dedicated_member_data_changed", "snl_dedicated_p2p_message" }; private const string CustomMessagingTypeName = "DedicatedServerMod.Shared.Networking.CustomMessaging"; private readonly Type _customMessagingType; private readonly MethodInfo? _trySendToServerMethod; private readonly MethodInfo? _sendToServerMethod; private readonly EventInfo _clientMessageEvent; private readonly Action _messageCallback; private bool _disposed; public bool IsDedicatedContextLikely { get; private set; } public event Action? MessageReceived; private DedicatedServerMessagingBridge(Type customMessagingType, MethodInfo? trySendToServerMethod, MethodInfo? sendToServerMethod, EventInfo clientMessageEvent) { _customMessagingType = customMessagingType; _trySendToServerMethod = trySendToServerMethod; _sendToServerMethod = sendToServerMethod; _clientMessageEvent = clientMessageEvent; _messageCallback = HandleClientMessageReceived; _clientMessageEvent.AddEventHandler(null, _messageCallback); } public static DedicatedServerMessagingBridge? TryCreate() { Type type = ResolveCustomMessagingType(); if (type == null) { return null; } MethodInfo method = type.GetMethod("TrySendToServer", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(string), typeof(string) }, null); MethodInfo method2 = type.GetMethod("SendToServer", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(string), typeof(string) }, null); EventInfo @event = type.GetEvent("ClientMessageReceived", BindingFlags.Static | BindingFlags.Public); if (@event == null) { return null; } if (method == null && method2 == null) { return null; } return new DedicatedServerMessagingBridge(type, method, method2, @event); } public bool TrySendToServer(string command, string payload) { if (_disposed || string.IsNullOrWhiteSpace(command)) { return false; } object[] parameters = new object[2] { command, payload ?? string.Empty }; try { if (_trySendToServerMethod != null && _trySendToServerMethod.Invoke(null, parameters) is bool result) { return result; } if (_sendToServerMethod != null) { _sendToServerMethod.Invoke(null, parameters); return true; } } catch { return false; } return false; } public void Dispose() { if (!_disposed) { try { _clientMessageEvent.RemoveEventHandler(null, _messageCallback); } catch { } _disposed = true; } } private void HandleClientMessageReceived(string command, string payload) { if (string.IsNullOrEmpty(command)) { return; } for (int i = 0; i < DedicatedSignalCommands.Length; i++) { if (string.Equals(command, DedicatedSignalCommands[i], StringComparison.Ordinal)) { IsDedicatedContextLikely = true; break; } } this.MessageReceived?.Invoke(command, payload ?? string.Empty); } private static Type? ResolveCustomMessagingType() { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { Type type = assembly.GetType("DedicatedServerMod.Shared.Networking.CustomMessaging", throwOnError: false); if (type != null) { return type; } } catch { } } return null; } } public class NetworkRules { public bool EnableRelay { get; set; } = true; public EP2PSend DefaultSendType { get; set; } = (EP2PSend)2; public int MinReceiveChannel { get; set; } = 0; public int MaxReceiveChannel { get; set; } = 3; public bool AcceptOnlyFriends { get; set; } = false; public Func? MessagePolicy { get; set; } } public enum NetworkSessionMode { None, LobbyP2P, DedicatedRelay } public class SteamLobbyData : IDisposable { private readonly SteamLobbyManager _lobbyManager; private readonly Dictionary _cachedData = new Dictionary(); private bool _disposed = false; private Callback? _lobbyDataUpdateCallback; public event EventHandler? OnLobbyDataChanged; public SteamLobbyData(SteamLobbyManager? lobbyManager) { _lobbyManager = lobbyManager ?? throw new ArgumentNullException("lobbyManager"); if (SteamNetworkUtils.IsSteamInitialized()) { _lobbyDataUpdateCallback = Callback.Create((DispatchDelegate)OnLobbyDataUpdateCallback); } } public void SetData(string key, string value) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) ValidateKey(key); if (!_lobbyManager.IsInLobby) { string dataKey = key; throw new LobbyException("Cannot set lobby data - not in a lobby", SteamNetworkErrorKind.NotInLobby, "SetData", null, null, dataKey, requiresHost: false, null, isRetryable: true); } CSteamID lobbyId = _lobbyManager.CurrentLobby.LobbyId; string data = GetData(key); if (!SteamMatchmaking.SetLobbyData(lobbyId, key, value)) { string message = "Failed to set lobby data for key: " + key; CSteamID? lobbyId2 = lobbyId; string dataKey = key; throw new LobbyException(message, SteamNetworkErrorKind.LobbyDataFailed, "SetData", lobbyId2, null, dataKey, requiresHost: true); } _cachedData[key] = value; this.OnLobbyDataChanged?.Invoke(this, new LobbyDataChangedEventArgs(key, data, value, _lobbyManager.LocalPlayerID)); } public string? GetData(string key) { //IL_002c: 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_0032: Unknown result type (might be due to invalid IL or missing references) ValidateKey(key); if (!_lobbyManager.IsInLobby) { return null; } CSteamID lobbyId = _lobbyManager.CurrentLobby.LobbyId; string lobbyData = SteamMatchmaking.GetLobbyData(lobbyId, key); if (!string.IsNullOrEmpty(lobbyData)) { _cachedData[key] = lobbyData; return lobbyData; } string value; return _cachedData.TryGetValue(key, out value) ? value : null; } public bool HasData(string key) { return !string.IsNullOrEmpty(GetData(key)); } public void RemoveData(string key) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) ValidateKey(key); if (!_lobbyManager.IsInLobby) { string dataKey = key; throw new LobbyException("Cannot remove lobby data - not in a lobby", SteamNetworkErrorKind.NotInLobby, "RemoveData", null, null, dataKey, requiresHost: false, null, isRetryable: true); } string data = GetData(key); if (data != null) { CSteamID lobbyId = _lobbyManager.CurrentLobby.LobbyId; if (!SteamMatchmaking.DeleteLobbyData(lobbyId, key)) { string message = "Failed to remove lobby data for key: " + key; CSteamID? lobbyId2 = lobbyId; string dataKey = key; throw new LobbyException(message, SteamNetworkErrorKind.LobbyDataFailed, "RemoveData", lobbyId2, null, dataKey, requiresHost: true); } _cachedData.Remove(key); this.OnLobbyDataChanged?.Invoke(this, new LobbyDataChangedEventArgs(key, data, null, _lobbyManager.LocalPlayerID)); } } public Dictionary GetAllData() { //IL_002d: 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_0033: 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) Dictionary dictionary = new Dictionary(); if (!_lobbyManager.IsInLobby) { return dictionary; } CSteamID lobbyId = _lobbyManager.CurrentLobby.LobbyId; int lobbyDataCount = SteamMatchmaking.GetLobbyDataCount(lobbyId); for (int i = 0; i < lobbyDataCount; i++) { string text = ""; string value = ""; if (SteamMatchmaking.GetLobbyDataByIndex(lobbyId, i, ref text, 256, ref value, 8192) && !string.IsNullOrEmpty(text)) { dictionary[text] = value; _cachedData[text] = value; } } return dictionary; } public void ClearAllData() { //IL_007d: Unknown result type (might be due to invalid IL or missing references) if (!_lobbyManager.IsInLobby) { throw new LobbyException("Cannot clear lobby data - not in a lobby", SteamNetworkErrorKind.NotInLobby, "ClearAllData", null, null, null, requiresHost: false, null, isRetryable: true); } if (!_lobbyManager.IsHost) { throw new LobbyException("Only the lobby host can clear all lobby data", SteamNetworkErrorKind.PermissionDenied, "ClearAllData", _lobbyManager.CurrentLobby?.LobbyId, null, null, requiresHost: true); } Dictionary allData = GetAllData(); foreach (string key in allData.Keys) { RemoveData(key); } } public void SetDataBatch(Dictionary data) { //IL_00cf: Unknown result type (might be due to invalid IL or missing references) if (data == null || data.Count == 0) { return; } if (!_lobbyManager.IsInLobby) { throw new LobbyException("Cannot set lobby data - not in a lobby", SteamNetworkErrorKind.NotInLobby, "SetDataBatch", null, null, null, requiresHost: false, null, isRetryable: true); } foreach (KeyValuePair datum in data) { try { SetData(datum.Key, datum.Value); } catch (Exception ex) { string message = "Failed to set data for key '" + datum.Key + "': " + ex.Message; CSteamID? lobbyId = _lobbyManager.CurrentLobby?.LobbyId; string key = datum.Key; throw new LobbyException(message, ex, SteamNetworkErrorKind.LobbyDataFailed, "SetDataBatch", lobbyId, null, key, requiresHost: true); } } } public int GetDataCount() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (!_lobbyManager.IsInLobby) { return 0; } return SteamMatchmaking.GetLobbyDataCount(_lobbyManager.CurrentLobby.LobbyId); } public List GetDataKeys() { //IL_002a: 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_0030: 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) List list = new List(); if (!_lobbyManager.IsInLobby) { return list; } CSteamID lobbyId = _lobbyManager.CurrentLobby.LobbyId; int lobbyDataCount = SteamMatchmaking.GetLobbyDataCount(lobbyId); for (int i = 0; i < lobbyDataCount; i++) { string text = ""; string text2 = ""; if (SteamMatchmaking.GetLobbyDataByIndex(lobbyId, i, ref text, 256, ref text2, 8192) && !string.IsNullOrEmpty(text)) { list.Add(text); } } return list; } public void RefreshData() { if (_lobbyManager.IsInLobby) { _cachedData.Clear(); GetAllData(); } } private void ValidateKey(string key) { if (string.IsNullOrEmpty(key)) { throw new ArgumentException("Lobby data key cannot be null or empty"); } if (key.Length > 255) { throw new ArgumentException("Lobby data key cannot exceed 255 characters"); } if (key.StartsWith("__steam_")) { throw new ArgumentException("Lobby data key cannot start with '__steam_' (reserved by Steam)"); } } private void OnLobbyDataUpdateCallback(LobbyDataUpdate_t result) { //IL_001a: 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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) try { if (!_lobbyManager.IsInLobby || _lobbyManager.CurrentLobby.LobbyId.m_SteamID != result.m_ulSteamIDLobby || result.m_bSuccess == 0) { return; } Dictionary dictionary = new Dictionary(_cachedData); RefreshData(); Dictionary cachedData = _cachedData; CSteamID changedBy = default(CSteamID); ((CSteamID)(ref changedBy))..ctor(result.m_ulSteamIDMember); foreach (KeyValuePair item in cachedData) { if (!dictionary.TryGetValue(item.Key, out var value) || value != item.Value) { this.OnLobbyDataChanged?.Invoke(this, new LobbyDataChangedEventArgs(item.Key, value, item.Value, changedBy)); } } foreach (KeyValuePair item2 in dictionary) { if (!cachedData.ContainsKey(item2.Key)) { this.OnLobbyDataChanged?.Invoke(this, new LobbyDataChangedEventArgs(item2.Key, item2.Value, null, changedBy)); } } } catch (Exception ex) { Console.WriteLine("Error in lobby data update callback: " + ex.Message); } } public void Dispose() { if (!_disposed) { try { _lobbyDataUpdateCallback?.Dispose(); _cachedData.Clear(); } catch (Exception ex) { Console.WriteLine("Error disposing SteamLobbyData: " + ex.Message); } _disposed = true; } } } public class SteamLobbyManager : IDisposable { private bool _disposed = false; private LobbyInfo? _currentLobby; private readonly List _lobbyMembers = new List(); private Callback? _lobbyCreatedCallback; private Callback? _lobbyEnteredCallback; private Callback? _chatUpdateCallback; private Callback? _lobbyJoinRequestedCallback; private Callback? _lobbyDataUpdateCallback; private readonly Dictionary _lobbyDataCache = new Dictionary(); private TaskCompletionSource? _createLobbyTcs; private TaskCompletionSource? _joinLobbyTcs; public LobbyInfo? CurrentLobby => _currentLobby; public CSteamID LocalPlayerID { get; private set; } public bool IsInLobby => _currentLobby != null && SteamNetworkUtils.IsValidSteamID(_currentLobby.LobbyId); public bool IsHost => IsInLobby && _currentLobby.OwnerId == LocalPlayerID; public event EventHandler? OnLobbyJoined; public event EventHandler? OnLobbyCreated; public event EventHandler? OnLobbyLeft; public event EventHandler? OnMemberJoined; public event EventHandler? OnMemberLeft; public event EventHandler? OnLobbyDataChanged; public SteamLobbyManager() { InitializeSteam(); } public async Task CreateLobbyAsync(ELobbyType lobbyType = 1, int maxMembers = 4) { //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) if (_createLobbyTcs != null && !_createLobbyTcs.Task.IsCompleted) { throw new LobbyException("A lobby creation is already in progress"); } if (IsInLobby) { LeaveLobby(); } _createLobbyTcs = new TaskCompletionSource(); SteamAPICall_t apiCall = SteamMatchmaking.CreateLobby(lobbyType, maxMembers); if (apiCall == SteamAPICall_t.Invalid) { _createLobbyTcs.SetException(new LobbyException("Failed to create lobby - Steam API call failed")); return await _createLobbyTcs.Task; } Task timeoutTask = Task.Delay(10000); if (await Task.WhenAny(new Task[2] { _createLobbyTcs.Task, timeoutTask }) == timeoutTask) { _createLobbyTcs.SetException(new LobbyException("Lobby creation timed out")); } return await _createLobbyTcs.Task; } public async Task JoinLobbyAsync(CSteamID lobbyId) { //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) if (!SteamNetworkUtils.IsValidSteamID(lobbyId)) { throw new LobbyException("Invalid lobby ID"); } if (_joinLobbyTcs != null && !_joinLobbyTcs.Task.IsCompleted) { throw new LobbyException("A lobby join is already in progress"); } if (IsInLobby) { LeaveLobby(); } _joinLobbyTcs = new TaskCompletionSource(); SteamMatchmaking.JoinLobby(lobbyId); Task timeoutTask = Task.Delay(10000); if (await Task.WhenAny(new Task[2] { _joinLobbyTcs.Task, timeoutTask }) == timeoutTask) { _joinLobbyTcs.SetException(new LobbyException("Lobby join timed out")); } return await _joinLobbyTcs.Task; } public void LeaveLobby() { //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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) if (IsInLobby) { CSteamID lobbyId = _currentLobby.LobbyId; string reason = "Player left voluntarily"; SteamMatchmaking.LeaveLobby(lobbyId); _currentLobby = null; _lobbyMembers.Clear(); this.OnLobbyLeft?.Invoke(this, new LobbyLeftEventArgs(lobbyId, reason)); } } public List GetLobbyMembers() { if (!IsInLobby) { return new List(); } UpdateLobbyMembers(); return new List(_lobbyMembers); } public void InviteFriend(CSteamID friendId) { //IL_001a: 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_003e: Unknown result type (might be due to invalid IL or missing references) if (!IsInLobby) { throw new LobbyException("Cannot invite friend - not in a lobby"); } if (!SteamNetworkUtils.IsValidSteamID(friendId)) { throw new LobbyException("Invalid friend Steam ID"); } SteamMatchmaking.InviteUserToLobby(_currentLobby.LobbyId, friendId); } public void OpenInviteDialog() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (!IsInLobby) { throw new LobbyException("Cannot open invite dialog - not in a lobby"); } SteamFriends.ActivateGameOverlayInviteDialog(_currentLobby.LobbyId); } private void InitializeSteam() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (!SteamNetworkUtils.IsSteamInitialized()) { throw new SteamNetworkException("Steam is not initialized. Make sure Steam is running and SteamAPI.Init() was called.", SteamNetworkErrorKind.SteamUnavailable, "InitializeSteam", isRetryable: true); } LocalPlayerID = SteamUser.GetSteamID(); _lobbyCreatedCallback = Callback.Create((DispatchDelegate)OnLobbyCreatedCallback); _lobbyEnteredCallback = Callback.Create((DispatchDelegate)OnLobbyEnteredCallback); _chatUpdateCallback = Callback.Create((DispatchDelegate)OnChatUpdateCallback); _lobbyJoinRequestedCallback = Callback.Create((DispatchDelegate)OnLobbyJoinRequestedCallback); _lobbyDataUpdateCallback = Callback.Create((DispatchDelegate)OnLobbyDataUpdateCallback); } private void OnLobbyCreatedCallback(LobbyCreated_t result) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 //IL_0046: 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) //IL_0060: 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_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0080: 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_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) try { if ((int)result.m_eResult != 1) { _createLobbyTcs?.SetException(new LobbyException("Failed to create lobby: " + SteamNetworkUtils.FormatSteamResult(result.m_eResult))); return; } CSteamID val = default(CSteamID); ((CSteamID)(ref val))..ctor(result.m_ulSteamIDLobby); LobbyInfo lobbyInfo = (_currentLobby = CreateLobbyInfo(val)); CSteamID val2 = val; CSteamID localPlayerID = LocalPlayerID; SteamMatchmaking.SetLobbyData(val2, "owner", ((object)(CSteamID)(ref localPlayerID)).ToString()); SteamMatchmaking.SetLobbyData(val, "created_at", DateTime.UtcNow.ToString("O")); UpdateLobbyMembers(); _createLobbyTcs?.SetResult(lobbyInfo); this.OnLobbyCreated?.Invoke(this, new LobbyCreatedEventArgs(lobbyInfo, result.m_eResult)); } catch (Exception ex) { _createLobbyTcs?.SetException(new LobbyException("Error in lobby created callback: " + ex.Message, ex)); } } private void OnLobbyEnteredCallback(LobbyEnter_t result) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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_0024: Unknown result type (might be due to invalid IL or missing references) try { CSteamID lobbyId = default(CSteamID); ((CSteamID)(ref lobbyId))..ctor(result.m_ulSteamIDLobby); if ((result.m_EChatRoomEnterResponse & 1) == 0) { string message = $"Failed to enter lobby: {result.m_EChatRoomEnterResponse}"; _joinLobbyTcs?.SetException(new LobbyException(message)); return; } LobbyInfo lobbyInfo = (_currentLobby = CreateLobbyInfo(lobbyId)); UpdateLobbyMembers(); _joinLobbyTcs?.SetResult(lobbyInfo); this.OnLobbyJoined?.Invoke(this, new LobbyJoinedEventArgs(lobbyInfo, (EResult)1)); } catch (Exception ex) { _joinLobbyTcs?.SetException(new LobbyException("Error in lobby entered callback: " + ex.Message, ex)); } } private void OnChatUpdateCallback(LobbyChatUpdate_t result) { //IL_0017: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0042: 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) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) if (!IsInLobby) { return; } try { CSteamID val = default(CSteamID); ((CSteamID)(ref val))..ctor(result.m_ulSteamIDUserChanged); CSteamID val2 = default(CSteamID); ((CSteamID)(ref val2))..ctor(result.m_ulSteamIDMakingChange); if (val2 == _currentLobby.LobbyId && val != LocalPlayerID && (result.m_rgfChatMemberStateChange & 2u) != 0) { LeaveLobby(); return; } List list = new List(_lobbyMembers); UpdateLobbyMembers(); List list2 = new List(_lobbyMembers); HashSet hashSet = list2.Select((SteamNetworkLib.Models.MemberInfo m) => m.SteamId).ToHashSet(); HashSet hashSet2 = list.Select((SteamNetworkLib.Models.MemberInfo m) => m.SteamId).ToHashSet(); foreach (SteamNetworkLib.Models.MemberInfo item in list2) { if (!hashSet2.Contains(item.SteamId)) { this.OnMemberJoined?.Invoke(this, new MemberJoinedEventArgs(item)); } } foreach (SteamNetworkLib.Models.MemberInfo item2 in list) { if (!hashSet.Contains(item2.SteamId)) { this.OnMemberLeft?.Invoke(this, new MemberLeftEventArgs(item2, "Left lobby")); } } } catch (Exception ex) { Console.WriteLine("Error in chat update callback: " + ex.Message); } } private void OnLobbyDataUpdateCallback(LobbyDataUpdate_t result) { //IL_0010: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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_0057: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) try { if (!IsInLobby || _currentLobby.LobbyId.m_SteamID != result.m_ulSteamIDLobby || result.m_bSuccess == 0) { return; } CSteamID lobbyId = _currentLobby.LobbyId; CSteamID changedBy = default(CSteamID); ((CSteamID)(ref changedBy))..ctor(result.m_ulSteamIDMember); Dictionary dictionary = new Dictionary(_lobbyDataCache); _lobbyDataCache.Clear(); int lobbyDataCount = SteamMatchmaking.GetLobbyDataCount(lobbyId); string text = default(string); string value = default(string); for (int i = 0; i < lobbyDataCount; i++) { if (SteamMatchmaking.GetLobbyDataByIndex(lobbyId, i, ref text, 256, ref value, 8192) && !string.IsNullOrEmpty(text)) { _lobbyDataCache[text] = value; } } foreach (KeyValuePair item in _lobbyDataCache) { if (!dictionary.TryGetValue(item.Key, out var value2) || value2 != item.Value) { this.OnLobbyDataChanged?.Invoke(this, new LobbyDataChangedEventArgs(item.Key, value2, item.Value, changedBy)); } } foreach (KeyValuePair item2 in dictionary) { if (!_lobbyDataCache.ContainsKey(item2.Key)) { this.OnLobbyDataChanged?.Invoke(this, new LobbyDataChangedEventArgs(item2.Key, item2.Value, null, changedBy)); } } } catch (Exception ex) { Console.WriteLine("Error in lobby data update callback: " + ex.Message); } } private void OnLobbyJoinRequestedCallback(GameLobbyJoinRequested_t result) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: 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_001d: Unknown result type (might be due to invalid IL or missing references) try { CSteamID steamIDLobby = result.m_steamIDLobby; if (IsInLobby) { LeaveLobby(); } JoinLobbyAsync(steamIDLobby); } catch (Exception ex) { Console.WriteLine("Error in lobby join requested callback: " + ex.Message); } } private LobbyInfo CreateLobbyInfo(CSteamID lobbyId) { //IL_0001: 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_0023: 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) //IL_002f: 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) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) string lobbyData = SteamMatchmaking.GetLobbyData(lobbyId, "owner"); CSteamID ownerId = (CSteamID)(string.IsNullOrEmpty(lobbyData) ? SteamMatchmaking.GetLobbyOwner(lobbyId) : new CSteamID(ulong.Parse(lobbyData))); return new LobbyInfo { LobbyId = lobbyId, OwnerId = ownerId, MemberCount = SteamMatchmaking.GetNumLobbyMembers(lobbyId), MaxMembers = SteamMatchmaking.GetLobbyMemberLimit(lobbyId), Name = SteamMatchmaking.GetLobbyData(lobbyId, "name"), CreatedAt = DateTime.UtcNow }; } private void UpdateLobbyMembers() { //IL_0025: 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_003c: 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_0043: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) if (!IsInLobby) { return; } _lobbyMembers.Clear(); CSteamID lobbyId = _currentLobby.LobbyId; int numLobbyMembers = SteamMatchmaking.GetNumLobbyMembers(lobbyId); for (int i = 0; i < numLobbyMembers; i++) { CSteamID lobbyMemberByIndex = SteamMatchmaking.GetLobbyMemberByIndex(lobbyId, i); if (!(lobbyMemberByIndex == CSteamID.Nil)) { SteamNetworkLib.Models.MemberInfo item = new SteamNetworkLib.Models.MemberInfo { SteamId = lobbyMemberByIndex, DisplayName = SteamNetworkUtils.GetPlayerName(lobbyMemberByIndex), IsOwner = (lobbyMemberByIndex == _currentLobby.OwnerId), IsLocalPlayer = (lobbyMemberByIndex == LocalPlayerID), JoinedAt = DateTime.UtcNow }; _lobbyMembers.Add(item); } } } public void Dispose() { if (_disposed) { return; } try { if (IsInLobby) { LeaveLobby(); } _lobbyCreatedCallback?.Dispose(); _lobbyEnteredCallback?.Dispose(); _chatUpdateCallback?.Dispose(); _lobbyJoinRequestedCallback?.Dispose(); _lobbyDataUpdateCallback?.Dispose(); _createLobbyTcs?.TrySetCanceled(); _joinLobbyTcs?.TrySetCanceled(); } catch (Exception ex) { Console.WriteLine("Error disposing SteamLobbyManager: " + ex.Message); } _disposed = true; } } public class SteamMemberData : IDisposable { private readonly SteamLobbyManager _lobbyManager; private readonly Dictionary<(ulong PlayerId, string Key), string> _cachedData = new Dictionary<(ulong, string), string>(); private bool _disposed = false; private Callback? _lobbyDataUpdateCallback; public event EventHandler? OnMemberDataChanged; public SteamMemberData(SteamLobbyManager? lobbyManager) { _lobbyManager = lobbyManager ?? throw new ArgumentNullException("lobbyManager"); if (SteamNetworkUtils.IsSteamInitialized()) { _lobbyDataUpdateCallback = Callback.Create((DispatchDelegate)OnLobbyDataUpdateCallback); } } public void SetMemberData(string key, string value) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) SetMemberData(_lobbyManager.LocalPlayerID, key, value); } public void SetMemberData(CSteamID playerId, string key, string value) { //IL_0051: 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_0029: 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_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) ValidateKey(key); if (!_lobbyManager.IsInLobby) { CSteamID? memberId = playerId; string dataKey = key; throw new LobbyException("Cannot set member data - not in a lobby", SteamNetworkErrorKind.NotInLobby, "SetMemberData", null, memberId, dataKey, requiresHost: false, null, isRetryable: true); } if (playerId != _lobbyManager.LocalPlayerID) { CSteamID? memberId = playerId; string dataKey = key; throw new LobbyException("Cannot set member data for other players - only for local player", SteamNetworkErrorKind.PermissionDenied, "SetMemberData", null, memberId, dataKey); } CSteamID lobbyId = _lobbyManager.CurrentLobby.LobbyId; string memberData = GetMemberData(playerId, key); SteamMatchmaking.SetLobbyMemberData(lobbyId, key, value); _cachedData[(playerId.m_SteamID, key)] = value; this.OnMemberDataChanged?.Invoke(this, new MemberDataChangedEventArgs(playerId, key, memberData, value)); } public string? GetMemberData(string key) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) return GetMemberData(_lobbyManager.LocalPlayerID, key); } public string? GetMemberData(CSteamID playerId, string key) { //IL_002c: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) ValidateKey(key); if (!_lobbyManager.IsInLobby) { return null; } CSteamID lobbyId = _lobbyManager.CurrentLobby.LobbyId; string lobbyMemberData = SteamMatchmaking.GetLobbyMemberData(lobbyId, playerId, key); if (!string.IsNullOrEmpty(lobbyMemberData)) { _cachedData[(playerId.m_SteamID, key)] = lobbyMemberData; return lobbyMemberData; } string value; return _cachedData.TryGetValue((playerId.m_SteamID, key), out value) ? value : null; } public bool HasMemberData(string key) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) return HasMemberData(_lobbyManager.LocalPlayerID, key); } public bool HasMemberData(CSteamID playerId, string key) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return !string.IsNullOrEmpty(GetMemberData(playerId, key)); } public void RemoveMemberData(string key) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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_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) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) ValidateKey(key); if (!_lobbyManager.IsInLobby) { throw new LobbyException("Cannot remove member data - not in a lobby", SteamNetworkErrorKind.NotInLobby, "RemoveMemberData", null, null, key, requiresHost: false, null, isRetryable: true); } CSteamID localPlayerID = _lobbyManager.LocalPlayerID; CSteamID lobbyId = _lobbyManager.CurrentLobby.LobbyId; string memberData = GetMemberData(localPlayerID, key); if (memberData != null) { SteamMatchmaking.SetLobbyMemberData(lobbyId, key, ""); _cachedData.Remove((localPlayerID.m_SteamID, key)); this.OnMemberDataChanged?.Invoke(this, new MemberDataChangedEventArgs(localPlayerID, key, memberData, null)); } } public Dictionary GetAllMemberData(CSteamID playerId) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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_00a4: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = new Dictionary(); if (!_lobbyManager.IsInLobby) { return dictionary; } CSteamID lobbyId = _lobbyManager.CurrentLobby.LobbyId; List list = (from k in _cachedData.Keys where k.PlayerId == playerId.m_SteamID select k.Key).Distinct().ToList(); foreach (string item in list) { string memberData = GetMemberData(playerId, item); if (!string.IsNullOrEmpty(memberData)) { dictionary[item] = memberData; } } return dictionary; } public Dictionary GetMemberDataForAllPlayers(string key) { //IL_004a: 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) ValidateKey(key); Dictionary dictionary = new Dictionary(); if (!_lobbyManager.IsInLobby) { return dictionary; } List lobbyMembers = _lobbyManager.GetLobbyMembers(); foreach (SteamNetworkLib.Models.MemberInfo item in lobbyMembers) { string memberData = GetMemberData(item.SteamId, key); if (!string.IsNullOrEmpty(memberData)) { dictionary[item.SteamId] = memberData; } } return dictionary; } public void SetMemberDataBatch(Dictionary data) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) if (data == null || data.Count == 0) { return; } if (!_lobbyManager.IsInLobby) { throw new LobbyException("Cannot set member data - not in a lobby", SteamNetworkErrorKind.NotInLobby, "SetMemberDataBatch", null, null, null, requiresHost: false, null, isRetryable: true); } foreach (KeyValuePair datum in data) { try { SetMemberData(datum.Key, datum.Value); } catch (Exception ex) { string message = "Failed to set member data for key '" + datum.Key + "': " + ex.Message; CSteamID? memberId = _lobbyManager.LocalPlayerID; string key = datum.Key; throw new LobbyException(message, ex, SteamNetworkErrorKind.MemberDataFailed, "SetMemberDataBatch", null, memberId, key); } } } public List GetPlayersWithData(string key) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) ValidateKey(key); List list = new List(); if (!_lobbyManager.IsInLobby) { return list; } List lobbyMembers = _lobbyManager.GetLobbyMembers(); foreach (SteamNetworkLib.Models.MemberInfo item in lobbyMembers) { if (HasMemberData(item.SteamId, key)) { list.Add(item.SteamId); } } return list; } public void RefreshMemberData(CSteamID playerId) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) if (!_lobbyManager.IsInLobby) { return; } List list = (from k in _cachedData.Keys where k.PlayerId == playerId.m_SteamID select k.Key).ToList(); foreach (string item in list) { _cachedData.Remove((playerId.m_SteamID, item)); GetMemberData(playerId, item); } } public void RefreshAllMemberData() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (!_lobbyManager.IsInLobby) { return; } List lobbyMembers = _lobbyManager.GetLobbyMembers(); foreach (SteamNetworkLib.Models.MemberInfo item in lobbyMembers) { RefreshMemberData(item.SteamId); } } public Dictionary> GetAllMemberDataSummary() { //IL_0042: 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) Dictionary> dictionary = new Dictionary>(); if (!_lobbyManager.IsInLobby) { return dictionary; } List lobbyMembers = _lobbyManager.GetLobbyMembers(); foreach (SteamNetworkLib.Models.MemberInfo item in lobbyMembers) { dictionary[item.SteamId] = GetAllMemberData(item.SteamId); } return dictionary; } private void ValidateKey(string key) { if (string.IsNullOrEmpty(key)) { throw new ArgumentException("Member data key cannot be null or empty"); } if (key.Length > 255) { throw new ArgumentException("Member data key cannot exceed 255 characters"); } if (key.StartsWith("__steam_")) { throw new ArgumentException("Member data key cannot start with '__steam_' (reserved by Steam)"); } } private void OnLobbyDataUpdateCallback(LobbyDataUpdate_t result) { //IL_001a: 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_0039: 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_0056: 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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) try { if (_lobbyManager.IsInLobby && _lobbyManager.CurrentLobby.LobbyId.m_SteamID == result.m_ulSteamIDLobby && result.m_bSuccess != 0) { CSteamID val = default(CSteamID); ((CSteamID)(ref val))..ctor(result.m_ulSteamIDMember); if (val != CSteamID.Nil && SteamNetworkUtils.IsValidSteamID(val)) { RefreshMemberData(val); } } } catch (Exception ex) { Console.WriteLine("Error in lobby data update callback for member data: " + ex.Message); } } public void Dispose() { if (!_disposed) { try { _lobbyDataUpdateCallback?.Dispose(); _cachedData.Clear(); } catch (Exception ex) { Console.WriteLine("Error disposing SteamMemberData: " + ex.Message); } _disposed = true; } } } public static class SteamP2PLimits { public const int UnreliableMaxPacketSize = 1200; public const int ReliableMaxPacketSize = 1048576; public static int GetMaxPacketSize(EP2PSend sendType) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_000b: 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_000f: Invalid comparison between Unknown and I4 //IL_0028: Unknown result type (might be due to invalid IL or missing references) if ((int)sendType > 1) { if (sendType - 2 <= 1) { return 1048576; } throw new ArgumentOutOfRangeException("sendType", sendType, "Unknown Steam P2P send type."); } return 1200; } public static bool IsReliable(EP2PSend sendType) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 return (int)sendType == 2 || (int)sendType == 3; } } public class SteamP2PManager : IDisposable { private readonly SteamLobbyManager _lobbyManager; private NetworkRules _rules = new NetworkRules(); private bool _disposed = false; private readonly Dictionary _activeSessions = new Dictionary(); private readonly Queue<(CSteamID Target, byte[] Data, int Channel, EP2PSend SendType)> _sendQueue = new Queue<(CSteamID, byte[], int, EP2PSend)>(); private bool _isProcessing = false; private Callback? _sessionRequestCallback; private Callback? _sessionConnectFailCallback; private readonly Dictionary>> _messageHandlers = new Dictionary>>(); private readonly Dictionary _customMessageTypes = new Dictionary(StringComparer.OrdinalIgnoreCase); private Func>? _packetSendOverride; private Func>? _memberProviderOverride; private Func? _localPlayerProviderOverride; private Func? _isInSessionOverride; public bool IsActive { get; private set; } public int MaxPacketSize => 1048576; public int ActiveSessionCount => _activeSessions.Count; public event EventHandler? OnPacketReceived; public event EventHandler? OnMessageReceived; public event EventHandler? OnSessionRequested; public event EventHandler? OnSessionConnectFail; public event EventHandler? OnPacketSent; public int GetMaxPacketSize(EP2PSend sendType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return SteamP2PLimits.GetMaxPacketSize(sendType); } public SteamP2PManager(SteamLobbyManager lobbyManager) { _lobbyManager = lobbyManager ?? throw new ArgumentNullException("lobbyManager"); InitializeP2P(); } public SteamP2PManager(SteamLobbyManager? lobbyManager, NetworkRules rules) { _lobbyManager = lobbyManager ?? throw new ArgumentNullException("lobbyManager"); _rules = rules ?? new NetworkRules(); InitializeP2P(); } public async Task SendMessageAsync(CSteamID targetId, P2PMessage message, int channel = 0, EP2PSend sendType = 2) { //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_002e: 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) if (!IsActive) { throw new P2PException("P2P manager is not active", SteamNetworkErrorKind.NotInitialized, "SendMessageAsync", targetId, channel, message.MessageType, null, null, null, isRetryable: true); } if (!SteamNetworkUtils.IsValidSteamID(targetId)) { throw new P2PException("Invalid target Steam ID", SteamNetworkErrorKind.InvalidSteamId, "SendMessageAsync", targetId, channel, message.MessageType); } try { message.SenderId = GetLocalPlayerId(); if (_rules.MessagePolicy != null) { try { (channel, sendType) = _rules.MessagePolicy(message); } catch { } } byte[] messageData = MessageSerializer.SerializeMessage(message); return await SendPacketAsync(targetId, messageData, channel, sendType); } catch (P2PException) { throw; } catch (Exception ex) { throw new P2PException("Failed to send message: " + ex.Message, ex, SteamNetworkErrorKind.SerializationFailed, "SendMessageAsync", targetId, channel, message.MessageType); } } public async Task SendPacketAsync(CSteamID targetId, byte[] data, int channel = 0, EP2PSend sendType = 2) { //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_002e: 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) if (!IsActive) { throw new P2PException("P2P manager is not active", SteamNetworkErrorKind.NotInitialized, "SendPacketAsync", targetId, channel, null, data.Length, null, null, isRetryable: true); } if (!SteamNetworkUtils.IsValidSteamID(targetId)) { throw new P2PException("Invalid target Steam ID", SteamNetworkErrorKind.InvalidSteamId, "SendPacketAsync", targetId, channel, null, data.Length); } int maxPacketSize = SteamP2PLimits.GetMaxPacketSize(sendType); if (data.Length > maxPacketSize) { throw new P2PException($"Packet too large for {sendType}: {data.Length} bytes (max: {maxPacketSize})", SteamNetworkErrorKind.PacketTooLarge, "SendPacketAsync", targetId, channel, null, data.Length, maxPacketSize); } try { if (_packetSendOverride != null && await _packetSendOverride(targetId, data, channel, sendType)) { return true; } await EnsureSessionAsync(targetId); bool success = SteamNetworking.SendP2PPacket(targetId, data, (uint)data.Length, sendType, channel); if (success) { _activeSessions[targetId] = DateTime.UtcNow; } this.OnPacketSent?.Invoke(this, new P2PPacketSentEventArgs(targetId, success, data.Length, channel, sendType)); return success; } catch (P2PException) { throw; } catch (Exception ex) { throw new P2PException("Failed to send packet: " + ex.Message, ex, SteamNetworkErrorKind.SessionFailed, "SendPacketAsync", targetId, channel, null, data.Length, null, null, isRetryable: true); } } public void BroadcastMessage(P2PMessage message, int channel = 0, EP2PSend sendType = 2) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: 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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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 (!IsInSession()) { throw new P2PException("Cannot broadcast - not in a lobby"); } if (message is StreamMessage streamMessage && (int)sendType == 2) { sendType = streamMessage.RecommendedSendType; } List sessionMembers = GetSessionMembers(); foreach (SteamNetworkLib.Models.MemberInfo item in sessionMembers) { if (item.SteamId != GetLocalPlayerId()) { SendMessageAsync(item.SteamId, message, channel, sendType); } } } public void BroadcastPacket(byte[] data, int channel = 0, EP2PSend sendType = 2) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) if (!IsInSession()) { throw new P2PException("Cannot broadcast - not in a lobby"); } List sessionMembers = GetSessionMembers(); foreach (SteamNetworkLib.Models.MemberInfo item in sessionMembers) { if (item.SteamId != GetLocalPlayerId()) { SendPacketAsync(item.SteamId, data, channel, sendType); } } } public void ConfigureOverrides(Func>? packetSendOverride, Func>? memberProvider, Func? localPlayerProvider, Func? isInSessionProvider) { _packetSendOverride = packetSendOverride; _memberProviderOverride = memberProvider; _localPlayerProviderOverride = localPlayerProvider; _isInSessionOverride = isInSessionProvider; } internal void ProcessExternalPacket(CSteamID senderId, byte[] data) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) ProcessExternalPacket(senderId, data, 0); } internal void ProcessExternalPacket(CSteamID senderId, byte[] data, int channel) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (IsActive && data != null && data.Length != 0) { ProcessReceivedPacket(senderId, data, channel); } } public void RegisterMessageHandler(Action handler) where T : P2PMessage, new() { Action handler2 = handler; string messageType = new T().MessageType; if (!_customMessageTypes.ContainsKey(messageType)) { _customMessageTypes[messageType] = typeof(T); } if (!_messageHandlers.ContainsKey(messageType)) { _messageHandlers[messageType] = new List>(); } _messageHandlers[messageType].Add(delegate(P2PMessage message, CSteamID senderId) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) if (message is T arg) { handler2(arg, senderId); } }); } public void UnregisterMessageHandler() where T : P2PMessage, new() { string messageType = new T().MessageType; _messageHandlers.Remove(messageType); } public void RegisterCustomMessageType() where T : P2PMessage, new() { string messageType = new T().MessageType; if (!_customMessageTypes.ContainsKey(messageType)) { _customMessageTypes[messageType] = typeof(T); Console.WriteLine("[SteamNetworkLib] Registered custom message type: " + messageType); } } public void UnregisterCustomMessageType() where T : P2PMessage, new() { string messageType = new T().MessageType; _customMessageTypes.Remove(messageType); } public void ProcessIncomingPackets() { //IL_0055: Unknown result type (might be due to invalid IL or missing references) if (!IsActive) { return; } try { uint num = default(uint); uint num2 = default(uint); CSteamID senderId = default(CSteamID); while (SteamNetworking.IsP2PPacketAvailable(ref num, 0)) { byte[] array = new byte[num]; if (SteamNetworking.ReadP2PPacket(array, num, ref num2, ref senderId, 0)) { if (num2 < num) { byte[] array2 = new byte[num2]; Array.Copy(array, array2, num2); array = array2; } ProcessReceivedPacket(senderId, array, 0); } } } catch (Exception ex) { Console.WriteLine("Error processing incoming P2P packets: " + ex.Message); } } public bool AcceptSession(CSteamID playerId) { //IL_0001: 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_0026: Unknown result type (might be due to invalid IL or missing references) if (!SteamNetworkUtils.IsValidSteamID(playerId)) { return false; } bool flag = SteamNetworking.AcceptP2PSessionWithUser(playerId); if (flag) { _activeSessions[playerId] = DateTime.UtcNow; } return flag; } public void CloseSession(CSteamID playerId) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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) if (SteamNetworkUtils.IsValidSteamID(playerId)) { SteamNetworking.CloseP2PSessionWithUser(playerId); _activeSessions.Remove(playerId); } } public List GetActiveSessions() { return new List(_activeSessions.Keys); } public P2PSessionState_t GetSessionState(CSteamID playerId) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //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_000e: Unknown result type (might be due to invalid IL or missing references) P2PSessionState_t result = default(P2PSessionState_t); SteamNetworking.GetP2PSessionState(playerId, ref result); return result; } public void CleanupInactiveSessions(TimeSpan inactiveThreshold) { //IL_0065: 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_006d: Unknown result type (might be due to invalid IL or missing references) DateTime cutoff = DateTime.UtcNow - inactiveThreshold; List list = (from kvp in _activeSessions where kvp.Value < cutoff select kvp.Key).ToList(); foreach (CSteamID item in list) { CloseSession(item); } } private void InitializeP2P() { if (!SteamNetworkUtils.IsSteamInitialized()) { throw new SteamNetworkException("Steam is not initialized. Make sure Steam is running and SteamAPI.Init() was called.", SteamNetworkErrorKind.SteamUnavailable, "InitializeP2P", isRetryable: true); } _sessionRequestCallback = Callback.Create((DispatchDelegate)OnSessionRequestCallback); _sessionConnectFailCallback = Callback.Create((DispatchDelegate)OnSessionConnectFailCallback); try { SteamNetworking.AllowP2PPacketRelay(_rules.EnableRelay); } catch { } _lobbyManager.OnLobbyJoined += OnLobbyJoinedAcceptAllPeers; _lobbyManager.OnMemberJoined += OnMemberJoinedAcceptPeer; IsActive = true; if (_lobbyManager.IsInLobby) { AcceptAllLobbyMembers(); } } public async Task SendLargeDataAsync(CSteamID targetId, string transferName, byte[] data, int channel = 0, int? chunkSize = null) { //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) if (data == null) { throw new ArgumentNullException("data"); } string transferId = Guid.NewGuid().ToString("N"); int payloadSize = chunkSize ?? CalculateFileTransferChunkPayloadSize(transferId, transferName, data.Length); if (payloadSize <= 0) { throw new P2PException("Unable to calculate a valid file-transfer chunk size.", SteamNetworkErrorKind.PacketTooLarge, "SendLargeDataAsync", targetId, channel, "FileTransferMessage", data.Length); } int maxPayloadSize = CalculateFileTransferChunkPayloadSize(transferId, transferName, data.Length); if (payloadSize > maxPayloadSize) { throw new P2PException($"Chunk payload too large: {payloadSize} bytes (max: {maxPayloadSize})", SteamNetworkErrorKind.PacketTooLarge, "SendLargeDataAsync", targetId, channel, "FileTransferMessage", payloadSize, maxPayloadSize); } int totalChunks = Math.Max(1, (int)Math.Ceiling((double)data.Length / (double)payloadSize)); for (int i = 0; i < totalChunks; i++) { int offset = i * payloadSize; int length = Math.Min(payloadSize, data.Length - offset); byte[] slice = new byte[length]; if (length > 0) { Array.Copy(data, offset, slice, 0, length); } FileTransferMessage message = new FileTransferMessage { TransferId = transferId, FileName = (transferName ?? string.Empty), FileSize = data.Length, ChunkIndex = i, TotalChunks = totalChunks, IsFileData = true, ChunkData = slice }; if (!(await SendMessageAsync(targetId, message, channel, (EP2PSend)2))) { return false; } } return true; } private int CalculateFileTransferChunkPayloadSize(string transferId, string transferName, int dataLength) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) int maxPacketSize = SteamP2PLimits.GetMaxPacketSize((EP2PSend)2); int num = maxPacketSize; for (int i = 0; i < 4; i++) { int num2 = Math.Max(1, (int)Math.Ceiling((double)Math.Max(dataLength, 1) / (double)Math.Max(num, 1))); FileTransferMessage fileTransferMessage = new FileTransferMessage(); fileTransferMessage.SenderId = GetLocalPlayerId(); fileTransferMessage.TransferId = transferId; fileTransferMessage.FileName = transferName ?? string.Empty; fileTransferMessage.FileSize = dataLength; fileTransferMessage.ChunkIndex = Math.Max(0, num2 - 1); fileTransferMessage.TotalChunks = num2; fileTransferMessage.IsFileData = true; fileTransferMessage.ChunkData = new byte[0]; FileTransferMessage message = fileTransferMessage; int num3 = MessageSerializer.SerializeMessage(message).Length; int num4 = maxPacketSize - num3; if (num4 == num) { break; } num = num4; } return num; } private void OnLobbyJoinedAcceptAllPeers(object? sender, LobbyJoinedEventArgs e) { if (!_disposed) { AcceptAllLobbyMembers(); } } private void OnMemberJoinedAcceptPeer(object? sender, MemberJoinedEventArgs e) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002d: 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 (!_disposed && e.Member != null && !(e.Member.SteamId == _lobbyManager.LocalPlayerID)) { TryAdmitPeer(e.Member.SteamId); } } private void AcceptAllLobbyMembers() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) try { List lobbyMembers = _lobbyManager.GetLobbyMembers(); foreach (SteamNetworkLib.Models.MemberInfo item in lobbyMembers) { if (!(item.SteamId == _lobbyManager.LocalPlayerID)) { TryAdmitPeer(item.SteamId); } } } catch (Exception ex) { Console.WriteLine("[SteamNetworkLib] Error pre-accepting lobby member P2P sessions: " + ex.Message); } } private bool TryAdmitPeer(CSteamID peerId) { //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_001d: 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) string playerName = SteamNetworkUtils.GetPlayerName(peerId); P2PSessionRequestEventArgs p2PSessionRequestEventArgs = new P2PSessionRequestEventArgs(peerId, playerName); if (_rules.AcceptOnlyFriends && !SteamNetworkUtils.IsFriend(peerId)) { p2PSessionRequestEventArgs.ShouldAccept = false; } this.OnSessionRequested?.Invoke(this, p2PSessionRequestEventArgs); if (p2PSessionRequestEventArgs.ShouldAccept) { return AcceptSession(peerId); } return false; } private bool IsInSession() { if (_lobbyManager.IsInLobby) { return true; } if (_isInSessionOverride != null) { try { return _isInSessionOverride(); } catch { return false; } } return false; } private List GetSessionMembers() { if (_lobbyManager.IsInLobby) { return _lobbyManager.GetLobbyMembers(); } if (_memberProviderOverride != null) { try { return _memberProviderOverride() ?? new List(); } catch { return new List(); } } return new List(); } private CSteamID GetLocalPlayerId() { //IL_002a: 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_0032: 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) if (_localPlayerProviderOverride != null) { try { return _localPlayerProviderOverride(); } catch { } } return _lobbyManager.LocalPlayerID; } private async Task EnsureSessionAsync(CSteamID targetId) { //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) if (!_activeSessions.ContainsKey(targetId)) { if (!AcceptSession(targetId)) { throw new P2PException($"Failed to establish P2P session with {targetId}", targetId); } await Task.Delay(50); } } private P2PMessage? CreateCustomMessage(string messageType, byte[] data) { try { if (_customMessageTypes.TryGetValue(messageType, out Type value)) { MethodInfo methodInfo = typeof(MessageSerializer).GetMethod("CreateMessage")?.MakeGenericMethod(value); if (methodInfo != null) { object[] parameters = new object[1] { data }; return (P2PMessage)methodInfo.Invoke(null, parameters); } } else if (_messageHandlers.ContainsKey(messageType)) { Console.WriteLine("[SteamNetworkLib] Received unregistered custom message type '" + messageType + "'. Call RegisterCustomMessageType() during initialization to enable receiving this message type."); } } catch (Exception ex) { Console.WriteLine("[SteamNetworkLib] Error creating custom message of type '" + messageType + "': " + ex.Message); } return null; } private void ProcessReceivedPacket(CSteamID senderId, byte[] data, int channel) { //IL_0056: 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_0021: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) try { _activeSessions[senderId] = DateTime.UtcNow; this.OnPacketReceived?.Invoke(this, new P2PPacketReceivedEventArgs(senderId, data, channel, (uint)data.Length)); if (MessageSerializer.IsValidMessage(data)) { ProcessSteamNetworkLibMessage(senderId, data, channel); } } catch (Exception ex) { Console.WriteLine($"Error processing received packet from {senderId}: {ex.Message}"); } } private void ProcessSteamNetworkLibMessage(CSteamID senderId, byte[] data, int channel) { //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) try { string messageType = MessageSerializer.GetMessageType(data); if (string.IsNullOrEmpty(messageType)) { return; } P2PMessage p2PMessage = null; if (1 == 0) { } P2PMessage p2PMessage2 = messageType switch { "TEXT" => MessageSerializer.CreateMessage(data), "DATA_SYNC" => MessageSerializer.CreateMessage(data), "FILE_TRANSFER" => MessageSerializer.CreateMessage(data), "STREAM" => MessageSerializer.CreateMessage(data), "HEARTBEAT" => MessageSerializer.CreateMessage(data), "EVENT" => MessageSerializer.CreateMessage(data), _ => CreateCustomMessage(messageType, data), }; if (1 == 0) { } p2PMessage = p2PMessage2; if (p2PMessage == null) { return; } this.OnMessageReceived?.Invoke(this, new P2PMessageReceivedEventArgs(p2PMessage, senderId, channel)); if (!_messageHandlers.TryGetValue(messageType, out List> value)) { return; } foreach (Action item in value) { try { item(p2PMessage, senderId); } catch (Exception ex) { Console.WriteLine("Error in message handler for " + messageType + ": " + ex.Message); } } } catch (Exception ex2) { Console.WriteLine("Error processing SteamNetworkLib message: " + ex2.Message); } } private void OnSessionRequestCallback(P2PSessionRequest_t result) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) try { TryAdmitPeer(result.m_steamIDRemote); } catch (Exception ex) { Console.WriteLine("Error in session request callback: " + ex.Message); } } public void UpdateRules(NetworkRules rules) { if (rules == null) { return; } _rules = rules; try { SteamNetworking.AllowP2PPacketRelay(_rules.EnableRelay); } catch { } } private void OnSessionConnectFailCallback(P2PSessionConnectFail_t result) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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_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) try { CSteamID steamIDRemote = result.m_steamIDRemote; EP2PSessionError error = (EP2PSessionError)result.m_eP2PSessionError; _activeSessions.Remove(steamIDRemote); this.OnSessionConnectFail?.Invoke(this, new P2PSessionConnectFailEventArgs(steamIDRemote, error)); } catch (Exception ex) { Console.WriteLine("Error in session connect fail callback: " + ex.Message); } } public void Dispose() { //IL_0065: 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_006d: Unknown result type (might be due to invalid IL or missing references) if (_disposed) { return; } try { IsActive = false; _lobbyManager.OnLobbyJoined -= OnLobbyJoinedAcceptAllPeers; _lobbyManager.OnMemberJoined -= OnMemberJoinedAcceptPeer; foreach (CSteamID item in _activeSessions.Keys.ToList()) { CloseSession(item); } _sessionRequestCallback?.Dispose(); _sessionConnectFailCallback?.Dispose(); _messageHandlers.Clear(); _activeSessions.Clear(); _sendQueue.Clear(); } catch (Exception ex) { Console.WriteLine("Error disposing SteamP2PManager: " + ex.Message); } _disposed = true; } } }