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.0.0
UserLibs/SteamNetworkLib-IL2Cpp.dll
Decompiled 3 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; 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.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.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("SteamNetworkLib")] [assembly: AssemblyTitle("SteamNetworkLib")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.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 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 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 => LobbyManager?.IsInLobby ?? false; public bool IsHost => LobbyManager?.IsHost ?? false; public CSteamID LocalPlayerId => LobbyManager?.LocalPlayerID ?? CSteamID.Nil; public LobbyInfo? CurrentLobby => LobbyManager?.CurrentLobby; 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 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."); } LobbyManager = new SteamLobbyManager(); LobbyData = new SteamLobbyData(LobbyManager); MemberData = new SteamMemberData(LobbyManager); P2PManager = new SteamP2PManager(LobbyManager); SubscribeToEvents(); _isInitialized = true; return true; } catch (Exception ex) { throw new SteamNetworkException("Failed to initialize SteamNetworkClient: " + ex.Message, ex); } } 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(); 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(); return await LobbyManager.JoinLobbyAsync(lobbyId); } public void LeaveLobby() { EnsureInitialized(); LobbyManager.LeaveLobby(); } public List<SteamNetworkLib.Models.MemberInfo> GetLobbyMembers() { EnsureInitialized(); return LobbyManager.GetLobbyMembers(); } public void InviteFriend(CSteamID friendId) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) EnsureInitialized(); LobbyManager.InviteFriend(friendId); } public void OpenInviteDialog() { EnsureInitialized(); LobbyManager.OpenInviteDialog(); } public void SetLobbyData(string key, string value) { EnsureInitialized(); LobbyData.SetData(key, value); } public string? GetLobbyData(string key) { EnsureInitialized(); return LobbyData.GetData(key); } public void SetMyData(string key, string value) { EnsureInitialized(); MemberData.SetMemberData(key, value); } public string? GetMyData(string key) { EnsureInitialized(); return MemberData.GetMemberData(key); } public string? GetPlayerData(CSteamID playerId, string key) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) EnsureInitialized(); return MemberData.GetMemberData(playerId, key); } public Dictionary<CSteamID, string> GetDataForAllPlayers(string key) { EnsureInitialized(); return MemberData.GetMemberDataForAllPlayers(key); } public void SetMyDataBatch(Dictionary<string, string> data) { EnsureInitialized(); 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 BroadcastMessageAsync(P2PMessage message) { EnsureInitialized(); List<SteamNetworkLib.Models.MemberInfo> members = LobbyManager.GetLobbyMembers(); List<Task<bool>> sendTasks = new List<Task<bool>>(); foreach (SteamNetworkLib.Models.MemberInfo member in members) { if (member.SteamId != LobbyManager.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) { 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"); } private void SubscribeToEvents() { LobbyManager.OnLobbyJoined += delegate(object s, LobbyJoinedEventArgs e) { this.OnLobbyJoined?.Invoke(this, e); }; LobbyManager.OnLobbyCreated += delegate(object s, LobbyCreatedEventArgs e) { this.OnLobbyCreated?.Invoke(this, e); }; LobbyManager.OnLobbyLeft += delegate(object s, LobbyLeftEventArgs e) { 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); }; 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"); }; MemberData.OnMemberDataChanged += delegate(object s, MemberDataChangedEventArgs e) { if (e.Key == "__snl_version") { SafeExecute(delegate { CheckLibraryVersionCompatibility(); }, "checking version compatibility on data change"); } }; } 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 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 { UnsubscribeFromEvents(); P2PManager?.Dispose(); MemberData?.Dispose(); LobbyData?.Dispose(); LobbyManager?.Dispose(); } 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 { private const int MAX_MESSAGE_SIZE = 4096; 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; if (num > 4096) { throw new P2PException($"Message too large: {num} bytes (max: {4096})"); } 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)) { throw new P2PException("Failed to serialize message: " + ex.Message, ex); } } public static (string MessageType, byte[] MessageData) DeserializeMessage(byte[] data) { try { if (data.Length < 6) { string message = $"Invalid message data: too short ({data.Length} bytes)"; throw new P2PException(message); } 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) + "'"; throw new P2PException(message2); } 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})"; throw new P2PException(message3); } 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)) { throw new P2PException("Failed to deserialize message: " + ex.Message, ex); } } 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) { throw new P2PException("Message type mismatch: expected '" + val.MessageType + "', got '" + item + "'"); } val.Deserialize(item2); return val; } catch (Exception ex) when (!(ex is P2PException)) { throw new P2PException("Failed to create message of type " + typeof(T).Name + ": " + ex.Message, ex); } } 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) { 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) { 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() { try { return SteamAPI.IsSteamRunning(); } 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_0055: 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_005d: 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) { 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.Streaming { public abstract class StreamChannel<T> : IDisposable where T : class { protected readonly SortedDictionary<uint, StreamFrame<T>> _jitterBuffer = new SortedDictionary<uint, StreamFrame<T>>(); protected uint _expectedSequence = 0u; protected uint _highestSequenceReceived = 0u; protected bool _isStreaming = false; protected bool _isDisposed = false; protected DateTime _lastFrameTime = DateTime.UtcNow; protected readonly object _bufferLock = new object(); public string StreamId { get; } public int BufferMs { get; set; } = 200; public int MaxBufferMs { get; set; } = 500; public bool EnablePacketLossDetection { get; set; } = true; public bool EnableJitterBuffer { get; set; } = true; public int BufferedFrameCount => _jitterBuffer.Count; public uint TotalFramesReceived { get; private set; } public uint TotalFramesDropped { get; private set; } public uint TotalFramesLate { get; private set; } public double AverageJitter { get; private set; } public event Action<T>? OnFrameReady; public event Action<uint>? OnFrameDropped; public event Action<StreamChannel<T>>? OnStreamStarted; public event Action<StreamChannel<T>>? OnStreamEnded; public event Action<uint, TimeSpan>? OnFrameLate; protected StreamChannel(string streamId) { StreamId = streamId ?? throw new ArgumentNullException("streamId"); } public virtual void ProcessStreamMessage(StreamMessage message, CSteamID senderId) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) if (_isDisposed || message.StreamId != StreamId) { return; } DateTime utcNow = DateTime.UtcNow; try { if (message.IsStreamStart && !_isStreaming) { Console.WriteLine($"[StreamChannel] Stream {StreamId} started from {senderId}"); StartStream(); } else if (message.IsStreamEnd && _isStreaming) { Console.WriteLine($"[StreamChannel] Stream {StreamId} ended from {senderId}"); EndStream(); return; } TotalFramesReceived++; _highestSequenceReceived = Math.Max(_highestSequenceReceived, message.SequenceNumber); if (_expectedSequence == 0 && message.SequenceNumber != 0) { _expectedSequence = message.SequenceNumber; Console.WriteLine($"[StreamChannel] Setting initial expected sequence to {_expectedSequence}"); } if (message.SequenceNumber < _expectedSequence) { Console.WriteLine($"[StreamChannel] Ignoring old/duplicate frame {message.SequenceNumber} (expected: {_expectedSequence})"); return; } if (message.SequenceNumber > _expectedSequence && TotalFramesReceived > 10) { uint num = message.SequenceNumber - _expectedSequence; Console.WriteLine($"[StreamChannel] Sequence gap detected: got {message.SequenceNumber}, expected {_expectedSequence} (gap: {num})"); } long num2 = (utcNow.Ticks - message.CaptureTimestamp) / 10000; if (message.StreamData == null || message.StreamData.Length == 0) { Console.WriteLine($"[StreamChannel] Warning: Empty StreamData received in frame {message.SequenceNumber}"); } else if (message.StreamData.Length > 4 && message.StreamData[0] == 0 && message.StreamData[1] == 0 && message.StreamData[2] == 0 && message.StreamData[3] == 0) { Console.WriteLine($"[StreamChannel] IL2CPP marshalling issue detected! StreamData contains all zeros in frame {message.SequenceNumber}"); string text = ""; for (int i = 0; i < Math.Min(message.StreamData.Length, 8); i++) { text = text + message.StreamData[i].ToString("X2") + " "; } Console.WriteLine("[StreamChannel] First bytes: " + text); TotalFramesDropped++; return; } DateTime utcNow2 = DateTime.UtcNow; T val = DeserializeFrame(message.StreamData, message); double totalMilliseconds = (DateTime.UtcNow - utcNow2).TotalMilliseconds; if (val == null) { Console.WriteLine($"[StreamChannel] Failed to deserialize frame {message.SequenceNumber}"); return; } StreamFrame<T> streamFrame = new StreamFrame<T> { SequenceNumber = message.SequenceNumber, Data = val, CaptureTimestamp = message.CaptureTimestamp, ReceivedTimestamp = utcNow.Ticks, FrameDurationMs = message.FrameDurationMs, IsRetransmit = message.IsRetransmit }; if (TotalFramesReceived % 50 == 0) { Console.WriteLine($"[StreamChannel] Frame {message.SequenceNumber}: End-to-end latency: {num2}ms, Deserialize time: {totalMilliseconds:F2}ms, Buffer level: {_jitterBuffer.Count}"); } if (EnableJitterBuffer) { AddToJitterBuffer(streamFrame); return; } this.OnFrameReady?.Invoke(streamFrame.Data); _expectedSequence = message.SequenceNumber + 1; } catch (Exception ex) { Console.WriteLine("[StreamChannel] Error processing stream message: " + ex.Message); } } public virtual void Update() { if (_isDisposed || !EnableJitterBuffer) { return; } lock (_bufferLock) { ProcessJitterBuffer(); CleanupOldFrames(); DetectLostFrames(); } } protected abstract T? DeserializeFrame(byte[] data, StreamMessage message); protected virtual T? HandleMissingFrame(uint sequenceNumber) { return null; } private void AddToJitterBuffer(StreamFrame<T> frame) { lock (_bufferLock) { if (frame.SequenceNumber >= _expectedSequence) { _jitterBuffer[frame.SequenceNumber] = frame; int num = Math.Max(5, MaxBufferMs / frame.FrameDurationMs); while (_jitterBuffer.Count > num) { KeyValuePair<uint, StreamFrame<T>> keyValuePair = _jitterBuffer.First(); _jitterBuffer.Remove(keyValuePair.Key); TotalFramesDropped++; this.OnFrameDropped?.Invoke(keyValuePair.Key); Console.WriteLine($"[StreamBuffer] Dropped old frame {keyValuePair.Key} - buffer too large ({_jitterBuffer.Count}/{num})"); } } } } private void ProcessJitterBuffer() { DateTime utcNow = DateTime.UtcNow; int num = ((_jitterBuffer.Count > 0) ? _jitterBuffer.Values.First().FrameDurationMs : 40); int num2 = Math.Max(2, BufferMs / num); if (_jitterBuffer.Count < num2 && _expectedSequence < 10) { if (_expectedSequence == 0 && _jitterBuffer.Count > 0) { Console.WriteLine($"[StreamBuffer] Building initial buffer: {_jitterBuffer.Count}/{num2} frames ({num}ms frames)"); } return; } if (_jitterBuffer.Count == 0 && _isStreaming) { Console.WriteLine("[StreamBuffer] Buffer underrun! Waiting for frames to rebuild buffer..."); return; } int num3 = 0; while (_jitterBuffer.ContainsKey(_expectedSequence) && num3 < 3) { StreamFrame<T> streamFrame = _jitterBuffer[_expectedSequence]; _jitterBuffer.Remove(_expectedSequence); DateTime dateTime = _lastFrameTime.AddMilliseconds(streamFrame.FrameDurationMs); DateTime utcNow2 = DateTime.UtcNow; if (utcNow2 > dateTime.AddMilliseconds(BufferMs)) { TotalFramesLate++; TimeSpan arg = utcNow2 - dateTime; this.OnFrameLate?.Invoke(_expectedSequence, arg); if (arg.TotalMilliseconds > 60.0) { Console.WriteLine($"[StreamBuffer] Frame {_expectedSequence} is {arg.TotalMilliseconds:F1}ms late!"); } } if (_expectedSequence % 100 == 0) { int count = _jitterBuffer.Count; double totalMilliseconds = (DateTime.UtcNow - utcNow).TotalMilliseconds; Console.WriteLine($"[StreamBuffer] Processed frame {_expectedSequence}, buffer level: {count}, processing time: {totalMilliseconds:F2}ms"); } this.OnFrameReady?.Invoke(streamFrame.Data); _lastFrameTime = utcNow2; _expectedSequence++; num3++; } if (!EnablePacketLossDetection || _jitterBuffer.ContainsKey(_expectedSequence)) { return; } uint num4 = _jitterBuffer.Keys.FirstOrDefault((uint seq) => seq > _expectedSequence); if (num4 == 0) { return; } uint num5 = num4 - _expectedSequence; if (num5 <= 3) { T val = HandleMissingFrame(_expectedSequence); if (val != null) { this.OnFrameReady?.Invoke(val); Console.WriteLine($"[StreamBuffer] Used PLC for missing frame {_expectedSequence}"); } else { TotalFramesDropped++; this.OnFrameDropped?.Invoke(_expectedSequence); } _expectedSequence++; } else { Console.WriteLine($"[StreamBuffer] Large gap detected ({num5} frames), skipping to frame {num4}"); for (uint num6 = _expectedSequence; num6 < num4; num6++) { TotalFramesDropped++; this.OnFrameDropped?.Invoke(num6); } _expectedSequence = num4; } } private void CleanupOldFrames() { DateTime cutoffTime = DateTime.UtcNow.AddMilliseconds(-MaxBufferMs); long ticks = cutoffTime.Ticks; List<uint> list = (from kvp in _jitterBuffer where new DateTime(kvp.Value.ReceivedTimestamp) < cutoffTime select kvp.Key).ToList(); foreach (uint item in list) { _jitterBuffer.Remove(item); TotalFramesDropped++; this.OnFrameDropped?.Invoke(item); } } private void DetectLostFrames() { if (_jitterBuffer.Count <= 0) { return; } uint num = _jitterBuffer.Keys.Min(); uint num2 = num - _expectedSequence; int num3 = Math.Max(5, BufferMs / 40); if (num2 > num3) { Console.WriteLine($"[StreamBuffer] Large gap detected: {num2} frames (max: {num3}), jumping to frame {num}"); for (uint num4 = _expectedSequence; num4 < num; num4++) { TotalFramesDropped++; this.OnFrameDropped?.Invoke(num4); } _expectedSequence = num; } } private void StartStream() { _isStreaming = true; _expectedSequence = 0u; _highestSequenceReceived = 0u; _lastFrameTime = DateTime.UtcNow; this.OnStreamStarted?.Invoke(this); } private void EndStream() { _isStreaming = false; this.OnStreamEnded?.Invoke(this); } public virtual void Reset() { lock (_bufferLock) { _jitterBuffer.Clear(); _expectedSequence = 0u; _highestSequenceReceived = 0u; TotalFramesReceived = 0u; TotalFramesDropped = 0u; TotalFramesLate = 0u; _isStreaming = false; } } public virtual void Dispose() { if (!_isDisposed) { lock (_bufferLock) { _jitterBuffer.Clear(); } _isDisposed = true; } } } public class StreamFrame<T> where T : class { public uint SequenceNumber { get; set; } public T Data { get; set; } = null; public long CaptureTimestamp { get; set; } public long ReceivedTimestamp { get; set; } public int FrameDurationMs { get; set; } = 20; public bool IsRetransmit { get; set; } } public abstract class StreamSender<T> : IDisposable where T : class { protected uint _currentSequence = 0u; protected bool _isDisposed = false; protected SteamNetworkClient? _networkClient; public string StreamId { get; } public string StreamType { get; protected set; } = "data"; public string PayloadType { get; protected set; } = "data"; public int FrameDurationMs { get; protected set; } = 20; public bool IsStreaming { get; private set; } public uint TotalFramesSent { get; private set; } public uint TotalBytesSent { get; private set; } public event Action<StreamSender<T>>? OnStreamStarted; public event Action<StreamSender<T>>? OnStreamEnded; public event Action<uint, int>? OnFrameSent; protected StreamSender(string streamId, SteamNetworkClient? networkClient) { StreamId = streamId ?? throw new ArgumentNullException("streamId"); _networkClient = networkClient; } public virtual void StartStream() { if (!IsStreaming && !_isDisposed) { IsStreaming = true; _currentSequence = 0u; StreamMessage message = CreateStreamMessage(new byte[0], isStart: true, isEnd: false); SendStreamMessage(message); this.OnStreamStarted?.Invoke(this); } } public virtual void StopStream() { if (IsStreaming && !_isDisposed) { StreamMessage message = CreateStreamMessage(new byte[0], isStart: false, isEnd: true); SendStreamMessage(message); IsStreaming = false; this.OnStreamEnded?.Invoke(this); } } protected virtual void SendFrame(T frameData) { if (!IsStreaming || _isDisposed || frameData == null) { return; } try { byte[] array = SerializeFrame(frameData); if (array != null && array.Length != 0) { StreamMessage message = CreateStreamMessage(array, isStart: false, isEnd: false); SendStreamMessage(message); TotalFramesSent++; TotalBytesSent += (uint)array.Length; this.OnFrameSent?.Invoke(_currentSequence - 1, array.Length); } } catch (Exception ex) { Console.WriteLine("Error sending frame: " + ex.Message); } } protected abstract byte[]? SerializeFrame(T frameData); protected virtual StreamMessage CreateStreamMessage(byte[] data, bool isStart, bool isEnd) { //IL_008c: Unknown result type (might be due to invalid IL or missing references) return new StreamMessage { StreamType = StreamType, StreamId = StreamId, SequenceNumber = _currentSequence++, CaptureTimestamp = DateTime.UtcNow.Ticks, IsStreamStart = isStart, IsStreamEnd = isEnd, StreamData = data, PayloadType = PayloadType, FrameDurationMs = FrameDurationMs, Priority = 128, RecommendedSendType = GetRecommendedSendType() }; } protected virtual EP2PSend GetRecommendedSendType() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) return (EP2PSend)1; } protected virtual void SendStreamMessage(StreamMessage message) { if (_networkClient == null) { Console.WriteLine("[StreamSender] Warning: No network client available for sending stream message"); return; } try { DateTime utcNow = DateTime.UtcNow; _networkClient.BroadcastMessage(message); double totalMilliseconds = (DateTime.UtcNow - utcNow).TotalMilliseconds; if (totalMilliseconds > 10.0) { Console.WriteLine($"[StreamSender] Network broadcast took {totalMilliseconds:F2}ms (slow!) for frame {message.SequenceNumber}"); } else if (message.SequenceNumber % 100 == 0) { Console.WriteLine($"[StreamSender] Frame {message.SequenceNumber}: Network send took {totalMilliseconds:F2}ms"); } } catch (Exception ex) { Console.WriteLine("[StreamSender] Error sending stream message: " + ex.Message); } } protected virtual async Task<bool> SendFrameToTarget(T frameData, CSteamID targetId) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (!IsStreaming || _isDisposed || frameData == null || _networkClient == null) { return false; } try { byte[] encodedData = SerializeFrame(frameData); if (encodedData == null || encodedData.Length == 0) { return false; } StreamMessage message = CreateStreamMessage(encodedData, isStart: false, isEnd: false); return await _networkClient.SendMessageToPlayerAsync(targetId, message); } catch (Exception ex) { Console.WriteLine("Error sending frame to target: " + ex.Message); return false; } } public virtual void Reset() { _currentSequence = 0u; TotalFramesSent = 0u; TotalBytesSent = 0u; IsStreaming = false; } public virtual void Dispose() { if (!_isDisposed) { if (IsStreaming) { StopStream(); } _isDisposed = true; } } } } namespace SteamNetworkLib.Models { public class DataSyncMessage : P2PMessage { public override string MessageType => "DATA_SYNC"; public string Key { get; set; } = string.Empty; public string Value { get; set; } = string.Empty; public string DataType { get; set; } = "string"; public override byte[] Serialize() { string text = Key.Replace("\"", "\\\""); string text2 = Value.Replace("\"", "\\\""); string text3 = DataType.Replace("\"", "\\\""); string s = "{" + CreateJsonBase("\"Key\":\"" + text + "\",\"Value\":\"" + text2 + "\",\"DataType\":\"" + text3 + "\"") + "}"; return Encoding.UTF8.GetBytes(s); } public override void Deserialize(byte[] data) { try { string @string = Encoding.UTF8.GetString(data); ParseJsonBase(@string); Key = ExtractJsonValue(@string, "Key"); Value = ExtractJsonValue(@string, "Value"); DataType = ExtractJsonValue(@string, "DataType"); } catch (Exception) { throw; } } } public class EventMessage : P2PMessage { public override string MessageType => "EVENT"; public string EventType { get; set; } = "user"; public string EventName { get; set; } = string.Empty; public string EventData { get; set; } = string.Empty; public int Priority { get; set; } = 1; public bool ShouldPersist { get; set; } public string TargetAudience { get; set; } = "all"; public string TargetPlayerIds { get; set; } = string.Empty; public bool RequiresAck { get; set; } public string EventId { get; set; } = Guid.NewGuid().ToString(); public DateTime? ExpiresAt { get; set; } public string Tags { get; set; } = string.Empty; public override byte[] Serialize() { string text = ExpiresAt?.ToString("O") ?? ""; string s = "{" + CreateJsonBase($"\"EventType\":\"{EventType}\",\"EventName\":\"{EventName}\",\"EventData\":\"{EventData}\",\"Priority\":{Priority},\"ShouldPersist\":{ShouldPersist.ToString().ToLower()},\"TargetAudience\":\"{TargetAudience}\",\"TargetPlayerIds\":\"{TargetPlayerIds}\",\"RequiresAck\":{RequiresAck.ToString().ToLower()},\"EventId\":\"{EventId}\",\"ExpiresAt\":\"{text}\",\"Tags\":\"{Tags}\"") + "}"; return Encoding.UTF8.GetBytes(s); } public override void Deserialize(byte[] data) { string @string = Encoding.UTF8.GetString(data); ParseJsonBase(@string); EventType = ExtractJsonValue(@string, "EventType"); EventName = ExtractJsonValue(@string, "EventName"); EventData = ExtractJsonValue(@string, "EventData"); if (int.TryParse(ExtractJsonValue(@string, "Priority"), out var result)) { Priority = result; } ShouldPersist = ExtractJsonValue(@string, "ShouldPersist").ToLower() == "true"; TargetAudience = ExtractJsonValue(@string, "TargetAudience"); TargetPlayerIds = ExtractJsonValue(@string, "TargetPlayerIds"); RequiresAck = ExtractJsonValue(@string, "RequiresAck").ToLower() == "true"; EventId = ExtractJsonValue(@string, "EventId"); string text = ExtractJsonValue(@string, "ExpiresAt"); if (!string.IsNullOrEmpty(text) && DateTime.TryParse(text, out var result2)) { ExpiresAt = result2; } Tags = ExtractJsonValue(@string, "Tags"); } } public class FileTransferMessage : P2PMessage { public override string MessageType => "FILE_TRANSFER"; public string FileName { get; set; } = string.Empty; public int FileSize { get; set; } public int ChunkIndex { get; set; } public int TotalChunks { get; set; } public bool IsFileData { get; set; } public byte[] ChunkData { get; set; } = new byte[0]; public override byte[] Serialize() { string s = "{" + CreateJsonBase($"\"FileName\":\"{FileName}\",\"FileSize\":{FileSize},\"ChunkIndex\":{ChunkIndex},\"TotalChunks\":{TotalChunks},\"IsFileData\":{IsFileData.ToString().ToLower()}") + "}"; byte[] bytes = Encoding.UTF8.GetBytes(s); byte[] array = new byte[4 + bytes.Length + ChunkData.Length]; int num = 0; BitConverter.GetBytes(bytes.Length).CopyTo(array, num); num += 4; bytes.CopyTo(array, num); num += bytes.Length; ChunkData.CopyTo(array, num); return array; } public override void Deserialize(byte[] data) { if (data.Length < 4) { return; } int num = BitConverter.ToInt32(data, 0); if (num > 0 && num <= data.Length - 4) { string @string = Encoding.UTF8.GetString(data, 4, num); ParseJsonBase(@string); FileName = ExtractJsonValue(@string, "FileName"); if (int.TryParse(ExtractJsonValue(@string, "FileSize"), out var result)) { FileSize = result; } if (int.TryParse(ExtractJsonValue(@string, "ChunkIndex"), out var result2)) { ChunkIndex = result2; } if (int.TryParse(ExtractJsonValue(@string, "TotalChunks"), out var result3)) { TotalChunks = result3; } IsFileData = ExtractJsonValue(@string, "IsFileData").ToLower() == "true"; int num2 = data.Length - 4 - num; if (num2 > 0) { ChunkData = new byte[num2]; Array.Copy(data, 4 + num, ChunkData, 0, num2); } } } } public class HeartbeatMessage : P2PMessage { public override string MessageType => "HEARTBEAT"; public string HeartbeatId { get; set; } = Guid.NewGuid().ToString(); public bool IsResponse { get; set; } public long HighPrecisionTimestamp { get; set; } public uint SequenceNumber { get; set; } public float PacketLossPercent { get; set; } public float AverageLatencyMs { get; set; } public int BandwidthUsage { get; set; } public string PlayerStatus { get; set; } = "online"; public string ConnectionInfo { get; set; } = string.Empty; public override byte[] Serialize() { if (HighPrecisionTimestamp == 0) { HighPrecisionTimestamp = DateTime.UtcNow.Ticks; } string s = "{" + CreateJsonBase($"\"HeartbeatId\":\"{HeartbeatId}\",\"IsResponse\":{IsResponse.ToString().ToLower()},\"HighPrecisionTimestamp\":{HighPrecisionTimestamp},\"SequenceNumber\":{SequenceNumber},\"PacketLossPercent\":{PacketLossPercent},\"AverageLatencyMs\":{AverageLatencyMs},\"BandwidthUsage\":{BandwidthUsage},\"PlayerStatus\":\"{PlayerStatus}\",\"ConnectionInfo\":\"{ConnectionInfo}\"") + "}"; return Encoding.UTF8.GetBytes(s); } public override void Deserialize(byte[] data) { try { string @string = Encoding.UTF8.GetString(data); ParseJsonBase(@string); HeartbeatId = ExtractJsonValue(@string, "HeartbeatId"); IsResponse = ExtractJsonValue(@string, "IsResponse").ToLower() == "true"; if (long.TryParse(ExtractJsonValue(@string, "HighPrecisionTimestamp"), out var result)) { HighPrecisionTimestamp = result; } if (uint.TryParse(ExtractJsonValue(@string, "SequenceNumber"), out var result2)) { SequenceNumber = result2; } if (float.TryParse(ExtractJsonValue(@string, "PacketLossPercent"), out var result3)) { PacketLossPercent = result3; } if (float.TryParse(ExtractJsonValue(@string, "AverageLatencyMs"), out var result4)) { AverageLatencyMs = result4; } if (int.TryParse(ExtractJsonValue(@string, "BandwidthUsage"), out var result5)) { BandwidthUsage = result5; } PlayerStatus = ExtractJsonValue(@string, "PlayerStatus"); ConnectionInfo = ExtractJsonValue(@string, "ConnectionInfo"); } catch (Exception) { throw; } } } public class LobbyInfo { public CSteamID LobbyId { get; set; } public CSteamID OwnerId { get; set; } public int MemberCount { get; set; } public int MaxMembers { get; set; } public string? Name { get; set; } public DateTime CreatedAt { get; set; } public LobbyInfo() { CreatedAt = DateTime.Now; } } public class MemberInfo { public CSteamID SteamId { get; set; } public string DisplayName { get; set; } = string.Empty; public bool IsOwner { get; set; } public bool IsLocalPlayer { get; set; } public DateTime JoinedAt { get; set; } public MemberInfo() { JoinedAt = DateTime.Now; } } public abstract class P2PMessage { public abstract string MessageType { get; } public CSteamID SenderId { get; set; } public DateTime Timestamp { get; set; } public abstract byte[] Serialize(); public abstract void Deserialize(byte[] data); protected P2PMessage() { Timestamp = DateTime.UtcNow; } protected string CreateJsonBase(string additionalData = "") { //IL_0002: Unknown result type (might be due to invalid IL or missing references) ulong num = SenderId.m_SteamID; if (num == 0) { num = 76561197960265728uL; } string text = $"\"SenderId\":{num},\"Timestamp\":\"{Timestamp:O}\""; return string.IsNullOrEmpty(additionalData) ? text : (text + "," + additionalData); } protected void ParseJsonBase(string json) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) try { string s = ExtractJsonValue(json, "SenderId"); if (ulong.TryParse(s, out var result)) { SenderId = new CSteamID(result); } string s2 = ExtractJsonValue(json, "Timestamp"); if (DateTime.TryParse(s2, out var result2)) { Timestamp = result2; } else { Timestamp = DateTime.UtcNow; } } catch (Exception) { SenderId = new CSteamID(76561197960265728uL); Timestamp = DateTime.UtcNow; } } protected string ExtractJsonValue(string json, string key) { try { string text = "\"" + key + "\":"; int num = json.IndexOf(text); if (num == -1) { return string.Empty; } for (num += text.Length; num < json.Length && char.IsWhiteSpace(json[num]); num++) { } if (num >= json.Length) { return string.Empty; } if (json[num] == '"') { num++; int num2 = -1; bool flag = false; for (int i = num; i < json.Length; i++) { if (json[i] == '\\' && !flag) { flag = true; continue; } if (json[i] == '"' && !flag) { num2 = i; break; } flag = false; } if (num2 == -1) { return string.Empty; } string text2 = json.Substring(num, num2 - num); return text2.Replace("\\\"", "\"").Replace("\\\\", "\\").Replace("\\/", "/") .Replace("\\b", "\b") .Replace("\\f", "\f") .Replace("\\n", "\n") .Replace("\\r", "\r") .Replace("\\t", "\t"); } int j; for (j = num; j < json.Length && json[j] != ',' && json[j] != '}' && json[j] != ']' && !char.IsWhiteSpace(json[j]); j++) { } return json.Substring(num, j - num); } catch (Exception) { return string.Empty; } } } public class StreamMessage : P2PMessage { public override string MessageType => "STREAM"; public string StreamType { get; set; } = "audio"; public string StreamId { get; set; } = string.Empty; public uint SequenceNumber { get; set; } public long CaptureTimestamp { get; set; } public int SampleRate { get; set; } = 44100; public int Channels { get; set; } = 1; public int BitsPerSample { get; set; } = 16; public int FrameSamples { get; set; } = 960; public string Codec { get; set; } = "none"; public int Quality { get; set; } = 75; public bool IsStreamStart { get; set; } public bool IsStreamEnd { get; set; } public EP2PSend RecommendedSendType { get; set; } = (EP2PSend)1; public byte[] StreamData { get; set; } = new byte[0]; public string Metadata { get; set; } = string.Empty; public uint? AckForSequence { get; set; } public bool IsRetransmit { get; set; } = false; public bool IsFecFrame { get; set; } = false; public string PayloadType { get; set; } = "audio"; public int FrameDurationMs { get; set; } = 20; public byte Priority { get; set; } = 128; public override byte[] Serialize() { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected I4, but got Unknown string s = "{" + CreateJsonBase(string.Format("\"StreamType\":\"{0}\",\"StreamId\":\"{1}\",\"SequenceNumber\":{2},\"CaptureTimestamp\":{3},\"SampleRate\":{4},\"Channels\":{5},\"BitsPerSample\":{6},\"FrameSamples\":{7},\"Codec\":\"{8}\",\"Quality\":{9},\"IsStreamStart\":{10},\"IsStreamEnd\":{11},\"RecommendedSendType\":{12},\"Metadata\":\"{13}\",\"AckForSequence\":{14},\"IsRetransmit\":{15},\"IsFecFrame\":{16},\"PayloadType\":\"{17}\",\"FrameDurationMs\":{18},\"Priority\":{19}", StreamType, StreamId, SequenceNumber, CaptureTimestamp, SampleRate, Channels, BitsPerSample, FrameSamples, Codec, Quality, IsStreamStart.ToString().ToLower(), IsStreamEnd.ToString().ToLower(), (int)RecommendedSendType, Metadata, AckForSequence?.ToString() ?? "null", IsRetransmit.ToString().ToLower(), IsFecFrame.ToString().ToLower(), PayloadType, FrameDurationMs, Priority)) + "}"; byte[] bytes = Encoding.UTF8.GetBytes(s); byte[] array = new byte[4 + bytes.Length + StreamData.Length]; int num = 0; BitConverter.GetBytes(bytes.Length).CopyTo(array, num); num += 4; bytes.CopyTo(array, num); num += bytes.Length; StreamData.CopyTo(array, num); return array; } public override void Deserialize(byte[] data) { if (data.Length < 4) { return; } int num = BitConverter.ToInt32(data, 0); if (num <= 0 || num > data.Length - 4) { return; } string @string = Encoding.UTF8.GetString(data, 4, num); ParseJsonBase(@string); StreamType = ExtractJsonValue(@string, "StreamType"); StreamId = ExtractJsonValue(@string, "StreamId"); if (uint.TryParse(ExtractJsonValue(@string, "SequenceNumber"), out var result)) { SequenceNumber = result; } if (long.TryParse(ExtractJsonValue(@string, "CaptureTimestamp"), out var result2)) { CaptureTimestamp = result2; } if (int.TryParse(ExtractJsonValue(@string, "SampleRate"), out var result3)) { SampleRate = result3; } if (int.TryParse(ExtractJsonValue(@string, "Channels"), out var result4)) { Channels = result4; } if (int.TryParse(ExtractJsonValue(@string, "BitsPerSample"), out var result5)) { BitsPerSample = result5; } if (int.TryParse(ExtractJsonValue(@string, "FrameSamples"), out var result6)) { FrameSamples = result6; } Codec = ExtractJsonValue(@string, "Codec"); if (int.TryParse(ExtractJsonValue(@string, "Quality"), out var result7)) { Quality = result7; } IsStreamStart = ExtractJsonValue(@string, "IsStreamStart").ToLower() == "true"; IsStreamEnd = ExtractJsonValue(@string, "IsStreamEnd").ToLower() == "true"; if (int.TryParse(ExtractJsonValue(@string, "RecommendedSendType"), out var result8)) { RecommendedSendType = (EP2PSend)result8; } Metadata = ExtractJsonValue(@string, "Metadata"); string text = ExtractJsonValue(@string, "AckForSequence"); if (!string.IsNullOrEmpty(text) && text != "null" && uint.TryParse(text, out var result9)) { AckForSequence = result9; } IsRetransmit = ExtractJsonValue(@string, "IsRetransmit").ToLower() == "true"; IsFecFrame = ExtractJsonValue(@string, "IsFecFrame").ToLower() == "true"; PayloadType = ExtractJsonValue(@string, "PayloadType"); if (int.TryParse(ExtractJsonValue(@string, "FrameDurationMs"), out var result10)) { FrameDurationMs = result10; } if (byte.TryParse(ExtractJsonValue(@string, "Priority"), out var result11)) { Priority = result11; } int num2 = data.Length - 4 - num; if (num2 > 0) { StreamData = new byte[num2]; for (int i = 0; i < num2; i++) { if (4 + num + i < data.Length) { StreamData[i] = data[4 + num + i]; } } bool flag = num2 > 4; for (int j = 0; j < Math.Min(4, num2) && flag; j++) { if (StreamData[j] != 0) { flag = false; } } if (flag && num2 > 4) { Console.WriteLine($"[StreamMessage] Warning: StreamData may be corrupted (all zeros) for frame {SequenceNumber}"); } } else { StreamData = new byte[0]; } } } public class TextMessage : P2PMessage { public override string MessageType => "TEXT"; public string Content { get; set; } = string.Empty; public override byte[] Serialize() { string text = Content.Replace("\"", "\\\""); string s = "{" + CreateJsonBase("\"Content\":\"" + text + "\"") + "}"; return Encoding.UTF8.GetBytes(s); } public override void Deserialize(byte[] data) { try { string @string = Encoding.UTF8.GetString(data); ParseJsonBase(@string); Content = ExtractJsonValue(@string, "Content"); } catch (Exception) { throw; } } } } namespace SteamNetworkLib.Exceptions { public class LobbyException : SteamNetworkException { public EResult? SteamResult { get; } public CSteamID? LobbyId { get; } public LobbyException() { } public LobbyException(string message) : base(message) { } public LobbyException(string message, Exception inner) : base(message, inner) { } public LobbyException(string message, EResult steamResult) : base(message) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) SteamResult = steamResult; } public LobbyException(string message, CSteamID lobbyId) : base(message) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) LobbyId = lobbyId; } public LobbyException(string message, EResult steamResult, CSteamID lobbyId) : base(message) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) SteamResult = steamResult; LobbyId = lobbyId; } } public class P2PException : SteamNetworkException { public CSteamID? TargetId { get; } public EP2PSessionError? SessionError { get; } public int Channel { get; } public P2PException() { } public P2PException(string message) : base(message) { } public P2PException(string message, Exception inner) : base(message, inner) { } public P2PException(string message, CSteamID targetId) : base(message) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) TargetId = targetId; } public P2PException(string message, EP2PSessionError sessionError) : base(message) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) SessionError = sessionError; } public P2PException(string message, CSteamID targetId, int channel) : base(message) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) TargetId = targetId; Channel = channel; } public P2PException(string message, CSteamID targetId, EP2PSessionError sessionError) : base(message) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) TargetId = targetId; SessionError = sessionError; } } public class SteamNetworkException : Exception { public SteamNetworkException() { } public SteamNetworkException(string message) : base(message) { } public SteamNetworkException(string message, Exception inner) : base(message, inner) { } } } namespace SteamNetworkLib.Events { public class LobbyJoinedEventArgs : EventArgs { public LobbyInfo Lobby { get; } public EResult Result { get; } public LobbyJoinedEventArgs(LobbyInfo lobby, EResult result = 1) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) Lobby = lobby; Result = result; } } public class LobbyCreatedEventArgs : EventArgs { public LobbyInfo Lobby { get; } public EResult Result { get; } public LobbyCreatedEventArgs(LobbyInfo lobby, EResult result = 1) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) Lobby = lobby; Result = result; } } public class LobbyLeftEventArgs : EventArgs { public CSteamID LobbyId { get; } public string Reason { get; } public LobbyLeftEventArgs(CSteamID lobbyId, string reason = "") { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) LobbyId = lobbyId; Reason = reason; } } public class MemberJoinedEventArgs : EventArgs { public SteamNetworkLib.Models.MemberInfo Member { get; } public MemberJoinedEventArgs(SteamNetworkLib.Models.MemberInfo member) { Member = member; } } public class MemberLeftEventArgs : EventArgs { public SteamNetworkLib.Models.MemberInfo Member { get; } public string Reason { get; } public MemberLeftEventArgs(SteamNetworkLib.Models.MemberInfo member, string reason = "") { Member = member; Reason = reason; } } public class LobbyDataChangedEventArgs : EventArgs { public string Key { get; } public string? OldValue { get; } public string? NewValue { get; } public CSteamID ChangedBy { get; } public LobbyDataChangedEventArgs(string key, string? oldValue, string? newValue, CSteamID changedBy) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) Key = key; OldValue = oldValue; NewValue = newValue; ChangedBy = changedBy; } } public class MemberDataChangedEventArgs : EventArgs { public CSteamID MemberId { get; } public string Key { get; } public string? OldValue { get; } public string? NewValue { get; } public MemberDataChangedEventArgs(CSteamID memberId, string key, string? oldValue, string? newValue) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) MemberId = memberId; Key = key; OldValue = oldValue; NewValue = newValue; } } public class VersionMismatchEventArgs : EventArgs { public string LocalVersion { get; } public Dictionary<CSteamID, string> PlayerVersions { get; } public List<CSteamID> IncompatiblePlayers { get; } public VersionMismatchEventArgs(string localVersion, Dictionary<CSteamID, string> playerVersions, List<CSteamID> incompatiblePlayers) { LocalVersion = localVersion; PlayerVersions = playerVersions; IncompatiblePlayers = incompatiblePlayers; } } public class P2PPacketReceivedEventArgs : EventArgs { public CSteamID SenderId { get; } public byte[] Data { get; } public int Channel { get; } public uint PacketSize { get; } public DateTime ReceivedAt { get; } public P2PPacketReceivedEventArgs(CSteamID senderId, byte[] data, int channel, uint packetSize) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) SenderId = senderId; Data = data; Channel = channel; PacketSize = packetSize; ReceivedAt = DateTime.UtcNow; } } public class P2PMessageReceivedEventArgs : EventArgs { public P2PMessage Message { get; } public CSteamID SenderId { get; } public int Channel { get; } public DateTime ReceivedAt { get; } public P2PMessageReceivedEventArgs(P2PMessage message, CSteamID senderId, int channel) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) Message = message; SenderId = senderId; Channel = channel; ReceivedAt = DateTime.UtcNow; } } public class P2PSessionRequestEventArgs : EventArgs { public CSteamID RequesterId { get; } public string RequesterName { get; } public bool ShouldAccept { get; set; } public P2PSessionRequestEventArgs(CSteamID requesterId, string requesterName) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) RequesterId = requesterId; RequesterName = requesterName; ShouldAccept = true; } } public class P2PSessionConnectFailEventArgs : EventArgs { public CSteamID TargetId { get; } public EP2PSessionError Error { get; } public P2PSessionConnectFailEventArgs(CSteamID targetId, EP2PSessionError error) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) TargetId = targetId; Error = error; } } public class P2PPacketSentEventArgs : EventArgs { public CSteamID TargetId { get; } public bool Success { get; } public int DataSize { get; } public int Channel { get; } public EP2PSend SendType { get; } public P2PPacketSentEventArgs(CSteamID targetId, bool success, int dataSize, int channel, EP2PSend sendType) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) TargetId = targetId; Success = success; DataSize = dataSize; Channel = channel; SendType = sendType; } } } namespace SteamNetworkLib.Core { public class SteamLobbyData : IDisposable { private readonly SteamLobbyManager _lobbyManager; private readonly Dictionary<string, string> _cachedData = new Dictionary<string, string>(); private bool _disposed = false; private Callback<LobbyDataUpdate_t>? _lobbyDataUpdateCallback; public event EventHandler<LobbyDataChangedEventArgs>? OnLobbyDataChanged; public SteamLobbyData(SteamLobbyManager lobbyManager) { _lobbyManager = lobbyManager ?? throw new ArgumentNullException("lobbyManager"); if (SteamNetworkUtils.IsSteamInitialized()) { _lobbyDataUpdateCallback = Callback<LobbyDataUpdate_t>.Create(DispatchDelegate<LobbyDataUpdate_t>.op_Implicit((Action<LobbyDataUpdate_t>)OnLobbyDataUpdateCallback)); } } public void SetData(string key, string value) { //IL_0032: 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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) ValidateKey(key); if (!_lobbyManager.IsInLobby) { throw new LobbyException("Cannot set lobby data - not in a lobby"); } CSteamID lobbyId = _lobbyManager.CurrentLobby.LobbyId; string data = GetData(key); if (!SteamMatchmaking.SetLobbyData(lobbyId, key, value)) { throw new LobbyException("Failed to set lobby data for key: " + key); } _cachedData[key] = value; this.OnLobbyDataChanged?.Invoke(this, new LobbyDataChangedEventArgs(key, data, value, _lobbyManager.LocalPlayerID)); } public string? GetData(string key) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) ValidateKey(key); if (!_lobbyManager.IsInLobby) { return null; } CSteamID lobbyId = _lobbyManager.CurrentLobby.LobbyId; string lobbyData = SteamMatchmaking.GetLobbyData(lobbyId, key); if (!string.IsNullOrEmpty(lobbyData)) { _cachedData[key] = lobbyData; return lobbyData; } string value; return _cachedData.TryGetValue(key, out value) ? value : null; } public bool HasData(string key) { return !string.IsNullOrEmpty(GetData(key)); } public void RemoveData(string key) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) ValidateKey(key); if (!_lobbyManager.IsInLobby) { throw new LobbyException("Cannot remove lobby data - not in a lobby"); } string data = GetData(key); if (data != null) { CSteamID lobbyId = _lobbyManager.CurrentLobby.LobbyId; if (!SteamMatchmaking.DeleteLobbyData(lobbyId, key)) { throw new LobbyException("Failed to remove lobby data for key: " + key); } _cachedData.Remove(key); this.OnLobbyDataChanged?.Invoke(this, new LobbyDataChangedEventArgs(key, data, null, _lobbyManager.LocalPlayerID)); } } public Dictionary<string, string> GetAllData() { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) Dictionary<string, string> dictionary = new Dictionary<string, string>(); if (!_lobbyManager.IsInLobby) { return dictionary; } CSteamID lobbyId = _lobbyManager.CurrentLobby.LobbyId; int lobbyDataCount = SteamMatchmaking.GetLobbyDataCount(lobbyId); for (int i = 0; i < lobbyDataCount; i++) { string text = ""; string value = ""; if (SteamMatchmaking.GetLobbyDataByIndex(lobbyId, i, ref text, 256, ref value, 256) && !string.IsNullOrEmpty(text)) { dictionary[text] = value; _cachedData[text] = value; } } return dictionary; } public void ClearAllData() { if (!_lobbyManager.IsInLobby) { throw new LobbyException("Cannot clear lobby data - not in a lobby"); } if (!_lobbyManager.IsHost) { throw new LobbyException("Only the lobby host can clear all lobby data"); } Dictionary<string, string> allData = GetAllData(); foreach (string key in allData.Keys) { RemoveData(key); } } public void SetDataBatch(Dictionary<string, string> data) { if (data == null || data.Count == 0) { return; } if (!_lobbyManager.IsInLobby) { throw new LobbyException("Cannot set lobby data - not in a lobby"); } foreach (KeyValuePair<string, string> datum in data) { try { SetData(datum.Key, datum.Value); } catch (Exception ex) { throw new LobbyException("Failed to set data for key '" + datum.Key + "': " + ex.Message, ex); } } } public int GetDataCount() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (!_lobbyManager.IsInLobby) { return 0; } return SteamMatchmaking.GetLobbyDataCount(_lobbyManager.CurrentLobby.LobbyId); } public List<string> GetDataKeys() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) List<string> list = new List<string>(); if (!_lobbyManager.IsInLobby) { return list; } CSteamID lobbyId = _lobbyManager.CurrentLobby.LobbyId; int lobbyDataCount = SteamMatchmaking.GetLobbyDataCount(lobbyId); for (int i = 0; i < lobbyDataCount; i++) { string text = ""; string text2 = ""; if (SteamMatchmaking.GetLobbyDataByIndex(lobbyId, i, ref text, 256, ref text2, 256) && !string.IsNullOrEmpty(text)) { list.Add(text); } } return list; } public void RefreshData() { if (_lobbyManager.IsInLobby) { _cachedData.Clear(); GetAllData(); } } private void ValidateKey(string key) { if (string.IsNullOrEmpty(key)) { throw new ArgumentException("Lobby data key cannot be null or empty"); } if (key.Length > 255) { throw new ArgumentException("Lobby data key cannot exceed 255 characters"); } if (key.StartsWith("__steam_")) { throw new ArgumentException("Lobby data key cannot start with '__steam_' (reserved by Steam)"); } } private void OnLobbyDataUpdateCallback(LobbyDataUpdate_t result) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) try { if (!_lobbyManager.IsInLobby || _lobbyManager.CurrentLobby.LobbyId.m_SteamID != result.m_ulSteamIDLobby || result.m_bSuccess == 0) { return; } Dictionary<string, string> dictionary = new Dictionary<string, string>(_cachedData); RefreshData(); Dictionary<string, string> cachedData = _cachedData; CSteamID changedBy = default(CSteamID); ((CSteamID)(ref changedBy))..ctor(result.m_ulSteamIDMember); foreach (KeyValuePair<string, string> item in cachedData) { if (!dictionary.TryGetValue(item.Key, out var value) || value != item.Value) { this.OnLobbyDataChanged?.Invoke(this, new LobbyDataChangedEventArgs(item.Key, value, item.Value, changedBy)); } } foreach (KeyValuePair<string, string> item2 in dictionary) { if (!cachedData.ContainsKey(item2.Key)) { this.OnLobbyDataChanged?.Invoke(this, new LobbyDataChangedEventArgs(item2.Key, item2.Value, null, changedBy)); } } } catch (Exception ex) { Console.WriteLine("Error in lobby data update callback: " + ex.Message); } } public void Dispose() { if (!_disposed) { try { _lobbyDataUpdateCallback?.Dispose(); _cachedData.Clear(); } catch (Exception ex) { Console.WriteLine("Error disposing SteamLobbyData: " + ex.Message); } _disposed = true; } } } public class SteamLobbyManager : IDisposable { private bool _disposed = false; private LobbyInfo? _currentLobby; private readonly List<SteamNetworkLib.Models.MemberInfo> _lobbyMembers = new List<SteamNetworkLib.Models.MemberInfo>(); private Callback<LobbyCreated_t>? _lobbyCreatedCallback; private Callback<LobbyEnter_t>? _lobbyEnteredCallback; private Callback<LobbyChatUpdate_t>? _chatUpdateCallback; private Callback<GameLobbyJoinRequested_t>? _lobbyJoinRequestedCallback; private TaskCompletionSource<LobbyInfo>? _createLobbyTcs; private TaskCompletionSource<LobbyInfo>? _joinLobbyTcs; public LobbyInfo? CurrentLobby => _currentLobby; public CSteamID LocalPlayerID { get; private set; } public bool IsInLobby => _currentLobby != null && SteamNetworkUtils.IsValidSteamID(_currentLobby.LobbyId); public bool IsHost => IsInLobby && _currentLobby.OwnerId == LocalPlayerID; public event EventHandler<LobbyJoinedEventArgs>? OnLobbyJoined; public event EventHandler<LobbyCreatedEventArgs>? OnLobbyCreated; public event EventHandler<LobbyLeftEventArgs>? OnLobbyLeft; public event EventHandler<MemberJoinedEventArgs>? OnMemberJoined; public event EventHandler<MemberLeftEventArgs>? OnMemberLeft; public SteamLobbyManager() { InitializeSteam(); } 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) if (_createLobbyTcs != null && !_createLobbyTcs.Task.IsCompleted) { throw new LobbyException("A lobby creation is already in progress"); } if (IsInLobby) { LeaveLobby(); } _createLobbyTcs = new TaskCompletionSource<LobbyInfo>(); SteamAPICall_t apiCall = SteamMatchmaking.CreateLobby(lobbyType, maxMembers); if (apiCall == SteamAPICall_t.Invalid) { _createLobbyTcs.SetException(new LobbyException("Failed to create lobby - Steam API call failed")); return await _createLobbyTcs.Task; } Task timeoutTask = Task.Delay(10000); if (await Task.WhenAny(new Task[2] { _createLobbyTcs.Task, timeoutTask }) == timeoutTask) { _createLobbyTcs.SetException(new LobbyException("Lobby creation timed out")); } return await _createLobbyTcs.Task; } public async Task<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) if (!SteamNetworkUtils.IsValidSteamID(lobbyId)) { throw new LobbyException("Invalid lobby ID"); } if (_joinLobbyTcs != null && !_joinLobbyTcs.Task.IsCompleted) { throw new LobbyException("A lobby join is already in progress"); } if (IsInLobby) { LeaveLobby(); } _joinLobbyTcs = new TaskCompletionSource<LobbyInfo>(); SteamMatchmaking.JoinLobby(lobbyId); Task timeoutTask = Task.Delay(10000); if (await Task.WhenAny(new Task[2] { _joinLobbyTcs.Task, timeoutTask }) == timeoutTask) { _joinLobbyTcs.SetException(new LobbyException("Lobby join timed out")); } return await _joinLobbyTcs.Task; } public void LeaveLobby() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) if (IsInLobby) { CSteamID lobbyId = _currentLobby.LobbyId; string reason = "Player left voluntarily"; SteamMatchmaking.LeaveLobby(lobbyId); _currentLobby = null; _lobbyMembers.Clear(); this.OnLobbyLeft?.Invoke(this, new LobbyLeftEventArgs(lobbyId, reason)); } } public List<SteamNetworkLib.Models.MemberInfo> GetLobbyMembers() { if (!IsInLobby) { return new List<SteamNetworkLib.Models.MemberInfo>(); } UpdateLobbyMembers(); return new List<SteamNetworkLib.Models.MemberInfo>(_lobbyMembers); } public void InviteFriend(CSteamID friendId) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) if (!IsInLobby) { throw new LobbyException("Cannot invite friend - not in a lobby"); } if (!SteamNetworkUtils.IsValidSteamID(friendId)) { throw new LobbyException("Invalid friend Steam ID"); } SteamMatchmaking.InviteUserToLobby(_currentLobby.LobbyId, friendId); } public void OpenInviteDialog() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (!IsInLobby) { throw new LobbyException("Cannot open invite dialog - not in a lobby"); } SteamFriends.ActivateGameOverlayInviteDialog(_currentLobby.LobbyId); } private void InitializeSteam() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (!SteamNetworkUtils.IsSteamInitialized()) { throw new SteamNetworkException("Steam is not initialized. Make sure Steam is running and SteamAPI.Init() was called."); } LocalPlayerID = SteamUser.GetSteamID(); _lobbyCreatedCallback = Callback<LobbyCreated_t>.Create(DispatchDelegate<LobbyCreated_t>.op_Implicit((Action<LobbyCreated_t>)OnLobbyCreatedCallback)); _lobbyEnteredCallback = Callback<LobbyEnter_t>.Create(DispatchDelegate<LobbyEnter_t>.op_Implicit((Action<LobbyEnter_t>)OnLobbyEnteredCallback)); _chatUpdateCallback = Callback<LobbyChatUpdate_t>.Create(DispatchDelegate<LobbyChatUpdate_t>.op_Implicit((Action<LobbyChatUpdate_t>)OnChatUpdateCallback)); _lobbyJoinRequestedCallback = Callback<GameLobbyJoinRequested_t>.Create(DispatchDelegate<GameLobbyJoinRequested_t>.op_Implicit((Action<GameLobbyJoinRequested_t>)OnLobbyJoinRequestedCallback)); } private void OnLobbyCreatedCallback(LobbyCreated_t result) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) try { if ((int)result.m_eResult != 1) { _createLobbyTcs?.SetException(new LobbyException("Failed to create lobby: " + SteamNetworkUtils.FormatSteamResult(result.m_eResult))); return; } CSteamID val = default(CSteamID); ((CSteamID)(ref val))..ctor(result.m_ulSteamIDLobby); LobbyInfo lobbyInfo = (_currentLobby = CreateLobbyInfo(val)); CSteamID val2 = val; CSteamID localPlayerID = LocalPlayerID; SteamMatchmaking.SetLobbyData(val2, "owner", ((object)(CSteamID)(ref localPlayerID)).ToString()); SteamMatchmaking.SetLobbyData(val, "created_at", DateTime.UtcNow.ToString("O")); UpdateLobbyMembers(); _createLobbyTcs?.SetResult(lobbyInfo); this.OnLobbyCreated?.Invoke(this, new LobbyCreatedEventArgs(lobbyInfo, result.m_eResult)); } catch (Exception ex) { _createLobbyTcs?.SetException(new LobbyException("Error in lobby created callback: " + ex.Message, ex)); } } private void OnLobbyEnteredCallback(LobbyEnter_t result) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) try { CSteamID lobbyId = default(CSteamID); ((CSteamID)(ref lobbyId))..ctor(result.m_ulSteamIDLobby); if ((result.m_EChatRoomEnterResponse & 1) == 0) { string message = $"Failed to enter lobby: {result.m_EChatRoomEnterResponse}"; _joinLobbyTcs?.SetException(new LobbyException(message)); return; } LobbyInfo lobbyInfo = (_currentLobby = CreateLobbyInfo(lobbyId)); UpdateLobbyMembers(); _joinLobbyTcs?.SetResult(lobbyInfo); this.OnLobbyJoined?.Invoke(this, new LobbyJoinedEventArgs(lobbyInfo, (EResult)1)); } catch (Exception ex) { _joinLobbyTcs?.SetException(new LobbyException("Error in lobby entered callback: " + ex.Message, ex)); } } private void OnChatUpdateCallback(LobbyChatUpdate_t result) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) if (!IsInLobby) { return; } try { CSteamID val = default(CSteamID); ((CSteamID)(ref val))..ctor(result.m_ulSteamIDUserChanged); CSteamID val2 = default(CSteamID); ((CSteamID)(ref val2))..ctor(result.m_ulSteamIDMakingChange); if (val2 == _currentLobby.LobbyId && val != LocalPlayerID && (result.m_rgfChatMemberStateChange & 2u) != 0) { LeaveLobby(); return; } List<SteamNetworkLib.Models.MemberInfo> list = new List<SteamNetworkLib.Models.MemberInfo>(_lobbyMembers); UpdateLobbyMembers(); List<SteamNetworkLib.Models.MemberInfo> list2 = new List<SteamNetworkLib.Models.MemberInfo>(_lobbyMembers); HashSet<CSteamID> hashSet = list2.Select((SteamNetworkLib.Models.MemberInfo m) => m.SteamId).ToHashSet(); HashSet<CSteamID> hashSet2 = list.Select((SteamNetworkLib.Models.MemberInfo m) => m.SteamId).ToHashSet(); foreach (SteamNetworkLib.Models.MemberInfo item in list2) { if (!hashSet2.Contains(item.SteamId)) { this.OnMemberJoined?.Invoke(this, new MemberJoinedEventArgs(item)); } } foreach (SteamNetworkLib.Models.MemberInfo item2 in list) { if (!hashSet.Contains(item2.SteamId)) { this.OnMemberLeft?.Invoke(this, new MemberLeftEventArgs(item2, "Left lobby")); } } } catch (Exception ex) { Console.WriteLine("Error in chat update callback: " + ex.Message); } } private void OnLobbyJoinRequestedCallback(GameLobbyJoinRequested_t result) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) try { CSteamID steamIDLobby = result.m_steamIDLobby; if (IsInLobby) { LeaveLobby(); } JoinLobbyAsync(steamIDLobby); } catch (Exception ex) { Console.WriteLine("Error in lobby join requested callback: " + ex.Message); } } private LobbyInfo CreateLobbyInfo(CSteamID lobbyId) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) string lobbyData = SteamMatchmaking.GetLobbyData(lobbyId, "owner"); CSteamID ownerId = (CSteamID)(string.IsNullOrEmpty(lobbyData) ? SteamMatchmaking.GetLobbyOwner(lobbyId) : new CSteamID(ulong.Parse(lobbyData))); return new LobbyInfo { LobbyId = lobbyId, OwnerId = ownerId, MemberCount = SteamMatchmaking.GetNumLobbyMembers(lobbyId), MaxMembers = SteamMatchmaking.GetLobbyMemberLimit(lobbyId), Name = SteamMatchmaking.GetLobbyData(lobbyId, "name"), CreatedAt = DateTime.UtcNow }; } private void UpdateLobbyMembers() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) if (!IsInLobby) { return; } _lobbyMembers.Clear(); CSteamID lobbyId = _currentLobby.LobbyId; int numLobbyMembers = SteamMatchmaking.GetNumLobbyMembers(lobbyId); for (int i = 0; i < numLobbyMembers; i++) { CSteamID lobbyMemberByIndex = SteamMatchmaking.GetLobbyMemberByIndex(lobbyId, i); if (!(lobbyMemberByIndex == CSteamID.Nil)) { SteamNetworkLib.Models.MemberInfo item = new SteamNetworkLib.Models.MemberInfo { SteamId = lobbyMemberByIndex, DisplayName = SteamNetworkUtils.GetPlayerName(lobbyMemberByIndex), IsOwner = (lobbyMemberByIndex == _currentLobby.OwnerId), IsLocalPlayer = (lobbyMemberByIndex == LocalPlayerID), JoinedAt = DateTime.UtcNow }; _lobbyMembers.Add(item); } } } public void Dispose() { if (_disposed) { return; } try { if (IsInLobby) { LeaveLobby(); } _lobbyCreatedCallback?.Dispose(); _lobbyEnteredCallback?.Dispose(); _chatUpdateCallback?.Dispose(); _lobbyJoinRequestedCallback?.Dispose(); _createLobbyTcs?.TrySetCanceled(); _joinLobbyTcs?.TrySetCanceled(); } catch (Exception ex) { Console.WriteLine("Error disposing SteamLobbyManager: " + ex.Message); } _disposed = true; } } public class SteamMemberData : IDisposable { private readonly SteamLobbyManager _lobbyManager; private readonly Dictionary<(ulong PlayerId, string Key), string> _cachedData = new Dictionary<(ulong, string), string>(); private bool _disposed = false; private Callback<LobbyDataUpdate_t>? _lobbyDataUpdateCallback; public event EventHandler<MemberDataChangedEventArgs>? OnMemberDataChanged; public SteamMemberData(SteamLobbyManager lobbyManager) { _lobbyManager = lobbyManager ?? throw new ArgumentNullException("lobbyManager"); if (SteamNetworkUtils.IsSteamInitialized()) { _lobbyDataUpdateCallback = Callback<LobbyDataUpdate_t>.Create(DispatchDelegate<LobbyDataUpdate_t>.op_Implicit((Action<LobbyDataUpdate_t>)OnLobbyDataUpdateCallback)); } } public void SetMemberData(string key, string value) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) SetMemberData(_lobbyManager.LocalPlayerID, key, value); } public void SetMemberData(CSteamID playerId, string key, string value) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0071: 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) ValidateKey(key); if (!_lobbyManager.IsInLobby) { throw new LobbyException("Cannot set member data - not in a lobby"); } if (playerId != _lobbyManager.LocalPlayerID) { throw new LobbyException("Cannot set member data for other players - only for local player"); } CSteamID lobbyId = _lobbyManager.CurrentLobby.LobbyId; string memberData = GetMemberData(playerId, key); SteamMatchmaking.SetLobbyMemberData(lobbyId, key, value); _cachedData[(playerId.m_SteamID, key)] = value; this.OnMemberDataChanged?.Invoke(this, new MemberDataChangedEventArgs(playerId, key, memberData, value)); } public string? GetMemberData(string key) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) return GetMemberData(_lobbyManager.LocalPlayerID, key); } public string? GetMemberData(CSteamID playerId, string key) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) ValidateKey(key); if (!_lobbyManager.IsInLobby) { return null; } CSteamID lobbyId = _lobbyManager.CurrentLobby.LobbyId; string lobbyMemberData = SteamMatchmaking.GetLobbyMemberData(lobbyId, playerId, key); if (!string.IsNullOrEmpty(lobbyMemberData)) { _cachedData[(playerId.m_SteamID, key)] = lobbyMemberData; return lobbyMemberData; } string value; return _cachedData.TryGetValue((playerId.m_SteamID, key), out value) ? value : null; } public bool HasMemberData(string key) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) return HasMemberData(_lobbyManager.LocalPlayerID, key); } public bool HasMemberData(CSteamID playerId, string key) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return !string.IsNullOrEmpty(GetMemberData(playerId, key)); } public void RemoveMemberData(string key) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) ValidateKey(key); if (!_lobbyManager.IsInLobby) { throw new LobbyException("Cannot remove member data - not in a lobby"); } CSteamID localPlayerID = _lobbyManager.LocalPlayerID; CSteamID lobbyId = _lobbyManager.CurrentLobby.LobbyId; string memberData = GetMemberData(localPlayerID, key); if (memberData != null) { SteamMatchmaking.SetLobbyMemberData(lobbyId, key, ""); _cachedData.Remove((localPlayerID.m_SteamID, key)); this.OnMemberDataChanged?.Invoke(this, new MemberDataChangedEventArgs(localPlayerID, key, memberData, null)); } } public Dictionary<string, string> GetAllMemberData(CSteamID playerId) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) Dictionary<string, string> dictionary = new Dictionary<string, string>(); if (!_lobbyManager.IsInLobby) { return dictionary; } CSteamID lobbyId = _lobbyManager.CurrentLobby.LobbyId; List<string> list = (from k in _cachedData.Keys where k.PlayerId == playerId.m_SteamID select k.Key).Distinct().ToList(); foreach (string item in list) { string memberData = GetMemberData(playerId, item); if (!string.IsNullOrEmpty(memberData)) { dictionary[item] = memberData; } } return dictionary; } public Dictionary<CSteamID, string> GetMemberDataForAllPlayers(string key) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) ValidateKey(key); Dictionary<CSteamID, string> dictionary = new Dictionary<CSteamID, string>(); if (!_lobbyManager.IsInLobby) { return dictionary; } List<SteamNetworkLib.Models.MemberInfo> lobbyMembers = _lobbyManager.GetLobbyMembers(); foreach (SteamNetworkLib.Models.MemberInfo item in lobbyMembers) { string memberData = GetMemberData(item.SteamId, key); if (!string.IsNullOrEmpty(memberData)) { dictionary[item.SteamId] = memberData; } } return dictionary; } public void SetMemberDataBatch(Dictionary<string, string> data) { if (data == null || data.Count == 0) { return; } if (!_lobbyManager.IsInLobby) { throw new LobbyException("Cannot set member data - not in a lobby"); } foreach (KeyValuePair<string, string> datum in data) { try { SetMemberData(datum.Key, datum.Value); } catch (Exception ex) { throw new LobbyException("Failed to set member data for key '" + datum.Key + "': " + ex.Message, ex); } } } public List<CSteamID> GetPlayersWithData(string key) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) ValidateKey(key); List<CSteamID> list = new List<CSteamID>(); if (!_lobbyManager.IsInLobby)