#define DEBUG using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Net; using System.Net.Sockets; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Photon.Client; using Photon.Realtime; [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.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("Exit Games GmbH")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyCopyright("(c) Exit Games GmbH, http://www.photonengine.com")] [assembly: AssemblyDescription("Photon Realtime Api. Debug.")] [assembly: AssemblyFileVersion("5.1.9.0")] [assembly: AssemblyInformationalVersion("5.1.9")] [assembly: AssemblyProduct("Photon Realtime Api. Debug Debug.")] [assembly: AssemblyTitle("PhotonRealtime")] [assembly: AssemblyVersion("5.1.9.0")] namespace ExitGames.Client.Photon { [Obsolete("Use the RealtimeClient class instead. This was just renamed.")] public class LoadBalancingClient : RealtimeClient { [Obsolete("Use the RealtimeClient class instead. This was just renamed.")] public LoadBalancingClient(ConnectionProtocol protocol = 0) : base(protocol) { }//IL_0001: Unknown result type (might be due to invalid IL or missing references) } [Obsolete("Use the PhotonHashtable class instead. This was just renamed.")] public class Hashtable : PhotonHashtable { } } namespace Photon.Realtime { public class AppSettings { public string AppIdRealtime; public string AppIdFusion; public string AppIdQuantum; public string AppIdChat; public string AppIdVoice; public string AppVersion; public bool UseNameServer = true; public string FixedRegion; public string BestRegionSummaryFromStorage; public string Server; public ushort Port; public string ProxyServer; public ConnectionProtocol Protocol = (ConnectionProtocol)0; public bool EnableProtocolFallback = true; public AuthModeOption AuthMode = AuthModeOption.AuthOnceWss; public bool EnableLobbyStatistics; public LogLevel NetworkLogging = (LogLevel)1; public LogLevel ClientLogging = (LogLevel)2; public bool IsMasterServerAddress => !UseNameServer; public bool IsBestRegion => UseNameServer && string.IsNullOrEmpty(FixedRegion); public bool IsDefaultNameServer => UseNameServer && string.IsNullOrEmpty(Server); public bool IsDefaultPort => Port <= 0; public AppSettings() { }//IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) public AppSettings(AppSettings original = null) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) original?.CopyTo(this); } public string GetAppId(ClientAppType ct) { return ct switch { ClientAppType.Realtime => AppIdRealtime, ClientAppType.Fusion => AppIdFusion, ClientAppType.Quantum => AppIdQuantum, ClientAppType.Voice => AppIdVoice, ClientAppType.Chat => AppIdChat, _ => null, }; } public ClientAppType ClientTypeDetect() { bool flag = !string.IsNullOrEmpty(AppIdRealtime); bool flag2 = !string.IsNullOrEmpty(AppIdFusion); bool flag3 = !string.IsNullOrEmpty(AppIdQuantum); if (flag && !flag2 && !flag3) { return ClientAppType.Realtime; } if (flag2 && !flag && !flag3) { return ClientAppType.Fusion; } if (flag3 && !flag && !flag2) { return ClientAppType.Quantum; } Log.Error("ConnectUsingSettings requires that the AppSettings contain exactly one value set out of AppIdRealtime, AppIdFusion or AppIdQuantum.", (LogLevel)1); return ClientAppType.Detect; } public string ToStringFull() { //IL_01f2: Unknown result type (might be due to invalid IL or missing references) return string.Format("appId {0}{1}{2}{3}use ns: {4}, reg: {5}, {9}, {6}{7}{8}auth: {10}", string.IsNullOrEmpty(AppIdRealtime) ? string.Empty : ("Realtime/PUN: " + HideAppId(AppIdRealtime) + ", "), string.IsNullOrEmpty(AppIdFusion) ? string.Empty : ("Fusion: " + HideAppId(AppIdFusion) + ", "), string.IsNullOrEmpty(AppIdQuantum) ? string.Empty : ("Quantum: " + HideAppId(AppIdQuantum) + ", "), string.IsNullOrEmpty(AppIdChat) ? string.Empty : ("Chat: " + HideAppId(AppIdChat) + ", "), string.IsNullOrEmpty(AppIdVoice) ? string.Empty : ("Voice: " + HideAppId(AppIdVoice) + ", "), string.IsNullOrEmpty(AppVersion) ? string.Empty : ("AppVersion: " + AppVersion + ", "), "UseNameServer: " + UseNameServer + ", ", "Fixed Region: " + FixedRegion + ", ", string.IsNullOrEmpty(Server) ? string.Empty : ("Server: " + Server + ", "), IsDefaultPort ? string.Empty : ("Port: " + Port + ", "), string.IsNullOrEmpty(ProxyServer) ? string.Empty : ("Proxy: " + ProxyServer + ", "), Protocol, AuthMode); } public static bool IsAppId(string val) { try { new Guid(val); } catch { return false; } return true; } private string HideAppId(string appId) { return (string.IsNullOrEmpty(appId) || appId.Length < 8) ? appId : (appId.Substring(0, 8) + "***"); } public AppSettings CopyTo(AppSettings target) { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: 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) target.AppIdRealtime = AppIdRealtime; target.AppIdFusion = AppIdFusion; target.AppIdQuantum = AppIdQuantum; target.AppIdChat = AppIdChat; target.AppIdVoice = AppIdVoice; target.AppVersion = AppVersion; target.UseNameServer = UseNameServer; target.FixedRegion = FixedRegion; target.BestRegionSummaryFromStorage = BestRegionSummaryFromStorage; target.Server = Server; target.Port = Port; target.ProxyServer = ProxyServer; target.Protocol = Protocol; target.AuthMode = AuthMode; target.EnableLobbyStatistics = EnableLobbyStatistics; target.ClientLogging = ClientLogging; target.NetworkLogging = NetworkLogging; target.EnableProtocolFallback = EnableProtocolFallback; return target; } } public static class AsyncExtensions { internal static AsyncConfig Resolve(this AsyncConfig config) { return config ?? AsyncConfig.Global; } public static Task ConnectUsingSettingsAsync(this RealtimeClient client, AppSettings appSettings, AsyncConfig config = null) { if (client.IsConnectedAndReady && client.Server == ServerConnection.MasterServer) { return Task.CompletedTask; } return config.Resolve().TaskFactory.StartNew(delegate { if (client.State != ClientState.Disconnected && client.State != 0) { return Task.FromException(new OperationStartException("Client still connected")); } if (!client.ConnectUsingSettings(appSettings)) { return Task.FromException(new OperationStartException("Failed to start connecting")); } AsyncOperationHandler handler = client.CreateConnectionHandler(throwOnErrors: true, config.Resolve()); handler.Name = "ConnectUsingSettings"; handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnDisconnectedMsg m) { handler.SetException(new DisconnectException(m.cause)); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnCustomAuthenticationFailedMsg m) { handler.SetException(new AuthenticationFailedException(m.debugMessage)); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate { handler.SetResult(0); })); return handler.Task; }).Unwrap(); } public static Task ReconnectAndRejoinAsync(this RealtimeClient client, object ticket = null, bool throwOnError = true, AsyncConfig config = null) { return config.Resolve().TaskFactory.StartNew(delegate { if (client.State != ClientState.Disconnected && client.State != 0) { return Task.FromException(new OperationStartException("Client still connected")); } if (!client.ReconnectAndRejoin(ticket)) { return Task.FromException(new OperationStartException("Failed to start reconnecting")); } AsyncOperationHandler handler = client.CreateConnectionHandler(throwOnError, config.Resolve()); handler.Name = "ReconnectAndRejoin"; handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnDisconnectedMsg m) { handler.SetException(new DisconnectException(m.cause)); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate { handler.SetResult(0); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnJoinRoomFailedMsg m) { if (throwOnError) { handler.SetException(new OperationException(m.returnCode, m.message)); } else { handler.SetResult(m.returnCode); } })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnJoinRandomFailedMsg m) { if (throwOnError) { handler.SetException(new OperationException(m.returnCode, m.message)); } else { handler.SetResult(m.returnCode); } })); return handler.Task; }).Unwrap(); } [Obsolete("Use ReconnectAndRejoinAsync(this RealtimeClient client, object ticket, bool throwOnError, AsyncConfig config) instead")] public static Task ReconnectAndRejoinAsync(this RealtimeClient client, bool throwOnError = true, AsyncConfig config = null) { return client.ReconnectAndRejoinAsync(null, throwOnError, config); } public static Task ReconnectToMasterAsync(this RealtimeClient client, AsyncConfig config = null) { return config.Resolve().TaskFactory.StartNew(delegate { if (client.State != ClientState.Disconnected && client.State != 0) { return Task.FromException(new OperationStartException("Client still connected")); } if (!client.ReconnectToMaster()) { return Task.FromException(new OperationStartException("Failed to start reconnecting")); } AsyncOperationHandler handler = client.CreateConnectionHandler(throwOnErrors: true, config.Resolve()); handler.Name = "ReconnectToMaster"; handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnDisconnectedMsg m) { handler.SetException(new DisconnectException(m.cause)); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate { handler.SetResult(0); })); return handler.Task; }).Unwrap(); } public static Task DisconnectAsync(this RealtimeClient client, AsyncConfig config = null) { return config.Resolve().TaskFactory.StartNew(delegate { //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) if (client == null) { return Task.CompletedTask; } if (client.State == ClientState.Disconnected || client.State == ClientState.PeerCreated) { return Task.CompletedTask; } AsyncOperationHandler handler = client.CreateConnectionHandler(throwOnErrors: true, config.Resolve()); handler.Name = "Disconnect"; LogLevel logLevel = client.LogLevel; handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate { handler.SetResult(0); })); if (client.State != ClientState.Disconnecting) { client.Disconnect(); } return handler.Task; }).Unwrap(); } public static Task ConnectToNameserverAndWaitForRegionsAsync(this RealtimeClient client, AppSettings appSettings, bool pingRegions = true, AsyncConfig config = null) { AsyncConfig asyncConfig = config.Resolve(); return asyncConfig.TaskFactory.StartNew(delegate { if (client.State != ClientState.ConnectedToNameServer && client.State != ClientState.ConnectingToNameServer) { if (client.State != ClientState.Disconnected && client.State != 0) { return Task.FromException(new OperationStartException($"Client state ({client.State}) unuseable for name server connection.")); } AppSettings appSettings2 = new AppSettings(appSettings) { FixedRegion = null }; client.GetRegions(appSettings2, pingRegions); } if (client.RegionHandler?.EnabledRegions != null) { RegionHandler regionHandler = client.RegionHandler; if (regionHandler == null || regionHandler.EnabledRegions.Count > 0) { return Task.FromResult(client.RegionHandler); } } AsyncOperationHandler handler = client.CreateConnectionHandler(throwOnErrors: true, config.Resolve()); handler.Name = "ConnectToNameserverAndWaitForRegions"; client.CallbackMessage.ListenManual(delegate(OnRegionListReceivedMsg m) { if (pingRegions) { m.regionHandler.PingAvailableRegions(delegate { handler.SetResult(0); }); } else { handler.SetResult(0); } }); Task task = handler.Task.ContinueWith((Task c) => client.RegionHandler, asyncConfig.TaskScheduler); task.ContinueWith((Task c) => client.DisconnectAsync(), asyncConfig.TaskScheduler); return task; }).Unwrap(); } public static Task CreateAndJoinRoomAsync(this RealtimeClient client, EnterRoomArgs enterRoomArgs, bool throwOnError = true, AsyncConfig config = null) { return config.Resolve().TaskFactory.StartNew(delegate { if (!client.OpCreateRoom(enterRoomArgs)) { return Task.FromException(new OperationStartException("Failed to send CreateRoom operation")); } AsyncOperationHandler handler = client.CreateConnectionHandler(throwOnError, config.Resolve()); handler.Name = "CreateAndJoinRoom"; handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnDisconnectedMsg m) { handler.SetException(new DisconnectException(m.cause)); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate { handler.SetResult(0); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnCreateRoomFailedMsg m) { if (throwOnError) { handler.SetException(new OperationException(m.returnCode, m.message)); } else { handler.SetResult(m.returnCode); } })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnJoinRoomFailedMsg m) { if (throwOnError) { handler.SetException(new OperationException(m.returnCode, m.message)); } else { handler.SetResult(m.returnCode); } })); return handler.Task; }).Unwrap(); } public static Task JoinRoomAsync(this RealtimeClient client, EnterRoomArgs enterRoomArgs, bool throwOnError = true, AsyncConfig config = null) { return config.Resolve().TaskFactory.StartNew(delegate { if (!client.OpJoinRoom(enterRoomArgs)) { return Task.FromException(new OperationStartException("Failed to send JoinRoom operation")); } AsyncOperationHandler handler = client.CreateConnectionHandler(throwOnError, config.Resolve()); handler.Name = "JoinRoom"; handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnDisconnectedMsg m) { handler.SetException(new DisconnectException(m.cause)); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate { handler.SetResult(0); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnJoinRoomFailedMsg m) { if (throwOnError) { handler.SetException(new OperationException(m.returnCode, m.message)); } else { handler.SetResult(m.returnCode); } })); return handler.Task; }).Unwrap(); } public static Task RejoinRoomAsync(this RealtimeClient client, string roomName, object ticket = null, bool throwOnError = true, AsyncConfig config = null) { return config.Resolve().TaskFactory.StartNew(delegate { if (client.State != ClientState.ConnectedToMasterServer) { return Task.FromException(new OperationStartException("Must be connected to master server")); } if (!client.OpRejoinRoom(roomName, ticket)) { return Task.FromException(new OperationStartException("Failed to send RejoinRoom operation")); } AsyncOperationHandler handler = client.CreateConnectionHandler(throwOnError, config.Resolve()); handler.Name = "RejoinRoom"; handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnDisconnectedMsg m) { handler.SetException(new DisconnectException(m.cause)); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate { handler.SetResult(0); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnJoinRoomFailedMsg m) { if (throwOnError) { handler.SetException(new OperationException(m.returnCode, m.message)); } else { handler.SetResult(m.returnCode); } })); return handler.Task; }).Unwrap(); } public static Task JoinOrCreateRoomAsync(this RealtimeClient client, EnterRoomArgs enterRoomArgs, bool throwOnError = true, AsyncConfig config = null) { return config.Resolve().TaskFactory.StartNew(delegate { if (!client.OpJoinOrCreateRoom(enterRoomArgs)) { return Task.FromException(new OperationStartException("Failed to send JoinRoom operation")); } AsyncOperationHandler handler = client.CreateConnectionHandler(throwOnError, config.Resolve()); handler.Name = "JoinOrCreateRoom"; handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnDisconnectedMsg m) { handler.SetException(new DisconnectException(m.cause)); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate { handler.SetResult(0); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnCreateRoomFailedMsg m) { if (throwOnError) { handler.SetException(new OperationException(m.returnCode, m.message)); } else { handler.SetResult(m.returnCode); } })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnJoinRoomFailedMsg m) { if (throwOnError) { handler.SetException(new OperationException(m.returnCode, m.message)); } else { handler.SetResult(m.returnCode); } })); return handler.Task; }).Unwrap(); } public static Task JoinRandomOrCreateRoomAsync(this RealtimeClient client, JoinRandomRoomArgs joinRandomRoomParams = null, EnterRoomArgs enterRoomArgs = null, bool throwOnError = true, AsyncConfig config = null) { return config.Resolve().TaskFactory.StartNew(delegate { if (!client.OpJoinRandomOrCreateRoom(joinRandomRoomParams, enterRoomArgs)) { return Task.FromException(new OperationStartException("Failed to send JoinRandomOrCreateRoom operation")); } AsyncOperationHandler handler = client.CreateConnectionHandler(throwOnError, config.Resolve()); handler.Name = "JoinRandomOrCreateRoom"; handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnDisconnectedMsg m) { handler.SetException(new DisconnectException(m.cause)); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate { handler.SetResult(0); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnCreateRoomFailedMsg m) { if (throwOnError) { handler.SetException(new OperationException(m.returnCode, m.message)); } else { handler.SetResult(m.returnCode); } })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnJoinRandomFailedMsg m) { if (throwOnError) { handler.SetException(new OperationException(m.returnCode, m.message)); } else { handler.SetResult(m.returnCode); } })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnJoinRoomFailedMsg m) { if (throwOnError) { handler.SetException(new OperationException(m.returnCode, m.message)); } else { handler.SetResult(m.returnCode); } })); return handler.Task; }).Unwrap(); } public static Task JoinRandomRoomAsync(this RealtimeClient client, JoinRandomRoomArgs joinRandomRoomParams = null, bool throwOnError = true, AsyncConfig config = null) { return config.Resolve().TaskFactory.StartNew(delegate { if (!client.OpJoinRandomRoom(joinRandomRoomParams)) { return Task.FromException(new OperationStartException("Failed to send JoinRandomRoom operation")); } AsyncOperationHandler handler = client.CreateConnectionHandler(throwOnError, config.Resolve()); handler.Name = "JoinRandomRoom"; handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnDisconnectedMsg m) { handler.SetException(new DisconnectException(m.cause)); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate { handler.SetResult(0); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnJoinRandomFailedMsg m) { if (throwOnError) { handler.SetException(new OperationException(m.returnCode, m.message)); } else { handler.SetResult(m.returnCode); } })); return handler.Task; }).Unwrap(); } public static Task LeaveRoomAsync(this RealtimeClient client, bool becomeInactive = false, bool throwOnError = true, AsyncConfig config = null) { return config.Resolve().TaskFactory.StartNew(delegate { if (client.State != ClientState.Joined) { return Task.FromException(new OperationStartException("Must be inside a room")); } if (!client.OpLeaveRoom(becomeInactive)) { return Task.FromException(new OperationStartException("Failed to send LeaveRoom operation")); } AsyncOperationHandler handler = client.CreateConnectionHandler(throwOnError, config.Resolve()); handler.Name = "LeaveRoom"; handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnDisconnectedMsg m) { handler.SetException(new DisconnectException(m.cause)); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate { handler.SetResult(0); })); return handler.Task; }).Unwrap(); } public static Task JoinLobbyAsync(this RealtimeClient client, TypedLobby lobby = null, bool throwOnError = true, AsyncConfig config = null) { return config.Resolve().TaskFactory.StartNew(delegate { if (!client.OpJoinLobby(lobby)) { return Task.FromException(new OperationStartException("Failed to send JoinLobby operation")); } AsyncOperationHandler handler = client.CreateConnectionHandler(throwOnError, config.Resolve()); handler.Name = "JoinLobby"; handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnDisconnectedMsg m) { handler.SetException(new DisconnectException(m.cause)); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate { handler.SetResult(0); })); return handler.Task; }).Unwrap(); } public static Task LeaveLobbyAsync(this RealtimeClient client, bool throwOnError = true, AsyncConfig config = null) { if (client.State == ClientState.ConnectedToMasterServer) { return Task.FromResult((short)0); } if (client.State != ClientState.JoinedLobby) { return Task.FromException(new OperationStartException("Must be inside a lobby")); } return config.Resolve().TaskFactory.StartNew(delegate { if (!client.OpLeaveLobby()) { return Task.FromException(new OperationStartException("Failed to send LeaveLobby operation")); } AsyncOperationHandler handler = client.CreateConnectionHandler(throwOnError, config.Resolve()); handler.Name = "LeaveLobby"; handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate(OnDisconnectedMsg m) { handler.SetException(new DisconnectException(m.cause)); })); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate { handler.SetResult(0); })); return handler.Task; }).Unwrap(); } public static AsyncOperationHandler CreateConnectionHandler(this RealtimeClient client, bool throwOnErrors = true, AsyncConfig config = null) { AsyncOperationHandler asyncOperationHandler = new AsyncOperationHandler(config.Resolve().OperationTimeoutSec); client.CreateServiceTask(asyncOperationHandler.Token, asyncOperationHandler.CompletionSource, asyncOperationHandler, config.Resolve()); return asyncOperationHandler; } public static void CreateServiceTask(this RealtimeClient client, CancellationToken token, TaskCompletionSource completionSource = null, IDisposable disposable = null, AsyncConfig config = null) { DateTime now = DateTime.Now; config.Resolve().TaskFactory.StartNew((Func)async delegate { CancellationTokenSource linkedCancellationSource = null; CancellationToken combinedToken = token; if (config.Resolve().CancellationToken != CancellationToken.None) { try { linkedCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(token, config.Resolve().CancellationToken); combinedToken = linkedCancellationSource.Token; } catch (Exception ex) { Exception e = ex; completionSource?.TrySetException(e); } } if (!config.Resolve().CreateServiceTask) { await completionSource.Task; } else { while (!combinedToken.IsCancellationRequested) { try { client.Service(); await Task.Delay(config.Resolve().ServiceIntervalMs, combinedToken); } catch (OperationCanceledException) { break; } catch (Exception e2) { completionSource?.TrySetException(e2); break; } } } if (!token.IsCancellationRequested && completionSource != null) { switch (completionSource.Task.Status) { default: completionSource.TrySetException(new OperationCanceledException("Operation canceled")); break; case TaskStatus.RanToCompletion: case TaskStatus.Canceled: case TaskStatus.Faulted: break; } } disposable?.Dispose(); linkedCancellationSource?.Dispose(); }, TaskCreationOptions.LongRunning); } public static Task WaitForDisconnect(this RealtimeClient client, AsyncConfig config = null) { return config.Resolve().TaskFactory.StartNew(delegate { if (client == null) { return Task.CompletedTask; } if (client.State == ClientState.Disconnected || client.State == ClientState.PeerCreated) { return Task.CompletedTask; } AsyncOperationHandler handler = new AsyncOperationHandler(); handler.Disposables.Enqueue(client.CallbackMessage.ListenManual(delegate { handler.SetResult(0); })); return handler.Task; }).Unwrap(); } } public class AsyncOperationHandler : IDisposable { private TaskCompletionSource _result; private CancellationTokenSource _cancellation; public Task Task => _result.Task; public TaskCompletionSource CompletionSource => _result; public CancellationToken Token => (_cancellation == null) ? CancellationToken.None : _cancellation.Token; public bool IsCancellationRequested => _cancellation != null && _cancellation.IsCancellationRequested; public ConcurrentQueue Disposables { get; private set; } public string Name { get; set; } public AsyncOperationHandler(float operationTimeoutSec) { _result = new TaskCompletionSource(); _cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(operationTimeoutSec)); _cancellation.Token.Register(delegate { SetException(new OperationTimeoutException("Operation timed out " + Name)); }); Disposables = new ConcurrentQueue(); } public AsyncOperationHandler() { _result = new TaskCompletionSource(); Disposables = new ConcurrentQueue(); } public void SetResult(short result) { if (_result.TrySetResult(result)) { if (_cancellation != null && !_cancellation.IsCancellationRequested) { _cancellation.Cancel(); } Dispose(); } } public void SetException(Exception e) { if (_result.TrySetException(e)) { if (_cancellation != null && !_cancellation.IsCancellationRequested) { _cancellation.Cancel(); } Dispose(); } } public void Dispose() { _cancellation?.Dispose(); _cancellation = null; IDisposable result; while (Disposables.TryDequeue(out result)) { result.Dispose(); } } } public class AsyncConfig { public static AsyncConfig Global = new AsyncConfig(); public bool CreateServiceTask { get; set; } = true; public int ServiceIntervalMs { get; set; } = 10; public float OperationTimeoutSec { get; set; } = 15f; public TaskFactory TaskFactory { get; set; } = Task.Factory; public TaskScheduler TaskScheduler { get { if (TaskFactory?.Scheduler != null) { return TaskFactory.Scheduler; } return TaskScheduler.Default; } } public CancellationToken CancellationToken { get; set; } public bool IsCancellationRequested => CancellationToken.IsCancellationRequested; public static void InitForUnity() { Global = CreateUnityAsyncConfig(); } public static AsyncConfig CreateUnityAsyncConfig() { return new AsyncConfig { TaskFactory = CreateUnityTaskFactory() }; } public static TaskFactory CreateUnityTaskFactory() { return new TaskFactory(CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskContinuationOptions.DenyChildAttach | TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.FromCurrentSynchronizationContext()); } } public class ConnectionServiceScope : IDisposable { private CancellationTokenSource cancellationTokenSource; public ConnectionServiceScope(RealtimeClient client, AsyncConfig config = null) { cancellationTokenSource = new CancellationTokenSource(); client.CreateServiceTask(cancellationTokenSource.Token, null, null, config.Resolve()); } public void Dispose() { cancellationTokenSource.Cancel(); cancellationTokenSource.Dispose(); cancellationTokenSource = null; } } public class DisconnectException : Exception { public DisconnectCause Cause; public DisconnectException(DisconnectCause cause) : base($"DisconnectException: {cause}") { Cause = cause; } } public class AuthenticationFailedException : Exception { public AuthenticationFailedException(string message) : base(message) { } } public class OperationException : Exception { public short ErrorCode; public OperationException(short errorCode, string message) : base($"{message} (ErrorCode: {errorCode})") { ErrorCode = errorCode; } } public class OperationStartException : Exception { public OperationStartException(string message) : base(message) { } } public class OperationTimeoutException : Exception { public OperationTimeoutException(string message) : base(message) { } } public class AuthenticationValues { private CustomAuthenticationType authType = CustomAuthenticationType.None; public CustomAuthenticationType AuthType { get { return authType; } set { authType = value; } } public string AuthGetParameters { get; set; } public object AuthPostData { get; private set; } protected internal object Token { get; set; } public string UserId { get; set; } public AuthenticationValues() { } public AuthenticationValues(string userId) { UserId = userId; } public virtual void SetAuthPostData(string stringData) { AuthPostData = (string.IsNullOrEmpty(stringData) ? null : stringData); } public virtual void SetAuthPostData(byte[] byteData) { AuthPostData = byteData; } public virtual void SetAuthPostData(Dictionary dictData) { AuthPostData = dictData; } public virtual void AddAuthParameter(string key, string value) { string text = (string.IsNullOrEmpty(AuthGetParameters) ? "" : "&"); AuthGetParameters = $"{AuthGetParameters}{text}{Uri.EscapeDataString(key)}={Uri.EscapeDataString(value)}"; } public virtual bool AreValid() { switch (authType) { case CustomAuthenticationType.Steam: return AuthGetParametersContain("ticket"); case CustomAuthenticationType.Facebook: case CustomAuthenticationType.NintendoSwitch: case CustomAuthenticationType.Epic: case CustomAuthenticationType.FacebookGaming: return AuthGetParametersContain("token"); case CustomAuthenticationType.Oculus: return AuthGetParametersContain("userid", "nonce"); case CustomAuthenticationType.PlayStation4: case CustomAuthenticationType.PlayStation5: return AuthGetParametersContain("userName", "token", "env"); case CustomAuthenticationType.Xbox: return AuthPostData != null; case CustomAuthenticationType.Viveport: return AuthGetParametersContain("userToken"); default: return true; } } public bool AuthGetParametersContain(params string[] keys) { if (string.IsNullOrEmpty(AuthGetParameters)) { return false; } if (keys == null) { return true; } foreach (string text in keys) { string pattern = ".*" + text + "=\\w+"; if (!Regex.IsMatch(AuthGetParameters, pattern)) { return false; } } return true; } public override string ToString() { return string.Format("AuthenticationValues = AuthType: {0} UserId: {1}{2}{3}{4}", AuthType, UserId, string.IsNullOrEmpty(AuthGetParameters) ? " GetParameters: yes" : "", (AuthPostData == null) ? "" : " PostData: yes", (Token == null) ? "" : " Token: yes"); } public AuthenticationValues CopyTo(AuthenticationValues copy) { copy.AuthType = AuthType; copy.AuthGetParameters = AuthGetParameters; copy.AuthPostData = AuthPostData; copy.UserId = UserId; return copy; } } public interface IConnectionCallbacks { [Obsolete("Use OnConnectedToMaster or check the debug logging if you need to know if the client ever connected.")] void OnConnected(); void OnConnectedToMaster(); void OnDisconnected(DisconnectCause cause); void OnRegionListReceived(RegionHandler regionHandler); void OnCustomAuthenticationResponse(Dictionary data); void OnCustomAuthenticationFailed(string debugMessage); } [Obsolete("Rely on OnConnectedToMasterMsg or on the debug logging.")] public class OnConnectedMsg { } public class OnConnectedToMasterMsg { } public class OnDisconnectedMsg { public DisconnectCause cause; } public class OnRegionListReceivedMsg { public RegionHandler regionHandler; } public class OnCustomAuthenticationResponseMsg { public Dictionary data; } public class OnCustomAuthenticationFailedMsg { public string debugMessage; } public interface ILobbyCallbacks { void OnJoinedLobby(); void OnLeftLobby(); void OnRoomListUpdate(List roomList); void OnLobbyStatisticsUpdate(List lobbyStatistics); } public class OnJoinedLobbyMsg { } public class OnLeftLobbyMsg { } public class OnRoomListUpdateMsg { public List roomList; internal OnRoomListUpdateMsg(List roomList) { this.roomList = roomList; } } public class OnLobbyStatisticsUpdateMsg { public List lobbyStatistics; internal OnLobbyStatisticsUpdateMsg(List lobbyStatistics) { this.lobbyStatistics = lobbyStatistics; } } public interface IMatchmakingCallbacks { void OnFriendListUpdate(List friendList); void OnCreatedRoom(); void OnCreateRoomFailed(short returnCode, string message); void OnJoinedRoom(); void OnJoinRoomFailed(short returnCode, string message); void OnJoinRandomFailed(short returnCode, string message); void OnLeftRoom(); } public class OnFriendListUpdateMsg { public List friendList; internal OnFriendListUpdateMsg(List friendList) { this.friendList = friendList; } } public class OnCreatedRoomMsg { } public class OnCreateRoomFailedMsg { public short returnCode; public string message; internal OnCreateRoomFailedMsg(short returnCode, string message) { this.returnCode = returnCode; this.message = message; } } public class OnJoinedRoomMsg { } public class OnJoinRoomFailedMsg { public short returnCode; public string message; internal OnJoinRoomFailedMsg(short returnCode, string message) { this.returnCode = returnCode; this.message = message; } } public class OnJoinRandomFailedMsg { public short returnCode; public string message; internal OnJoinRandomFailedMsg(short returnCode, string message) { this.returnCode = returnCode; this.message = message; } } public class OnLeftRoomMsg { } public interface IInRoomCallbacks { void OnPlayerEnteredRoom(Player newPlayer); void OnPlayerLeftRoom(Player otherPlayer); void OnRoomPropertiesUpdate(PhotonHashtable propertiesThatChanged); void OnPlayerPropertiesUpdate(Player targetPlayer, PhotonHashtable changedProps); void OnMasterClientSwitched(Player newMasterClient); } public class OnPlayerEnteredRoomMsg { public Player newPlayer; internal OnPlayerEnteredRoomMsg(Player newPlayer) { this.newPlayer = newPlayer; } } public class OnPlayerLeftRoomMsg { public Player otherPlayer; internal OnPlayerLeftRoomMsg(Player otherPlayer) { this.otherPlayer = otherPlayer; } } public class OnRoomPropertiesUpdateMsg { public PhotonHashtable changedProps; [Obsolete("Use changedProps.")] public PhotonHashtable propertiesThatChanged { get { return changedProps; } set { changedProps = value; } } internal OnRoomPropertiesUpdateMsg(PhotonHashtable propertiesThatChanged) { changedProps = propertiesThatChanged; } } public class OnPlayerPropertiesUpdateMsg { public Player targetPlayer; public PhotonHashtable changedProps; internal OnPlayerPropertiesUpdateMsg(Player targetPlayer, PhotonHashtable changedProps) { this.targetPlayer = targetPlayer; this.changedProps = changedProps; } } public class OnMasterClientSwitchedMsg { public Player newMasterClient; internal OnMasterClientSwitchedMsg(Player newMasterClient) { this.newMasterClient = newMasterClient; } } public interface IErrorInfoCallback { void OnErrorInfo(ErrorInfo errorInfo); } public class OnErrorInfoMsg { public ErrorInfo errorInfo; internal OnErrorInfoMsg(ErrorInfo errorInfo) { this.errorInfo = errorInfo; } } public interface IOnEventCallback { void OnEvent(EventData photonEvent); } public interface IOnMessageCallback { void OnMessage(bool isRawMessage, object message); } internal class CallbackTargetChange { public readonly object Target; public readonly bool AddTarget; public CallbackTargetChange(object target, bool addTarget) { Target = target; AddTarget = addTarget; } } public class ConnectionCallbacksContainer : List, IConnectionCallbacks { private readonly RealtimeClient client; public ConnectionCallbacksContainer(RealtimeClient client) { this.client = client; } public void OnConnected() { } public void OnConnectedToMaster() { client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { IConnectionCallbacks current = enumerator.Current; current.OnConnectedToMaster(); } } client.CallbackMessage.Raise(new OnConnectedToMasterMsg()); } public void OnRegionListReceived(RegionHandler regionHandler) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) string text = client.AppSettings.BestRegionSummaryFromStorage ?? "N/A"; Log.Info("OnRegionListReceived(" + regionHandler.AvailableRegionCodes + ") previous Summary: " + text, client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { IConnectionCallbacks current = enumerator.Current; current.OnRegionListReceived(regionHandler); } } client.CallbackMessage.Raise(new OnRegionListReceivedMsg { regionHandler = regionHandler }); } public void OnDisconnected(DisconnectCause cause) { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) if (client.Handler != null) { client.Handler.RemoveInstance(); } string empty = string.Empty; if (cause != DisconnectCause.ApplicationQuit && cause != DisconnectCause.DisconnectByClientLogic) { Log.Warn($"OnDisconnected({cause}) PeerId: {client.RealtimePeer.PeerID} SystemConnectionSummary: {client.SystemConnectionSummary} Server: {client.CurrentServerAddress}", client.LogLevel, client.LogPrefix); } else { Log.Info($"OnDisconnected({cause})", client.LogLevel, client.LogPrefix); } client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { IConnectionCallbacks current = enumerator.Current; current.OnDisconnected(cause); } } client.CallbackMessage.Raise(new OnDisconnectedMsg { cause = cause }); } public void OnCustomAuthenticationResponse(Dictionary data) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) Log.Info("OnCustomAuthenticationResponse()", client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { IConnectionCallbacks current = enumerator.Current; current.OnCustomAuthenticationResponse(data); } } client.CallbackMessage.Raise(new OnCustomAuthenticationResponseMsg { data = data }); } public void OnCustomAuthenticationFailed(string debugMessage) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) Log.Error("OnCustomAuthenticationFailed() debugMessage: " + debugMessage, client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { IConnectionCallbacks current = enumerator.Current; current.OnCustomAuthenticationFailed(debugMessage); } } client.CallbackMessage.Raise(new OnCustomAuthenticationFailedMsg { debugMessage = debugMessage }); } } public class MatchMakingCallbacksContainer : List, IMatchmakingCallbacks { private readonly RealtimeClient client; public MatchMakingCallbacksContainer(RealtimeClient client) { this.client = client; } public void OnCreatedRoom() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) Log.Info($"OnCreatedRoom() name: {client.CurrentRoom}", client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { IMatchmakingCallbacks current = enumerator.Current; current.OnCreatedRoom(); } } client.CallbackMessage.Raise(new OnCreatedRoomMsg()); } public void OnJoinedRoom() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) Log.Info($"OnJoinedRoom() {client.CurrentRoom}", client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { IMatchmakingCallbacks current = enumerator.Current; current.OnJoinedRoom(); } } client.CallbackMessage.Raise(new OnJoinedRoomMsg()); } public void OnCreateRoomFailed(short returnCode, string message) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) Log.Error($"OnCreateRoomFailed({returnCode}, \"{message}\")", client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { IMatchmakingCallbacks current = enumerator.Current; current.OnCreateRoomFailed(returnCode, message); } } client.CallbackMessage.Raise(new OnCreateRoomFailedMsg(returnCode, message)); } public void OnJoinRandomFailed(short returnCode, string message) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) Log.Warn($"OnJoinRandomFailed({returnCode}, \"{message}\")", client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { IMatchmakingCallbacks current = enumerator.Current; current.OnJoinRandomFailed(returnCode, message); } } client.CallbackMessage.Raise(new OnJoinRandomFailedMsg(returnCode, message)); } public void OnJoinRoomFailed(short returnCode, string message) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) Log.Error($"OnJoinRoomFailed({returnCode}, \"{message}\")", client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { IMatchmakingCallbacks current = enumerator.Current; current.OnJoinRoomFailed(returnCode, message); } } client.CallbackMessage.Raise(new OnJoinRoomFailedMsg(returnCode, message)); } public void OnLeftRoom() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) Log.Info("OnLeftRoom()", client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { IMatchmakingCallbacks current = enumerator.Current; current.OnLeftRoom(); } } client.CallbackMessage.Raise(new OnLeftRoomMsg()); } public void OnFriendListUpdate(List friendList) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) Log.Debug("OnFriendListUpdate()", client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { IMatchmakingCallbacks current = enumerator.Current; current.OnFriendListUpdate(friendList); } } client.CallbackMessage.Raise(new OnFriendListUpdateMsg(friendList)); } } internal class InRoomCallbacksContainer : List, IInRoomCallbacks { private readonly RealtimeClient client; public InRoomCallbacksContainer(RealtimeClient client) { this.client = client; } public void OnPlayerEnteredRoom(Player newPlayer) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) Log.Info($"OnPlayerEnteredRoom() Player: {newPlayer}", client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { IInRoomCallbacks current = enumerator.Current; current.OnPlayerEnteredRoom(newPlayer); } } client.CallbackMessage.Raise(new OnPlayerEnteredRoomMsg(newPlayer)); } public void OnPlayerLeftRoom(Player otherPlayer) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) Log.Info($"OnPlayerLeftRoom() Player: {otherPlayer}", client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { IInRoomCallbacks current = enumerator.Current; current.OnPlayerLeftRoom(otherPlayer); } } client.CallbackMessage.Raise(new OnPlayerLeftRoomMsg(otherPlayer)); } public void OnRoomPropertiesUpdate(PhotonHashtable propertiesThatChanged) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) Log.Debug("OnRoomPropertiesUpdate()", client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { IInRoomCallbacks current = enumerator.Current; current.OnRoomPropertiesUpdate(propertiesThatChanged); } } client.CallbackMessage.Raise(new OnRoomPropertiesUpdateMsg(propertiesThatChanged)); } public void OnPlayerPropertiesUpdate(Player targetPlayer, PhotonHashtable changedProp) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) Log.Debug("OnPlayerPropertiesUpdate()", client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { IInRoomCallbacks current = enumerator.Current; current.OnPlayerPropertiesUpdate(targetPlayer, changedProp); } } client.CallbackMessage.Raise(new OnPlayerPropertiesUpdateMsg(targetPlayer, changedProp)); } public void OnMasterClientSwitched(Player newMasterClient) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) Log.Info("OnMasterClientSwitched()", client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { IInRoomCallbacks current = enumerator.Current; current.OnMasterClientSwitched(newMasterClient); } } client.CallbackMessage.Raise(new OnMasterClientSwitchedMsg(newMasterClient)); } } internal class LobbyCallbacksContainer : List, ILobbyCallbacks { private readonly RealtimeClient client; public LobbyCallbacksContainer(RealtimeClient client) { this.client = client; } public void OnJoinedLobby() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) Log.Info("OnJoinedLobby()", client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { ILobbyCallbacks current = enumerator.Current; current.OnJoinedLobby(); } } client.CallbackMessage.Raise(new OnJoinedLobbyMsg()); } public void OnLeftLobby() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) Log.Info("OnLeftLobby()", client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { ILobbyCallbacks current = enumerator.Current; current.OnLeftLobby(); } } client.CallbackMessage.Raise(new OnLeftLobbyMsg()); } public void OnRoomListUpdate(List roomList) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) Log.Debug("OnRoomListUpdate()", client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { ILobbyCallbacks current = enumerator.Current; current.OnRoomListUpdate(roomList); } } client.CallbackMessage.Raise(new OnRoomListUpdateMsg(roomList)); } public void OnLobbyStatisticsUpdate(List lobbyStatistics) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) Log.Debug("OnLobbyStatisticsUpdate()", client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { ILobbyCallbacks current = enumerator.Current; current.OnLobbyStatisticsUpdate(lobbyStatistics); } } client.CallbackMessage.Raise(new OnLobbyStatisticsUpdateMsg(lobbyStatistics)); } } internal class ErrorInfoCallbacksContainer : List, IErrorInfoCallback { private RealtimeClient client; public ErrorInfoCallbacksContainer(RealtimeClient client) { this.client = client; } public void OnErrorInfo(ErrorInfo errorInfo) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) Log.Error("OnErrorInfo(" + errorInfo.Info + ")", client.LogLevel, client.LogPrefix); client.UpdateCallbackTargets(); using (Enumerator enumerator = GetEnumerator()) { while (enumerator.MoveNext()) { IErrorInfoCallback current = enumerator.Current; current.OnErrorInfo(errorInfo); } } client.CallbackMessage.Raise(new OnErrorInfoMsg(errorInfo)); } } public class ConnectionHandler { public string Id; [Obsolete("After the KeepAliveInBackground, the client will always properly disconnect with DisconnectCause.ClientServiceInactivity.")] public bool DisconnectAfterKeepAlive = false; public int KeepAliveInBackground = 60000; [NonSerialized] public static bool AppQuits; [NonSerialized] public static bool AppPause; [NonSerialized] public static bool AppPauseRecent; [NonSerialized] public static bool AppOutOfFocus; [NonSerialized] public static bool AppOutOfFocusRecent; private bool didSendAcks; private bool didWarnAboutMissingService; private int timeWarnAboutMissingService = 5000; private readonly Stopwatch backgroundStopwatch = new Stopwatch(); private Timer stateTimer; public RealtimeClient Client { get; set; } public int CountSendAcksOnly { get; private set; } public bool FallbackThreadRunning { get; private set; } public static ConnectionHandler BuildInstance(RealtimeClient client, string id = null) { ConnectionHandler connectionHandler = new ConnectionHandler(); connectionHandler.Id = id; connectionHandler.Client = client; return connectionHandler; } public void RemoveInstance() { StopFallbackSendAckThread(); } public static bool IsNetworkReachableUnity() { return true; } public void StartFallbackSendAckThread() { if (stateTimer == null) { stateTimer = new Timer(RealtimeFallback, null, 50, 50); FallbackThreadRunning = true; } } public void StopFallbackSendAckThread() { if (stateTimer != null) { stateTimer.Dispose(); stateTimer = null; } FallbackThreadRunning = false; } public void RealtimeFallbackInvoke() { RealtimeFallback(); } public void RealtimeFallback(object state = null) { //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) if (Client == null) { return; } if (Client.IsConnected && Client.RealtimePeer.ConnectionTime - Client.RealtimePeer.Stats.LastSendOutgoingTimestamp > 100) { if (!didSendAcks) { backgroundStopwatch.Restart(); } if (backgroundStopwatch.ElapsedMilliseconds > KeepAliveInBackground) { Client.Disconnect(DisconnectCause.ClientServiceInactivity); StopFallbackSendAckThread(); return; } didSendAcks = true; CountSendAcksOnly++; if (!didWarnAboutMissingService && backgroundStopwatch.ElapsedMilliseconds > timeWarnAboutMissingService) { didWarnAboutMissingService = true; if (Client.State == ClientState.Disconnecting) { Log.Warn($"The RealtimeClient is in Disconnecting state but DispatchIncomingCommands() wasn't called for > {timeWarnAboutMissingService} seconds. Continue to call DispatchIncomingCommands() after Disconnect() to get the OnDisconnected callback.", Client.LogLevel, Client.LogPrefix); } else { Log.Warn($"RealtimeClient.SendOutgoingCommands() was not called for > {timeWarnAboutMissingService} seconds. After the KeepAliveInBackground ({KeepAliveInBackground / 1000}sec) this causes a disconnect.", Client.LogLevel, Client.LogPrefix); } } Client.RealtimePeer.SendAcksOnly(); } else { if (backgroundStopwatch.IsRunning) { backgroundStopwatch.Reset(); } didSendAcks = false; } } } public class SystemConnectionSummary { internal class SCSBitPos { internal const int Version = 28; internal const int UsedProtocol = 25; internal const int EmptyBit = 24; internal const int AppQuits = 23; internal const int AppPause = 22; internal const int AppPauseRecent = 21; internal const int AppOutOfFocus = 20; internal const int AppOutOfFocusRecent = 19; internal const int NetworkReachable = 18; internal const int ErrorCodeFits = 17; internal const int ErrorCodeWinSock = 16; } public readonly byte Version = 0; public byte UsedProtocol; public bool AppQuits; public bool AppPause; public bool AppPauseRecent; public bool AppOutOfFocus; public bool AppOutOfFocusRecent; public bool NetworkReachable; public bool ErrorCodeFits; public bool ErrorCodeWinSock; public int SocketErrorCode; private static readonly string[] ProtocolIdToName = new string[8] { "UDP", "TCP", "2(N/A)", "3(N/A)", "WS", "WSS", "6(N/A)", "7WebRTC" }; public SystemConnectionSummary(RealtimeClient client) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (client != null) { UsedProtocol = (byte)(client.RealtimePeer.UsedProtocol & 7); SocketErrorCode = client.RealtimePeer.SocketErrorCode; } AppQuits = ConnectionHandler.AppQuits; AppPause = ConnectionHandler.AppPause; AppPauseRecent = ConnectionHandler.AppPauseRecent; AppOutOfFocus = ConnectionHandler.AppOutOfFocus; AppOutOfFocusRecent = ConnectionHandler.AppOutOfFocusRecent; NetworkReachable = ConnectionHandler.IsNetworkReachableUnity(); ErrorCodeFits = SocketErrorCode <= 32767; ErrorCodeWinSock = true; } public SystemConnectionSummary(int summary) { Version = GetBits(ref summary, 28, 15); UsedProtocol = GetBits(ref summary, 25, 7); AppQuits = GetBit(ref summary, 23); AppPause = GetBit(ref summary, 22); AppPauseRecent = GetBit(ref summary, 21); AppOutOfFocus = GetBit(ref summary, 20); AppOutOfFocusRecent = GetBit(ref summary, 19); NetworkReachable = GetBit(ref summary, 18); ErrorCodeFits = GetBit(ref summary, 17); ErrorCodeWinSock = GetBit(ref summary, 16); SocketErrorCode = summary & 0xFFFF; } public int ToInt() { int value = 0; SetBits(ref value, Version, 28); SetBits(ref value, UsedProtocol, 25); SetBit(ref value, AppQuits, 23); SetBit(ref value, AppPause, 22); SetBit(ref value, AppPauseRecent, 21); SetBit(ref value, AppOutOfFocus, 20); SetBit(ref value, AppOutOfFocusRecent, 19); SetBit(ref value, NetworkReachable, 18); SetBit(ref value, ErrorCodeFits, 17); SetBit(ref value, ErrorCodeWinSock, 16); int num = SocketErrorCode & 0xFFFF; return value | num; } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); string arg = ProtocolIdToName[UsedProtocol]; stringBuilder.Append($"SCS v{Version} {arg} SocketErrorCode: {SocketErrorCode} "); if (AppQuits) { stringBuilder.Append("AppQuits "); } if (AppPause) { stringBuilder.Append("AppPause "); } if (!AppPause && AppPauseRecent) { stringBuilder.Append("AppPauseRecent "); } if (AppOutOfFocus) { stringBuilder.Append("AppOutOfFocus "); } if (!AppOutOfFocus && AppOutOfFocusRecent) { stringBuilder.Append("AppOutOfFocusRecent "); } if (!NetworkReachable) { stringBuilder.Append("NetworkUnreachable "); } if (!ErrorCodeFits) { stringBuilder.Append("ErrorCodeRangeExceeded "); } if (ErrorCodeWinSock) { stringBuilder.Append("WinSock"); } else { stringBuilder.Append("BSDSock"); } return stringBuilder.ToString(); } internal static bool GetBit(ref int value, int bitpos) { int num = (value >> bitpos) & 1; return num != 0; } internal static byte GetBits(ref int value, int bitpos, byte mask) { int num = (value >> bitpos) & mask; return (byte)num; } internal static void SetBit(ref int value, bool bitval, int bitpos) { if (bitval) { value |= 1 << bitpos; } else { value &= ~(1 << bitpos); } } internal static void SetBits(ref int value, byte bitvals, int bitpos) { value |= bitvals << bitpos; } } public class EnterRoomArgs { public string RoomName; public RoomOptions RoomOptions; public TypedLobby Lobby; public string[] ExpectedUsers; public object Ticket; protected internal bool OnGameServer = true; protected internal JoinMode JoinMode; internal static EnterRoomArgs ShallowCopyToNewArgs(EnterRoomArgs o) { EnterRoomArgs enterRoomArgs = new EnterRoomArgs(); if (o != null) { enterRoomArgs.RoomName = o.RoomName; enterRoomArgs.RoomOptions = o.RoomOptions; enterRoomArgs.Lobby = o.Lobby; enterRoomArgs.ExpectedUsers = o.ExpectedUsers; enterRoomArgs.Ticket = o.Ticket; enterRoomArgs.JoinMode = o.JoinMode; } return enterRoomArgs; } } [Obsolete("Use EnterRoomArgs")] public class EnterRoomParams : EnterRoomArgs { } public enum ClientState { PeerCreated, Authenticating, Authenticated, JoiningLobby, JoinedLobby, DisconnectingFromMasterServer, ConnectingToGameServer, ConnectedToGameServer, Joining, Joined, Leaving, DisconnectingFromGameServer, ConnectingToMasterServer, Disconnecting, Disconnected, ConnectedToMasterServer, ConnectingToNameServer, ConnectedToNameServer, DisconnectingFromNameServer, ConnectWithFallbackProtocol } internal class EncryptionDataParameters { public const byte Mode = 0; public const byte Secret1 = 1; public const byte Secret2 = 2; } internal enum JoinType { CreateRoom, JoinRoom, JoinRandomRoom, JoinRandomOrCreateRoom, JoinOrCreateRoom } public enum DisconnectCause { None, ExceptionOnConnect, DnsExceptionOnConnect, ServerAddressInvalid, Exception, ServerTimeout, ClientTimeout, DisconnectByServerLogic, DisconnectByServerReasonUnknown, InvalidAuthentication, CustomAuthenticationFailed, AuthenticationTicketExpired, MaxCcuReached, InvalidRegion, OperationNotAllowedInCurrentState, DisconnectByClientLogic, DisconnectByOperationLimit, DisconnectByDisconnectMessage, ApplicationQuit, ClientServiceInactivity } public enum ServerConnection { MasterServer, GameServer, NameServer } public enum ClientAppType { Detect, Realtime, Voice, Chat, Fusion, Quantum } public enum EncryptionMode { PayloadEncryption = 0, DatagramEncryptionGCM = 13 } internal enum RoomOptionBit { CheckUserOnJoin = 1, DeleteCacheOnLeave = 2, SuppressRoomEvents = 4, PublishUserId = 8, DeleteNullProps = 0x10, BroadcastPropsChangeToAll = 0x20, SuppressPlayerInfo = 0x40 } public class ErrorCode { public const int Ok = 0; public const int OperationNotAllowedInCurrentState = -3; public const int InvalidOperation = -2; public const int InternalServerError = -1; public const int InvalidAuthentication = 32767; public const int GameIdAlreadyExists = 32766; public const int GameFull = 32765; public const int GameClosed = 32764; public const int ServerFull = 32762; public const int UserBlocked = 32761; public const int NoRandomMatchFound = 32760; public const int GameDoesNotExist = 32758; public const int MaxCcuReached = 32757; public const int InvalidRegion = 32756; public const int CustomAuthenticationFailed = 32755; public const int AuthenticationTicketExpired = 32753; public const int PluginReportedError = 32752; public const int PluginMismatch = 32751; public const int JoinFailedPeerAlreadyJoined = 32750; public const int JoinFailedFoundInactiveJoiner = 32749; public const int JoinFailedWithRejoinerNotFound = 32748; public const int JoinFailedFoundExcludedUserId = 32747; public const int JoinFailedFoundActiveJoiner = 32746; public const int HttpLimitReached = 32745; public const int ExternalHttpCallFailed = 32744; public const int OperationLimitReached = 32743; public const int SlotError = 32742; public const int InvalidEncryptionParameters = 32741; } public class ActorProperties { [Obsolete("Renamed. Use ActorProperties.NickName.")] public const byte PlayerName = byte.MaxValue; public const byte NickName = byte.MaxValue; public const byte IsInactive = 254; public const byte UserId = 253; } public class GamePropertyKey { public const byte MaxPlayers = byte.MaxValue; public const byte MaxPlayersInt = 243; public const byte IsVisible = 254; public const byte IsOpen = 253; public const byte PlayerCount = 252; public const byte Removed = 251; public const byte PropsListedInLobby = 250; public const byte CleanupCacheOnLeave = 249; public const byte MasterClientId = 248; public const byte ExpectedUsers = 247; public const byte PlayerTtl = 246; public const byte EmptyRoomTtl = 245; } public class EventCode { public const byte GameList = 230; public const byte GameListUpdate = 229; public const byte QueueState = 228; public const byte Match = 227; public const byte AppStats = 226; public const byte LobbyStats = 224; public const byte Join = byte.MaxValue; public const byte Leave = 254; public const byte PropertiesChanged = 253; public const byte ErrorInfo = 251; public const byte CacheSliceChanged = 250; public const byte AuthEvent = 223; public static byte CommandEvent = 220; } public class ParameterCode { public const byte SuppressRoomEvents = 237; public const byte EmptyRoomTTL = 236; public const byte PlayerTTL = 235; public const byte EventForward = 234; public const byte IsInactive = 233; public const byte CheckUserOnJoin = 232; public const byte ExpectedValues = 231; public const byte Address = 230; public const byte PeerCount = 229; public const byte GameCount = 228; public const byte MasterPeerCount = 227; public const byte UserId = 225; public const byte ApplicationId = 224; public const byte Position = 223; public const byte MatchMakingType = 223; public const byte GameList = 222; public const byte Token = 221; public const byte AppVersion = 220; public const byte RoomName = byte.MaxValue; public const byte Broadcast = 250; public const byte ActorList = 252; public const byte ActorNr = 254; public const byte PlayerProperties = 249; public const byte CustomEventContent = 245; public const byte Data = 245; public const byte Code = 244; public const byte GameProperties = 248; public const byte Properties = 251; public const byte TargetActorNr = 253; public const byte ReceiverGroup = 246; public const byte Cache = 247; public const byte CleanupCacheOnLeave = 241; public const byte Group = 240; public const byte Remove = 239; public const byte PublishUserId = 239; public const byte Add = 238; public const byte Info = 218; public const byte ClientAuthenticationType = 217; public const byte ClientAuthenticationParams = 216; public const byte JoinMode = 215; public const byte ClientAuthenticationData = 214; public const byte MasterClientId = 203; public const byte FindFriendsRequestList = 1; public const byte FindFriendsOptions = 2; public const byte FindFriendsResponseOnlineList = 1; public const byte FindFriendsResponseRoomIdList = 2; public const byte LobbyName = 213; public const byte LobbyType = 212; public const byte LobbyStats = 211; public const byte Region = 210; public const byte UriPath = 209; public const byte WebRpcParameters = 208; public const byte WebRpcReturnCode = 207; public const byte WebRpcReturnMessage = 206; public const byte CacheSliceIndex = 205; public const byte Plugins = 204; public const byte NickName = 202; public const byte PluginName = 201; public const byte PluginVersion = 200; public const byte Cluster = 196; public const byte ExpectedProtocol = 195; public const byte CustomInitData = 194; public const byte EncryptionMode = 193; public const byte EncryptionData = 192; public const byte RoomOptionFlags = 191; public const byte Ticket = 190; public const byte MatchmakingGroupId = 189; public const byte AllowRepeats = 188; } public class OperationCode { public const byte AuthenticateOnce = 231; public const byte Authenticate = 230; public const byte JoinLobby = 229; public const byte LeaveLobby = 228; public const byte CreateGame = 227; public const byte JoinGame = 226; public const byte JoinRandomGame = 225; public const byte Leave = 254; public const byte RaiseEvent = 253; public const byte SetProperties = 252; public const byte GetProperties = 251; public const byte ChangeGroups = 248; public const byte FindFriends = 222; public const byte GetLobbyStats = 221; public const byte GetRegions = 220; public const byte ServerSettings = 218; public const byte GetGameList = 217; } public enum JoinMode : byte { Default, CreateIfNotExists, JoinOrRejoin, RejoinOnly } public enum MatchmakingMode : byte { FillRoom, SerialMatching, RandomMatching } public enum ReceiverGroup : byte { Others, All, MasterClient } public enum EventCaching : byte { DoNotCache = 0, AddToRoomCache = 4, AddToRoomCacheGlobal = 5, RemoveFromRoomCache = 6, RemoveFromRoomCacheForActorsLeft = 7, SliceIncreaseIndex = 10, SliceSetIndex = 11, SlicePurgeIndex = 12, SlicePurgeUpToIndex = 13 } internal enum CommandEventSubcode : byte { GenerateTicket = 1, UpdateMute, UpdateProximity } [Flags] public enum PropertyTypeFlag : byte { None = 0, Game = 1, Actor = 2, GameAndActor = 3 } public enum LobbyType : byte { Default = 0, Sql = 2, [Obsolete("Use LobbyType.Sql")] SqlLobby = 2, AsyncRandom = 3, [Obsolete("Use LobbyType.AsyncRandom")] AsyncRandomLobby = 3 } public enum AuthModeOption { Auth, AuthOnce, AuthOnceWss } public enum CustomAuthenticationType : byte { Custom = 0, Steam = 1, Facebook = 2, Oculus = 3, PlayStation4 = 4, Xbox = 5, Viveport = 10, NintendoSwitch = 11, PlayStation5 = 12, Epic = 13, FacebookGaming = 15, None = byte.MaxValue } public class ErrorInfo { public readonly string Info; public ErrorInfo(EventData eventData) { Info = eventData[(byte)218] as string; } public override string ToString() { return $"ErrorInfo: {Info}"; } } public class EventBetter { public class YieldListener : IEnumerator, IDisposable where MessageType : class { private Delegate handler; internal EventBetter EventBetterInstance; internal List Messages { get; private set; } internal MessageType First { get { if (Messages == null || Messages.Count == 0) { return null; } return Messages[0]; } } object IEnumerator.Current => null; internal YieldListener() { handler = EventBetterInstance.RegisterInternal(this, delegate(MessageType msg) { OnMessage(msg); }, HandlerFlags.DontInvokeIfAddedInAHandler); } public void Dispose() { if ((object)handler != null) { EventBetterInstance.UnlistenHandler(typeof(MessageType), handler); handler = null; } } private void OnMessage(MessageType msg) { if (Messages == null) { Messages = new List(); } Messages.Add(msg); } bool IEnumerator.MoveNext() { if (Messages != null) { Dispose(); return false; } return true; } void IEnumerator.Reset() { } } private class ManualHandlerDisposable : IDisposable { private object disposeLock = new object(); public Type MessageType { get; set; } public Delegate Handler { get; set; } public EventBetter EventBetterInstance { get; set; } public void Dispose() { lock (disposeLock) { if ((object)Handler == null) { return; } try { if (EventBetterInstance != null) { EventBetterInstance.UnlistenHandler(MessageType, Handler); } } finally { MessageType = null; Handler = null; EventBetterInstance = null; } } } } [Flags] private enum HandlerFlags { None = 0, OnlyIfActiveAndEnabled = 1, Once = 2, DontInvokeIfAddedInAHandler = 4, IsUnityObject = 8 } private sealed class EventBetterWorker { } private class EventEntry { public uint invocationCount = 0u; public bool needsCleanup = false; public readonly List listeners = new List(); public readonly List handlers = new List(); public readonly List flags = new List(); public int Count => listeners.Count; public bool HasFlag(int i, HandlerFlags flag) { return (flags[i] & flag) == flag; } public void SetFlag(int i, HandlerFlags flag, bool value) { if (value) { flags[i] |= flag; } else { flags[i] &= ~flag; } } public void Add(object listener, Delegate handler, HandlerFlags flag) { if (invocationCount == 0) { flag &= ~HandlerFlags.DontInvokeIfAddedInAHandler; } listeners.Add(listener); handlers.Add(handler); flags.Add(flag); } public void NullifyAt(int i) { listeners[i] = null; handlers[i] = null; flags[i] = HandlerFlags.None; } public void RemoveAt(int i) { listeners.RemoveAt(i); handlers.RemoveAt(i); flags.RemoveAt(i); } [Conditional("SUPPORTED_UNITY")] private void UnityAssertListSize() { } } private ConcurrentDictionary s_entries = new ConcurrentDictionary(); private List s_entriesList = new List(); private EventBetterWorker s_worker; public IDisposable ListenManual(Action handler) { Delegate handler2 = RegisterInternal((object)s_entries, (Action)delegate(MessageType msg) { handler(msg); }, HandlerFlags.None); ManualHandlerDisposable manualHandlerDisposable = new ManualHandlerDisposable(); manualHandlerDisposable.Handler = handler2; manualHandlerDisposable.MessageType = typeof(MessageType); manualHandlerDisposable.EventBetterInstance = this; return manualHandlerDisposable; } public bool Raise(MessageType message) { return RaiseInternal(message); } public void Clear() { s_entries.Clear(); s_entriesList.Clear(); if (s_worker != null) { s_worker = null; } } public void RemoveUnusedHandlers() { foreach (EventEntry s_entries in s_entriesList) { RemoveUnusedHandlers(s_entries); } } public YieldListener ListenWait() where MessageType : class { return new YieldListener(); } private bool RaiseInternal(T message) { if (!s_entries.TryGetValue(typeof(T), out var value)) { return false; } bool result = false; uint num = ++value.invocationCount; try { int num2 = value.Count; for (int i = 0; i < value.Count; i++) { object aliveTarget = GetAliveTarget(value.listeners[i]); bool flag = true; if (aliveTarget != null) { if (i >= num2 && value.HasFlag(i, HandlerFlags.DontInvokeIfAddedInAHandler)) { value.SetFlag(i, HandlerFlags.DontInvokeIfAddedInAHandler, value: false); continue; } if (!value.HasFlag(i, HandlerFlags.Once)) { flag = false; } ((Action)value.handlers[i])(message); result = true; } if (flag) { if (num == 1) { value.RemoveAt(i); i--; num2--; } else { value.needsCleanup = true; value.NullifyAt(i); } } } } finally { value.invocationCount--; if (num == 1 && value.needsCleanup) { value.needsCleanup = false; RemoveUnusedHandlers(value); } } return result; } private Delegate RegisterInternal(ListenerType listener, Action handler, HandlerFlags flags) { return RegisterInternal((object)listener, handler, flags); } private Delegate RegisterInternal(object listener, Action handler, HandlerFlags flags) { if (listener == null) { throw new ArgumentNullException("listener"); } if (handler == null) { throw new ArgumentNullException("handler"); } if ((flags & HandlerFlags.IsUnityObject) == HandlerFlags.IsUnityObject) { EnsureWorkerExistsAndIsActive(); } if (!s_entries.TryGetValue(typeof(T), out var value)) { value = new EventEntry(); value = s_entries.GetOrAdd(typeof(T), value); s_entriesList.Add(value); } value.Add(listener, handler, flags); return handler; } private bool UnlistenHandler(Type messageType, Delegate handler) { return UnregisterInternal(messageType, handler, (EventEntry eventEntry, int index, Delegate _handler) => eventEntry.handlers[index] == _handler); } private bool UnregisterInternal(Type messageType, ParamType param, Func predicate) { if (!s_entries.TryGetValue(messageType, out var value)) { return false; } return UnregisterInternal(value, param, predicate); } private bool UnregisterInternal(EventEntry entry, ParamType param, Func predicate) { bool result = false; for (int i = 0; i < entry.Count; i++) { if (entry.listeners[i] != null && (predicate == null || predicate(entry, i, param))) { result = true; if (entry.invocationCount == 0) { entry.RemoveAt(i); i--; } else { entry.needsCleanup = true; entry.NullifyAt(i); } } } return result; } private object GetAliveTarget(object target) { return target; } private void RemoveUnusedHandlers(EventEntry entry) { for (int i = 0; i < entry.Count; i++) { object obj = entry.listeners[i]; if (entry.HasFlag(i, HandlerFlags.IsUnityObject) || obj == null) { if (entry.invocationCount == 0) { entry.RemoveAt(i--); } else { entry.NullifyAt(i); } } } } private void EnsureWorkerExistsAndIsActive() { } } public static class Extensions { private static readonly List keysWithNullValue = new List(); public static int GetStableHashCode(this string str) { int num = 5381; int num2 = num; for (int i = 0; i < str.Length && str[i] != 0; i += 2) { num = ((num << 5) + num) ^ str[i]; if (i == str.Length - 1 || str[i + 1] == '\0') { break; } num2 = ((num2 << 5) + num2) ^ str[i + 1]; } return num + num2 * 1566083941; } public static string ToStringFull(this PhotonHashtable origin) { return SupportClass.DictionaryToString((IDictionary)origin, false); } public static string ToStringFull(this List data) { if (data == null) { return "null"; } string[] array = new string[data.Count]; for (int i = 0; i < data.Count; i++) { object obj = data[i]; array[i] = ((obj != null) ? obj.ToString() : "null"); } return string.Join(", ", array); } public static string ToStringFull(this byte[] list, int count = -1) { return SupportClass.ByteArrayToString(list, count); } public static string ToStringFull(this ArraySegment segment, int count = -1) { if (count < 0 || count > segment.Count) { count = segment.Count; } return BitConverter.ToString(segment.Array, segment.Offset, count); } public static string ToStringFull(this object[] data) { if (data == null) { return "null"; } string[] array = new string[data.Length]; for (int i = 0; i < data.Length; i++) { object obj = data[i]; array[i] = ((obj != null) ? obj.ToString() : "null"); } return string.Join(", ", array); } public static bool CustomPropKeyTypesValid(this PhotonHashtable original, bool NullOrZeroAccepted = false) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (original == null || ((Dictionary)(object)original).Count == 0) { return NullOrZeroAccepted; } DictionaryEntryEnumerator enumerator = original.GetEnumerator(); try { while (((DictionaryEntryEnumerator)(ref enumerator)).MoveNext()) { DictionaryEntry current = ((DictionaryEntryEnumerator)(ref enumerator)).Current; if (!(current.Key is string) && !(current.Key is int)) { return false; } } } finally { ((IDisposable)(DictionaryEntryEnumerator)(ref enumerator)).Dispose(); } return true; } public static bool CustomPropKeyTypesValid(this object[] array, bool NullOrZeroAccepted = false) { if (array == null || array.Length == 0) { return NullOrZeroAccepted; } foreach (object obj in array) { if (!(obj is string) && !(obj is int)) { return false; } } return true; } public static void StripKeysWithNullValues(this PhotonHashtable original) { //IL_0020: 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) lock (keysWithNullValue) { keysWithNullValue.Clear(); DictionaryEntryEnumerator enumerator = original.GetEnumerator(); try { while (((DictionaryEntryEnumerator)(ref enumerator)).MoveNext()) { DictionaryEntry current = ((DictionaryEntryEnumerator)(ref enumerator)).Current; if (current.Value == null) { keysWithNullValue.Add(current.Key); } } } finally { ((IDisposable)(DictionaryEntryEnumerator)(ref enumerator)).Dispose(); } for (int i = 0; i < keysWithNullValue.Count; i++) { object key = keysWithNullValue[i]; ((Dictionary)(object)original).Remove(key); } } } public static void Merge(this PhotonHashtable target, PhotonHashtable addHash) { if (addHash == null || ((object)target).Equals((object?)addHash)) { return; } foreach (object key in ((Dictionary)(object)addHash).Keys) { target[key] = addHash[key]; } } public static void MergeStringKeys(this PhotonHashtable target, PhotonHashtable addHash) { //IL_0017: 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) if (addHash == null || ((object)target).Equals((object?)addHash)) { return; } DictionaryEntryEnumerator enumerator = addHash.GetEnumerator(); try { while (((DictionaryEntryEnumerator)(ref enumerator)).MoveNext()) { DictionaryEntry current = ((DictionaryEntryEnumerator)(ref enumerator)).Current; if (current.Key is string) { target[current.Key] = current.Value; } } } finally { ((IDisposable)(DictionaryEntryEnumerator)(ref enumerator)).Dispose(); } } public static bool Contains(this int[] target, int nr) { if (target == null) { return false; } for (int i = 0; i < target.Length; i++) { if (target[i] == nr) { return true; } } return false; } } public class FindFriendsArgs { public bool CreatedOnGs = false; public bool Visible = false; public bool Open = false; internal int ToIntFlags() { int num = 0; if (CreatedOnGs) { num |= 1; } if (Visible) { num |= 2; } if (Open) { num |= 4; } return num; } } [Obsolete("Use FindFriendsArgs")] public class FindFriendsOptions : FindFriendsArgs { } public class FriendInfo { public string UserId { get; protected internal set; } public bool IsOnline { get; protected internal set; } public string Room { get; protected internal set; } public bool IsInRoom => IsOnline && !string.IsNullOrEmpty(Room); public override string ToString() { return string.Format("{0}\t is: {1}", UserId, (!IsOnline) ? "offline" : (IsInRoom ? "playing" : "on master")); } } public class JoinRandomRoomArgs { public PhotonHashtable ExpectedCustomRoomProperties; public int ExpectedMaxPlayers; public MatchmakingMode MatchingType; public TypedLobby Lobby; public string SqlLobbyFilter; public string[] ExpectedUsers; public object Ticket; } [Obsolete("Use JoinRandomRoomArgs")] public class OpJoinRandomRoomParams : JoinRandomRoomArgs { } public class TypedLobbyInfo : TypedLobby { public int PlayerCount { get; private set; } public int RoomCount { get; private set; } internal TypedLobbyInfo(string name, LobbyType type, int playerCount, int roomCount) { base.Name = name; base.Type = type; PlayerCount = playerCount; RoomCount = roomCount; } public override string ToString() { return $"LobbyInfo '{base.Name}'[{base.Type}] rooms: {RoomCount}, players: {PlayerCount}]"; } } public class TypedLobby { private static readonly TypedLobby DefaultLobby = new TypedLobby(); public string Name { get; protected set; } public LobbyType Type { get; protected set; } public static TypedLobby Default => DefaultLobby; public bool IsDefault => string.IsNullOrEmpty(Name); public TypedLobby(string name, LobbyType type) { if (string.IsNullOrEmpty(name)) { Log.Warn("Make sure to always set a name when creating a new Lobby!", (LogLevel)2); } Name = name; Type = type; } public TypedLobby(TypedLobby original = null) { if (original != null) { Name = original.Name; Type = original.Type; } } public override string ToString() { return $"'{Name}'[{Type}]"; } } public static class Log { public enum PrefixOptions { None, Time, Level, TimeAndLevel } public enum LogOutputOption { Auto, Console, Debug, UnityDebug } public static PrefixOptions LogPrefix; private static Action onError; private static Action onWarn; private static Action onInfo; private static Action onDebug; private static Action onException; private static Stopwatch sw; private static readonly StringBuilder prefixesBuilder; static Log() { LogPrefix = PrefixOptions.None; prefixesBuilder = new StringBuilder(); Init(LogOutputOption.Auto); } public static void Init(LogOutputOption logOutput) { onError = null; onWarn = null; onInfo = null; onDebug = null; sw = new Stopwatch(); sw.Restart(); if (logOutput == LogOutputOption.UnityDebug || logOutput == LogOutputOption.Auto) { logOutput = LogOutputOption.Console; } switch (logOutput) { case LogOutputOption.Console: onError = delegate(string msg) { Console.WriteLine(msg); }; onWarn = delegate(string msg) { Console.WriteLine(msg); }; onInfo = delegate(string msg) { Console.WriteLine(msg); }; onDebug = delegate(string msg) { Console.WriteLine(msg); }; break; case LogOutputOption.Debug: onError = delegate(string msg) { System.Diagnostics.Debug.WriteLine(msg); }; onWarn = delegate(string msg) { System.Diagnostics.Debug.WriteLine(msg); }; onInfo = delegate(string msg) { System.Diagnostics.Debug.WriteLine(msg); }; onDebug = delegate(string msg) { System.Diagnostics.Debug.WriteLine(msg); }; break; } } public static void Init(Action error, Action warn, Action info, Action debug, Action exception) { sw = new Stopwatch(); sw.Restart(); onError = error; onWarn = warn; onInfo = info; onDebug = debug; onException = exception; } private static string ApplyPrefixes(string msg, LogLevel lvl = 1, string prefix = null) { //IL_00e3: Unknown result type (might be due to invalid IL or missing references) lock (prefixesBuilder) { prefixesBuilder.Clear(); if (LogPrefix == PrefixOptions.Time || LogPrefix == PrefixOptions.TimeAndLevel) { TimeSpan elapsed = sw.Elapsed; if (elapsed.Minutes > 0) { prefixesBuilder.Append($"[{elapsed.Minutes}:{elapsed.Seconds:D2}.{elapsed.Milliseconds:D3}]"); } else { prefixesBuilder.Append($"[{elapsed.Seconds:D2}.{elapsed.Milliseconds:D3}]"); } } if (LogPrefix == PrefixOptions.Level || LogPrefix == PrefixOptions.TimeAndLevel) { prefixesBuilder.Append($"[{lvl}]"); } if (!string.IsNullOrEmpty(prefix)) { prefixesBuilder.Append(prefix + ": "); } else if (prefixesBuilder.Length > 0) { prefixesBuilder.Append(" "); } prefixesBuilder.Append(msg); return prefixesBuilder.ToString(); } } public static void Exception(Exception ex, LogLevel lvl = 1, string prefix = null) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_001d: Unknown result type (might be due to invalid IL or missing references) if ((int)lvl >= 1 && onException != null) { string arg = ApplyPrefixes(ex.Message, lvl, prefix); onException(ex, arg); } } public static void Error(string msg, LogLevel lvl = 1, string prefix = null) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0018: Unknown result type (might be due to invalid IL or missing references) if ((int)lvl >= 1 && onError != null) { string obj = ApplyPrefixes(msg, lvl, prefix); onError(obj); } } [Conditional("DEBUG")] [Conditional("PHOTON_LOG_WARNING")] public static void Warn(string msg, LogLevel lvl = 2, string prefix = null) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0018: Unknown result type (might be due to invalid IL or missing references) if ((int)lvl >= 2 && onWarn != null) { string obj = ApplyPrefixes(msg, lvl, prefix); onWarn(obj); } } [Conditional("DEBUG")] [Conditional("PHOTON_LOG_INFO")] public static void Info(string msg, LogLevel lvl = 3, string prefix = null) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0018: Unknown result type (might be due to invalid IL or missing references) if ((int)lvl >= 3 && onInfo != null) { string obj = ApplyPrefixes(msg, lvl, prefix); onInfo(obj); } } [Conditional("DEBUG")] [Conditional("PHOTON_LOG_DEBUG")] public static void Debug(string msg, LogLevel lvl = 4, string prefix = null) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0018: Unknown result type (might be due to invalid IL or missing references) if ((int)lvl >= 4 && onDebug != null) { string obj = ApplyPrefixes(msg, lvl, prefix); onDebug(obj); } } } public static class MatchmakingExtensions { public static Task ConnectToRoomAsync(this RealtimeClient client, MatchmakingArguments arguments) { return ConnectToRoomAsync(arguments, client); } public static Task ConnectToRoomAsync(MatchmakingArguments arguments, RealtimeClient client = null) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) arguments.Validate(); if (arguments.PhotonSettings != null) { Log.Info("Connecting to room", arguments.PhotonSettings.ClientLogging); } AsyncConfig asyncConfig = arguments.AsyncConfig ?? AsyncConfig.Global; return asyncConfig.TaskFactory.StartNew(async delegate { client = client ?? arguments.NetworkClient ?? new RealtimeClient((ConnectionProtocol)0); bool isRandom = string.IsNullOrEmpty(arguments.RoomName); bool canCreate = !arguments.CanOnlyJoin; if (!client.IsConnected) { if (arguments.AuthValues != null) { client.AuthValues = arguments.AuthValues.CopyTo(new AuthenticationValues()); } client.RealtimePeer.CrcEnabled = arguments.EnableCrc; } await client.ConnectUsingSettingsAsync(arguments.PhotonSettings, asyncConfig); short result = (isRandom ? ((!canCreate) ? (await client.JoinRandomRoomAsync(arguments.BuildJoinRandomRoomArgs(), throwOnError: true, asyncConfig)) : (await client.JoinRandomOrCreateRoomAsync(arguments.BuildJoinRandomRoomArgs(), arguments.BuildEnterRoomArgs(), throwOnError: true, asyncConfig))) : ((!canCreate) ? (await client.JoinRoomAsync(arguments.BuildEnterRoomArgs(), throwOnError: true, asyncConfig)) : (await client.JoinOrCreateRoomAsync(arguments.BuildEnterRoomArgs(), throwOnError: true, asyncConfig)))); if (result == 0) { if (arguments.ReconnectInformation != null) { arguments.ReconnectInformation.Set(client); } return client; } throw new OperationException(result, $"Failed to connect and join with error '{result}'"); }).Unwrap(); } public static Task ReconnectToRoomAsync(this RealtimeClient client, MatchmakingArguments arguments) { return ReconnectToRoomAsync(arguments, client); } public static Task ReconnectToRoomAsync(MatchmakingArguments arguments, RealtimeClient client = null) { arguments.Validate(); if (arguments.ReconnectInformation == null) { throw new ArgumentException("ReconnectInformation missing"); } if (arguments.ReconnectInformation.AppVersion != arguments.PhotonSettings.AppVersion) { throw new ArgumentException("AppVersion mismatch"); } if (string.IsNullOrEmpty(arguments.ReconnectInformation.UserId)) { throw new ArgumentException("UserId not set"); } if (arguments.AuthValues != null && arguments.ReconnectInformation.UserId != arguments.AuthValues.UserId) { throw new ArgumentException("UserId mismatch"); } if (arguments.ReconnectInformation.HasTimedOut) { Log.Warn($"ReconnectInformation timed out: {arguments.ReconnectInformation.Timeout} (now = {DateTime.Now})", (LogLevel)2); } Log.Info("Reconnecting to room " + arguments.ReconnectInformation.Room, (LogLevel)3); AsyncConfig asyncConfig = arguments.AsyncConfig ?? AsyncConfig.Global; CancellationToken cancelToken = asyncConfig.CancellationToken; return asyncConfig.TaskFactory.StartNew(async delegate { object obj = client ?? arguments.NetworkClient; if (obj == null) { obj = new RealtimeClient((ConnectionProtocol)0); } client = (RealtimeClient)obj; short? result = null; if (arguments.CanRejoin && !arguments.FastReconnectDisabled) { try { result = await client.ReconnectAndRejoinAsync(arguments.Ticket, throwOnError: true, asyncConfig); if (result.HasValue && result.Value == 0) { arguments.ReconnectInformation?.Set(client); return client; } } catch (Exception ex2) { Exception ex = ex2; Log.Info($"Direct reconnect via ReconnectAndRejoinAsync() failed (Message: {ex}). Attempting a normal connect and rejoin.", client.LogLevel, client.LogPrefix); } } if (client.IsConnected && (client.State != ClientState.ConnectedToMasterServer || client.State == ClientState.JoinedLobby)) { await client.DisconnectAsync(asyncConfig); } if (!client.IsConnected) { if (arguments.AuthValues != null) { client.AuthValues = arguments.AuthValues.CopyTo(new AuthenticationValues()); } client.RealtimePeer.CrcEnabled = arguments.EnableCrc; arguments.PhotonSettings.FixedRegion = arguments.ReconnectInformation.Region; await client.ConnectUsingSettingsAsync(arguments.PhotonSettings, asyncConfig); } bool canRejoin = arguments.CanRejoin; int joinIterations = 0; while (joinIterations++ < 10) { if (result.HasValue) { if (result.Value == 0) { break; } if (result.Value == 32746) { await Task.Delay(1000, cancelToken); } else { if (result.Value != 32748) { throw new OperationException(result.Value, $"Failed to join the room with error '{result}'"); } canRejoin = false; } } if (joinIterations > 1) { Log.Info($"Reconnection attempt ({joinIterations}/10)", client.LogLevel, client.LogPrefix); } if (cancelToken.IsCancellationRequested) { throw new TaskCanceledException(); } result = ((!canRejoin) ? new short?(await client.JoinRoomAsync(arguments.BuildEnterRoomArgs().SetRoomName(arguments.ReconnectInformation.Room), throwOnError: false, asyncConfig)) : new short?(await client.RejoinRoomAsync(arguments.ReconnectInformation.Room, arguments.Ticket, throwOnError: false, asyncConfig))); } if (result.HasValue && result.Value == 0 && arguments.ReconnectInformation != null) { arguments.ReconnectInformation.Set(client); } return client; }).Unwrap(); } private static EnterRoomArgs SetRoomName(this EnterRoomArgs args, string roomName) { args.RoomName = roomName; return args; } private static EnterRoomArgs BuildEnterRoomArgs(this MatchmakingArguments arguments) { EnterRoomArgs obj = new EnterRoomArgs { RoomName = arguments.RoomName, Lobby = arguments.Lobby, Ticket = arguments.Ticket, ExpectedUsers = arguments.ExpectedUsers }; object obj2 = arguments.CustomRoomOptions; if (obj2 == null) { obj2 = new RoomOptions { MaxPlayers = (byte)arguments.MaxPlayers, IsOpen = (!arguments.IsRoomOpen.HasValue || arguments.IsRoomOpen.Value), IsVisible = (!arguments.IsRoomVisible.HasValue || arguments.IsRoomVisible.Value), DeleteNullProperties = true, PlayerTtl = arguments.PlayerTtlInSeconds * 1000, EmptyRoomTtl = arguments.EmptyRoomTtlInSeconds * 1000, Plugins = arguments.Plugins, SuppressRoomEvents = false, SuppressPlayerInfo = false, PublishUserId = true, CustomRoomProperties = arguments.CustomProperties }; object obj3 = obj2; object[] customLobbyProperties = arguments.CustomLobbyProperties; ((RoomOptions)obj3).CustomRoomPropertiesForLobby = customLobbyProperties; } obj.RoomOptions = (RoomOptions)obj2; return obj; } private static JoinRandomRoomArgs BuildJoinRandomRoomArgs(this MatchmakingArguments arguments) { return new JoinRandomRoomArgs { Ticket = arguments.Ticket, ExpectedUsers = arguments.ExpectedUsers, ExpectedCustomRoomProperties = arguments.CustomProperties, SqlLobbyFilter = arguments.SqlLobbyFilter, ExpectedMaxPlayers = arguments.MaxPlayers, Lobby = arguments.Lobby, MatchingType = arguments.RandomMatchingType }; } } [Serializable] public struct MatchmakingArguments { public AppSettings PhotonSettings; public int PlayerTtlInSeconds; public int EmptyRoomTtlInSeconds; public string RoomName; public int MaxPlayers; public bool CanOnlyJoin; public AsyncConfig AsyncConfig; public RealtimeClient NetworkClient; public AuthenticationValues AuthValues; public string PluginName; public MatchmakingReconnectInformation ReconnectInformation; public PhotonHashtable CustomProperties; public string[] ExpectedUsers; public TypedLobby Lobby; public string[] CustomLobbyProperties; public string SqlLobbyFilter; public object Ticket; public MatchmakingMode RandomMatchingType; public RoomOptions CustomRoomOptions; public bool? IsRoomVisible; public bool? IsRoomOpen; public bool EnableCrc; public bool FastReconnectDisabled; public string UserId { get { return AuthValues?.UserId; } set { if (AuthValues == null) { AuthValues = new AuthenticationValues(); } AuthValues.UserId = value; } } public string[] Plugins => string.IsNullOrEmpty(PluginName) ? new string[0] : new string[1] { PluginName }; public bool CanRejoin => PlayerTtlInSeconds > 0; public override string ToString() { return "PhotonSettings " + PhotonSettings.ToStringFull() + "\n" + $"PlayerTtlInSeconds: {PlayerTtlInSeconds}\n" + $"EmptyRoomTtlInSeconds${EmptyRoomTtlInSeconds}\n" + "RoomName " + RoomName + "\n" + $"MaxPlayers {MaxPlayers}\n" + $"CanOnlyJoin {CanOnlyJoin}\n" + $"RealtimeClient {NetworkClient} \n" + $"AuthValues {AuthValues} \n" + $"ReconnectInformation {ReconnectInformation}"; } public void Validate() { Assert(MaxPlayers >= 0, "MaxPlayer must be greater or equal than 0"); Assert(MaxPlayers < 256, "MaxPlayer must be less than 256"); Assert(PhotonSettings != null, "PhotonSettings must be set"); } private static void Assert(bool expected, string message) { if (!expected) { throw new ArgumentException(message); } } } [Serializable] public class MatchmakingReconnectInformation { public string Room; public string Region; public string AppVersion; public string UserId; public long TimeoutInTicks; public TimeSpan DefaultTimeout = TimeSpan.FromSeconds(20.0); public DateTime Timeout { get { return new DateTime(TimeoutInTicks); } set { TimeoutInTicks = value.Ticks; } } public bool HasTimedOut => Timeout < DateTime.Now; public virtual void Set(RealtimeClient client) { Set(client, DefaultTimeout); } public void Set(RealtimeClient client, TimeSpan timeSpan) { if (client != null) { Room = client.CurrentRoom.Name; Region = client.CurrentRegion; Timeout = DateTime.Now + timeSpan; UserId = client.UserId; AppVersion = client.AppSettings.AppVersion; } } public override string ToString() { return $"Room '{Room}' Region '{Region}' Timeout {Timeout}' AppVersion '{AppVersion}' UserId '{UserId}'"; } } public abstract class PhotonPing : IDisposable { public string DebugString = ""; public bool Successful; protected internal bool GotResult; protected internal int PingLength = 13; protected internal byte[] PingBytes = new byte[13] { 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 125, 0 }; protected internal byte PingId; private static readonly Random RandomIdProvider = new Random(); public abstract bool StartPing(string ip); public abstract bool Done(); public abstract void Dispose(); protected internal void Init() { GotResult = false; Successful = false; PingId = (byte)RandomIdProvider.Next(255); } } public class PingMono : PhotonPing { private Socket sock; public override bool StartPing(string ip) { Init(); try { if (sock == null) { if (ip.Contains(".")) { sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); } else { sock = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp); } sock.ReceiveTimeout = 5000; sock.Connect(ip, RegionHandler.UdpPortToPing); } PingBytes[PingBytes.Length - 1] = PingId; sock.Send(PingBytes); PingBytes[PingBytes.Length - 1] = (byte)(PingId + 1); } catch (Exception ex) { sock = null; Debug.WriteLine(ex.ToString()); throw; } return false; } public override bool Done() { if (GotResult || sock == null) { return true; } int num = 0; try { if (!sock.Poll(0, SelectMode.SelectRead)) { return false; } num = sock.Receive(PingBytes, SocketFlags.None); } catch (Exception ex) { if (sock != null) { sock.Close(); sock = null; } DebugString = DebugString + " Exception of socket! " + ex.GetType()?.ToString() + " "; return true; } bool flag = PingBytes[PingBytes.Length - 1] == PingId && num == PingLength; if (!flag) { DebugString += " ReplyMatch is false! "; } Successful = flag; GotResult = true; return true; } public override void Dispose() { if (sock != null) { try { sock.Close(); } catch { } sock = null; } } } public class Player { private int actorNumber = -1; public readonly bool IsLocal; private string nickName = string.Empty; public object TagObject; protected internal Room RoomReference { get; set; } public int ActorNumber => actorNumber; public bool HasRejoined { get; internal set; } public string NickName { get { return nickName; } set { if (string.IsNullOrEmpty(nickName) || !nickName.Equals(value)) { nickName = value; if (IsLocal) { SetNickNameProperty(); } } } } public string UserId { get; internal set; } public bool IsMasterClient { get { if (RoomReference == null) { return false; } return ActorNumber == RoomReference.MasterClientId; } } public bool IsInactive { get; protected internal set; } public PhotonHashtable CustomProperties { get; set; } protected internal Player(string nickName, int actorNumber, bool isLocal, PhotonHashtable playerProperties = null) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown IsLocal = isLocal; this.actorNumber = actorNumber; NickName = nickName; CustomProperties = new PhotonHashtable(); InternalCacheProperties(playerProperties); } public Player Get(int id) { if (RoomReference == null) { return null; } return RoomReference.GetPlayer(id); } public Player GetNext() { return GetNextFor(ActorNumber); } public Player GetNextFor(Player currentPlayer) { if (currentPlayer == null) { return null; } return GetNextFor(currentPlayer.ActorNumber); } public Player GetNextFor(int currentPlayerId) { if (RoomReference == null || RoomReference.Players == null || RoomReference.Players.Count < 2) { return null; } Dictionary players = RoomReference.Players; int num = int.MaxValue; int num2 = currentPlayerId; foreach (int key in players.Keys) { if (key < num2) { num2 = key; } else if (key > currentPlayerId && key < num) { num = key; } } return (num != int.MaxValue) ? players[num] : players[num2]; } protected internal void InternalCacheProperties(PhotonHashtable properties) { if (properties != null && ((Dictionary)(object)properties).Count != 0 && !((object)CustomProperties).Equals((object?)properties)) { if (!IsLocal && properties.ContainsKey(byte.MaxValue)) { string text = (string)properties[byte.MaxValue]; NickName = text; } if (properties.ContainsKey((byte)253)) { UserId = (string)properties[(byte)253]; } if (properties.ContainsKey((byte)254)) { IsInactive = (bool)properties[(byte)254]; } CustomProperties.Merge(properties); CustomProperties.StripKeysWithNullValues(); } } public override string ToString() { return $"#{ActorNumber:00} '{NickName}'"; } public string ToStringFull() { return string.Format("#{0:00} '{1}'{2} {3}", ActorNumber, NickName, IsInactive ? " (inactive)" : "", CustomProperties.ToStringFull()); } public override bool Equals(object p) { return p is Player player && GetHashCode() == player.GetHashCode(); } public override int GetHashCode() { return ActorNumber; } protected internal void ChangeLocalID(int newID) { if (IsLocal) { actorNumber = newID; } } public bool SetCustomProperties(PhotonHashtable propertiesToSet, PhotonHashtable expectedValues = null) { if (!propertiesToSet.CustomPropKeyTypesValid()) { Log.Error("Player.SetCustomProperties() failed. Parameter propertiesToSet must be non-null, not empty and contain only int or string keys.", (LogLevel)1); return false; } if (expectedValues != null && !expectedValues.CustomPropKeyTypesValid()) { Log.Error("Player.SetCustomProperties() failed. Parameter expectedValues must contain only int or string keys if it is not null.", (LogLevel)1); return false; } if (RoomReference != null) { if (RoomReference.IsOffline) { CustomProperties.Merge(propertiesToSet); CustomProperties.StripKeysWithNullValues(); RoomReference.RealtimeClient.InRoomCallbackTargets.OnPlayerPropertiesUpdate(this, propertiesToSet); return true; } return RoomReference.RealtimeClient.OpSetPropertiesOfActor(actorNumber, propertiesToSet, expectedValues); } if (IsLocal && expectedValues == null) { CustomProperties.Merge(propertiesToSet); CustomProperties.StripKeysWithNullValues(); return true; } return false; } internal bool UpdateNickNameOnJoined() { if (RoomReference == null || RoomReference.CustomProperties == null || !IsLocal) { return false; } string b = CustomProperties[byte.MaxValue] as string; if (!string.Equals(NickName, b)) { return SetNickNameProperty(); } return true; } private bool SetNickNameProperty() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown if (RoomReference != null && !RoomReference.IsOffline) { PhotonHashtable val = new PhotonHashtable(); val[byte.MaxValue] = NickName; return RoomReference.RealtimeClient.OpSetPropertiesOfActor(ActorNumber, val); } return false; } } public class ProtocolPorts { public ServerPorts Udp = new ServerPorts { NameServer = 27000, MasterServer = 27001, GameServer = 27002 }; public ServerPorts Tcp = new ServerPorts { NameServer = 4533 }; public ServerPorts Ws = new ServerPorts { NameServer = 80 }; public ServerPorts Wss = new ServerPorts { NameServer = 443 }; public ushort Get(ConnectionProtocol protocol, ServerConnection serverType) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected I4, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) return (int)protocol switch { 0 => Udp.Get(serverType), 1 => Tcp.Get(serverType), 4 => Ws.Get(serverType), 5 => Wss.Get(serverType), _ => throw new ArgumentOutOfRangeException("protocol", protocol, null), }; } public void SetUdpDefault() { Udp = new ServerPorts { NameServer = 27000, MasterServer = 27001, GameServer = 27002 }; } public void SetUdpDefaultOld() { Udp = new ServerPorts { NameServer = 5058, MasterServer = 5055, GameServer = 5056 }; } public void SetTcpDefault() { Tcp = new ServerPorts { NameServer = 4533, MasterServer = 4530, GameServer = 4531 }; } public void SetWsDefault() { Ws = new ServerPorts { NameServer = 80, MasterServer = 80, GameServer = 80 }; } public void SetWsDefaultOld() { Ws = new ServerPorts { NameServer = 9093, MasterServer = 9090, GameServer = 9091 }; } public void SetWssDefault() { Wss = new ServerPorts { NameServer = 433, MasterServer = 443, GameServer = 443 }; } public void SetWssDefaultOld() { Wss = new ServerPorts { NameServer = 19093, MasterServer = 19090, GameServer = 19091 }; } public void SetOldDefaults() { SetUdpDefaultOld(); SetTcpDefault(); SetWsDefaultOld(); SetWssDefaultOld(); } } public struct ServerPorts { public ushort NameServer; public ushort MasterServer; public ushort GameServer; public ushort Get(ServerConnection serverType) { return serverType switch { ServerConnection.NameServer => NameServer, ServerConnection.MasterServer => MasterServer, ServerConnection.GameServer => GameServer, _ => throw new ArgumentOutOfRangeException("serverType", serverType, null), }; } } public struct RaiseEventArgs { public static readonly RaiseEventArgs Default = default(RaiseEventArgs); public EventCaching CachingOption; public byte InterestGroup; public int[] TargetActors; public ReceiverGroup Receivers; } public class RealtimeClient : IPhotonPeerListener { private enum ClientWorkflowOption { Default, GetRegionsAndPing, GetRegionsOnly } public readonly PhotonPeer RealtimePeer; public string LogPrefix; public LogLevel LogLevel = (LogLevel)2; public int LogStatsInterval = 5000; private int lastStatsLogTime; public EncryptionMode EncryptionMode = EncryptionMode.PayloadEncryption; public string NameServerHost = "ns.photonengine.io"; public ProtocolPorts ProtocolPorts = new ProtocolPorts(); public Func AddressRewriter; private ClientState state = ClientState.PeerCreated; internal ConnectionCallbacksContainer ConnectionCallbackTargets; internal MatchMakingCallbacksContainer MatchMakingCallbackTargets; internal InRoomCallbacksContainer InRoomCallbackTargets; internal LobbyCallbacksContainer LobbyCallbackTargets; internal ErrorInfoCallbacksContainer ErrorInfoCallbackTargets; public SystemConnectionSummary SystemConnectionSummary; private TypedLobby targetLobbyCache; private readonly List lobbyStatistics = new List(); public readonly Player LocalPlayer = new Player(string.Empty, -1, isLocal: true); private JoinType lastJoinType; private EnterRoomArgs enterRoomArgumentsCache; private OperationResponse failedRoomEntryOperation; private const int FriendRequestListMax = 512; private string[] friendListRequested; public RegionHandler RegionHandler; private readonly Queue callbackTargetChanges = new Queue(); private readonly HashSet callbackTargets = new HashSet(); private ClientWorkflowOption clientWorkflow; public EventBetter CallbackMessage = new EventBetter(); private readonly Pool paramDictionaryPool = new Pool((Func)(() => new ParameterDictionary()), (Action)delegate(ParameterDictionary x) { x.Clear(); }, 1); public const string Version = "5.1.9"; [Obsolete("Use RealtimePeer")] public PhotonPeer LoadBalancingPeer => RealtimePeer; public LogLevel LogLevelPeer { get { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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) return RealtimePeer.LogLevel; } set { //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) RealtimePeer.LogLevel = value; } } public SerializationProtocol SerializationProtocol { get { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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) return RealtimePeer.SerializationProtocolType; } set { //IL_0007: Unknown result type (might be due to invalid IL or missing references) RealtimePeer.SerializationProtocolType = value; } } public AppSettings AppSettings { get; private set; } [Obsolete("Use RealtimeClient.AppSettings.GetAppId(this.ClientType) instead.")] public string AppId { get { if (AppSettings == null) { return null; } return AppSettings.GetAppId(ClientType); } } [Obsolete("Use AppSettings.AppVersion instead. ConnectUsingSettings() will overwrite the AppVersion!")] public string AppVersion { get { if (AppSettings == null) { return null; } return AppSettings.AppVersion; } } public ClientAppType ClientType { get; set; } public AuthenticationValues AuthValues { get; set; } private object TokenForInit { get { if (AppSettings.AuthMode == AuthModeOption.Auth || AuthValues == null) { return null; } return AuthValues.Token; } } public string NameServerAddress => GetNameServerAddress(); public string CurrentServerAddress => RealtimePeer.ServerAddress; public string MasterServerAddress { get; set; } public string GameServerAddress { get; protected internal set; } public ServerConnection Server { get; private set; } public ClientState State { get { return state; } set { if (state != value) { ClientState arg = state; state = value; if (this.StateChanged != null) { this.StateChanged(arg, state); } } } } public ConnectionHandler Handler { get; private set; } public bool IsConnected => State != 0 && State != ClientState.Disconnected; public bool IsConnectedAndReady { get { switch (State) { case ClientState.PeerCreated: case ClientState.Authenticating: case ClientState.DisconnectingFromMasterServer: case ClientState.ConnectingToGameServer: case ClientState.Joining: case ClientState.Leaving: case ClientState.DisconnectingFromGameServer: case ClientState.ConnectingToMasterServer: case ClientState.Disconnecting: case ClientState.Disconnected: case ClientState.ConnectingToNameServer: case ClientState.DisconnectingFromNameServer: return false; default: return true; } } } public DisconnectCause DisconnectedCause { get; protected set; } public bool InLobby => CurrentLobby != null; public TypedLobby CurrentLobby { get; internal set; } public string NickName { get { return LocalPlayer.NickName; } set { if (LocalPlayer != null) { LocalPlayer.NickName = value; } } } public string UserId { get { if (AuthValues != null) { return AuthValues.UserId; } return null; } set { if (AuthValues == null) { AuthValues = new AuthenticationValues(); } AuthValues.UserId = value; } } public Room CurrentRoom { get; set; } public bool InRoom => state == ClientState.Joined && CurrentRoom != null; public int PlayersOnMasterCount { get; internal set; } public int PlayersInRoomsCount { get; internal set; } public int RoomsCount { get; internal set; } public bool IsFetchingFriendList => friendListRequested != null; public string CurrentCluster { get; private set; } public string CurrentRegion { get; private set; } [Obsolete("Use CurrentRegion instead.")] public string CloudRegion => CurrentRegion; public string SummaryToCache => RegionHandler?.SummaryToCache; public event Action StateChanged; public event Action EventReceived; public event Action MessageReceived; public event Action OpResponseReceived; public RealtimeClient(ConnectionProtocol protocol = 0) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown AppSettings = new AppSettings(); RealtimePeer = new PhotonPeer((IPhotonPeerListener)(object)this, protocol); ConnectionCallbackTargets = new ConnectionCallbacksContainer(this); MatchMakingCallbackTargets = new MatchMakingCallbacksContainer(this); InRoomCallbackTargets = new InRoomCallbacksContainer(this); LobbyCallbackTargets = new LobbyCallbacksContainer(this); ErrorInfoCallbackTargets = new ErrorInfoCallbacksContainer(this); SerializationProtocol = (SerializationProtocol)1; State = ClientState.PeerCreated; } public virtual bool ConnectUsingSettings(AppSettings appSettings) { //IL_0007: 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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Invalid comparison between Unknown and I4 //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Invalid comparison between Unknown and I4 //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_0355: Unknown result type (might be due to invalid IL or missing references) //IL_0372: Unknown result type (might be due to invalid IL or missing references) //IL_0319: Unknown result type (might be due to invalid IL or missing references) //IL_03f6: Unknown result type (might be due to invalid IL or missing references) if ((int)RealtimePeer.PeerState != 0 && State != ClientState.ConnectedToNameServer) { PeerStateValue peerState = RealtimePeer.PeerState; Log.Warn("ConnectUsingSettings() failed. Can only connect while in state 'Disconnected'. Current state: " + ((object)(PeerStateValue)(ref peerState)).ToString(), LogLevel, LogPrefix); return false; } if (appSettings == null) { Log.Error("ConnectUsingSettings() failed. The appSettings can't be null.'", LogLevel, LogPrefix); return false; } AppSettings = new AppSettings(appSettings); if (!ClientTypeChecks()) { Log.Error("Can not connect. AppId or ClientType not set correctly.", LogLevel, LogPrefix); return false; } GameServerAddress = string.Empty; MasterServerAddress = string.Empty; LogLevel = AppSettings.ClientLogging; RealtimePeer.LogLevel = AppSettings.NetworkLogging; CurrentRegion = null; DisconnectedCause = DisconnectCause.None; SystemConnectionSummary = null; if (AuthValues != null) { AuthValues.Token = null; } if (State == ClientState.ConnectedToNameServer) { return CallAuthenticate(); } if (IPAddress.TryParse(AppSettings.Server, out IPAddress _)) { if ((int)AppSettings.Protocol == 4 || (int)AppSettings.Protocol == 5) { Log.Error("AppSettings.Server is an IP address. Can not use WS or WSS protocols with IP addresses.", LogLevel, LogPrefix); return false; } if (AppSettings.AuthMode == AuthModeOption.AuthOnceWss) { Log.Warn("AppSettings.Server is an IP address. Changing this client's AuthMode to AuthOnce.", LogLevel, LogPrefix); AppSettings.AuthMode = AuthModeOption.AuthOnce; } } if (AppSettings.AuthMode == AuthModeOption.AuthOnceWss) { RealtimePeer.TransportProtocol = (ConnectionProtocol)5; } else { RealtimePeer.TransportProtocol = AppSettings.Protocol; } if (Handler == null) { Handler = ConnectionHandler.BuildInstance(this, ClientType.ToString()); } Handler.StartFallbackSendAckThread(); if (AppSettings.UseNameServer) { Server = ServerConnection.NameServer; if (!appSettings.IsDefaultNameServer) { NameServerHost = appSettings.Server; } if (!RealtimePeer.Connect(NameServerAddress, AppSettings.GetAppId(ClientType), TokenForInit, (object)null, AppSettings.ProxyServer)) { return false; } State = ClientState.ConnectingToNameServer; Log.Info(ConnectLog("ConnectUsingSettings() " + DateTime.UtcNow.ToString(CultureInfo.InvariantCulture) + " UTC"), LogLevel, LogPrefix); } else { Server = ServerConnection.MasterServer; int port = (appSettings.IsDefaultPort ? ProtocolPorts.Get(RealtimePeer.TransportProtocol, ServerConnection.MasterServer) : appSettings.Port); MasterServerAddress = ToProtocolAddress(appSettings.Server, port, RealtimePeer.TransportProtocol); if (!RealtimePeer.Connect(MasterServerAddress, AppSettings.GetAppId(ClientType), TokenForInit, (object)null, AppSettings.ProxyServer)) { return false; } State = ClientState.ConnectingToMasterServer; Log.Info(ConnectLog("ConnectUsingSettings() " + DateTime.UtcNow.ToString(CultureInfo.InvariantCulture) + " UTC"), LogLevel, LogPrefix); } return true; } private bool ClientTypeChecks() { if (ClientType == ClientAppType.Detect) { ClientAppType clientAppType = AppSettings.ClientTypeDetect(); if (clientAppType == ClientAppType.Detect) { Log.Error("ConnectUsingSettings requires that the AppSettings contain exactly one value set out of AppIdRealtime, AppIdFusion or AppIdQuantum.", (LogLevel)1); return false; } ClientType = clientAppType; } return true; } public void GetRegions(AppSettings appSettings, bool ping = true) { if (State != ClientState.Disconnected && State != 0) { Log.Warn("Can not GetRegions() while connected. Disconnect first.", (LogLevel)2); return; } clientWorkflow = (ping ? ClientWorkflowOption.GetRegionsAndPing : ClientWorkflowOption.GetRegionsOnly); ConnectUsingSettings(appSettings); AppSettings.BestRegionSummaryFromStorage = null; } [Conditional("UNITY_WEBGL")] private void CheckConnectSetupWebGl() { } private bool CallConnect(ServerConnection serverType) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) if (State == ClientState.Disconnecting) { Log.Error("CallConnect() failed. Can't connect while disconnecting (still). Current state: " + State, LogLevel, LogPrefix); return false; } if (serverType != ServerConnection.NameServer) { if (AppSettings.AuthMode != 0 && TokenForInit == null) { Log.Error("Connect() failed. Can't connect to " + serverType.ToString() + " with Token == null in AuthMode: " + AppSettings.AuthMode, LogLevel, LogPrefix); return false; } RealtimePeer.TransportProtocol = AppSettings.Protocol; } string text = null; ClientState clientState = ClientState.Disconnected; switch (serverType) { case ServerConnection.NameServer: text = GetNameServerAddress(); clientState = ClientState.ConnectingToNameServer; if (AuthValues != null) { AuthValues.Token = null; } RealtimePeer.TransportProtocol = (ConnectionProtocol)5; break; case ServerConnection.MasterServer: text = MasterServerAddress; clientState = ClientState.ConnectingToMasterServer; break; case ServerConnection.GameServer: text = GameServerAddress; clientState = ClientState.ConnectingToGameServer; break; } bool flag = RealtimePeer.Connect(text, AppSettings.GetAppId(ClientType), TokenForInit, (object)null, AppSettings.ProxyServer); if (flag) { DisconnectedCause = DisconnectCause.None; SystemConnectionSummary = null; Server = serverType; State = clientState; } return flag; } public bool ReconnectToMaster() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 //IL_001f: 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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) if ((int)RealtimePeer.PeerState > 0) { PeerStateValue peerState = RealtimePeer.PeerState; Log.Warn("ReconnectToMaster() failed. Can only connect while in state 'Disconnected'. Current state: " + ((object)(PeerStateValue)(ref peerState)).ToString(), LogLevel, LogPrefix); return false; } if (string.IsNullOrEmpty(MasterServerAddress)) { Log.Warn("ReconnectToMaster() failed. MasterServerAddress is null or empty.", LogLevel, LogPrefix); return false; } if (AuthValues == null || AuthValues.Token == null) { Log.Warn("ReconnectToMaster() failed. It seems the client doesn't have any previous authentication-token to re-connect.", LogLevel, LogPrefix); return false; } return CallConnect(ServerConnection.MasterServer); } public bool ReconnectAndRejoin(object ticket = null) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 //IL_001f: 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_0038: 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_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) if ((int)RealtimePeer.PeerState > 0) { PeerStateValue peerState = RealtimePeer.PeerState; Log.Warn("ReconnectAndRejoin() failed. Can only connect while in state 'Disconnected'. Current state: " + ((object)(PeerStateValue)(ref peerState)).ToString(), LogLevel, LogPrefix); return false; } if (string.IsNullOrEmpty(GameServerAddress)) { Log.Warn("ReconnectAndRejoin() failed. It seems the client wasn't connected to a game server before (no address).", LogLevel, LogPrefix); return false; } if (enterRoomArgumentsCache == null) { Log.Warn("ReconnectAndRejoin() failed. It seems the client doesn't have any previous room to re-join.", LogLevel, LogPrefix); return false; } if (AuthValues == null || AuthValues.Token == null) { Log.Warn("ReconnectAndRejoin() failed. It seems the client doesn't have any previous authentication token to re-connect.", LogLevel, LogPrefix); return false; } lastJoinType = JoinType.JoinRoom; enterRoomArgumentsCache.JoinMode = JoinMode.RejoinOnly; enterRoomArgumentsCache.Ticket = ticket; return CallConnect(ServerConnection.GameServer); } public void Disconnect() { Disconnect(DisconnectCause.DisconnectByClientLogic); } internal void Disconnect(DisconnectCause cause = DisconnectCause.DisconnectByClientLogic) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (State == ClientState.Disconnecting || State == ClientState.Disconnected || State == ClientState.PeerCreated) { Log.Info($"Disconnect() skipped because State is: {State}. Called for cause: {cause}. Current DisconnectedCause: {DisconnectedCause}.", LogLevel, LogPrefix); return; } State = ClientState.Disconnecting; DisconnectedCause = cause; RealtimePeer.Disconnect(); } private void DisconnectToReconnect() { switch (Server) { case ServerConnection.NameServer: State = ClientState.DisconnectingFromNameServer; break; case ServerConnection.MasterServer: State = ClientState.DisconnectingFromMasterServer; break; case ServerConnection.GameServer: State = ClientState.DisconnectingFromGameServer; break; } RealtimePeer.Disconnect(); } public void SimulateConnectionLoss(bool simulateTimeout) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) Log.Warn("SimulateConnectionLoss() set to: " + simulateTimeout, LogLevel, LogPrefix); if (simulateTimeout) { RealtimePeer.NetworkSimulationSettings.IncomingLossPercentage = 100; RealtimePeer.NetworkSimulationSettings.OutgoingLossPercentage = 100; } RealtimePeer.IsSimulationEnabled = simulateTimeout; } private bool CallAuthenticate() { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01ea: Unknown result type (might be due to invalid IL or missing references) if (AppSettings.UseNameServer && Server != ServerConnection.NameServer && (AuthValues == null || AuthValues.Token == null)) { Log.Error($"Authenticate without Token is only allowed on Name Server. Will not authenticate on {Server}: {CurrentServerAddress}. State: {State}", LogLevel, LogPrefix); return false; } if (AuthValues != null && !AuthValues.AreValid()) { Log.Warn($"AuthValues.AuthType is {AuthValues.AuthType} but not all mandatory parameters are set. Current GET parameters: {AuthValues.AuthGetParameters}", LogLevel, LogPrefix); } if (!string.IsNullOrEmpty(AppSettings.FixedRegion) && Server == ServerConnection.NameServer) { CurrentRegion = AppSettings.FixedRegion; } if (AppSettings.AuthMode == AuthModeOption.Auth) { if (!CheckIfOpCanBeSent(230, Server, "Authenticate")) { return false; } return OpAuthenticate(AppSettings.GetAppId(ClientType), AppSettings.AppVersion, AuthValues, CurrentRegion, AppSettings.EnableLobbyStatistics && Server == ServerConnection.MasterServer); } if (!CheckIfOpCanBeSent(231, Server, "AuthenticateOnce")) { return false; } ConnectionProtocol protocol = AppSettings.Protocol; return OpAuthenticateOnce(AppSettings.GetAppId(ClientType), AppSettings.AppVersion, AuthValues, CurrentRegion, EncryptionMode, protocol); } public void Service() { RealtimePeer.Service(); LogStats(); } public bool DispatchIncomingCommands() { return RealtimePeer.DispatchIncomingCommands(); } public bool SendOutgoingCommands() { bool result = RealtimePeer.SendOutgoingCommands(); LogStats(); return result; } private void LogStats() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 //IL_006a: Unknown result type (might be due to invalid IL or missing references) if ((int)LogLevel >= 3 && LogStatsInterval > 0 && State != ClientState.Disconnected) { int num = RealtimePeer.ConnectionTime - lastStatsLogTime; if (num >= LogStatsInterval) { lastStatsLogTime = RealtimePeer.ConnectionTime; Log.Info(RealtimePeer.VitalStatsToString(false), LogLevel, LogPrefix); } } } private string ConnectLog(string prefix) { //IL_00d7: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); string appId = AppSettings.GetAppId(ClientType); string text = ((!string.IsNullOrEmpty(appId) && appId.Length > 8) ? appId.Substring(0, 8) : appId); string text2 = ((AuthValues != null) ? AuthValues.AuthType.ToString() : "N/A"); string text3 = (string.IsNullOrEmpty(CurrentRegion) ? "" : ("(" + CurrentRegion + ")")); string text4 = DateTime.UtcNow.ToShortTimeString(); stringBuilder.Append($"{prefix} UTC: {text4} AppID: \"{text}***\" AppVersion: \"{AppSettings.AppVersion}\" Auth: {text2} Client: v{PhotonPeer.Version} ({RealtimePeer.TargetFramework}, {RealtimePeer.SocketImplementation.Name}, {EncryptionMode}) Server: {CurrentServerAddress} {text3}"); return stringBuilder.ToString(); } [Conditional("SUPPORTED_UNITY")] private void ConfigUnitySockets() { Type type = null; type = Type.GetType("Photon.Client.SocketWebTcp, PhotonWebSocket", throwOnError: false); if (type == null) { type = Type.GetType("Photon.Client.SocketWebTcp, Assembly-CSharp-firstpass", throwOnError: false); } if (type == null) { type = Type.GetType("Photon.Client.SocketWebTcp, Assembly-CSharp", throwOnError: false); } if (type != null) { RealtimePeer.SocketImplementationConfig[(ConnectionProtocol)4] = type; RealtimePeer.SocketImplementationConfig[(ConnectionProtocol)5] = type; } } private string GetNameServerAddress() { //IL_000d: 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) //IL_0057: 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) ushort port = ProtocolPorts.Get(RealtimePeer.TransportProtocol, ServerConnection.NameServer); if (AppSettings.UseNameServer && AppSettings.Port != 0) { Log.Info($"NameServer port becomes AppSettings.Port: {AppSettings.Port} with protocol: {RealtimePeer.TransportProtocol}", LogLevel, LogPrefix); port = AppSettings.Port; } return ToProtocolAddress(NameServerHost, port, RealtimePeer.TransportProtocol); } private string ToProtocolAddress(string address, int port, ConnectionProtocol protocol) { //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_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected I4, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) string empty = string.Empty; switch ((int)protocol) { case 0: case 1: return $"{address}:{port}"; case 4: empty = "ws://"; break; case 5: empty = "wss://"; break; default: throw new ArgumentOutOfRangeException($"Can not handle protocol: {protocol}."); } Uri uri = new Uri(empty + address); string text = $"{uri.Scheme}://{uri.Host}:{port}{uri.AbsolutePath}"; if (AddressRewriter != null) { text = AddressRewriter(text, ServerConnection.NameServer); } return text; } protected internal static string ReplacePortWithAlternative(string address, ushort replacementPort) { if (string.IsNullOrEmpty(address) || replacementPort == 0) { return address; } if (address.StartsWith("ws")) { UriBuilder uriBuilder = new UriBuilder(address); uriBuilder.Port = replacementPort; return uriBuilder.ToString(); } UriBuilder uriBuilder2 = new UriBuilder("scheme://" + address); return $"{uriBuilder2.Host}:{replacementPort}"; } private string GetMatchmakingHash(TypedLobby lobbyInArgs) { string text = ""; text = ((lobbyInArgs != null) ? lobbyInArgs.ToString() : ((CurrentLobby == null) ? TypedLobby.Default.ToString() : CurrentLobby.ToString())); string text2 = (AppSettings.GetAppId(ClientType) + AppSettings.AppVersion + CurrentRegion + CurrentCluster).GetStableHashCode().ToString("x"); return "MMH: " + text2 + " " + text; } private void ReadoutProperties(PhotonHashtable gameProperties, PhotonHashtable actorProperties, int targetActorNr) { //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Expected O, but got Unknown if (CurrentRoom != null && gameProperties != null) { CurrentRoom.InternalCacheProperties(gameProperties); if (InRoom) { InRoomCallbackTargets.OnRoomPropertiesUpdate(gameProperties); } } if (actorProperties == null || ((Dictionary)(object)actorProperties).Count <= 0) { return; } if (targetActorNr > 0) { Player player = CurrentRoom.GetPlayer(targetActorNr); if (player != null) { PhotonHashtable val = ReadoutPropertiesForActorNr(actorProperties, targetActorNr); player.InternalCacheProperties(val); InRoomCallbackTargets.OnPlayerPropertiesUpdate(player, val); } return; } foreach (object key in ((Dictionary)(object)actorProperties).Keys) { int num = (int)key; if (num != 0) { PhotonHashtable val2 = (PhotonHashtable)actorProperties[key]; string nickName = (string)val2[byte.MaxValue]; Player player2 = CurrentRoom.GetPlayer(num); if (player2 == null) { player2 = new Player(nickName, num, isLocal: false, val2); CurrentRoom.StorePlayer(player2); } player2.InternalCacheProperties(val2); } } } private PhotonHashtable ReadoutPropertiesForActorNr(PhotonHashtable actorProperties, int actorNr) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown if (((Dictionary)(object)actorProperties).ContainsKey((object)actorNr)) { return (PhotonHashtable)actorProperties[actorNr]; } return actorProperties; } public void ChangeLocalID(int newID) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (LocalPlayer == null) { Log.Warn($"Local actor is null. CurrentRoom: {CurrentRoom} CurrentRoom.Players: {CurrentRoom?.Players == null} newID: {newID}", LogLevel, LogPrefix); } if (CurrentRoom == null) { LocalPlayer.ChangeLocalID(newID); LocalPlayer.RoomReference = null; } else { CurrentRoom.RemovePlayer(LocalPlayer); LocalPlayer.ChangeLocalID(newID); CurrentRoom.StorePlayer(LocalPlayer); } } private void GameEnteredOnGameServer(OperationResponse operationResponse) { //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Expected O, but got Unknown //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Expected O, but got Unknown //IL_00f7: Unknown result type (might be due to invalid IL or missing references) CurrentRoom = CreateRoom(enterRoomArgumentsCache.RoomName, enterRoomArgumentsCache.RoomOptions); CurrentRoom.RealtimeClient = this; CurrentRoom.Lobby = enterRoomArgumentsCache.Lobby; int newID = (int)operationResponse[(byte)254]; ChangeLocalID(newID); if (operationResponse.Parameters.ContainsKey((byte)252)) { int[] actorsInGame = (int[])operationResponse.Parameters[(byte)252]; UpdatedActorList(actorsInGame); } if (operationResponse.Parameters.ContainsKey((byte)201)) { string text = (string)operationResponse.Parameters[(byte)201]; if (!string.IsNullOrEmpty(text) && !string.Equals(text, "webhooks", StringComparison.InvariantCultureIgnoreCase)) { Log.Info("GameEnteredOnGameServer() plugin: " + text, LogLevel, LogPrefix); } } PhotonHashtable actorProperties = (PhotonHashtable)operationResponse[(byte)249]; PhotonHashtable gameProperties = (PhotonHashtable)operationResponse[(byte)248]; ReadoutProperties(gameProperties, actorProperties, 0); object obj = default(object); if (operationResponse.Parameters.TryGetValue((byte)191, ref obj)) { CurrentRoom.InternalCacheRoomFlags((int)obj); } if (CurrentRoom.SuppressRoomEvents) { State = ClientState.Joined; LocalPlayer.UpdateNickNameOnJoined(); if (lastJoinType == JoinType.CreateRoom || (lastJoinType == JoinType.JoinOrCreateRoom && LocalPlayer.ActorNumber == 1)) { MatchMakingCallbackTargets.OnCreatedRoom(); } MatchMakingCallbackTargets.OnJoinedRoom(); } } private void UpdatedActorList(int[] actorsInGame) { if (actorsInGame == null) { return; } foreach (int num in actorsInGame) { if (num != 0) { Player player = CurrentRoom.GetPlayer(num); if (player == null) { CurrentRoom.StorePlayer(new Player(string.Empty, num, isLocal: false)); } } } } protected internal virtual Room CreateRoom(string roomName, RoomOptions opt) { return new Room(roomName, opt); } private bool CheckIfOpAllowedOnServer(byte opCode, ServerConnection serverConnection) { switch (serverConnection) { case ServerConnection.MasterServer: switch (opCode) { case 217: case 218: case 221: case 222: case 225: case 226: case 227: case 228: case 229: case 230: case 231: return true; } break; case ServerConnection.GameServer: switch (opCode) { case 218: case 226: case 227: case 230: case 231: case 248: case 251: case 252: case 253: case 254: return true; } break; case ServerConnection.NameServer: { byte b = opCode; byte b2 = b; if (b2 != 218 && b2 != 220 && (uint)(b2 - 230) > 1u) { break; } return true; } default: throw new ArgumentOutOfRangeException("serverConnection", serverConnection, null); } return false; } private bool CheckIfOpCanBeSent(byte opCode, ServerConnection serverConnection, string opName) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Invalid comparison between Unknown and I4 //IL_0133: 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) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) if (!CheckIfOpAllowedOnServer(opCode, serverConnection)) { Log.Error($"Operation {opName} ({opCode}) not allowed on current server ({serverConnection})", LogLevel, LogPrefix); return false; } if (!CheckIfClientIsReadyToCallOperation(opCode)) { if (opCode == 253 && (State == ClientState.Leaving || State == ClientState.Disconnecting || State == ClientState.DisconnectingFromGameServer)) { Log.Info($"Operation {opName} ({opCode}) not called while leaving the room or game server. Client state: {Enum.GetName(typeof(ClientState), State)}", LogLevel, LogPrefix); return false; } Log.Error($"Operation {opName} ({opCode}) not called because client is not connected or not ready yet. Client state: {Enum.GetName(typeof(ClientState), State)}", LogLevel, LogPrefix); return false; } if ((int)RealtimePeer.PeerState != 3) { Log.Error($"Operation {opName} ({opCode}) can't be sent because peer is not connected. Peer state: {RealtimePeer.PeerState}", LogLevel, LogPrefix); return false; } return true; } private bool CheckIfClientIsReadyToCallOperation(byte opCode) { switch (opCode) { case 230: case 231: return IsConnectedAndReady || State == ClientState.ConnectingToNameServer || State == ClientState.ConnectingToMasterServer || State == ClientState.ConnectingToGameServer; case 248: case 251: case 252: case 253: case 254: return InRoom; case 226: case 227: return State == ClientState.ConnectedToMasterServer || InLobby || State == ClientState.ConnectedToGameServer; case 228: return InLobby; case 217: case 221: case 222: case 225: case 229: return State == ClientState.ConnectedToMasterServer || InLobby; case 220: return State == ClientState.ConnectedToNameServer; default: return IsConnected; } } public virtual void DebugReturn(LogLevel level, string message) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected I4, but got Unknown //IL_002a: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) switch ((int)level) { case 0: break; case 1: Log.Error(message, RealtimePeer.LogLevel, LogPrefix); break; case 2: Log.Warn(message, RealtimePeer.LogLevel, LogPrefix); break; case 3: Log.Info(message, RealtimePeer.LogLevel, LogPrefix); break; default: Log.Debug(message, RealtimePeer.LogLevel, LogPrefix); break; } } private void CallbackRoomEnterFailed(OperationResponse operationResponse) { if (operationResponse.ReturnCode != 0) { if (operationResponse.OperationCode == 226) { MatchMakingCallbackTargets.OnJoinRoomFailed(operationResponse.ReturnCode, operationResponse.DebugMessage); } else if (operationResponse.OperationCode == 227) { MatchMakingCallbackTargets.OnCreateRoomFailed(operationResponse.ReturnCode, operationResponse.DebugMessage); } else if (operationResponse.OperationCode == 225) { MatchMakingCallbackTargets.OnJoinRandomFailed(operationResponse.ReturnCode, operationResponse.DebugMessage); } } } public virtual void OnOperationResponse(OperationResponse operationResponse) { //IL_085e: Unknown result type (might be due to invalid IL or missing references) //IL_0865: Expected O, but got Unknown //IL_0837: Unknown result type (might be due to invalid IL or missing references) //IL_058d: Unknown result type (might be due to invalid IL or missing references) //IL_0951: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_05d8: Unknown result type (might be due to invalid IL or missing references) //IL_077f: Unknown result type (might be due to invalid IL or missing references) //IL_0891: Unknown result type (might be due to invalid IL or missing references) //IL_089b: Expected O, but got Unknown //IL_0638: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_031e: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_033d: Unknown result type (might be due to invalid IL or missing references) //IL_034d: Unknown result type (might be due to invalid IL or missing references) //IL_036a: Unknown result type (might be due to invalid IL or missing references) //IL_03c6: Unknown result type (might be due to invalid IL or missing references) if (operationResponse.Parameters.ContainsKey((byte)221)) { if (AuthValues == null) { AuthValues = new AuthenticationValues(); } AuthValues.Token = operationResponse.Parameters[(byte)221]; } if (operationResponse.ReturnCode == 32743) { Disconnect(DisconnectCause.DisconnectByOperationLimit); } switch (operationResponse.OperationCode) { case 230: case 231: { if (operationResponse.ReturnCode != 0) { Log.Error($"{operationResponse.ToStringFull()} Server: {Server} Address: {RealtimePeer.ServerAddress}", LogLevel, LogPrefix); switch (operationResponse.ReturnCode) { case short.MaxValue: DisconnectedCause = DisconnectCause.InvalidAuthentication; break; case 32755: DisconnectedCause = DisconnectCause.CustomAuthenticationFailed; ConnectionCallbackTargets.OnCustomAuthenticationFailed(operationResponse.DebugMessage); break; case 32756: DisconnectedCause = DisconnectCause.InvalidRegion; break; case 32757: DisconnectedCause = DisconnectCause.MaxCcuReached; break; case -3: case -2: DisconnectedCause = DisconnectCause.OperationNotAllowedInCurrentState; break; case 32753: DisconnectedCause = DisconnectCause.AuthenticationTicketExpired; break; } Disconnect(DisconnectedCause); break; } if (Server == ServerConnection.NameServer || Server == ServerConnection.MasterServer) { if (operationResponse.Parameters.ContainsKey((byte)225)) { string text3 = (string)operationResponse.Parameters[(byte)225]; if (!string.IsNullOrEmpty(text3)) { UserId = text3; LocalPlayer.UserId = text3; Log.Info("Server sets UserID: " + UserId, LogLevel, LogPrefix); } } if (operationResponse.Parameters.ContainsKey((byte)202)) { NickName = (string)operationResponse.Parameters[(byte)202]; Log.Info("Server sets NickName: " + NickName, LogLevel, LogPrefix); } if (operationResponse.Parameters.ContainsKey((byte)192)) { SetupEncryption((Dictionary)operationResponse.Parameters[(byte)192]); } } if (Server == ServerConnection.NameServer) { if (AppSettings.AuthMode == AuthModeOption.AuthOnceWss && RealtimePeer.TransportProtocol != AppSettings.Protocol) { Log.Debug($"AuthOnceWss response switches TransportProtocol to: {AppSettings.Protocol}.", LogLevel, LogPrefix); RealtimePeer.TransportProtocol = AppSettings.Protocol; } string text4 = operationResponse[(byte)196] as string; if (!string.IsNullOrEmpty(text4)) { CurrentCluster = text4; } MasterServerAddress = operationResponse[(byte)230] as string; ushort num2 = ProtocolPorts.Get(RealtimePeer.TransportProtocol, ServerConnection.MasterServer); if (num2 != 0) { MasterServerAddress = ReplacePortWithAlternative(MasterServerAddress, num2); } if (AddressRewriter != null) { MasterServerAddress = AddressRewriter(MasterServerAddress, ServerConnection.MasterServer); } DisconnectToReconnect(); } else if (Server == ServerConnection.MasterServer) { State = ClientState.ConnectedToMasterServer; if (failedRoomEntryOperation == null) { ConnectionCallbackTargets.OnConnectedToMaster(); } else { CallbackRoomEnterFailed(failedRoomEntryOperation); failedRoomEntryOperation = null; } if (AppSettings.AuthMode != 0) { OpSettings(AppSettings.EnableLobbyStatistics); } } else if (Server == ServerConnection.GameServer) { State = ClientState.Joining; enterRoomArgumentsCache.OnGameServer = true; if (lastJoinType == JoinType.JoinRoom || lastJoinType == JoinType.JoinRandomRoom || lastJoinType == JoinType.JoinRandomOrCreateRoom || lastJoinType == JoinType.JoinOrCreateRoom) { OpJoinRoomIntern(enterRoomArgumentsCache); } else if (lastJoinType == JoinType.CreateRoom) { OpCreateRoomIntern(enterRoomArgumentsCache); } break; } Dictionary dictionary = (Dictionary)operationResponse[(byte)245]; if (dictionary != null) { ConnectionCallbackTargets.OnCustomAuthenticationResponse(dictionary); } break; } case 220: if (operationResponse.ReturnCode == short.MaxValue) { Log.Error(string.Format("GetRegions failed. AppId is unknown on the (cloud) server. " + operationResponse.DebugMessage), LogLevel, LogPrefix); Disconnect(DisconnectCause.InvalidAuthentication); break; } if (operationResponse.ReturnCode != 0) { Log.Error($"GetRegions failed. Can't provide regions list. ReturnCode: {operationResponse.ReturnCode}: {operationResponse.DebugMessage}", LogLevel, LogPrefix); Disconnect(DisconnectCause.InvalidAuthentication); break; } if (RegionHandler == null) { RegionHandler = new RegionHandler(ProtocolPorts.Get((ConnectionProtocol)0, ServerConnection.MasterServer)); } if (RegionHandler.IsPinging) { Log.Warn("Received response for OpGetRegions while the RegionHandler is pinging regions already. Skipping this response in favor of completing the current region-pinging.", LogLevel, LogPrefix); return; } RegionHandler.SetRegions(operationResponse, this); if (clientWorkflow == ClientWorkflowOption.GetRegionsOnly) { Disconnect(); ConnectionCallbackTargets.OnRegionListReceived(RegionHandler); return; } if (clientWorkflow == ClientWorkflowOption.Default) { ConnectionCallbackTargets.OnRegionListReceived(RegionHandler); } if (State == ClientState.ConnectedToNameServer) { RegionHandler.PingMinimumOfRegions(OnRegionPingCompleted, AppSettings.BestRegionSummaryFromStorage); } break; case 225: case 226: case 227: { if (operationResponse.ReturnCode != 0) { if (Server == ServerConnection.GameServer) { failedRoomEntryOperation = operationResponse; DisconnectToReconnect(); } else { State = (InLobby ? ClientState.JoinedLobby : ClientState.ConnectedToMasterServer); CallbackRoomEnterFailed(operationResponse); } break; } if (Server == ServerConnection.GameServer) { GameEnteredOnGameServer(operationResponse); break; } GameServerAddress = (string)operationResponse[(byte)230]; ushort num = ProtocolPorts.Get(RealtimePeer.TransportProtocol, ServerConnection.GameServer); if (num != 0) { GameServerAddress = ReplacePortWithAlternative(GameServerAddress, num); } if (AddressRewriter != null) { GameServerAddress = AddressRewriter(GameServerAddress, ServerConnection.GameServer); } string text2 = operationResponse[byte.MaxValue] as string; if (!string.IsNullOrEmpty(text2)) { enterRoomArgumentsCache.RoomName = text2; } DisconnectToReconnect(); break; } case 217: { if (operationResponse.ReturnCode != 0) { Log.Error("GetGameList failed: " + operationResponse.ToStringFull(), LogLevel, LogPrefix); break; } List list2 = new List(); PhotonHashtable val = (PhotonHashtable)operationResponse[(byte)222]; foreach (string key in ((Dictionary)(object)val).Keys) { list2.Add(new RoomInfo(key, (PhotonHashtable)val[(object)key])); } LobbyCallbackTargets.OnRoomListUpdate(list2); break; } case 229: CurrentLobby = targetLobbyCache; targetLobbyCache = null; State = ClientState.JoinedLobby; LobbyCallbackTargets.OnJoinedLobby(); break; case 228: CurrentLobby = null; targetLobbyCache = null; State = ClientState.ConnectedToMasterServer; LobbyCallbackTargets.OnLeftLobby(); break; case 254: DisconnectToReconnect(); break; case 222: { if (operationResponse.ReturnCode != 0) { Log.Error("OpFindFriends failed: " + operationResponse.ToStringFull(), LogLevel, LogPrefix); friendListRequested = null; break; } bool[] array = operationResponse[(byte)1] as bool[]; string[] array2 = operationResponse[(byte)2] as string[]; List list = new List(friendListRequested.Length); for (int i = 0; i < friendListRequested.Length; i++) { FriendInfo friendInfo = new FriendInfo(); friendInfo.UserId = friendListRequested[i]; friendInfo.Room = array2[i]; friendInfo.IsOnline = array[i]; list.Insert(i, friendInfo); } friendListRequested = null; MatchMakingCallbackTargets.OnFriendListUpdate(list); break; } } if (this.OpResponseReceived != null) { this.OpResponseReceived(operationResponse); } } public virtual void OnStatusChanged(StatusCode statusCode) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected I4, but got Unknown //IL_04f3: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_0582: Unknown result type (might be due to invalid IL or missing references) //IL_0588: Invalid comparison between Unknown and I4 //IL_02dd: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Unknown result type (might be due to invalid IL or missing references) //IL_0658: Unknown result type (might be due to invalid IL or missing references) //IL_065e: Invalid comparison between Unknown and I4 //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Invalid comparison between Unknown and I4 //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Invalid comparison between Unknown and I4 //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_04ae: Unknown result type (might be due to invalid IL or missing references) //IL_037b: Unknown result type (might be due to invalid IL or missing references) switch (statusCode - 1022) { case 2: if (State == ClientState.ConnectingToNameServer) { Server = ServerConnection.NameServer; } if (State == ClientState.ConnectingToGameServer) { Server = ServerConnection.GameServer; } if (State == ClientState.ConnectingToMasterServer) { Server = ServerConnection.MasterServer; AppSettings.EnableProtocolFallback = false; } if ((int)LogLevel >= 3) { lastStatsLogTime = RealtimePeer.ConnectionTime; Log.Info($"Connected to {Server}: {CurrentServerAddress} ({CurrentRegion}) PeerID: {RealtimePeer.PeerID}", LogLevel, LogPrefix); } if ((int)RealtimePeer.TransportProtocol != 5) { if (Server == ServerConnection.NameServer || AppSettings.AuthMode == AuthModeOption.Auth) { RealtimePeer.EstablishEncryption(); } break; } goto case 26; case 26: if (Server == ServerConnection.NameServer) { State = ClientState.ConnectedToNameServer; if (string.IsNullOrEmpty(AppSettings.FixedRegion)) { OpGetRegions(); break; } } else if (AppSettings.AuthMode == AuthModeOption.AuthOnce || AppSettings.AuthMode == AuthModeOption.AuthOnceWss) { break; } if (CallAuthenticate()) { State = ClientState.Authenticating; } else { Log.Error($"OpAuthenticate failed. Check log output and AuthValues. State: {State}", LogLevel, LogPrefix); } break; case 3: { friendListRequested = null; bool flag = CurrentRoom != null; CurrentRoom = null; ChangeLocalID(-1); CurrentLobby = null; targetLobbyCache = null; if (Server == ServerConnection.GameServer && flag) { MatchMakingCallbackTargets.OnLeftRoom(); } if (RealtimePeer.TransportProtocol != AppSettings.Protocol) { Log.Info($"Disconnect switches TransportProtocol to: {AppSettings.Protocol}.", LogLevel, LogPrefix); RealtimePeer.TransportProtocol = AppSettings.Protocol; } switch (State) { case ClientState.ConnectWithFallbackProtocol: AppSettings.EnableProtocolFallback = false; AppSettings.Protocol = (ConnectionProtocol)5; RealtimePeer.TransportProtocol = (ConnectionProtocol)5; AppSettings.UseNameServer = true; AppSettings.Port = 0; if (AuthValues != null) { AuthValues.Token = null; } if (RealtimePeer.Connect(NameServerAddress, AppSettings.GetAppId(ClientType), TokenForInit, (object)null, AppSettings.ProxyServer)) { State = ClientState.ConnectingToNameServer; } break; case ClientState.PeerCreated: case ClientState.Disconnecting: Handler.StopFallbackSendAckThread(); State = ClientState.Disconnected; ConnectionCallbackTargets.OnDisconnected(DisconnectedCause); break; case ClientState.DisconnectingFromGameServer: case ClientState.DisconnectingFromNameServer: CallConnect(ServerConnection.MasterServer); break; case ClientState.DisconnectingFromMasterServer: CallConnect(ServerConnection.GameServer); break; case ClientState.Disconnected: break; default: { string text = ""; text = new StackTrace(fNeedFileInfo: true).ToString(); Log.Warn($"Unexpected Disconnect. State: {State}. {Server}: {CurrentServerAddress} Trace: {text}", LogLevel, LogPrefix); Handler.StopFallbackSendAckThread(); State = ClientState.Disconnected; ConnectionCallbackTargets.OnDisconnected(DisconnectedCause); break; } } break; } case 20: Log.Error("MaxCcuReached. Connection rejected due to the AppId CCU limit.", LogLevel, LogPrefix); DisconnectedCause = DisconnectCause.MaxCcuReached; State = ClientState.Disconnecting; break; case 29: DisconnectedCause = DisconnectCause.DnsExceptionOnConnect; State = ClientState.Disconnecting; break; case 28: DisconnectedCause = DisconnectCause.ServerAddressInvalid; State = ClientState.Disconnecting; break; case 0: case 1: case 27: SystemConnectionSummary = new SystemConnectionSummary(this); DisconnectedCause = DisconnectCause.ExceptionOnConnect; if (AppSettings.EnableProtocolFallback && (State == ClientState.ConnectingToNameServer || State == ClientState.ConnectingToMasterServer) && (int)RealtimePeer.UsedProtocol != 5) { State = ClientState.ConnectWithFallbackProtocol; } else { State = ClientState.Disconnecting; } break; case 4: case 8: case 17: SystemConnectionSummary = new SystemConnectionSummary(this); DisconnectedCause = DisconnectCause.Exception; State = ClientState.Disconnecting; break; case 19: SystemConnectionSummary = new SystemConnectionSummary(this); DisconnectedCause = DisconnectCause.ServerTimeout; State = ClientState.Disconnecting; break; case 21: DisconnectedCause = DisconnectCause.DisconnectByServerLogic; State = ClientState.Disconnecting; break; case 22: DisconnectedCause = DisconnectCause.DisconnectByServerReasonUnknown; State = ClientState.Disconnecting; break; case 18: SystemConnectionSummary = new SystemConnectionSummary(this); DisconnectedCause = DisconnectCause.ClientTimeout; if (AppSettings.EnableProtocolFallback && (State == ClientState.ConnectingToNameServer || State == ClientState.ConnectingToMasterServer) && (int)RealtimePeer.UsedProtocol != 5) { State = ClientState.ConnectWithFallbackProtocol; } else { State = ClientState.Disconnecting; } break; case 5: case 6: case 7: case 9: case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 23: case 24: case 25: break; } } public virtual void OnEvent(EventData photonEvent) { //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown //IL_0366: Unknown result type (might be due to invalid IL or missing references) //IL_036d: Expected O, but got Unknown //IL_0350: Unknown result type (might be due to invalid IL or missing references) //IL_0357: Expected O, but got Unknown int sender = photonEvent.Sender; Player player = ((CurrentRoom != null) ? CurrentRoom.GetPlayer(sender) : null); switch (photonEvent.Code) { case 229: case 230: { List list = new List(); PhotonHashtable val3 = (PhotonHashtable)photonEvent[(byte)222]; foreach (string key in ((Dictionary)(object)val3).Keys) { list.Add(new RoomInfo(key, (PhotonHashtable)val3[(object)key])); } LobbyCallbackTargets.OnRoomListUpdate(list); break; } case byte.MaxValue: { PhotonHashtable val2 = (PhotonHashtable)photonEvent[(byte)249]; if (player == null) { if (sender > 0) { player = new Player(string.Empty, sender, isLocal: false, val2); CurrentRoom.StorePlayer(player); } } else { player.InternalCacheProperties(val2); player.IsInactive = false; player.HasRejoined = sender != LocalPlayer.ActorNumber; } if (sender == LocalPlayer.ActorNumber) { int[] actorsInGame = (int[])photonEvent[(byte)252]; UpdatedActorList(actorsInGame); player.HasRejoined = enterRoomArgumentsCache.JoinMode == JoinMode.RejoinOnly; State = ClientState.Joined; LocalPlayer.UpdateNickNameOnJoined(); if (lastJoinType == JoinType.CreateRoom || (lastJoinType == JoinType.JoinOrCreateRoom && LocalPlayer.ActorNumber == 1)) { MatchMakingCallbackTargets.OnCreatedRoom(); } MatchMakingCallbackTargets.OnJoinedRoom(); } else { InRoomCallbackTargets.OnPlayerEnteredRoom(player); } break; } case 254: if (player != null) { bool flag = false; if (photonEvent.Parameters.ContainsKey((byte)233)) { flag = (bool)photonEvent.Parameters[(byte)233]; } player.IsInactive = flag; player.HasRejoined = false; if (!flag) { CurrentRoom.RemovePlayer(sender); } } if (photonEvent.Parameters.ContainsKey((byte)203)) { int num = (int)photonEvent[(byte)203]; if (num != 0) { CurrentRoom.MasterClientId = num; InRoomCallbackTargets.OnMasterClientSwitched(CurrentRoom.GetPlayer(num)); } } InRoomCallbackTargets.OnPlayerLeftRoom(player); break; case 253: { int num2 = 0; if (photonEvent.Parameters.ContainsKey((byte)253)) { num2 = (int)photonEvent[(byte)253]; } PhotonHashtable gameProperties = null; PhotonHashtable actorProperties = null; if (num2 == 0) { gameProperties = (PhotonHashtable)photonEvent[(byte)251]; } else { actorProperties = (PhotonHashtable)photonEvent[(byte)251]; } ReadoutProperties(gameProperties, actorProperties, num2); break; } case 226: PlayersInRoomsCount = (int)photonEvent[(byte)229]; RoomsCount = (int)photonEvent[(byte)228]; PlayersOnMasterCount = (int)photonEvent[(byte)227]; break; case 224: { string[] array = photonEvent[(byte)213] as string[]; int[] array2 = photonEvent[(byte)229] as int[]; int[] array3 = photonEvent[(byte)228] as int[]; object obj = photonEvent[(byte)212]; ByteArraySlice val = (ByteArraySlice)((obj is ByteArraySlice) ? obj : null); bool flag2 = val != null; byte[] array4 = ((!flag2) ? (photonEvent[(byte)212] as byte[]) : val.Buffer); lobbyStatistics.Clear(); for (int i = 0; i < array.Length; i++) { TypedLobbyInfo item = new TypedLobbyInfo(array[i], (LobbyType)array4[i], array2[i], array3[i]); lobbyStatistics.Add(item); } if (flag2) { val.Release(); } LobbyCallbackTargets.OnLobbyStatisticsUpdate(lobbyStatistics); break; } case 251: ErrorInfoCallbackTargets.OnErrorInfo(new ErrorInfo(photonEvent)); break; case 223: if (AuthValues == null) { AuthValues = new AuthenticationValues(); } AuthValues.Token = photonEvent[(byte)221]; break; } UpdateCallbackTargets(); if (this.EventReceived != null) { this.EventReceived(photonEvent); } } public virtual void OnMessage(bool isRawMessage, object message) { UpdateCallbackTargets(); if (this.MessageReceived != null) { this.MessageReceived(isRawMessage, message); } } public void OnDisconnectMessage(DisconnectMessage obj) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) Log.Error($"OnDisconnectMessage. Code: {obj.Code} Msg: \"{obj.DebugMessage}\". Debug Info: {obj.Parameters}", LogLevel, LogPrefix); Disconnect(DisconnectCause.DisconnectByDisconnectMessage); } private void OnRegionPingCompleted(RegionHandler regionHandler) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) if ((int)LogLevel == 3) { Log.Info("Region pinging summary: " + SummaryToCache, LogLevel, LogPrefix); } else if ((int)LogLevel == 4) { Log.Info("Region pinging results: " + regionHandler.GetResults(), LogLevel, LogPrefix); } if (clientWorkflow == ClientWorkflowOption.GetRegionsAndPing) { Disconnect(); ConnectionCallbackTargets.OnRegionListReceived(regionHandler); return; } CurrentRegion = regionHandler.BestRegion.Code; if (State == ClientState.ConnectedToNameServer) { CallAuthenticate(); } else { CallConnect(ServerConnection.NameServer); } } private void SetupEncryption(Dictionary encryptionData) { switch ((EncryptionMode)(byte)encryptionData[0]) { case EncryptionMode.PayloadEncryption: { byte[] array2 = (byte[])encryptionData[1]; RealtimePeer.InitPayloadEncryption(array2); break; } case EncryptionMode.DatagramEncryptionGCM: { byte[] array = (byte[])encryptionData[1]; RealtimePeer.InitDatagramEncryption(array, (byte[])null); break; } default: throw new ArgumentOutOfRangeException(); } } public void AddCallbackTarget(object target) { callbackTargetChanges.Enqueue(new CallbackTargetChange(target, addTarget: true)); } public void RemoveCallbackTarget(object target) { callbackTargetChanges.Enqueue(new CallbackTargetChange(target, addTarget: false)); } protected internal void UpdateCallbackTargets() { while (callbackTargetChanges.Count > 0) { CallbackTargetChange callbackTargetChange = callbackTargetChanges.Dequeue(); if (callbackTargetChange.AddTarget) { if (callbackTargets.Contains(callbackTargetChange.Target)) { continue; } callbackTargets.Add(callbackTargetChange.Target); } else { if (!callbackTargets.Contains(callbackTargetChange.Target)) { continue; } callbackTargets.Remove(callbackTargetChange.Target); } UpdateCallbackTarget(callbackTargetChange, InRoomCallbackTargets); UpdateCallbackTarget(callbackTargetChange, ConnectionCallbackTargets); UpdateCallbackTarget(callbackTargetChange, MatchMakingCallbackTargets); UpdateCallbackTarget(callbackTargetChange, LobbyCallbackTargets); UpdateCallbackTarget(callbackTargetChange, ErrorInfoCallbackTargets); if (callbackTargetChange.Target is IOnEventCallback onEventCallback) { if (callbackTargetChange.AddTarget) { EventReceived += onEventCallback.OnEvent; } else { EventReceived -= onEventCallback.OnEvent; } } if (callbackTargetChange.Target is IOnMessageCallback onMessageCallback) { if (callbackTargetChange.AddTarget) { MessageReceived += onMessageCallback.OnMessage; } else { MessageReceived -= onMessageCallback.OnMessage; } } } } private void UpdateCallbackTarget(CallbackTargetChange change, List container) where T : class { if (change.Target is T item) { if (change.AddTarget) { container.Add(item); } else { container.Remove(item); } } } public virtual bool OpGetRegions() { //IL_0029: 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_0088: Unknown result type (might be due to invalid IL or missing references) if (!CheckIfOpCanBeSent(220, Server, "GetRegions")) { return false; } Log.Info("OpGetRegions()", LogLevel, LogPrefix); ParameterDictionary val = paramDictionaryPool.Acquire(); val[(byte)224] = AppSettings.GetAppId(ClientType); PhotonPeer realtimePeer = RealtimePeer; SendOptions val2 = default(SendOptions); ((SendOptions)(ref val2)).Reliability = true; val2.Encrypt = true; bool result = realtimePeer.SendOperation((byte)220, val, val2); paramDictionaryPool.Release(val); return result; } public bool OpFindFriends(string[] friendsToFind, FindFriendsArgs args = null) { //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_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) if (!CheckIfOpCanBeSent(222, Server, "FindFriends")) { return false; } if (IsFetchingFriendList) { Log.Warn("OpFindFriends skipped: already fetching friends list.", LogLevel, LogPrefix); return false; } if (friendsToFind == null || friendsToFind.Length == 0) { Log.Error("OpFindFriends skipped: friendsToFind array is null or empty.", LogLevel, LogPrefix); return false; } if (friendsToFind.Length > 512) { Log.Error($"OpFindFriends skipped: friendsToFind array exceeds allowed length of {512}.", LogLevel, LogPrefix); return false; } List list = new List(friendsToFind.Length); for (int i = 0; i < friendsToFind.Length; i++) { string text = friendsToFind[i]; if (string.IsNullOrEmpty(text)) { Log.Warn($"friendsToFind array contains a null or empty UserId, element at position {i} skipped.", LogLevel, LogPrefix); } else if (text.Equals(UserId)) { Log.Warn($"friendsToFind array contains local player's UserId \"{text}\", element at position {i} skipped.", LogLevel, LogPrefix); } else if (list.Contains(text)) { Log.Warn($"friendsToFind array contains duplicate UserId \"{text}\", element at position {i} skipped.", LogLevel, LogPrefix); } else { list.Add(text); } } if (list.Count == 0) { Log.Error("OpFindFriends failed. No friends to find (check warnings).", LogLevel, LogPrefix); return false; } Log.Info("OpFindFriends()", LogLevel, LogPrefix); string[] array = list.ToArray(); ParameterDictionary val = paramDictionaryPool.Acquire(); if (array != null && array.Length != 0) { val[(byte)1] = array; } if (args != null) { val[(byte)2] = args.ToIntFlags(); } SendOptions val2 = default(SendOptions); ((SendOptions)(ref val2)).Reliability = true; val2.Encrypt = true; SendOptions val3 = val2; bool flag = RealtimePeer.SendOperation((byte)222, val, val3); paramDictionaryPool.Release(val); friendListRequested = (flag ? array : null); return flag; } public virtual bool OpJoinLobby(TypedLobby lobby = null) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) if (!CheckIfOpCanBeSent(229, Server, "JoinLobby")) { return false; } if (lobby == null) { lobby = TypedLobby.Default; } Log.Info($"OpJoinLobby() {lobby}", LogLevel, LogPrefix); ParameterDictionary val = paramDictionaryPool.Acquire(); if (lobby != null && !lobby.IsDefault) { val[(byte)213] = lobby.Name; val[(byte)212] = (byte)lobby.Type; } bool flag = RealtimePeer.SendOperation((byte)229, val, SendOptions.SendReliable); paramDictionaryPool.Release(val); if (flag) { targetLobbyCache = lobby; State = ClientState.JoiningLobby; } return flag; } public bool OpLeaveLobby() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (!CheckIfOpCanBeSent(228, Server, "LeaveLobby")) { return false; } Log.Info("OpLeaveLobby()", LogLevel, LogPrefix); return RealtimePeer.SendOperation((byte)228, (ParameterDictionary)null, SendOptions.SendReliable); } private void RoomOptionsToOpParameters(ParameterDictionary op, RoomOptions roomOptions, bool usePropertiesKey = false) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown if (roomOptions == null) { roomOptions = new RoomOptions(); } PhotonHashtable val = new PhotonHashtable(); val[(byte)253] = roomOptions.IsOpen; val[(byte)254] = roomOptions.IsVisible; val[(byte)250] = ((roomOptions.CustomRoomPropertiesForLobby == null) ? new object[0] : roomOptions.CustomRoomPropertiesForLobby); val.Merge(roomOptions.CustomRoomProperties); if (roomOptions.MaxPlayers > 0) { byte b = (byte)((roomOptions.MaxPlayers <= 255) ? ((byte)roomOptions.MaxPlayers) : 0); val[byte.MaxValue] = b; val[(byte)243] = roomOptions.MaxPlayers; } if (!usePropertiesKey) { op[(byte)248] = val; } else { op[(byte)251] = val; } int num = 0; if (roomOptions.CleanupCacheOnLeave) { op[(byte)241] = true; num |= 2; } else { op[(byte)241] = false; val[(byte)249] = false; } num |= 1; op[(byte)232] = true; if (roomOptions.PlayerTtl > 0 || roomOptions.PlayerTtl == -1) { op[(byte)235] = roomOptions.PlayerTtl; } if (roomOptions.EmptyRoomTtl > 0) { op[(byte)236] = roomOptions.EmptyRoomTtl; } if (roomOptions.SuppressRoomEvents) { num |= 4; op[(byte)237] = true; } if (roomOptions.SuppressPlayerInfo) { num |= 0x40; } if (roomOptions.Plugins != null) { op[(byte)204] = roomOptions.Plugins; } if (roomOptions.PublishUserId) { num |= 8; op[(byte)239] = true; } if (roomOptions.DeleteNullProperties) { num |= 0x10; } if (roomOptions.BroadcastPropsChangeToAll) { num |= 0x20; } op[(byte)191] = num; } public bool OpJoinRandomRoom(JoinRandomRoomArgs joinRandomRoomArgs = null) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Unknown result type (might be due to invalid IL or missing references) if (!CheckIfOpCanBeSent(225, Server, "JoinRandomGame")) { return false; } if (joinRandomRoomArgs == null) { joinRandomRoomArgs = new JoinRandomRoomArgs(); } if (!joinRandomRoomArgs.ExpectedCustomRoomProperties.CustomPropKeyTypesValid(NullOrZeroAccepted: true)) { Log.Error("OpJoinRandomRoom() expected properties must use key type of string or int.", LogLevel, LogPrefix); return false; } Log.Info("OpJoinRandomRoom() " + GetMatchmakingHash(joinRandomRoomArgs.Lobby), LogLevel, LogPrefix); PhotonHashtable val = new PhotonHashtable(); val.Merge(joinRandomRoomArgs.ExpectedCustomRoomProperties); if (joinRandomRoomArgs.ExpectedMaxPlayers > 0) { byte b = (byte)((joinRandomRoomArgs.ExpectedMaxPlayers <= 255) ? ((byte)joinRandomRoomArgs.ExpectedMaxPlayers) : 0); val[byte.MaxValue] = b; if (joinRandomRoomArgs.ExpectedMaxPlayers > 255) { val[(byte)243] = joinRandomRoomArgs.ExpectedMaxPlayers; } } ParameterDictionary val2 = paramDictionaryPool.Acquire(); SendOptions val3 = default(SendOptions); ((SendOptions)(ref val3)).Reliability = true; SendOptions val4 = val3; if (((Dictionary)(object)val).Count > 0) { val2[(byte)248] = val; } if (joinRandomRoomArgs.MatchingType != 0) { val2[(byte)223] = (byte)joinRandomRoomArgs.MatchingType; } if (joinRandomRoomArgs.Lobby != null && !joinRandomRoomArgs.Lobby.IsDefault) { val2[(byte)213] = joinRandomRoomArgs.Lobby.Name; val2[(byte)212] = (byte)joinRandomRoomArgs.Lobby.Type; } if (!string.IsNullOrEmpty(joinRandomRoomArgs.SqlLobbyFilter)) { val2[(byte)245] = joinRandomRoomArgs.SqlLobbyFilter; } if (joinRandomRoomArgs.ExpectedUsers != null && joinRandomRoomArgs.ExpectedUsers.Length != 0) { val2[(byte)238] = joinRandomRoomArgs.ExpectedUsers; val4.Encrypt = true; } if (joinRandomRoomArgs.Ticket != null) { val2[(byte)190] = joinRandomRoomArgs.Ticket; } val2[(byte)188] = true; bool flag = RealtimePeer.SendOperation((byte)225, val2, val4); paramDictionaryPool.Release(val2); if (flag) { State = ClientState.Joining; lastJoinType = JoinType.JoinRandomRoom; enterRoomArgumentsCache = new EnterRoomArgs(); enterRoomArgumentsCache.Lobby = ((CurrentLobby != null && !CurrentLobby.IsDefault && joinRandomRoomArgs.Lobby == null) ? CurrentLobby : joinRandomRoomArgs.Lobby); enterRoomArgumentsCache.ExpectedUsers = joinRandomRoomArgs.ExpectedUsers; if (joinRandomRoomArgs.Ticket != null) { enterRoomArgumentsCache.Ticket = joinRandomRoomArgs.Ticket; } } return flag; } public bool OpJoinRandomOrCreateRoom(JoinRandomRoomArgs joinRandomRoomArgs = null, EnterRoomArgs createRoomArgs = null) { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Unknown result type (might be due to invalid IL or missing references) if (!CheckIfOpCanBeSent(225, Server, "OpJoinRandomOrCreateRoom")) { return false; } if (joinRandomRoomArgs == null) { joinRandomRoomArgs = new JoinRandomRoomArgs(); } if (createRoomArgs == null) { createRoomArgs = new EnterRoomArgs(); } if (!joinRandomRoomArgs.ExpectedCustomRoomProperties.CustomPropKeyTypesValid(NullOrZeroAccepted: true)) { Log.Error("OpJoinRandomOrCreateRoom() expected properties must use key type of string or int.", LogLevel, LogPrefix); return false; } Log.Info("OpJoinRandomOrCreateRoom() " + GetMatchmakingHash(joinRandomRoomArgs.Lobby), LogLevel, LogPrefix); createRoomArgs.JoinMode = JoinMode.CreateIfNotExists; PhotonHashtable val = new PhotonHashtable(); val.Merge(joinRandomRoomArgs.ExpectedCustomRoomProperties); if (joinRandomRoomArgs.ExpectedMaxPlayers > 0) { byte b = (byte)((joinRandomRoomArgs.ExpectedMaxPlayers <= 255) ? ((byte)joinRandomRoomArgs.ExpectedMaxPlayers) : 0); val[byte.MaxValue] = b; if (joinRandomRoomArgs.ExpectedMaxPlayers > 255) { val[(byte)243] = joinRandomRoomArgs.ExpectedMaxPlayers; } } ParameterDictionary val2 = paramDictionaryPool.Acquire(); SendOptions val3 = default(SendOptions); ((SendOptions)(ref val3)).Reliability = true; SendOptions val4 = val3; if (((Dictionary)(object)val).Count > 0) { val2[(byte)248] = val; } if (joinRandomRoomArgs.MatchingType != 0) { val2[(byte)223] = (byte)joinRandomRoomArgs.MatchingType; } if (joinRandomRoomArgs.Lobby != null && !joinRandomRoomArgs.Lobby.IsDefault) { val2[(byte)213] = joinRandomRoomArgs.Lobby.Name; val2[(byte)212] = (byte)joinRandomRoomArgs.Lobby.Type; } if (!string.IsNullOrEmpty(joinRandomRoomArgs.SqlLobbyFilter)) { val2[(byte)245] = joinRandomRoomArgs.SqlLobbyFilter; } if (joinRandomRoomArgs.ExpectedUsers != null && joinRandomRoomArgs.ExpectedUsers.Length != 0) { val2[(byte)238] = joinRandomRoomArgs.ExpectedUsers; val4.Encrypt = true; } if (joinRandomRoomArgs.Ticket != null) { val2[(byte)190] = joinRandomRoomArgs.Ticket; } val2[(byte)215] = (byte)1; val2[(byte)188] = true; if (!string.IsNullOrEmpty(createRoomArgs.RoomName)) { val2[byte.MaxValue] = createRoomArgs.RoomName; } bool flag = RealtimePeer.SendOperation((byte)225, val2, val4); paramDictionaryPool.Release(val2); if (flag) { State = ClientState.Joining; lastJoinType = JoinType.JoinRandomOrCreateRoom; enterRoomArgumentsCache = EnterRoomArgs.ShallowCopyToNewArgs(createRoomArgs); enterRoomArgumentsCache.Lobby = ((CurrentLobby != null && !CurrentLobby.IsDefault && joinRandomRoomArgs.Lobby == null) ? CurrentLobby : joinRandomRoomArgs.Lobby); enterRoomArgumentsCache.ExpectedUsers = joinRandomRoomArgs.ExpectedUsers; if (joinRandomRoomArgs.Ticket != null) { enterRoomArgumentsCache.Ticket = joinRandomRoomArgs.Ticket; } } return flag; } public bool OpCreateRoom(EnterRoomArgs enterRoomArgs) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) if (!CheckIfOpCanBeSent(227, Server, "CreateGame")) { return false; } if (enterRoomArgs == null) { Log.Error("OpCreateRoom() failed. Parameter enterRoomArgs can not be null.", LogLevel, LogPrefix); return false; } if (!(enterRoomArgs.OnGameServer = Server == ServerConnection.GameServer)) { enterRoomArgumentsCache = enterRoomArgs; enterRoomArgumentsCache.Lobby = ((CurrentLobby != null && !CurrentLobby.IsDefault && enterRoomArgs.Lobby == null) ? CurrentLobby : enterRoomArgs.Lobby); } Log.Info("OpCreateRoom() " + GetMatchmakingHash(enterRoomArgs.Lobby), LogLevel, LogPrefix); bool flag = OpCreateRoomIntern(enterRoomArgs); if (flag) { lastJoinType = JoinType.CreateRoom; State = ClientState.Joining; } return flag; } public bool OpJoinOrCreateRoom(EnterRoomArgs enterRoomArgs) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) if (!CheckIfOpCanBeSent(226, Server, "JoinOrCreateRoom")) { return false; } if (enterRoomArgs == null) { Log.Error("OpJoinOrCreateRoom() failed. Parameter enterRoomArgs can not be null.", LogLevel, LogPrefix); return false; } bool flag = Server == ServerConnection.GameServer; enterRoomArgs.JoinMode = JoinMode.CreateIfNotExists; enterRoomArgs.OnGameServer = flag; if (!flag) { Log.Info("OpJoinOrCreateRoom(" + enterRoomArgs.RoomName + ") " + GetMatchmakingHash(enterRoomArgs.Lobby), LogLevel, LogPrefix); enterRoomArgumentsCache = enterRoomArgs; enterRoomArgumentsCache.Lobby = ((CurrentLobby != null && !CurrentLobby.IsDefault && enterRoomArgs.Lobby == null) ? CurrentLobby : enterRoomArgs.Lobby); if (enterRoomArgs.Ticket != null) { enterRoomArgumentsCache.Ticket = enterRoomArgs.Ticket; } } bool flag2 = OpJoinRoomIntern(enterRoomArgs); if (flag2) { lastJoinType = JoinType.JoinOrCreateRoom; State = ClientState.Joining; } return flag2; } public bool OpJoinRoom(EnterRoomArgs enterRoomArgs) { //IL_0037: 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 (!CheckIfOpCanBeSent(226, Server, "JoinRoom")) { return false; } if (enterRoomArgs == null) { Log.Error("OpJoinRoom() failed. Parameter enterRoomArgs can not be null.", LogLevel, LogPrefix); return false; } if (!(enterRoomArgs.OnGameServer = Server == ServerConnection.GameServer)) { enterRoomArgumentsCache = enterRoomArgs; enterRoomArgumentsCache.Lobby = null; } Log.Info("OpJoinRoom() " + GetMatchmakingHash(null), LogLevel, LogPrefix); bool flag = OpJoinRoomIntern(enterRoomArgs); if (flag) { lastJoinType = ((enterRoomArgs.JoinMode != JoinMode.CreateIfNotExists) ? JoinType.JoinRoom : JoinType.JoinOrCreateRoom); State = ClientState.Joining; } return flag; } private bool OpCreateRoomIntern(EnterRoomArgs opArgs) { //IL_0010: 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_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Expected O, but got Unknown if (opArgs == null) { Log.Error("OpCreateRoom() failed. Parameter opArgs must be non null.", LogLevel, LogPrefix); return false; } if (opArgs.RoomOptions == null) { opArgs.RoomOptions = new RoomOptions(); } if (!opArgs.RoomOptions.CustomRoomProperties.CustomPropKeyTypesValid(NullOrZeroAccepted: true)) { Log.Error("OpCreateRoom() failed. Custom Room Properties contains key which is not string nor int.", LogLevel, LogPrefix); return false; } if (!opArgs.RoomOptions.CustomRoomPropertiesForLobby.CustomPropKeyTypesValid(NullOrZeroAccepted: true)) { Log.Error("OpCreateRoom() failed. RoomOptions.CustomRoomPropertiesForLobby can be null, have zero items or all items must be int or string.", LogLevel, LogPrefix); return false; } Log.Info("OpCreateRoom()", LogLevel, LogPrefix); ParameterDictionary val = paramDictionaryPool.Acquire(); SendOptions val2 = default(SendOptions); ((SendOptions)(ref val2)).Reliability = true; SendOptions val3 = val2; if (!string.IsNullOrEmpty(opArgs.RoomName)) { val[byte.MaxValue] = opArgs.RoomName; } if (opArgs.Lobby != null && !opArgs.Lobby.IsDefault) { val[(byte)213] = opArgs.Lobby.Name; val[(byte)212] = (byte)opArgs.Lobby.Type; } if (opArgs.ExpectedUsers != null && opArgs.ExpectedUsers.Length != 0) { val[(byte)238] = opArgs.ExpectedUsers; val3.Encrypt = true; } if (opArgs.Ticket != null) { val[(byte)190] = opArgs.Ticket; } if (opArgs.OnGameServer) { if (LocalPlayer != null) { if (!string.IsNullOrEmpty(LocalPlayer.NickName)) { if (LocalPlayer.CustomProperties == null) { LocalPlayer.CustomProperties = new PhotonHashtable(); } LocalPlayer.CustomProperties[byte.MaxValue] = LocalPlayer.NickName; } if (LocalPlayer.CustomProperties != null && ((Dictionary)(object)LocalPlayer.CustomProperties).Count > 0) { val[(byte)249] = LocalPlayer.CustomProperties; } } val[(byte)250] = true; RoomOptionsToOpParameters(val, opArgs.RoomOptions); } bool result = RealtimePeer.SendOperation((byte)227, val, val3); paramDictionaryPool.Release(val); return result; } private bool OpJoinRoomIntern(EnterRoomArgs opArgs) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_026c: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Expected O, but got Unknown if (opArgs == null) { Log.Error("OpJoinRoom() failed. Parameter opArgs must be non null.", LogLevel, LogPrefix); return false; } ParameterDictionary val = paramDictionaryPool.Acquire(); SendOptions val2 = default(SendOptions); ((SendOptions)(ref val2)).Reliability = true; SendOptions val3 = val2; if (!string.IsNullOrEmpty(opArgs.RoomName)) { val[byte.MaxValue] = opArgs.RoomName; } if (opArgs.JoinMode == JoinMode.CreateIfNotExists) { val[(byte)215] = (byte)1; if (opArgs.Lobby != null && !opArgs.Lobby.IsDefault) { val[(byte)213] = opArgs.Lobby.Name; val[(byte)212] = (byte)opArgs.Lobby.Type; } } else if (opArgs.JoinMode == JoinMode.RejoinOnly) { val[(byte)215] = (byte)3; } if (opArgs.ExpectedUsers != null && opArgs.ExpectedUsers.Length != 0) { val[(byte)238] = opArgs.ExpectedUsers; val3.Encrypt = true; } if (opArgs.Ticket != null) { val[(byte)190] = opArgs.Ticket; } if (opArgs.OnGameServer) { if (LocalPlayer != null && opArgs.JoinMode != JoinMode.RejoinOnly) { if (!string.IsNullOrEmpty(LocalPlayer.NickName)) { if (LocalPlayer.CustomProperties == null) { LocalPlayer.CustomProperties = new PhotonHashtable(); } LocalPlayer.CustomProperties[byte.MaxValue] = LocalPlayer.NickName; } if (LocalPlayer.CustomProperties != null && ((Dictionary)(object)LocalPlayer.CustomProperties).Count > 0) { val[(byte)249] = LocalPlayer.CustomProperties; } } val[(byte)250] = true; RoomOptionsToOpParameters(val, opArgs.RoomOptions); } bool result = RealtimePeer.SendOperation((byte)226, val, val3); paramDictionaryPool.Release(val); return result; } public virtual bool OpLeaveRoom(bool becomeInactive) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) if (!CheckIfOpCanBeSent(254, Server, "LeaveRoom")) { return false; } Log.Info(string.Format("OpLeaveRoom({0})", becomeInactive ? "inactive=true" : ""), LogLevel, LogPrefix); ParameterDictionary val = paramDictionaryPool.Acquire(); if (becomeInactive) { val[(byte)233] = true; } bool flag = RealtimePeer.SendOperation((byte)254, val, SendOptions.SendReliable); paramDictionaryPool.Release(val); if (flag) { State = ClientState.Leaving; GameServerAddress = string.Empty; enterRoomArgumentsCache = null; } return flag; } public bool OpRejoinRoom(string roomName, object ticket = null) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (!CheckIfOpCanBeSent(226, Server, "RejoinRoom")) { return false; } Log.Info($"OpRejoinRoom({roomName})", LogLevel, LogPrefix); bool onGameServer = Server == ServerConnection.GameServer; EnterRoomArgs enterRoomArgs = new EnterRoomArgs(); enterRoomArgs.RoomName = roomName; enterRoomArgs.OnGameServer = onGameServer; enterRoomArgs.JoinMode = JoinMode.RejoinOnly; enterRoomArgs.Ticket = ticket; enterRoomArgumentsCache = enterRoomArgs; bool flag = OpJoinRoomIntern(enterRoomArgs); if (flag) { lastJoinType = JoinType.JoinRoom; State = ClientState.Joining; } return flag; } public bool OpSetCustomPropertiesOfActor(int actorNr, PhotonHashtable propertiesToSet, PhotonHashtable expectedProperties = null) { //IL_0016: 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) if (!propertiesToSet.CustomPropKeyTypesValid()) { Log.Error("OpSetCustomPropertiesOfActor() failed. Parameter propertiesToSet must be non-null, not empty and contain only int or string keys.", LogLevel, LogPrefix); return false; } if (CurrentRoom == null) { Log.Error("OpSetCustomPropertiesOfActor() failed because the client is not in a room. Use LocalPlayer.SetCustomProperties() to change this player's properties even while not in a room.", LogLevel, LogPrefix); return false; } return OpSetPropertiesOfActor(actorNr, propertiesToSet, expectedProperties); } protected internal bool OpSetPropertiesOfActor(int actorNr, PhotonHashtable actorProperties, PhotonHashtable expectedProperties = null) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) if (!CheckIfOpCanBeSent(252, Server, "SetProperties")) { return false; } if (actorNr <= 0 || actorProperties == null || ((Dictionary)(object)actorProperties).Count == 0) { Log.Error("OpSetPropertiesOfActor() failed. Parameter actorProperties must be non-null and not empty.", LogLevel, LogPrefix); return false; } Log.Info("OpSetPropertiesOfActor()", LogLevel, LogPrefix); ParameterDictionary val = paramDictionaryPool.Acquire(); val.Add((byte)251, actorProperties); val.Add((byte)254, actorNr); val.Add((byte)250, true); if (expectedProperties != null && ((Dictionary)(object)expectedProperties).Count != 0) { val.Add((byte)231, expectedProperties); } bool flag = RealtimePeer.SendOperation((byte)252, val, SendOptions.SendReliable); paramDictionaryPool.Release(val); if (flag && !CurrentRoom.BroadcastPropertiesChangeToAll && (expectedProperties == null || ((Dictionary)(object)expectedProperties).Count == 0)) { Player player = CurrentRoom.GetPlayer(actorNr); if (player != null) { player.InternalCacheProperties(actorProperties); InRoomCallbackTargets.OnPlayerPropertiesUpdate(player, actorProperties); } } return flag; } public bool OpSetCustomPropertiesOfRoom(PhotonHashtable propertiesToSet, PhotonHashtable expectedProperties = null) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (!propertiesToSet.CustomPropKeyTypesValid()) { Log.Error("OpSetCustomPropertiesOfRoom() failed. Parameter propertiesToSet must be non-null, not empty and contain only int or string keys.", LogLevel, LogPrefix); return false; } return OpSetPropertiesOfRoom(propertiesToSet, expectedProperties); } protected internal bool OpSetPropertyOfRoom(byte propCode, object value) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown PhotonHashtable val = new PhotonHashtable(); val[propCode] = value; return OpSetPropertiesOfRoom(val); } protected internal bool OpSetPropertiesOfRoom(PhotonHashtable gameProperties, PhotonHashtable expectedProperties = null) { //IL_0060: 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_00c7: Unknown result type (might be due to invalid IL or missing references) if (!CheckIfOpCanBeSent(252, Server, "SetProperties")) { return false; } if (gameProperties == null || ((Dictionary)(object)gameProperties).Count == 0) { Log.Error("OpSetPropertiesOfRoom() failed. Parameter gameProperties must not be null nor empty.", LogLevel, LogPrefix); return false; } Log.Info("OpSetPropertiesOfRoom()", LogLevel, LogPrefix); ParameterDictionary val = paramDictionaryPool.Acquire(); val.Add((byte)251, gameProperties); val.Add((byte)250, true); if (expectedProperties != null && ((Dictionary)(object)expectedProperties).Count != 0) { val.Add((byte)231, expectedProperties); } bool flag = RealtimePeer.SendOperation((byte)252, val, SendOptions.SendReliable); paramDictionaryPool.Release(val); if (flag && !CurrentRoom.BroadcastPropertiesChangeToAll && (expectedProperties == null || ((Dictionary)(object)expectedProperties).Count == 0)) { CurrentRoom.InternalCacheProperties(gameProperties); InRoomCallbackTargets.OnRoomPropertiesUpdate(gameProperties); } return flag; } public virtual bool OpChangeGroups(byte[] groupsToRemove, byte[] groupsToAdd) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) if (!CheckIfOpCanBeSent(248, Server, "ChangeGroups")) { return false; } Log.Info("OpChangeGroups()", LogLevel, LogPrefix); ParameterDictionary val = paramDictionaryPool.Acquire(); if (groupsToRemove != null) { val[(byte)239] = groupsToRemove; } if (groupsToAdd != null) { val[(byte)238] = groupsToAdd; } bool result = RealtimePeer.SendOperation((byte)248, val, SendOptions.SendReliable); paramDictionaryPool.Release(val); return result; } public virtual bool OpGetGameList(TypedLobby lobby, string queryData) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Invalid comparison between Unknown and I4 //IL_00c8: Unknown result type (might be due to invalid IL or missing references) if (!CheckIfOpCanBeSent(217, Server, "GetGameList")) { return false; } if (string.IsNullOrEmpty(queryData)) { Log.Error("Operation GetGameList requires a filter.", LogLevel, LogPrefix); return false; } if (lobby == null || lobby.Type != LobbyType.Sql || lobby.IsDefault) { Log.Error("Operation GetGameList can only be used for named lobbies of type SqlLobby.", LogLevel, LogPrefix); return false; } Log.Info("OpGetGameList()", LogLevel, LogPrefix); if (string.IsNullOrEmpty(queryData)) { if ((int)LogLevel >= 3) { Log.Info("OpGetGameList not sent. queryData must be not null and not empty.", LogLevel, LogPrefix); } return false; } ParameterDictionary val = paramDictionaryPool.Acquire(); val[(byte)213] = lobby.Name; val[(byte)212] = (byte)lobby.Type; val[(byte)245] = queryData; bool result = RealtimePeer.SendOperation((byte)217, val, SendOptions.SendReliable); paramDictionaryPool.Release(val); return result; } public bool OpSetCustomPropertiesOfActor(int actorNr, PhotonHashtable actorProperties) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (!actorProperties.CustomPropKeyTypesValid()) { Log.Error("For OpSetCustomPropertiesOfActor the actorProperties can only contain keys of type string.", LogLevel, LogPrefix); return false; } return OpSetPropertiesOfActor(actorNr, actorProperties); } public bool OpSetCustomPropertiesOfRoom(PhotonHashtable gameProperties) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (!gameProperties.CustomPropKeyTypesValid()) { Log.Error("For OpSetCustomPropertiesOfRoom the gameProperties can only contain keys of type string.", LogLevel, LogPrefix); return false; } return OpSetPropertiesOfRoom(gameProperties); } public virtual bool OpAuthenticate(string appId, string appVersion, AuthenticationValues authValues, string regionCode, bool getLobbyStatistics) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) Log.Debug("OpAuthenticate()", LogLevel, LogPrefix); ParameterDictionary val = paramDictionaryPool.Acquire(); if (getLobbyStatistics) { val[(byte)211] = true; } bool result; if (authValues != null && authValues.Token != null) { val[(byte)221] = authValues.Token; result = RealtimePeer.SendOperation((byte)230, val, SendOptions.SendReliable); paramDictionaryPool.Release(val); return result; } val[(byte)220] = appVersion; val[(byte)224] = appId; val[(byte)210] = regionCode; if (authValues != null) { if (!string.IsNullOrEmpty(authValues.UserId)) { val[(byte)225] = authValues.UserId; } if (authValues.AuthType != CustomAuthenticationType.None) { val[(byte)217] = (byte)authValues.AuthType; if (!string.IsNullOrEmpty(authValues.AuthGetParameters)) { val[(byte)216] = authValues.AuthGetParameters; } if (authValues.AuthPostData != null) { val[(byte)214] = authValues.AuthPostData; } } } PhotonPeer realtimePeer = RealtimePeer; SendOptions val2 = default(SendOptions); ((SendOptions)(ref val2)).Reliability = true; val2.Encrypt = true; result = realtimePeer.SendOperation((byte)230, val, val2); paramDictionaryPool.Release(val); return result; } public virtual bool OpAuthenticateOnce(string appId, string appVersion, AuthenticationValues authValues, string regionCode, EncryptionMode encryptionMode, ConnectionProtocol expectedProtocol) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Expected I4, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) if (encryptionMode == EncryptionMode.DatagramEncryptionGCM && (int)expectedProtocol > 0) { Log.Error($"OpAuthenticateOnce() failed. Can not use EncryptionMode '{encryptionMode}' on protocol {expectedProtocol}", LogLevel, LogPrefix); return false; } Log.Debug($"OpAuthenticateOnce() authValues = {authValues}, region = {regionCode}, encryption = {encryptionMode}", LogLevel, LogPrefix); ParameterDictionary val = paramDictionaryPool.Acquire(); bool result; if (authValues != null && authValues.Token != null) { val[(byte)221] = authValues.Token; result = RealtimePeer.SendOperation((byte)231, val, SendOptions.SendReliable); paramDictionaryPool.Release(val); return result; } val[(byte)195] = (byte)(int)expectedProtocol; val[(byte)193] = (byte)encryptionMode; val[(byte)220] = appVersion; val[(byte)224] = appId; val[(byte)210] = regionCode; if (authValues != null) { if (!string.IsNullOrEmpty(authValues.UserId)) { val[(byte)225] = authValues.UserId; } if (authValues.AuthType != CustomAuthenticationType.None) { val[(byte)217] = (byte)authValues.AuthType; if (authValues.Token != null) { val[(byte)221] = authValues.Token; } else { if (!string.IsNullOrEmpty(authValues.AuthGetParameters)) { val[(byte)216] = authValues.AuthGetParameters; } if (authValues.AuthPostData != null) { val[(byte)214] = authValues.AuthPostData; } } } } PhotonPeer realtimePeer = RealtimePeer; SendOptions val2 = default(SendOptions); ((SendOptions)(ref val2)).Reliability = true; val2.Encrypt = true; result = realtimePeer.SendOperation((byte)231, val, val2); paramDictionaryPool.Release(val); return result; } public virtual bool OpRaiseEvent(byte eventCode, object customEventContent, RaiseEventArgs raiseEventArgs, SendOptions sendOptions) { //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) if (!CheckIfOpCanBeSent(253, Server, "RaiseEvent")) { return false; } ParameterDictionary val = paramDictionaryPool.Acquire(); try { if (raiseEventArgs.CachingOption != 0) { val.Add((byte)247, (byte)raiseEventArgs.CachingOption); } switch (raiseEventArgs.CachingOption) { case EventCaching.SliceSetIndex: case EventCaching.SlicePurgeIndex: case EventCaching.SlicePurgeUpToIndex: return RealtimePeer.SendOperation((byte)253, val, sendOptions); case EventCaching.RemoveFromRoomCacheForActorsLeft: case EventCaching.SliceIncreaseIndex: return RealtimePeer.SendOperation((byte)253, val, sendOptions); case EventCaching.RemoveFromRoomCache: if (raiseEventArgs.TargetActors != null) { val.Add((byte)252, (object)raiseEventArgs.TargetActors); } break; default: if (raiseEventArgs.TargetActors != null) { val.Add((byte)252, (object)raiseEventArgs.TargetActors); } else if (raiseEventArgs.InterestGroup != 0) { val.Add((byte)240, raiseEventArgs.InterestGroup); } else if (raiseEventArgs.Receivers != 0) { val.Add((byte)246, (byte)raiseEventArgs.Receivers); } break; } val.Add((byte)244, eventCode); if (customEventContent != null) { val.Add((byte)245, customEventContent); } return RealtimePeer.SendOperation((byte)253, val, sendOptions); } finally { paramDictionaryPool.Release(val); } } public bool OpCreateMatchmakingTicket(int[] actorsToInclude) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) if (!CheckIfOpCanBeSent(253, Server, "OpCreateMatchmakingTicket (RaiseEvent())")) { return false; } Log.Info("OpCreateMatchmakingTicket()", LogLevel, LogPrefix); int[] array = new int[1] { LocalPlayer.ActorNumber }; ParameterDictionary val = paramDictionaryPool.Acquire(); try { object[] array2 = new object[2] { (byte)1, actorsToInclude }; val.Add((byte)244, EventCode.CommandEvent); val.Add((byte)252, (object)array); val.Add((byte)245, (object)array2); return RealtimePeer.SendOperation((byte)253, val, SendOptions.SendReliable); } finally { paramDictionaryPool.Release(val); } } protected internal bool OpSettings(bool receiveLobbyStats) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) if (!receiveLobbyStats) { return false; } if (!IsConnectedAndReady || Server != 0) { return false; } Log.Info("OpSettings()", LogLevel, LogPrefix); ParameterDictionary val = paramDictionaryPool.Acquire(); val[(byte)0] = receiveLobbyStats; bool result = RealtimePeer.SendOperation((byte)218, val, SendOptions.SendReliable); paramDictionaryPool.Release(val); return result; } } public class Region { public string Code { get; private set; } public string Cluster { get; private set; } public string HostAndPort { get; protected internal set; } public int Ping { get; set; } public bool WasPinged => Ping != int.MaxValue; public Region(string code, string address) { SetCodeAndCluster(code); HostAndPort = address; Ping = int.MaxValue; } private void SetCodeAndCluster(string codeAsString) { if (codeAsString == null) { Code = ""; Cluster = ""; return; } codeAsString = codeAsString.ToLower(); int num = codeAsString.IndexOf('/'); Code = ((num <= 0) ? codeAsString : codeAsString.Substring(0, num)); Cluster = ((num <= 0) ? "" : codeAsString.Substring(num + 1, codeAsString.Length - num - 1)); } public override string ToString() { return ToString(); } public string ToString(bool compact = false) { string text = Code; if (!string.IsNullOrEmpty(Cluster)) { text = text + "/" + Cluster; } if (compact) { return $"{text}:{Ping}"; } return string.Format("{0}[{2}]: {1}ms", text, Ping, HostAndPort); } } public class RegionHandler { public static Type PingImplementation; private Region bestRegionCache; private readonly List pingerList = new List(); private Action onCompleteCall; private int previousPing; private string previousSummaryProvided; private float rePingFactor = 1.2f; private float pingSimilarityFactor = 1.2f; public int BestRegionSummaryPingLimit = 90; protected internal static ushort UdpPortToPing; public List EnabledRegions { get; protected internal set; } public string AvailableRegionCodes { get; private set; } public Region BestRegion { get { if (EnabledRegions == null) { return null; } if (bestRegionCache != null) { return bestRegionCache; } EnabledRegions.Sort((Region a, Region b) => a.Ping.CompareTo(b.Ping)); int num = (int)((float)EnabledRegions[0].Ping * pingSimilarityFactor); Region region = EnabledRegions[0]; foreach (Region enabledRegion in EnabledRegions) { if (enabledRegion.Ping <= num && enabledRegion.Code.CompareTo(region.Code) < 0) { region = enabledRegion; } } bestRegionCache = region; return bestRegionCache; } } public string SummaryToCache { get { if (BestRegion != null && BestRegion.Ping < RegionPinger.MaxMillisecondsPerPing) { return $"{BestRegion.Code};{BestRegion.Ping};{AvailableRegionCodes}"; } return AvailableRegionCodes; } } public bool IsPinging { get; private set; } public bool Aborted { get; private set; } public string GetResults() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendFormat("Summary: {0}\n", SummaryToCache); foreach (RegionPinger pinger in pingerList) { stringBuilder.AppendLine(pinger.GetResults()); } string arg = previousSummaryProvided ?? "N/A"; stringBuilder.AppendFormat("Previous summary: {0}", arg); return stringBuilder.ToString(); } public void SetRegions(OperationResponse opGetRegions, RealtimeClient client = null) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) if (opGetRegions.OperationCode != 220 || opGetRegions.ReturnCode != 0) { return; } string[] array = opGetRegions[(byte)210] as string[]; string[] array2 = opGetRegions[(byte)230] as string[]; if (array == null || array2 == null || array.Length != array2.Length) { if (client != null) { Log.Error("RegionHandler.SetRegions() failed. Received regions and servers must be non null and of equal length. Could not read regions.", client.LogLevel, client.LogPrefix); } return; } bestRegionCache = null; EnabledRegions = new List(array.Length); for (int i = 0; i < array.Length; i++) { string text = array2[i]; if (client != null && client.AddressRewriter != null) { text = client.AddressRewriter(text, ServerConnection.MasterServer); } Region region = new Region(array[i], text); if (!string.IsNullOrEmpty(region.Code)) { EnabledRegions.Add(region); } } Array.Sort(array); AvailableRegionCodes = string.Join(",", array); } public RegionHandler(ushort masterServerUdpPort = 0) { UdpPortToPing = masterServerUdpPort; } public bool PingAvailableRegions(Action onCompleteCallback) { return PingMinimumOfRegions(onCompleteCallback, null); } public bool PingMinimumOfRegions(Action onCompleteCallback, string previousSummary) { if (EnabledRegions == null || EnabledRegions.Count == 0) { return false; } if (IsPinging) { return false; } Aborted = false; IsPinging = true; previousSummaryProvided = previousSummary; onCompleteCall = onCompleteCallback; if (string.IsNullOrEmpty(previousSummary)) { return PingEnabledRegions(); } string[] array = previousSummary.Split(new char[1] { ';' }); if (array.Length < 3) { return PingEnabledRegions(); } if (!int.TryParse(array[1], out var result)) { return PingEnabledRegions(); } string prevBestRegionCode = array[0]; string value = array[2]; if (string.IsNullOrEmpty(prevBestRegionCode)) { return PingEnabledRegions(); } if (string.IsNullOrEmpty(value)) { return PingEnabledRegions(); } if (!AvailableRegionCodes.Equals(value) || !AvailableRegionCodes.Contains(prevBestRegionCode)) { return PingEnabledRegions(); } if (result >= RegionPinger.PingWhenFailed) { return PingEnabledRegions(); } previousPing = result; Region region = EnabledRegions.Find((Region r) => r.Code.Equals(prevBestRegionCode)); RegionPinger regionPinger = new RegionPinger(region, OnPreferredRegionPinged); lock (pingerList) { pingerList.Clear(); pingerList.Add(regionPinger); } regionPinger.Start(); return true; } public void Abort() { if (Aborted) { return; } Aborted = true; lock (pingerList) { foreach (RegionPinger pinger in pingerList) { pinger.Abort(); } } } private void OnPreferredRegionPinged(Region preferredRegion) { if (preferredRegion.Ping > BestRegionSummaryPingLimit || (float)preferredRegion.Ping > (float)previousPing * rePingFactor) { PingEnabledRegions(); return; } IsPinging = false; onCompleteCall(this); } private bool PingEnabledRegions() { if (EnabledRegions == null || EnabledRegions.Count == 0) { return false; } lock (pingerList) { pingerList.Clear(); foreach (Region enabledRegion in EnabledRegions) { RegionPinger regionPinger = new RegionPinger(enabledRegion, OnRegionDone); if (regionPinger.Start()) { pingerList.Add(regionPinger); } } } return true; } private void OnRegionDone(Region region) { lock (pingerList) { if (!IsPinging) { return; } bestRegionCache = null; foreach (RegionPinger pinger in pingerList) { if (!pinger.Done) { return; } } IsPinging = false; } if (!Aborted) { onCompleteCall(this); } } } public class RegionPinger { public static int Attempts = 5; public static int MaxMillisecondsPerPing = 800; public static int PingWhenFailed = Attempts * MaxMillisecondsPerPing; public int CurrentAttempt = 0; private Action onDoneCall; private PhotonPing ping; private List rttResults; private Region region; private string regionAddress; public bool Done { get; private set; } public bool Aborted { get; internal set; } public RegionPinger(Region region, Action onDoneCallback) { this.region = region; this.region.Ping = PingWhenFailed; Done = false; onDoneCall = onDoneCallback; } private PhotonPing GetPingImplementation() { PhotonPing photonPing = null; if (RegionHandler.PingImplementation == null || RegionHandler.PingImplementation == typeof(PingMono)) { photonPing = new PingMono(); } if (photonPing == null && RegionHandler.PingImplementation != null) { photonPing = (PhotonPing)Activator.CreateInstance(RegionHandler.PingImplementation); } return photonPing; } public bool Start() { ping = GetPingImplementation(); Done = false; CurrentAttempt = 0; rttResults = new List(Attempts); if (Aborted) { return false; } bool flag = false; try { flag = ThreadPool.QueueUserWorkItem(delegate { RegionPingThreaded(); }); } catch { flag = false; } if (!flag) { Log.Error("RegionPinger.Start() failed. Could not queue region pinging. Please contact us.", (LogLevel)1); return false; } return true; } protected internal void Abort() { Aborted = true; if (ping != null) { ping.Dispose(); } } protected internal bool RegionPingThreaded() { region.Ping = PingWhenFailed; int num = 0; int num2 = 0; Stopwatch stopwatch = new Stopwatch(); try { string text = region.HostAndPort; int num3 = text.LastIndexOf(':'); if (num3 > 1) { text = text.Substring(0, num3); } stopwatch.Start(); regionAddress = ResolveHost(text); stopwatch.Stop(); if (stopwatch.ElapsedMilliseconds > 100) { Log.Info($"RegionPingThreaded.ResolveHost() took: {stopwatch.ElapsedMilliseconds}ms", (LogLevel)3); } } catch (Exception arg) { Log.Info($"RegionPingThreaded ResolveHost failed for {region}. Caught: {arg}", (LogLevel)3); Aborted = true; } CurrentAttempt = 0; while (CurrentAttempt < Attempts && !Aborted) { stopwatch.Reset(); stopwatch.Start(); try { ping.StartPing(regionAddress); } catch (Exception ex) { Log.Info("RegionPinger.RegionPingThreaded() caught exception for ping.StartPing(). Exception: " + ex?.ToString() + " Source: " + ex.Source + " Message: " + ex.Message, (LogLevel)3); break; } while (!ping.Done() && stopwatch.ElapsedMilliseconds < MaxMillisecondsPerPing) { Thread.Sleep(1); } stopwatch.Stop(); int num4 = (int)(ping.Successful ? stopwatch.ElapsedMilliseconds : MaxMillisecondsPerPing); rttResults.Add(num4); num += num4; num2++; region.Ping = num / num2; int num5 = 4; while (!ping.Done() && num5 > 0) { num5--; Thread.Sleep(100); } Thread.Sleep(10); CurrentAttempt++; } Done = true; ping.Dispose(); if (rttResults.Count > 1 && num2 > 0) { int num6 = rttResults.Min(); int num7 = rttResults.Max(); int num8 = num - num7 + num6; region.Ping = num8 / num2; } onDoneCall(region); return false; } public string GetResults() { return $"{region.Code}: {region.Ping} ({rttResults.ToStringFull()})"; } public static string ResolveHost(string hostName) { if (hostName.StartsWith("wss://")) { hostName = hostName.Substring(6); } if (hostName.StartsWith("ws://")) { hostName = hostName.Substring(5); } string text = string.Empty; try { IPAddress[] hostAddresses = Dns.GetHostAddresses(hostName); if (hostAddresses.Length == 1) { return hostAddresses[0].ToString(); } foreach (IPAddress iPAddress in hostAddresses) { if (iPAddress != null) { if (iPAddress.ToString().Contains(":")) { return iPAddress.ToString(); } if (string.IsNullOrEmpty(text)) { text = hostAddresses.ToString(); } } } } catch (Exception ex) { Debug.WriteLine("RegionPinger.ResolveHost() caught an exception for Dns.GetHostAddresses(). Exception: " + ex?.ToString() + " Source: " + ex.Source + " Message: " + ex.Message); } return text; } } public class Room : RoomInfo { private bool isOffline; private Dictionary players = new Dictionary(); public RealtimeClient RealtimeClient { get; set; } public new string Name { get { return name; } internal set { name = value; } } public bool IsOffline { get { return isOffline; } private set { isOffline = value; } } public new bool IsOpen { get { return isOpen; } set { //IL_0026: 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_0043: Expected O, but got Unknown if (value != isOpen && !isOffline) { RealtimeClient realtimeClient = RealtimeClient; PhotonHashtable val = new PhotonHashtable(); val.Add((byte)253, (object)value); realtimeClient.OpSetPropertiesOfRoom(val); } isOpen = value; } } public new bool IsVisible { get { return isVisible; } set { //IL_0026: 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_0043: Expected O, but got Unknown if (value != isVisible && !isOffline) { RealtimeClient realtimeClient = RealtimeClient; PhotonHashtable val = new PhotonHashtable(); val.Add((byte)254, (object)value); realtimeClient.OpSetPropertiesOfRoom(val); } isVisible = value; } } public new int MaxPlayers { get { return maxPlayers; } set { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown if (value >= 0 && value != maxPlayers) { maxPlayers = value; byte b = (byte)((value <= 255) ? ((byte)value) : 0); if (!isOffline) { RealtimeClient realtimeClient = RealtimeClient; PhotonHashtable val = new PhotonHashtable(); val.Add(byte.MaxValue, (object)b); val.Add((byte)243, (object)maxPlayers); realtimeClient.OpSetPropertiesOfRoom(val); } } } } public new int PlayerCount { get { if (Players == null) { return 0; } return (byte)Players.Count; } } public Dictionary Players { get { return players; } private set { players = value; } } public string[] ExpectedUsers => expectedUsers; public int PlayerTtl { get { return playerTtl; } set { if (value != playerTtl && !isOffline) { RealtimeClient.OpSetPropertyOfRoom(246, value); } playerTtl = value; } } public int EmptyRoomTtl { get { return emptyRoomTtl; } set { if (value != emptyRoomTtl && !isOffline) { RealtimeClient.OpSetPropertyOfRoom(245, value); } emptyRoomTtl = value; } } public int MasterClientId { get { return masterClientId; } protected internal set { masterClientId = value; } } public object[] PropertiesListedInLobby { get { return propertiesListedInLobby; } private set { propertiesListedInLobby = value; } } public bool AutoCleanUp => autoCleanUp; public bool BroadcastPropertiesChangeToAll { get; private set; } public bool SuppressRoomEvents { get; private set; } public bool SuppressPlayerInfo { get; private set; } public bool PublishUserId { get; private set; } public bool DeleteNullProperties { get; private set; } public TypedLobby Lobby { get; internal set; } public Room(string roomName, RoomOptions options, bool isOffline = false) : base(roomName, options?.CustomRoomProperties) { if (options != null) { isVisible = options.IsVisible; isOpen = options.IsOpen; maxPlayers = options.MaxPlayers; propertiesListedInLobby = options.CustomRoomPropertiesForLobby; } this.isOffline = isOffline; } internal void InternalCacheRoomFlags(int roomFlags) { BroadcastPropertiesChangeToAll = (roomFlags & 0x20) != 0; SuppressRoomEvents = (roomFlags & 4) != 0; SuppressPlayerInfo = (roomFlags & 0x40) != 0; PublishUserId = (roomFlags & 8) != 0; DeleteNullProperties = (roomFlags & 0x10) != 0; autoCleanUp = (roomFlags & 2) != 0; } protected internal override void InternalCacheProperties(PhotonHashtable propertiesToCache) { int num = masterClientId; base.InternalCacheProperties(propertiesToCache); if (num != 0 && masterClientId != num) { RealtimeClient.InRoomCallbackTargets.OnMasterClientSwitched(GetPlayer(masterClientId)); } } public virtual bool SetCustomProperties(PhotonHashtable propertiesToSet, PhotonHashtable expectedValues = null) { //IL_001b: 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) if (!propertiesToSet.CustomPropKeyTypesValid()) { Log.Error("Room.SetCustomProperties() failed. Parameter propertiesToSet must be non-null, not empty and contain only int or string keys.", RealtimeClient.LogLevel); return false; } if (expectedValues != null && !expectedValues.CustomPropKeyTypesValid()) { Log.Error("Room.SetCustomProperties() failed. Parameter expectedValues must contain only int or string keys if it is not null.", RealtimeClient.LogLevel); return false; } if (isOffline) { base.CustomProperties.Merge(propertiesToSet); base.CustomProperties.StripKeysWithNullValues(); RealtimeClient.InRoomCallbackTargets.OnRoomPropertiesUpdate(propertiesToSet); return true; } return RealtimeClient.OpSetPropertiesOfRoom(propertiesToSet, expectedValues); } public bool SetPropertiesListedInLobby(object[] lobbyProps) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (isOffline) { return false; } if (!lobbyProps.CustomPropKeyTypesValid(NullOrZeroAccepted: true)) { Log.Error("Room.SetPropertiesListedInLobby() failed. Parameter lobbyProps can be null, have zero items or all items must be int or string.", RealtimeClient.LogLevel); return false; } PhotonHashtable val = new PhotonHashtable(); val[(byte)250] = lobbyProps; return RealtimeClient.OpSetPropertiesOfRoom(val); } protected internal virtual void RemovePlayer(Player player) { Players.Remove(player.ActorNumber); player.RoomReference = null; } protected internal virtual void RemovePlayer(int id) { RemovePlayer(GetPlayer(id)); } public bool SetMasterClient(Player masterClientPlayer) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //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_004a: Expected O, but got Unknown if (isOffline) { return false; } PhotonHashtable val = new PhotonHashtable(); val.Add((byte)248, (object)masterClientPlayer.ActorNumber); PhotonHashtable gameProperties = val; PhotonHashtable val2 = new PhotonHashtable(); val2.Add((byte)248, (object)MasterClientId); PhotonHashtable expectedProperties = val2; return RealtimeClient.OpSetPropertiesOfRoom(gameProperties, expectedProperties); } public virtual bool AddPlayer(Player player) { if (!Players.ContainsKey(player.ActorNumber)) { StorePlayer(player); return true; } return false; } public virtual Player StorePlayer(Player player) { Players[player.ActorNumber] = player; player.RoomReference = this; return player; } public virtual Player GetPlayer(int id, bool findMaster = false) { int key = ((findMaster && id == 0) ? MasterClientId : id); Player value = null; Players.TryGetValue(key, out value); return value; } public bool ClearExpectedUsers() { if (ExpectedUsers == null || ExpectedUsers.Length == 0) { return false; } return SetExpectedUsers(new string[0], ExpectedUsers); } public bool SetExpectedUsers(string[] newExpectedUsers) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (newExpectedUsers == null || newExpectedUsers.Length == 0) { Log.Error("SetExpectedUsers() failed. Parameter newExpectedUsers array is null or empty. To set no expected users, call Room.ClearExpectedUsers() instead.", RealtimeClient.LogLevel); return false; } return SetExpectedUsers(newExpectedUsers, ExpectedUsers); } private bool SetExpectedUsers(string[] newExpectedUsers, string[] currentKnownExpectedUsers) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown if (isOffline) { return false; } PhotonHashtable val = new PhotonHashtable(1); val.Add((byte)247, (object)newExpectedUsers); PhotonHashtable val2 = new PhotonHashtable(1); val2.Add((byte)247, (object)currentKnownExpectedUsers); return RealtimeClient.OpSetPropertiesOfRoom(val, val2); } public override string ToString() { return string.Format("Room: '{0}' {1},{2} {3}/{4} players {5}.", name, isVisible ? "visible" : "hidden", isOpen ? "open" : "closed", PlayerCount, maxPlayers, (Lobby == null) ? "DefaultLobby" : Lobby.ToString()); } public new string ToStringFull() { return string.Format("Room: '{0}' {1},{2} {3}/{4} players {5}.\n customProps: {6}", name, isVisible ? "visible" : "hidden", isOpen ? "open" : "closed", PlayerCount, maxPlayers, (Lobby == null) ? "DefaultLobby" : Lobby.ToString(), base.CustomProperties.ToStringFull()); } } public class RoomInfo { public bool RemovedFromList; private PhotonHashtable customProperties = new PhotonHashtable(); protected int maxPlayers = 0; protected int emptyRoomTtl = 0; protected int playerTtl = 0; protected string[] expectedUsers; protected bool isOpen = true; protected bool isVisible = true; protected bool autoCleanUp = true; protected string name; protected int masterClientId; protected object[] propertiesListedInLobby; public PhotonHashtable CustomProperties => customProperties; public string Name => name; public int PlayerCount { get; private set; } public int MaxPlayers => maxPlayers; public bool IsOpen => isOpen; public bool IsVisible => isVisible; protected internal RoomInfo(string roomName, PhotonHashtable roomProperties) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown InternalCacheProperties(roomProperties); name = roomName; } public override bool Equals(object other) { return other is RoomInfo roomInfo && Name.Equals(roomInfo.name); } public override int GetHashCode() { return name.GetHashCode(); } public override string ToString() { return string.Format("Room: '{0}' {1},{2} {4}/{3} players.{5}", name, isVisible ? "visible" : "hidden", isOpen ? "open" : "closed", maxPlayers, PlayerCount, RemovedFromList ? " removed!" : ""); } public string ToStringFull() { return string.Format("Room: '{0}' {1},{2} {4}/{3} players.\ncustomProps: {5}", name, isVisible ? "visible" : "hidden", isOpen ? "open" : "closed", maxPlayers, PlayerCount, customProperties.ToStringFull()); } protected internal virtual void InternalCacheProperties(PhotonHashtable propertiesToCache) { if (propertiesToCache == null || ((Dictionary)(object)propertiesToCache).Count == 0 || ((object)customProperties).Equals((object?)propertiesToCache)) { return; } if (propertiesToCache.ContainsKey((byte)251)) { RemovedFromList = (bool)propertiesToCache[(byte)251]; if (RemovedFromList) { return; } } if (propertiesToCache.ContainsKey((byte)243)) { maxPlayers = Convert.ToInt32(propertiesToCache[(byte)243]); } else if (propertiesToCache.ContainsKey(byte.MaxValue)) { maxPlayers = Convert.ToInt32(propertiesToCache[byte.MaxValue]); } if (propertiesToCache.ContainsKey((byte)253)) { isOpen = (bool)propertiesToCache[(byte)253]; } if (propertiesToCache.ContainsKey((byte)254)) { isVisible = (bool)propertiesToCache[(byte)254]; } if (propertiesToCache.ContainsKey((byte)252)) { PlayerCount = Convert.ToInt32(propertiesToCache[(byte)252]); } if (propertiesToCache.ContainsKey((byte)249)) { autoCleanUp = (bool)propertiesToCache[(byte)249]; } if (propertiesToCache.ContainsKey((byte)248)) { masterClientId = (int)propertiesToCache[(byte)248]; } if (propertiesToCache.ContainsKey((byte)250)) { propertiesListedInLobby = propertiesToCache[(byte)250] as object[]; } if (propertiesToCache.ContainsKey((byte)247)) { expectedUsers = (string[])propertiesToCache[(byte)247]; } if (propertiesToCache.ContainsKey((byte)245)) { emptyRoomTtl = (int)propertiesToCache[(byte)245]; } if (propertiesToCache.ContainsKey((byte)246)) { playerTtl = (int)propertiesToCache[(byte)246]; } customProperties.Merge(propertiesToCache); customProperties.StripKeysWithNullValues(); } } public class RoomOptions { private bool isVisible = true; private bool isOpen = true; public int MaxPlayers; public int PlayerTtl; public int EmptyRoomTtl; private bool cleanupCacheOnLeave = true; public PhotonHashtable CustomRoomProperties; public object[] CustomRoomPropertiesForLobby; public string[] Plugins; private bool broadcastPropsChangeToAll = true; public bool IsVisible { get { return isVisible; } set { isVisible = value; } } public bool IsOpen { get { return isOpen; } set { isOpen = value; } } public bool CleanupCacheOnLeave { get { return cleanupCacheOnLeave; } set { cleanupCacheOnLeave = value; } } public bool SuppressRoomEvents { get; set; } public bool SuppressPlayerInfo { get; set; } public bool PublishUserId { get; set; } public bool DeleteNullProperties { get; set; } public bool BroadcastPropsChangeToAll { get { return broadcastPropsChangeToAll; } set { broadcastPropsChangeToAll = value; } } } }