Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of SteamNetworkLib Il2Cpp v1.5.0
UserLibs\SteamNetworkLib-IL2Cpp.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading.Tasks; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSteamworks; using Microsoft.CodeAnalysis; using SteamNetworkLib.Core; using SteamNetworkLib.Events; using SteamNetworkLib.Exceptions; using SteamNetworkLib.Models; using SteamNetworkLib.Sync; using SteamNetworkLib.Utilities; [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("Il2cpp")] [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<IDisposable> _syncVars = new List<IDisposable>(); private readonly Dictionary<ulong, SteamNetworkLib.Models.MemberInfo> _virtualMembers = new Dictionary<ulong, SteamNetworkLib.Models.MemberInfo>(); private readonly Dictionary<string, string> _virtualLobbyData = new Dictionary<string, string>(); private readonly Dictionary<ulong, Dictionary<string, string>> _virtualMemberData = new Dictionary<ulong, Dictionary<string, string>>(); 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<VersionMismatchEventArgs>? OnVersionMismatch; public event EventHandler<LobbyJoinedEventArgs>? OnLobbyJoined; public event EventHandler<LobbyCreatedEventArgs>? OnLobbyCreated; public event EventHandler<LobbyLeftEventArgs>? OnLobbyLeft; public event EventHandler<MemberJoinedEventArgs>? OnMemberJoined; public event EventHandler<MemberLeftEventArgs>? OnMemberLeft; public event EventHandler<LobbyDataChangedEventArgs>? OnLobbyDataChanged; public event EventHandler<MemberDataChangedEventArgs>? OnMemberDataChanged; public event EventHandler<P2PMessageReceivedEventArgs>? 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<LobbyInfo> 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<LobbyInfo> 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<SteamNetworkLib.Models.MemberInfo> GetLobbyMembers() { EnsureInitialized(); if (_sessionMode == NetworkSessionMode.DedicatedRelay) { return _virtualMembers.Values.Select(CloneMember).ToList(); } return LobbyManager.GetLobbyMembers(); } public List<SteamNetworkLib.Models.MemberInfo> 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<CSteamID, string> 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<CSteamID, string> dictionary = new Dictionary<CSteamID, string>(); 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<string, string> data) { EnsureInitialized(); if (_sessionMode == NetworkSessionMode.DedicatedRelay) { if (data == null || data.Count == 0) { return; } { foreach (KeyValuePair<string, string> datum in data) { SetMyData(datum.Key, datum.Value); } return; } } MemberData.SetMemberDataBatch(data); } public async Task<bool> 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<bool> 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<SteamNetworkLib.Models.MemberInfo> members = GetLobbyMembers(); List<Task<bool>> sendTasks = new List<Task<bool>>(); 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<bool> 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<bool> 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<T>(Action<T, CSteamID> handler) where T : P2PMessage, new() { EnsureInitialized(); P2PManager.RegisterMessageHandler(handler); } public void ProcessIncomingMessages() { if (_isInitialized) { AttachDedicatedBridgeIfAvailable(); UpdateSessionMode(forceApplyOverrides: false); TryRegisterDedicatedSession(force: false); try { SteamAPI.RunCallbacks(); } catch (Exception ex) { Console.WriteLine("[SteamNetworkLib] Error running Steam callbacks: " + ex.Message); } 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<CSteamID, string> 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<CSteamID, string> dataForAllPlayers = GetDataForAllPlayers("__snl_version"); if (dataForAllPlayers.Count == 0) { return true; } List<string> list = dataForAllPlayers.Values.Distinct().ToList(); if (list.Count <= 1) { return true; } string libraryVersion = LibraryVersion; List<CSteamID> list2 = new List<CSteamID>(); foreach (KeyValuePair<CSteamID, string> 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<CSteamID, string> kvp) => $"{kvp.Key}:{kvp.Value}")); Console.WriteLine("[SteamNetworkLib] WARNING: Version mismatch detected! Local version: " + libraryVersion + ", Player versions: " + text); return false; } public Dictionary<CSteamID, string> GetPlayerLibraryVersions() { EnsureInitialized(); return GetDataForAllPlayers("__snl_version"); } public HostSyncVar<T> CreateHostSyncVar<T>(string key, T defaultValue, NetworkSyncOptions? options = null, ISyncValidator<T>? validator = null) { EnsureInitialized(); HostSyncVar<T> hostSyncVar = new HostSyncVar<T>(this, key, defaultValue, options, validator); _syncVars.Add(hostSyncVar); return hostSyncVar; } public ClientSyncVar<T> CreateClientSyncVar<T>(string key, T defaultValue, NetworkSyncOptions? options = null, ISyncValidator<T>? validator = null) { EnsureInitialized(); ClientSyncVar<T> clientSyncVar = new ClientSyncVar<T>(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<DedicatedCompatibilityProtocol.SnapshotPayload>(data ?? string.Empty); } catch { return; } if (snapshotPayload == null) { return; } Dictionary<ulong, SteamNetworkLib.Models.MemberInfo> dictionary = new Dictionary<ulong, SteamNetworkLib.Models.MemberInfo>(_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<string, string> lobbyDatum in snapshotPayload.LobbyData) { _virtualLobbyData[lobbyDatum.Key] = lobbyDatum.Value ?? string.Empty; } } if (snapshotPayload.MemberData != null) { foreach (KeyValuePair<string, Dictionary<string, string>> memberDatum in snapshotPayload.MemberData) { if (!TryParseSteamId(memberDatum.Key, out var steamId)) { continue; } Dictionary<string, string> dictionary2 = new Dictionary<string, string>(); if (memberDatum.Value != null) { foreach (KeyValuePair<string, string> 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<ulong, SteamNetworkLib.Models.MemberInfo> virtualMember in _virtualMembers) { if (!dictionary.ContainsKey(virtualMember.Key) && virtualMember.Value.SteamId != LocalPlayerId) { this.OnMemberJoined?.Invoke(this, new MemberJoinedEventArgs(CloneMember(virtualMember.Value))); } } foreach (KeyValuePair<ulong, SteamNetworkLib.Models.MemberInfo> 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<DedicatedCompatibilityProtocol.MemberJoinedPayload>(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<DedicatedCompatibilityProtocol.MemberLeftPayload>(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<DedicatedCompatibilityProtocol.LobbyDataChangedPayload>(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<DedicatedCompatibilityProtocol.MemberDataChangedPayload>(data ?? string.Empty); if (memberDataChangedPayload != null && !string.IsNullOrEmpty(memberDataChangedPayload.MemberSteamId) && !string.IsNullOrEmpty(memberDataChangedPayload.Key) && TryParseSteamId(memberDataChangedPayload.MemberSteamId, out var steamId)) { Dictionary<string, string> 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<DedicatedCompatibilityProtocol.P2PMessagePayload>(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<bool> 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<string, string> value)) { return null; } if (!value.TryGetValue(key, out var value2) || string.IsNullOrEmpty(value2)) { return null; } return value2; } private Dictionary<string, string> GetOrCreateVirtualMemberMap(ulong playerSteamId) { if (!_virtualMemberData.TryGetValue(playerSteamId, out Dictionary<string, string> value)) { value = new Dictionary<string, string>(); _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<ulong, SteamNetworkLib.Models.MemberInfo> 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<Task> action, string operation) { Func<Task> 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 => false; public static string RuntimeType => "IL2CPP"; 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<T>(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 string BytesToHexString(byte[] bytes) { StringBuilder stringBuilder = new StringBuilder(bytes.Length * 3); foreach (byte b in bytes) { stringBuilder.Append(b.ToString("X2")); stringBuilder.Append(' '); } return stringBuilder.ToString(); } } 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<T> : IDisposable { private readonly SteamNetworkClient _client; private readonly string _key; private readonly string _fullKey; private readonly NetworkSyncOptions _options; private readonly ISyncSerializer _serializer; private readonly ISyncValidator<T>? _validator; private readonly T _defaultValue; private T _localValue; private readonly Dictionary<ulong, T> _playerValues = new Dictionary<ulong, T>(); 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<CSteamID, T, T>? OnValueChanged; public event Action<T, T>? OnMyValueChanged; public event Action<Exception>? OnSyncError; internal ClientSyncVar(SteamNetworkClient client, string key, T defaultValue, NetworkSyncOptions? options = null, ISyncValidator<T>? 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<T>, Dictionary<string, T>, 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<T>(playerData); _playerValues[playerId.m_SteamID] = val; return val; } } catch (Exception obj) { this.OnSyncError?.Invoke(obj); } } return _defaultValue; } public Dictionary<CSteamID, T> 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<CSteamID, T> dictionary = new Dictionary<CSteamID, T>(); if (!_client.IsInLobby) { return dictionary; } List<SteamNetworkLib.Models.MemberInfo> 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<SteamNetworkLib.Models.MemberInfo> 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<T>(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<T>(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<T>(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<T> : IDisposable { private readonly SteamNetworkClient _client; private readonly string _key; private readonly string _fullKey; private readonly NetworkSyncOptions _options; private readonly ISyncSerializer _serializer; private readonly ISyncValidator<T>? _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<T, T>? OnValueChanged; public event Action<T>? OnWriteIgnored; public event Action<Exception>? OnSyncError; internal HostSyncVar(SteamNetworkClient client, string key, T defaultValue, NetworkSyncOptions? options = null, ISyncValidator<T>? 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<T>, Dictionary<string, T>, 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<T>(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<T>(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>(T value); T Deserialize<T>(string data); bool CanSerialize(Type type); } public interface ISyncValidator<T> { bool IsValid(T value); string? GetErrorMessage(T value); } public class PredicateValidator<T> : ISyncValidator<T> { private readonly Func<T, bool> _predicate; private readonly string _errorMessage; public PredicateValidator(Func<T, bool> 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<T> : ISyncValidator<T> where T : IComparable<T> { 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<T> : ISyncValidator<T> { private readonly ISyncValidator<T>[] _validators; public CompositeValidator(params ISyncValidator<T>[] validators) { if (validators == null) { throw new ArgumentNullException("validators"); } _validators = validators; } public bool IsValid(T value) { ISyncValidator<T>[] validators = _validators; foreach (ISyncValidator<T> syncValidator in validators) { if (!syncValidator.IsValid(value)) { return false; } } return true; } public string? GetErrorMessage(T value) { ISyncValidator<T>[] validators = _validators; foreach (ISyncValidator<T> syncValidator in validators) { if (!syncValidator.IsValid(value)) { return syncValidator.GetErrorMessage(value); } } return null; } } public class JsonSyncSerializer : ISyncSerializer { public string Serialize<T>(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<T>(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<Type>()); } private bool CanSerializeType(Type type, HashSet<Type> 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<string> 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<string> 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<string, string> dictionary2 = ParseJsonObject(json); foreach (KeyValuePair<string, string> item2 in dictionary2) { dictionary[item2.Key] = DeserializeValue(item2.Value, type3); } return dictionary; } } if (type.IsClass || type.IsValueType) { object obj = Activator.CreateInstance(type); Dictionary<string, string> 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<string> ParseJsonArray(string json) { List<string> list = new List<string>(); 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<string, string> ParseJsonObject(string json) { Dictionary<string, string> dictionary = new Dictionary<string, string>(); 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) { }