Decompiled source of Multiside v1.0.2
Mods/Multiside.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using Il2Cpp; using MelonLoader; using Microsoft.CodeAnalysis; using MultiSide; using MultiSide.shared; using Photon.Client; using Photon.Realtime; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(ModController), "Multiside", "1.0.2", "gameknight963", null)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("Multiside")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+6347de5dc2cc3a2238ced384127693269050d1ee")] [assembly: AssemblyProduct("Multiside")] [assembly: AssemblyTitle("Multiside")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace MultiSide { public static class Config { public static readonly string DefaultRoom = "KoolRoom"; public static readonly AppSettings settings = new AppSettings { AppIdRealtime = "301df3f0-b282-4a33-b588-60e22cdf7d87", AppVersion = "1.0.2", FixedRegion = "us", Protocol = (ConnectionProtocol)0, NetworkLogging = (LogLevel)3 }; } public static class HelperFunctions { public static GameObject CreateKiri() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) ModController.CoolLogger.Msg("adding new player"); Scene activeScene = SceneManager.GetActiveScene(); if (((Scene)(ref activeScene)).name != "Version 1.9 POST") { throw new InvalidOperationException("Problem detected, dont call it in this scene"); } GameObject val = GameObject.Find("Kiri"); GameObject val2 = Object.Instantiate<GameObject>(val); GameObject gameObject = ((Component)val2.transform.Find("Zero/PLAYER Armature/Rig Root/Hips/Spine/Chest/Neck2/Neck1/Head/CameraHoldHead/playerCamera")).gameObject; try { ((Behaviour)gameObject.GetComponent<Camera>()).enabled = false; kiriMoveBasic component = val2.GetComponent<kiriMoveBasic>(); if (component != null) { ((Behaviour)component).enabled = false; } MonoBehaviour val3 = ((IEnumerable<MonoBehaviour>)val2.GetComponents<MonoBehaviour>()).FirstOrDefault((Func<MonoBehaviour, bool>)((MonoBehaviour mb) => ((object)mb).GetType().Name == "BhopMovement")); if (val3 != null) { ((Behaviour)val3).enabled = false; } kiriLook component2 = ((Component)val2.transform.Find("PsuedoHead")).gameObject.GetComponent<kiriLook>(); if (component2 != null) { ((Behaviour)component2).enabled = false; } Rigidbody component3 = val2.GetComponent<Rigidbody>(); if (component3 != null) { component3.isKinematic = true; } } catch (NullReferenceException) { ModController.CoolLogger.Error("Null exception while creating new player. One of the components disabled may not exist"); } return val2; } public static string[] GetModList() { return MelonTypeBase<MelonMod>.RegisteredMelons.Select((MelonMod m) => ((MelonBase)m).Info.Name).ToArray(); } } public class ModController : MelonMod { private const float _updateInterval = 0.1f; private float _sendTimer = 0.1f; public static GameObject? Kiri { get; private set; } public static Instance CoolLogger { get; private set; } public override void OnInitializeMelon() { CoolLogger = ((MelonBase)this).LoggerInstance; PhotonManager.Instance.Init(); } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { if (!(sceneName != "Version 1.9 POST")) { Kiri = GameObject.Find("Kiri"); PhotonManager.Instance.client.OpJoinRandomRoom((JoinRandomRoomArgs)null); } } public override void OnUpdate() { PhotonManager.Instance.Service(); PhotonManager.Instance.UpdatePlayerTransforms(); } public override void OnFixedUpdate() { //IL_00cf: 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) if (PhotonManager.Instance.client == null) { ((MelonBase)this).LoggerInstance.Msg("client null"); } if (PhotonManager.Instance.client?.LocalPlayer == null) { ((MelonBase)this).LoggerInstance.Msg("LocalPlayer null"); } if (PhotonManager.Instance.client != null && PhotonManager.Instance.client.LocalPlayer != null && !((Object)(object)Kiri == (Object)null)) { _sendTimer += Time.fixedUnscaledDeltaTime; if (_sendTimer > 0.1f) { _sendTimer = 0f; PlayerPositionData data = new PlayerPositionData(PhotonManager.Instance.client.LocalPlayer.ActorNumber, Kiri.transform.position, Kiri.transform.rotation); PhotonManager.Instance.SendPlayerData(data); } } } } public static class ModListChecker { public static readonly string Channel = "multiside.modlist"; public static void Init() { if (NetworkRegistry.Provider != null) { NetworkRegistry.Provider.OnReceived += OnReceived; NetworkRegistry.Provider.OnPlayerJoined += OnPlayerJoined; } } private static void OnPlayerJoined(int actor) { INetworkProvider provider = NetworkRegistry.Provider; if (provider != null) { provider.SendTo(actor, Channel, (object)HelperFunctions.GetModList(), true); } } private static void OnReceived(int actor, string channel, object data) { if (channel != Channel) { return; } string[] array = (string[])data; string[] modList = HelperFunctions.GetModList(); string[] array2 = modList.Except(array).ToArray(); string[] array3 = array.Except(modList).ToArray(); if (array2.Any() || array3.Any()) { ModController.CoolLogger.Warning($"Mod mismatch with player {actor}!"); if (array3.Any()) { ModController.CoolLogger.Warning(" You are missing: " + string.Join(", ", array3)); } if (array2.Any()) { ModController.CoolLogger.Warning(" They are missing: " + string.Join(", ", array2)); } } } } public class PhotonCallbacks : IConnectionCallbacks, IMatchmakingCallbacks, IOnEventCallback, IInRoomCallbacks { public void OnConnected() { ModController.CoolLogger.Msg("Connected to Photon server"); } public void OnConnectedToMaster() { ModController.CoolLogger.Msg("Connected to master server"); } public void OnDisconnected(DisconnectCause cause) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) ModController.CoolLogger.Msg($"Disconnected from server: {cause}"); } public void OnJoinedRoom() { foreach (KeyValuePair<int, Player> player in PhotonManager.Instance.client.CurrentRoom.Players) { Player value = player.Value; if (value.ActorNumber != PhotonManager.Instance.client.LocalPlayer.ActorNumber) { ModController.CoolLogger.Msg($"Spawning existing player {value.NickName} ({value.ActorNumber})"); GameObject value2 = HelperFunctions.CreateKiri(); PhotonManager.Instance.playerObjects[value.ActorNumber] = value2; } } ModListChecker.Init(); INetworkProvider provider = NetworkRegistry.Provider; if (provider != null) { provider.Send(ModListChecker.Channel, (object)HelperFunctions.GetModList(), true); } ModController.CoolLogger.Msg("Joined room successfully"); PhotonManager.Instance.FireRoomJoined(); } public void OnJoinRandomFailed(short returnCode, string message) { //IL_0059: 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_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown ModController.CoolLogger.Msg($"Join random room failed: {returnCode} - {message}"); ModController.CoolLogger.Msg("No valid room found, creating default room"); RoomOptions roomOptions = new RoomOptions { MaxPlayers = 20, IsVisible = true }; EnterRoomArgs val = new EnterRoomArgs { RoomName = Config.DefaultRoom, RoomOptions = roomOptions }; PhotonManager.Instance.client.OpCreateRoom(val); } public void OnEvent(EventData photonEvent) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown object customData = photonEvent.CustomData; switch (photonEvent.Code) { case 0: { PhotonHashtable val2 = (PhotonHashtable)customData; int id = (int)val2[(object)"actor"]; float[] array = (float[])val2[(object)"pos"]; float[] array2 = (float[])val2[(object)"rot"]; Vector3 pos = default(Vector3); ((Vector3)(ref pos))..ctor(array[0], array[1], array[2]); Quaternion rot = default(Quaternion); ((Quaternion)(ref rot))..ctor(array2[0], array2[1], array2[2], array2[3]); PlayerPositionData posData = new PlayerPositionData(id, pos, rot); PhotonManager.Instance.UpdatePlayer(posData); break; } case 99: { PhotonHashtable val = (PhotonHashtable)customData; string channel = (string)val[(object)"channel"]; object data = val[(object)"data"]; PhotonManager.Instance.RouteReceived(photonEvent.Sender, channel, data); break; } default: ModController.CoolLogger.Msg($"Unknown event code: {photonEvent.Code}"); break; } } public void OnPlayerEnteredRoom(Player newPlayer) { ModController.CoolLogger.Msg($"Player entered room: {newPlayer.NickName} ({newPlayer.ActorNumber})"); GameObject value = HelperFunctions.CreateKiri(); PhotonManager.Instance.playerObjects[newPlayer.ActorNumber] = value; PhotonManager.Instance.FirePlayerJoined(newPlayer.ActorNumber); } public void OnPlayerLeftRoom(Player otherPlayer) { ModController.CoolLogger.Msg($"Player left room: {otherPlayer.NickName} ({otherPlayer.ActorNumber})"); if (PhotonManager.Instance.playerObjects.TryGetValue(otherPlayer.ActorNumber, out GameObject value)) { Object.Destroy((Object)(object)value); PhotonManager.Instance.playerObjects.Remove(otherPlayer.ActorNumber); PhotonManager.Instance.FirePlayerLeft(otherPlayer.ActorNumber); } } public void OnMasterClientSwitched(Player newMasterClient) { ModController.CoolLogger.Msg($"Master client switched: {newMasterClient.NickName} ({newMasterClient.ActorNumber})"); } public void OnCreatedRoom() { } public void OnCreateRoomFailed(short returnCode, string message) { } public void OnJoinRoomFailed(short returnCode, string message) { } public void OnLeftRoom() { } public void OnFriendListUpdate(List<FriendInfo> friendList) { } public void OnPlayerPropertiesUpdate(Player targetPlayer, PhotonHashtable changedProps) { } public void OnRoomPropertiesUpdate(PhotonHashtable propertiesThatChanged) { } public void OnCustomAuthenticationFailed(string debugMessage) { } public void OnCustomAuthenticationResponse(Dictionary<string, object> data) { } public void OnRegionListReceived(RegionHandler regionHandler) { } } public class PhotonManager : INetworkProvider { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static SerializeStreamMethod <>9__27_0; public static DeserializeStreamMethod <>9__27_1; internal short <Init>b__27_0(StreamBuffer outStream, object obj) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) Vector3 val = (Vector3)obj; byte[] array = new byte[12]; Buffer.BlockCopy(BitConverter.GetBytes(val.x), 0, array, 0, 4); Buffer.BlockCopy(BitConverter.GetBytes(val.y), 0, array, 4, 4); Buffer.BlockCopy(BitConverter.GetBytes(val.z), 0, array, 8, 4); outStream.Write(array, 0, 12); return 12; } internal object <Init>b__27_1(StreamBuffer inStream, short length) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) byte[] array = new byte[12]; inStream.Read(array, 0, 12); return (object)new Vector3(BitConverter.ToSingle(array, 0), BitConverter.ToSingle(array, 4), BitConverter.ToSingle(array, 8)); } } public RealtimeClient client = new RealtimeClient(Config.settings.Protocol); public PhotonCallbacks callbacks = new PhotonCallbacks(); public Dictionary<int, GameObject> playerObjects = new Dictionary<int, GameObject>(); public Dictionary<int, Vector3> _targetPositions = new Dictionary<int, Vector3>(); public Dictionary<int, Quaternion> _targetRotations = new Dictionary<int, Quaternion>(); public static PhotonManager Instance { get; private set; } = new PhotonManager(); public bool IsConnected { get { RealtimeClient obj = client; return obj != null && obj.IsConnected; } } public IReadOnlyList<int> ConnectedActors => playerObjects.Keys.ToList(); public bool IsMasterClient => client.LocalPlayer.IsMasterClient; public int MasterClientActorNumber => client.CurrentRoom.MasterClientId; public IReadOnlyDictionary<int, GameObject> PlayerObjects => playerObjects; public int LocalActorNumber => client.LocalPlayer.ActorNumber; public GameObject? LocalPlayerObject => ModController.Kiri; public bool IsInRoom { get { RealtimeClient obj = client; return obj != null && obj.InRoom; } } public event Action<int, string, object>? OnReceived; public event Action<int>? OnPlayerJoined; public event Action<int>? OnPlayerLeft; public event Action? OnRoomJoined; public void FireRoomJoined() { this.OnRoomJoined?.Invoke(); } public void Init() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown ModController.CoolLogger.Msg("Initializing!"); client.ConnectUsingSettings(Config.settings); client.AddCallbackTarget((object)callbacks); Type typeFromHandle = typeof(Vector3); object obj2 = <>c.<>9__27_0; if (obj2 == null) { SerializeStreamMethod val = delegate(StreamBuffer outStream, object obj) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) Vector3 val3 = (Vector3)obj; byte[] array2 = new byte[12]; Buffer.BlockCopy(BitConverter.GetBytes(val3.x), 0, array2, 0, 4); Buffer.BlockCopy(BitConverter.GetBytes(val3.y), 0, array2, 4, 4); Buffer.BlockCopy(BitConverter.GetBytes(val3.z), 0, array2, 8, 4); outStream.Write(array2, 0, 12); return 12; }; <>c.<>9__27_0 = val; obj2 = (object)val; } object obj3 = <>c.<>9__27_1; if (obj3 == null) { DeserializeStreamMethod val2 = delegate(StreamBuffer inStream, short length) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) byte[] array = new byte[12]; inStream.Read(array, 0, 12); return (object)new Vector3(BitConverter.ToSingle(array, 0), BitConverter.ToSingle(array, 4), BitConverter.ToSingle(array, 8)); }; <>c.<>9__27_1 = val2; obj3 = (object)val2; } PhotonPeer.RegisterType(typeFromHandle, (byte)86, (SerializeStreamMethod)obj2, (DeserializeStreamMethod)obj3); NetworkRegistry.Register((INetworkProvider)(object)this); } public GameObject? GetPlayerObject(int actor) { GameObject value; return playerObjects.TryGetValue(actor, out value) ? value : null; } public void SendPlayerData(PlayerPositionData data) { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Expected O, but got Unknown //IL_00a8: Expected O, but got Unknown //IL_00b2: 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_00c0: 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_00d2: Unknown result type (might be due to invalid IL or missing references) float[] value = new float[3] { data.position.x, data.position.y, data.position.z }; float[] value2 = new float[4] { data.rotation.x, data.rotation.y, data.rotation.z, data.rotation.w }; PhotonHashtable val = new PhotonHashtable(); ((Dictionary<object, object>)val).Add((object)"actor", (object)data.actorNumber); ((Dictionary<object, object>)val).Add((object)"pos", (object)value); ((Dictionary<object, object>)val).Add((object)"rot", (object)value2); PhotonHashtable val2 = val; RealtimeClient obj = client; RaiseEventArgs val3 = new RaiseEventArgs { Receivers = (ReceiverGroup)0 }; SendOptions val4 = default(SendOptions); ((SendOptions)(ref val4)).Reliability = false; obj.OpRaiseEvent((byte)0, (object)val2, val3, val4); } public void Send(string channel, object data, bool reliable = true) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0021: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) PhotonHashtable val = new PhotonHashtable(); ((Dictionary<object, object>)val).Add((object)"channel", (object)channel); ((Dictionary<object, object>)val).Add((object)"data", data); PhotonHashtable val2 = val; RealtimeClient obj = client; RaiseEventArgs val3 = new RaiseEventArgs { Receivers = (ReceiverGroup)0 }; SendOptions val4 = default(SendOptions); ((SendOptions)(ref val4)).Reliability = reliable; obj.OpRaiseEvent((byte)99, (object)val2, val3, val4); } public void SendTo(int actor, string channel, object data, bool reliable = true) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0021: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) PhotonHashtable val = new PhotonHashtable(); ((Dictionary<object, object>)val).Add((object)"channel", (object)channel); ((Dictionary<object, object>)val).Add((object)"data", data); PhotonHashtable val2 = val; RealtimeClient obj = client; RaiseEventArgs val3 = new RaiseEventArgs { TargetActors = new int[1] { actor } }; SendOptions val4 = default(SendOptions); ((SendOptions)(ref val4)).Reliability = reliable; obj.OpRaiseEvent((byte)99, (object)val2, val3, val4); } public void UpdatePlayer(PlayerPositionData posData) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) if (playerObjects.TryGetValue(posData.actorNumber, out GameObject _)) { _targetPositions[posData.actorNumber] = posData.position; _targetRotations[posData.actorNumber] = posData.rotation; } } public void UpdatePlayerTransforms() { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) foreach (KeyValuePair<int, GameObject> playerObject in playerObjects) { int key = playerObject.Key; GameObject value = playerObject.Value; if (_targetPositions.TryGetValue(key, out var value2)) { value.transform.position = Vector3.Lerp(value.transform.position, value2, Time.unscaledDeltaTime * 10f); } if (_targetRotations.TryGetValue(key, out var value3)) { value.transform.rotation = Quaternion.Lerp(value.transform.rotation, value3, Time.unscaledDeltaTime * 10f); } } } public void RouteReceived(int actor, string channel, object data) { this.OnReceived?.Invoke(actor, channel, data); } public void FirePlayerJoined(int actor) { this.OnPlayerJoined?.Invoke(actor); } public void FirePlayerLeft(int actor) { this.OnPlayerLeft?.Invoke(actor); } public void Service() { RealtimeClient obj = client; if (obj != null) { obj.Service(); } } } [Serializable] public class PlayerPositionData { public int actorNumber; public Vector3 position; public Quaternion rotation; public PlayerPositionData(int id, Vector3 pos, Quaternion rot) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) actorNumber = id; position = pos; rotation = rot; } } }
Mods/Multiside.shared.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("Multiside.shared")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+1c0c4e65910fe40ae396c7a93b3b14a4e2bc66d7")] [assembly: AssemblyProduct("Multiside.shared")] [assembly: AssemblyTitle("Multiside.shared")] [assembly: AssemblyVersion("1.0.0.0")] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace MultiSide.shared { public interface INetworkProvider { bool IsConnected { get; } IReadOnlyList<int> ConnectedActors { get; } IReadOnlyDictionary<int, GameObject> PlayerObjects { get; } int LocalActorNumber { get; } GameObject? LocalPlayerObject { get; } bool IsInRoom { get; } bool IsMasterClient { get; } int MasterClientActorNumber { get; } event Action<int, string, object> OnReceived; event Action<int> OnPlayerJoined; event Action<int> OnPlayerLeft; event Action OnRoomJoined; void Send(string channel, object data, bool reliable = true); void SendTo(int actor, string channel, object data, bool reliable = true); GameObject? GetPlayerObject(int actor); } public static class NetworkRegistry { public static INetworkProvider? Provider { get; private set; } public static event Action<INetworkProvider>? OnProviderRegistered; public static void Register(INetworkProvider provider) { Provider = provider; NetworkRegistry.OnProviderRegistered?.Invoke(provider); } } }
UserLibs/PhotonChat.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Threading; using Photon.Client; [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.exitgames.com")] [assembly: AssemblyDescription("Photon Chat Api. Debug.")] [assembly: AssemblyFileVersion("5.1.9.0")] [assembly: AssemblyInformationalVersion("5.1.9")] [assembly: AssemblyProduct("Photon Chat Api. Debug.")] [assembly: AssemblyTitle("PhotonChat")] [assembly: AssemblyVersion("5.1.9.0")] namespace Photon.Chat; public class ChannelCreationOptions { public static ChannelCreationOptions Default = new ChannelCreationOptions(); public bool PublishSubscribers { get; set; } public int MaxSubscribers { get; set; } } public class ChannelWellKnownProperties { public const byte MaxSubscribers = byte.MaxValue; public const byte PublishSubscribers = 254; } public class ChatAppSettings { public string AppIdChat; public string AppVersion; public string FixedRegion; public string Server; public ushort Port; public string ProxyServer; public ConnectionProtocol Protocol = (ConnectionProtocol)0; public bool EnableProtocolFallback = true; public LogLevel NetworkLogging = (LogLevel)1; public LogLevel ClientLogging = (LogLevel)2; public bool IsDefaultNameServer => string.IsNullOrEmpty(Server); public bool IsDefaultPort => Port <= 0; public ChatAppSettings() { }//IL_0002: 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_0017: Unknown result type (might be due to invalid IL or missing references) public ChatAppSettings(ChatAppSettings original) { //IL_0002: 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_0017: Unknown result type (might be due to invalid IL or missing references) original?.CopyTo(this); } public ChatAppSettings CopyTo(ChatAppSettings target) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) target.AppIdChat = AppIdChat; target.AppVersion = AppVersion; target.FixedRegion = FixedRegion; target.Server = Server; target.Port = Port; target.ProxyServer = ProxyServer; target.Protocol = Protocol; target.ClientLogging = ClientLogging; target.NetworkLogging = NetworkLogging; target.EnableProtocolFallback = EnableProtocolFallback; return target; } } public class ChatChannel { public readonly string Name; public readonly List<string> Senders = new List<string>(); public readonly List<object> Messages = new List<object>(); public int MessageLimit; public int ChannelID; private Dictionary<object, object> properties; public readonly HashSet<string> Subscribers = new HashSet<string>(); private Dictionary<string, Dictionary<object, object>> usersProperties; public bool IsPrivate { get; protected internal set; } public int MessageCount => Messages.Count; public int LastMsgId { get; protected set; } public bool PublishSubscribers { get; protected set; } public int MaxSubscribers { get; protected set; } public ChatChannel(string name) { Name = name; } public void Add(string sender, object message, int msgId) { Senders.Add(sender); Messages.Add(message); LastMsgId = msgId; TruncateMessages(); } public void Add(string[] senders, object[] messages, int lastMsgId) { Senders.AddRange(senders); Messages.AddRange(messages); LastMsgId = lastMsgId; TruncateMessages(); } public void TruncateMessages() { if (MessageLimit > 0 && Messages.Count > MessageLimit) { int count = Messages.Count - MessageLimit; Senders.RemoveRange(0, count); Messages.RemoveRange(0, count); } } public void ClearMessages() { Senders.Clear(); Messages.Clear(); } public string ToStringMessages() { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < Messages.Count; i++) { stringBuilder.AppendLine($"{Senders[i]}: {Messages[i]}"); } return stringBuilder.ToString(); } internal void ReadChannelProperties(Dictionary<object, object> newProperties) { if (newProperties == null || newProperties.Count <= 0) { return; } if (properties == null) { properties = new Dictionary<object, object>(newProperties.Count); } foreach (KeyValuePair<object, object> newProperty in newProperties) { if (newProperty.Value == null) { properties.Remove(newProperty.Key); } else { properties[newProperty.Key] = newProperty.Value; } } if (properties.TryGetValue((byte)254, out var value)) { PublishSubscribers = (bool)value; } if (properties.TryGetValue(byte.MaxValue, out value)) { MaxSubscribers = (int)value; } } internal bool AddSubscribers(string[] users) { if (users == null) { return false; } bool result = true; for (int i = 0; i < users.Length; i++) { if (!Subscribers.Add(users[i])) { result = false; } } return result; } internal bool AddSubscriber(string userId) { return Subscribers.Add(userId); } internal bool RemoveSubscriber(string userId) { if (usersProperties != null) { usersProperties.Remove(userId); } return Subscribers.Remove(userId); } } public class ChatClient : IPhotonPeerListener { private const int FriendRequestListMax = 1024; public const int DefaultMaxSubscribers = 100; private const byte HttpForwardWebFlag = 1; private readonly string chatRegion = "eu"; public int MessageLimit; public int PrivateChatHistoryLength = -1; public readonly Dictionary<string, ChatChannel> PublicChannels; public readonly Dictionary<string, ChatChannel> PrivateChannels; private readonly HashSet<string> PublicChannelsUnsubscribing; private readonly IChatClientListener listener = null; public readonly PhotonPeer Peer; private const string ChatAppName = "chat"; private bool didAuthenticate; private int msDeltaForServiceCalls = 50; private Timer stateTimer; private int msTimestampOfLastServiceCall; public string NameServerHost = "ns.photonengine.io"; private static readonly Dictionary<ConnectionProtocol, int> ProtocolToNameServerPort = new Dictionary<ConnectionProtocol, int> { { (ConnectionProtocol)0, 5058 }, { (ConnectionProtocol)1, 4533 }, { (ConnectionProtocol)4, 9093 }, { (ConnectionProtocol)5, 19093 } }; public ushort NameServerPortOverride; public ChatAppSettings AppSettings { get; private set; } [Obsolete("Replaced by this.AppSettings. Calling ConnectUsingSettings() will set/replace this.AppSettings.")] public bool EnableProtocolFallback { get { return AppSettings?.EnableProtocolFallback ?? false; } set { if (AppSettings != null) { AppSettings.EnableProtocolFallback = value; } } } [Obsolete("Replaced by this.FixedRegionOrDefault. Setting a region should be done via ConnectUsingSettings() parameter AppSettings.")] public string ChatRegion => FixedRegionOrDefault; public string FixedRegionOrDefault { get { if (AppSettings != null && !string.IsNullOrEmpty(AppSettings.FixedRegion)) { return AppSettings.FixedRegion; } return chatRegion; } } [Obsolete("Replaced by this.AppSettings. Calling ConnectUsingSettings() will set/replace this.AppSettings.")] public string ProxyServerAddress => AppSettings?.ProxyServer ?? null; public string CurrentServerAddress => Peer.ServerAddress; public string FrontendAddress { get; private set; } public ChatState State { get; private set; } public ChatDisconnectCause DisconnectedCause { get; private set; } 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 Peer.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) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) Peer.LogLevel = value; AppSettings.NetworkLogging = value; } } public LogLevel LogLevelClient { 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 AppSettings.ClientLogging; } 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) AppSettings.ClientLogging = value; } } public bool CanChat => State == ChatState.ConnectedToFrontEnd; [Obsolete("Replaced by this.AppSettings. Calling ConnectUsingSettings() will set/replace this.AppSettings.")] public string AppVersion => AppSettings?.AppVersion ?? null; [Obsolete("Replaced by this.AppSettings. Calling ConnectUsingSettings() will set/replace this.AppSettings.")] public string AppId => AppSettings?.AppIdChat ?? null; public AuthenticationValues AuthValues { get; set; } public string UserId { get { return (AuthValues != null) ? AuthValues.UserId : null; } private set { if (AuthValues == null) { AuthValues = new AuthenticationValues(); } AuthValues.UserId = value; } } public bool UseBackgroundWorkerForSending { get; set; } public ConnectionProtocol TransportProtocol { 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 Peer.TransportProtocol; } private set { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (Peer == null || (int)Peer.PeerState > 0) { IChatClientListener chatClientListener = listener; object obj; if (Peer == null) { obj = "The Peer is null."; } else { PeerStateValue peerState = Peer.PeerState; obj = "PeerState: " + ((object)(PeerStateValue)(ref peerState)).ToString(); } chatClientListener.DebugReturn((LogLevel)2, "Can't set TransportProtocol. Disconnect first! " + (string?)obj); } else { Peer.TransportProtocol = value; } } } public Dictionary<ConnectionProtocol, Type> SocketImplementationConfig => Peer.SocketImplementationConfig; public string NameServerAddress => GetNameServerAddress(); internal virtual bool IsProtocolSecure => (int)TransportProtocol == 5; public bool CanChatInChannel(string channelName) { return CanChat && PublicChannels.ContainsKey(channelName) && !PublicChannelsUnsubscribing.Contains(channelName); } public ChatClient(IChatClientListener listener, ConnectionProtocol protocol = 0) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown this.listener = listener; State = ChatState.Uninitialized; AppSettings = new ChatAppSettings(); Peer = new PhotonPeer((IPhotonPeerListener)(object)this, protocol); Peer.SerializationProtocolType = (SerializationProtocol)1; PublicChannels = new Dictionary<string, ChatChannel>(); PrivateChannels = new Dictionary<string, ChatChannel>(); PublicChannelsUnsubscribing = new HashSet<string>(); } public bool ConnectUsingSettings(ChatAppSettings appSettings, AuthenticationValues authValues) { AuthValues = authValues; return ConnectUsingSettings(appSettings); } public bool ConnectUsingSettings(ChatAppSettings appSettings) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) if (appSettings == null) { listener.DebugReturn((LogLevel)1, "ConnectUsingSettings() failed. The appSettings can't be null.'"); return false; } AppSettings = new ChatAppSettings(appSettings); LogLevelPeer = appSettings.NetworkLogging; TransportProtocol = appSettings.Protocol; if (!appSettings.IsDefaultNameServer) { NameServerHost = appSettings.Server; } NameServerPortOverride = (ushort)((!appSettings.IsDefaultPort) ? appSettings.Port : 0); return ConnectIntern(); } [Obsolete("Use ConnectUsingSettings, which is more feature complete.")] public bool Connect(string appId, string appVersion, AuthenticationValues authValues) { if (authValues != null) { AuthValues = authValues; } AppSettings.AppIdChat = appId; AppSettings.AppVersion = appVersion; return ConnectIntern(); } private bool ConnectIntern() { Peer.PingInterval = 3000; Peer.QuickResendAttempts = 2; Peer.MaxResends = 7; PublicChannels.Clear(); PrivateChannels.Clear(); PublicChannelsUnsubscribing.Clear(); DisconnectedCause = ChatDisconnectCause.None; didAuthenticate = false; bool flag = Peer.Connect(NameServerAddress, AppSettings.AppIdChat, (object)null, (object)null, AppSettings.ProxyServer); if (flag) { State = ChatState.ConnectingToNameServer; } if (UseBackgroundWorkerForSending) { stateTimer = new Timer(SendOutgoingInBackground, null, msDeltaForServiceCalls, msDeltaForServiceCalls); } return flag; } public void Service() { while (Peer.DispatchIncomingCommands()) { } if (!UseBackgroundWorkerForSending && (Environment.TickCount - msTimestampOfLastServiceCall > msDeltaForServiceCalls || msTimestampOfLastServiceCall == 0)) { msTimestampOfLastServiceCall = Environment.TickCount; while (Peer.SendOutgoingCommands()) { } } } private void SendOutgoingInBackground(object state = null) { bool flag = true; while (State != ChatState.Disconnected && flag) { flag = Peer.SendOutgoingCommands(); } } public void Disconnect(ChatDisconnectCause cause = ChatDisconnectCause.DisconnectByClientLogic) { //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Invalid comparison between Unknown and I4 if (State == ChatState.Disconnecting || State == ChatState.Uninitialized) { listener.DebugReturn((LogLevel)3, "Disconnect() call gets skipped due to State " + State.ToString() + ". DisconnectedCause: " + DisconnectedCause.ToString() + " Parameter cause: " + cause); } else { if (DisconnectedCause == ChatDisconnectCause.None) { DisconnectedCause = cause; } if ((int)Peer.PeerState > 0) { State = ChatState.Disconnecting; Peer.Disconnect(); } } } public bool Subscribe(string[] channels, int[] lastMsgIds) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Invalid comparison between Unknown and I4 //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Invalid comparison between Unknown and I4 //IL_011e: 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_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Expected O, but got Unknown //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Invalid comparison between Unknown and I4 if (!CanChat) { if ((int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, "Subscribe called while not connected to front end server."); } return false; } if (channels == null || channels.Length == 0) { if ((int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, "Subscribe can't be called for empty or null channels-list."); } return false; } for (int i = 0; i < channels.Length; i++) { if (string.IsNullOrEmpty(channels[i])) { if ((int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, $"Subscribe can't be called with a null or empty channel name at index {i}."); } return false; } } if (lastMsgIds == null || lastMsgIds.Length != channels.Length) { if ((int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, "Subscribe can't be called when \"lastMsgIds\" array is null or does not have the same length as \"channels\" array."); } return false; } ParameterDictionary val = new ParameterDictionary(); val.Add((byte)0, (object)channels); val.Add((byte)9, (object)lastMsgIds); val.Add((byte)14, -1); ParameterDictionary val2 = val; return Peer.SendOperation((byte)0, val2, SendOptions.SendReliable); } public bool Subscribe(string[] channels, int messagesFromHistory = 0) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Invalid comparison between Unknown and I4 if (!CanChat) { if ((int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, "Subscribe called while not connected to front end server."); } return false; } if (channels == null || channels.Length == 0) { if ((int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, "Subscribe can't be called for empty or null channels-list."); } return false; } return SendChannelOperation(channels, 0, messagesFromHistory); } public bool Subscribe(string channel, int lastMsgId = 0, int messagesFromHistory = -1, ChannelCreationOptions creationOptions = null) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Invalid comparison between Unknown and I4 //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Invalid comparison between Unknown and I4 //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Invalid comparison between Unknown and I4 //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Invalid comparison between Unknown and I4 //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Expected O, but got Unknown //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Invalid comparison between Unknown and I4 //IL_0226: Unknown result type (might be due to invalid IL or missing references) if (creationOptions == null) { creationOptions = ChannelCreationOptions.Default; } int maxSubscribers = creationOptions.MaxSubscribers; bool publishSubscribers = creationOptions.PublishSubscribers; if (maxSubscribers < 0) { if ((int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, "Cannot set MaxSubscribers < 0."); } return false; } if (lastMsgId < 0) { if ((int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, "lastMsgId cannot be < 0."); } return false; } if (messagesFromHistory < -1) { if ((int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, "messagesFromHistory < -1, setting it to -1"); } messagesFromHistory = -1; } if (lastMsgId > 0 && messagesFromHistory == 0) { if ((int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, "lastMsgId will be ignored because messagesFromHistory == 0"); } lastMsgId = 0; } Dictionary<object, object> dictionary = null; if (publishSubscribers) { if (maxSubscribers > 100) { if ((int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, $"Cannot set MaxSubscribers > {100} when PublishSubscribers == true."); } return false; } dictionary = new Dictionary<object, object>(); dictionary[(byte)254] = true; } if (maxSubscribers > 0) { if (dictionary == null) { dictionary = new Dictionary<object, object>(); } dictionary[byte.MaxValue] = maxSubscribers; } ParameterDictionary val = new ParameterDictionary(); val.Add((byte)0, (object)new string[1] { channel }); ParameterDictionary val2 = val; if (messagesFromHistory != 0) { val2.Add((byte)14, messagesFromHistory); } if (lastMsgId > 0) { val2.Add((byte)9, (object)new int[1] { lastMsgId }); } if (dictionary != null && dictionary.Count > 0) { val2.Add((byte)22, (object)dictionary); } return Peer.SendOperation((byte)0, val2, SendOptions.SendReliable); } public bool Unsubscribe(string[] channels) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Invalid comparison between Unknown and I4 if (!CanChat) { if ((int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, "Unsubscribe called while not connected to front end server."); } return false; } if (channels == null || channels.Length == 0) { if ((int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, "Unsubscribe can't be called for empty or null channels-list."); } return false; } foreach (string item in channels) { PublicChannelsUnsubscribing.Add(item); } return SendChannelOperation(channels, 1, 0); } public bool PublishMessage(string channelName, object message, bool forwardAsWebhook = false) { return publishMessage(channelName, message, reliable: true, forwardAsWebhook); } internal bool PublishMessageUnreliable(string channelName, object message, bool forwardAsWebhook = false) { return publishMessage(channelName, message, reliable: false, forwardAsWebhook); } private bool publishMessage(string channelName, object message, bool reliable, bool forwardAsWebhook = false) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Invalid comparison between Unknown and I4 //IL_00b0: 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) if (!CanChat) { if ((int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, "PublishMessage called while not connected to front end server."); } return false; } if (string.IsNullOrEmpty(channelName) || message == null) { if ((int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, "PublishMessage parameters must be non-null and not empty."); } return false; } ParameterDictionary val = new ParameterDictionary(); val.Add((byte)1, channelName); val.Add((byte)3, message); ParameterDictionary val2 = val; if (forwardAsWebhook) { val2.Add((byte)21, (byte)1); } PhotonPeer peer = Peer; SendOptions val3 = default(SendOptions); ((SendOptions)(ref val3)).Reliability = reliable; return peer.SendOperation((byte)2, val2, val3); } public bool SendPrivateMessage(string target, object message, bool forwardAsWebhook = false) { return SendPrivateMessage(target, message, encrypt: false, forwardAsWebhook); } public bool SendPrivateMessage(string target, object message, bool encrypt, bool forwardAsWebhook) { return sendPrivateMessage(target, message, encrypt, reliable: true, forwardAsWebhook); } internal bool SendPrivateMessageUnreliable(string target, object message, bool encrypt, bool forwardAsWebhook = false) { return sendPrivateMessage(target, message, encrypt, reliable: false, forwardAsWebhook); } private bool sendPrivateMessage(string target, object message, bool encrypt, bool reliable, bool forwardAsWebhook = false) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Invalid comparison between Unknown and I4 //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) if (!CanChat) { if ((int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, "SendPrivateMessage called while not connected to front end server."); } return false; } if (string.IsNullOrEmpty(target) || message == null) { if ((int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, "SendPrivateMessage parameters must be non-null and not empty."); } return false; } ParameterDictionary val = new ParameterDictionary(); val.Add((byte)225, target); val.Add((byte)3, message); ParameterDictionary val2 = val; if (forwardAsWebhook) { val2.Add((byte)21, (byte)1); } PhotonPeer peer = Peer; SendOptions val3 = default(SendOptions); ((SendOptions)(ref val3)).Reliability = reliable; val3.Encrypt = encrypt; return peer.SendOperation((byte)3, val2, val3); } public bool SetOnlineStatus(int status, object message = null, bool skipMessage = false) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_0074: Unknown result type (might be due to invalid IL or missing references) if (!CanChat) { if ((int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, "SetOnlineStatus called while not connected to front end server."); } return false; } ParameterDictionary val = new ParameterDictionary(); val.Add((byte)10, status); ParameterDictionary val2 = val; if (skipMessage) { val2[(byte)12] = true; } else { val2[(byte)3] = message; } return Peer.SendOperation((byte)5, val2, SendOptions.SendReliable); } public bool AddFriends(string[] friends) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Invalid comparison between Unknown and I4 //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Expected O, but got Unknown //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Invalid comparison between Unknown and I4 if (!CanChat) { if ((int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, "AddFriends called while not connected to front end server."); } return false; } if (friends == null || friends.Length == 0) { if ((int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, "AddFriends can't be called for empty or null list."); } return false; } if (friends.Length > 1024) { if ((int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, "AddFriends max list size exceeded: " + friends.Length + " > " + 1024); } return false; } ParameterDictionary val = new ParameterDictionary(); val.Add((byte)11, (object)friends); ParameterDictionary val2 = val; return Peer.SendOperation((byte)6, val2, SendOptions.SendReliable); } public bool RemoveFriends(string[] friends) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Invalid comparison between Unknown and I4 //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Expected O, but got Unknown //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Invalid comparison between Unknown and I4 if (!CanChat) { if ((int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, "RemoveFriends called while not connected to front end server."); } return false; } if (friends == null || friends.Length == 0) { if ((int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, "RemoveFriends can't be called for empty or null list."); } return false; } if (friends.Length > 1024) { if ((int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, "RemoveFriends max list size exceeded: " + friends.Length + " > " + 1024); } return false; } ParameterDictionary val = new ParameterDictionary(); val.Add((byte)11, (object)friends); ParameterDictionary val2 = val; return Peer.SendOperation((byte)7, val2, SendOptions.SendReliable); } public string GetPrivateChannelNameByUser(string userName) { return $"{UserId}:{userName}"; } public bool TryGetChannel(string channelName, bool isPrivate, out ChatChannel channel) { if (!isPrivate) { return PublicChannels.TryGetValue(channelName, out channel); } return PrivateChannels.TryGetValue(channelName, out channel); } public bool TryGetChannel(string channelName, out ChatChannel channel) { bool flag = false; if (PublicChannels.TryGetValue(channelName, out channel)) { return true; } return PrivateChannels.TryGetValue(channelName, out channel); } public bool TryGetPrivateChannelByUser(string userId, out ChatChannel channel) { channel = null; if (string.IsNullOrEmpty(userId)) { return false; } string privateChannelNameByUser = GetPrivateChannelNameByUser(userId); return TryGetChannel(privateChannelNameByUser, isPrivate: true, out channel); } void IPhotonPeerListener.DebugReturn(LogLevel level, string message) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) listener.DebugReturn(level, message); } void IPhotonPeerListener.OnEvent(EventData eventData) { switch (eventData.Code) { case 0: HandleChatMessagesEvent(eventData); break; case 2: HandlePrivateMessageEvent(eventData); break; case 4: HandleStatusUpdate(eventData); break; case 5: HandleSubscribeEvent(eventData); break; case 6: HandleUnsubscribeEvent(eventData); break; case 8: HandleUserSubscribedEvent(eventData); break; case 9: HandleUserUnsubscribedEvent(eventData); break; case 1: case 3: case 7: break; } } void IPhotonPeerListener.OnOperationResponse(OperationResponse operationResponse) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Invalid comparison between Unknown and I4 if (operationResponse.ReturnCode == 32743) { Disconnect(ChatDisconnectCause.DisconnectByOperationLimit); } byte operationCode = operationResponse.OperationCode; byte b = operationCode; if ((uint)b > 3u && (uint)(b - 230) <= 1u) { HandleAuthResponse(operationResponse); } else if (operationResponse.ReturnCode != 0 && (int)LogLevelClient >= 1) { if (operationResponse.ReturnCode == -2) { listener.DebugReturn((LogLevel)1, $"Chat Operation {operationResponse.OperationCode} failed on server. Message by server: {operationResponse.DebugMessage}"); } else { listener.DebugReturn((LogLevel)1, $"Chat Operation {operationResponse.OperationCode} failed (Code: {operationResponse.ReturnCode}). Debug Message: {operationResponse.DebugMessage}"); } } } void IPhotonPeerListener.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_0025: Expected I4, but got Unknown //IL_0027: 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_0067: Expected I4, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Invalid comparison between Unknown and I4 //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Invalid comparison between Unknown and I4 //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Invalid comparison between Unknown and I4 switch (statusCode - 1022) { default: switch (statusCode - 1039) { default: return; case 9: TryAuthenticateOnNameServer(); return; case 3: listener.DebugReturn((LogLevel)1, "This connection was rejected due to the apps CCU limit."); Disconnect(ChatDisconnectCause.MaxCcuReached); return; case 12: Disconnect(ChatDisconnectCause.DnsExceptionOnConnect); return; case 11: Disconnect(ChatDisconnectCause.ServerAddressInvalid); return; case 10: break; case 0: goto IL_0331; case 2: Disconnect(ChatDisconnectCause.ServerTimeout); return; case 4: Disconnect(ChatDisconnectCause.DisconnectByServerLogic); return; case 5: Disconnect(ChatDisconnectCause.DisconnectByServerReasonUnknown); return; case 1: goto IL_0359; case 6: case 7: case 8: return; } goto case 0; case 2: if (!IsProtocolSecure) { if (!Peer.EstablishEncryption() && (int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, "Error establishing encryption"); } } else { TryAuthenticateOnNameServer(); } if (State == ChatState.ConnectingToNameServer) { State = ChatState.ConnectedToNameServer; listener.OnChatStateChange(State); } else if (State == ChatState.ConnectingToFrontEnd && !AuthenticateOnFrontEnd() && (int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, $"Error authenticating on frontend! Check log output, AuthValues and if you're connected. State: {State}"); } break; case 3: switch (State) { case ChatState.ConnectWithFallbackProtocol: AppSettings.EnableProtocolFallback = false; NameServerPortOverride = 0; Peer.TransportProtocol = (ConnectionProtocol)((int)Peer.TransportProtocol != 1); ConnectIntern(); return; case ChatState.Authenticated: ConnectToFrontEnd(); return; case ChatState.Disconnecting: if (stateTimer != null) { stateTimer.Dispose(); stateTimer = null; } break; default: { string empty = string.Empty; empty = new StackTrace(fNeedFileInfo: true).ToString(); listener.DebugReturn((LogLevel)2, $"Got an unexpected Disconnect in ChatState: {State}. DisconnectedCause: {DisconnectedCause}. Server: {Peer.ServerAddress} Trace: {empty}"); if (stateTimer != null) { stateTimer.Dispose(); stateTimer = null; } break; } } if (AuthValues != null) { AuthValues.Token = null; } State = ChatState.Disconnected; listener.OnChatStateChange(ChatState.Disconnected); listener.OnDisconnected(); break; case 0: case 1: DisconnectedCause = ChatDisconnectCause.ExceptionOnConnect; if (AppSettings.EnableProtocolFallback && State == ChatState.ConnectingToNameServer) { State = ChatState.ConnectWithFallbackProtocol; } else { Disconnect(ChatDisconnectCause.ExceptionOnConnect); } break; case 4: goto IL_0331; IL_0359: DisconnectedCause = ChatDisconnectCause.ClientTimeout; if (AppSettings.EnableProtocolFallback && State == ChatState.ConnectingToNameServer) { State = ChatState.ConnectWithFallbackProtocol; } else { Disconnect(ChatDisconnectCause.ClientTimeout); } break; IL_0331: Disconnect(ChatDisconnectCause.Exception); break; } } void IPhotonPeerListener.OnMessage(bool isRawMessage, object msg) { } public void OnDisconnectMessage(DisconnectMessage obj) { listener.DebugReturn((LogLevel)1, $"OnDisconnectMessage. Code: {obj.Code} Msg: \"{obj.DebugMessage}\"."); Disconnect(ChatDisconnectCause.DisconnectByDisconnectMessage); } private void TryAuthenticateOnNameServer() { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Invalid comparison between Unknown and I4 if (!didAuthenticate) { didAuthenticate = AuthenticateOnNameServer(AppSettings.AppIdChat, AppSettings.AppVersion, FixedRegionOrDefault, AuthValues); if (!didAuthenticate && (int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, $"Error calling OpAuthenticate! Did not work on NameServer. Check log output, AuthValues and if you're connected. State: {State}"); } } } private bool SendChannelOperation(string[] channels, byte operation, int historyLength) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) ParameterDictionary val = new ParameterDictionary(); val.Add((byte)0, (object)channels); ParameterDictionary val2 = val; if (historyLength != 0) { val2.Add((byte)14, historyLength); } return Peer.SendOperation(operation, val2, SendOptions.SendReliable); } private void HandlePrivateMessageEvent(EventData eventData) { object message = eventData.Parameters[(byte)3]; string text = (string)eventData.Parameters[(byte)5]; int msgId = (int)eventData.Parameters[(byte)8]; string privateChannelNameByUser; if (UserId != null && UserId.Equals(text)) { string userName = (string)eventData.Parameters[(byte)225]; privateChannelNameByUser = GetPrivateChannelNameByUser(userName); } else { privateChannelNameByUser = GetPrivateChannelNameByUser(text); } if (!PrivateChannels.TryGetValue(privateChannelNameByUser, out var value)) { value = new ChatChannel(privateChannelNameByUser); value.IsPrivate = true; value.MessageLimit = MessageLimit; PrivateChannels.Add(value.Name, value); } value.Add(text, message, msgId); listener.OnPrivateMessage(text, message, privateChannelNameByUser); } private void HandleChatMessagesEvent(EventData eventData) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Invalid comparison between Unknown and I4 object[] messages = (object[])eventData.Parameters[(byte)2]; string[] senders = (string[])eventData.Parameters[(byte)4]; string text = (string)eventData.Parameters[(byte)1]; int lastMsgId = (int)eventData.Parameters[(byte)8]; if (!PublicChannels.TryGetValue(text, out var value)) { if ((int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, "Channel " + text + " for incoming message event not found."); } } else { value.Add(senders, messages, lastMsgId); listener.OnGetMessages(text, senders, messages); } } private void HandleSubscribeEvent(EventData eventData) { string[] array = (string[])eventData.Parameters[(byte)0]; bool[] array2 = (bool[])eventData.Parameters[(byte)15]; object obj = default(object); for (int i = 0; i < array.Length; i++) { if (array2[i]) { string text = array[i]; if (!PublicChannels.TryGetValue(text, out var value)) { value = new ChatChannel(text); value.MessageLimit = MessageLimit; PublicChannels.Add(value.Name, value); } if (eventData.Parameters.TryGetValue((byte)22, ref obj)) { Dictionary<object, object> newProperties = obj as Dictionary<object, object>; value.ReadChannelProperties(newProperties); } if (value.PublishSubscribers) { value.AddSubscriber(UserId); } if (eventData.Parameters.TryGetValue((byte)23, ref obj)) { string[] users = obj as string[]; value.AddSubscribers(users); } } } listener.OnSubscribed(array, array2); } private void HandleUnsubscribeEvent(EventData eventData) { string[] array = (string[])eventData[(byte)0]; foreach (string text in array) { PublicChannels.Remove(text); PublicChannelsUnsubscribing.Remove(text); } listener.OnUnsubscribed(array); } private void HandleAuthResponse(OperationResponse operationResponse) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Invalid comparison between Unknown and I4 //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Invalid comparison between Unknown and I4 if ((int)LogLevelClient >= 3) { listener.DebugReturn((LogLevel)3, operationResponse.ToStringFull() + " on: " + CurrentServerAddress); } if (operationResponse.ReturnCode == 0) { if (State == ChatState.ConnectedToNameServer) { State = ChatState.Authenticated; listener.OnChatStateChange(State); if (operationResponse.Parameters.ContainsKey((byte)221)) { if (AuthValues == null) { AuthValues = new AuthenticationValues(); } AuthValues.Token = operationResponse[(byte)221]; FrontendAddress = (string)operationResponse[(byte)230]; Peer.Disconnect(); } else if ((int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, "No secret in authentication response."); } if (operationResponse.Parameters.ContainsKey((byte)225)) { string text = operationResponse.Parameters[(byte)225] as string; if (!string.IsNullOrEmpty(text)) { UserId = text; listener.DebugReturn((LogLevel)3, $"Received your UserID from server. Updating local value to: {UserId}"); } } } else if (State == ChatState.ConnectingToFrontEnd) { State = ChatState.ConnectedToFrontEnd; listener.OnChatStateChange(State); listener.OnConnected(); } Dictionary<string, object> dictionary = (Dictionary<string, object>)operationResponse[(byte)245]; if (dictionary != null) { listener.OnCustomAuthenticationResponse(dictionary); } } else { switch (operationResponse.ReturnCode) { case short.MaxValue: DisconnectedCause = ChatDisconnectCause.InvalidAuthentication; break; case 32755: DisconnectedCause = ChatDisconnectCause.CustomAuthenticationFailed; listener.OnCustomAuthenticationFailed(operationResponse.DebugMessage); break; case 32756: DisconnectedCause = ChatDisconnectCause.InvalidRegion; break; case 32757: DisconnectedCause = ChatDisconnectCause.MaxCcuReached; break; case -3: DisconnectedCause = ChatDisconnectCause.OperationNotAllowedInCurrentState; break; case 32753: DisconnectedCause = ChatDisconnectCause.AuthenticationTicketExpired; break; } if ((int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, $"{operationResponse.ToStringFull()} ClientState: {State} ServerAddress: {Peer.ServerAddress}"); } Disconnect(DisconnectedCause); } } private void HandleStatusUpdate(EventData eventData) { string user = (string)eventData.Parameters[(byte)5]; int status = (int)eventData.Parameters[(byte)10]; object message = null; bool flag = eventData.Parameters.ContainsKey((byte)3); if (flag) { message = eventData.Parameters[(byte)3]; } listener.OnStatusUpdate(user, status, flag, message); } private bool ConnectToFrontEnd() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Invalid comparison between Unknown and I4 State = ChatState.ConnectingToFrontEnd; if ((int)LogLevelClient >= 3) { listener.DebugReturn((LogLevel)3, "Connecting to frontend " + FrontendAddress); } if (!Peer.Connect(FrontendAddress, AppSettings.AppIdChat, AuthValues.Token, (object)null, AppSettings.ProxyServer)) { if ((int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, $"Connecting to frontend {FrontendAddress} failed."); } return false; } return true; } private bool AuthenticateOnFrontEnd() { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Invalid comparison between Unknown and I4 //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Invalid comparison between Unknown and I4 //IL_00a2: Unknown result type (might be due to invalid IL or missing references) if (AuthValues != null) { if (AuthValues.Token == null) { if ((int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, "Can't authenticate on front end server. Secret (AuthValues.Token) is not set"); } return false; } ParameterDictionary val = new ParameterDictionary(); val.Add((byte)221, AuthValues.Token); ParameterDictionary val2 = val; if (PrivateChatHistoryLength > -1) { val2[(byte)14] = PrivateChatHistoryLength; } return Peer.SendOperation((byte)230, val2, SendOptions.SendReliable); } if ((int)LogLevelClient >= 1) { listener.DebugReturn((LogLevel)1, "Can't authenticate on front end server. Authentication Values are not set"); } return false; } private void HandleUserUnsubscribedEvent(EventData eventData) { //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Invalid comparison between Unknown and I4 //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Invalid comparison between Unknown and I4 //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Invalid comparison between Unknown and I4 string text = eventData.Parameters[(byte)1] as string; string text2 = eventData.Parameters[(byte)225] as string; if (PublicChannels.TryGetValue(text, out var value)) { if (!value.PublishSubscribers && (int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, $"Channel \"{text}\" for incoming UserUnsubscribed (\"{text2}\") event does not have PublishSubscribers enabled."); } if (!value.RemoveSubscriber(text2) && (int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, $"Channel \"{text}\" does not contain unsubscribed user \"{text2}\"."); } } else if ((int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, $"Channel \"{text}\" not found for incoming UserUnsubscribed (\"{text2}\") event."); } listener.OnUserUnsubscribed(text, text2); } private void HandleUserSubscribedEvent(EventData eventData) { //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Invalid comparison between Unknown and I4 //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Invalid comparison between Unknown and I4 //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Invalid comparison between Unknown and I4 //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Invalid comparison between Unknown and I4 string text = eventData.Parameters[(byte)1] as string; string text2 = eventData.Parameters[(byte)225] as string; if (PublicChannels.TryGetValue(text, out var value)) { if (!value.PublishSubscribers && (int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, $"Channel \"{text}\" for incoming UserSubscribed (\"{text2}\") event does not have PublishSubscribers enabled."); } if (!value.AddSubscriber(text2)) { if ((int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, $"Channel \"{text}\" already contains newly subscribed user \"{text2}\"."); } } else if (value.MaxSubscribers > 0 && value.Subscribers.Count > value.MaxSubscribers && (int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, $"Channel \"{text}\"'s MaxSubscribers exceeded. count={value.Subscribers.Count} > MaxSubscribers={value.MaxSubscribers}."); } } else if ((int)LogLevelClient >= 2) { listener.DebugReturn((LogLevel)2, $"Channel \"{text}\" not found for incoming UserSubscribed (\"{text2}\") event."); } listener.OnUserSubscribed(text, text2); } [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) { SocketImplementationConfig[(ConnectionProtocol)4] = type; SocketImplementationConfig[(ConnectionProtocol)5] = type; } } private string GetNameServerAddress() { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected I4, but got Unknown int value = 0; ProtocolToNameServerPort.TryGetValue(TransportProtocol, out value); if (NameServerPortOverride != 0) { listener.DebugReturn((LogLevel)3, $"Using NameServerPortInAppSettings as port for Name Server: {NameServerPortOverride}"); value = NameServerPortOverride; } ConnectionProtocol transportProtocol = TransportProtocol; ConnectionProtocol val = transportProtocol; switch ((int)val) { case 0: case 1: return $"{NameServerHost}:{value}"; case 4: return $"ws://{NameServerHost}:{value}"; case 5: return $"wss://{NameServerHost}:{value}"; default: throw new ArgumentOutOfRangeException(); } } protected internal bool AuthenticateOnNameServer(string appId, string appVersion, string region, AuthenticationValues authValues) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Invalid comparison between Unknown and I4 //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) if ((int)LogLevelClient >= 3) { listener.DebugReturn((LogLevel)3, "OpAuthenticate()"); } ParameterDictionary val = new ParameterDictionary(); val[(byte)220] = appVersion; val[(byte)224] = appId; val[(byte)210] = region; 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 peer = Peer; SendOptions val2 = default(SendOptions); ((SendOptions)(ref val2)).Reliability = true; val2.Encrypt = true; return peer.SendOperation((byte)230, val, val2); } } public enum ChatDisconnectCause { None, ExceptionOnConnect, DnsExceptionOnConnect, ServerAddressInvalid, Exception, ServerTimeout, ClientTimeout, DisconnectByServerLogic, DisconnectByServerReasonUnknown, InvalidAuthentication, CustomAuthenticationFailed, AuthenticationTicketExpired, MaxCcuReached, InvalidRegion, OperationNotAllowedInCurrentState, DisconnectByClientLogic, DisconnectByOperationLimit, DisconnectByDisconnectMessage } public class ChatEventCode { public const byte ChatMessages = 0; public const byte Users = 1; public const byte PrivateMessage = 2; public const byte FriendsList = 3; public const byte StatusUpdate = 4; public const byte Subscribe = 5; public const byte Unsubscribe = 6; public const byte PropertiesChanged = 7; public const byte UserSubscribed = 8; public const byte UserUnsubscribed = 9; public const byte ErrorInfo = 10; } public class ChatOperationCode { public const byte Authenticate = 230; public const byte AuthenticateOnce = 231; public const byte Subscribe = 0; public const byte Unsubscribe = 1; public const byte Publish = 2; public const byte SendPrivate = 3; public const byte ChannelHistory = 4; public const byte UpdateStatus = 5; public const byte AddFriends = 6; public const byte RemoveFriends = 7; public const byte SetProperties = 8; } public class ChatParameterCode { public const byte ApplicationId = 224; public const byte Secret = 221; public const byte AppVersion = 220; public const byte ClientAuthenticationType = 217; public const byte ClientAuthenticationParams = 216; public const byte ClientAuthenticationData = 214; public const byte Region = 210; public const byte Address = 230; public const byte UserId = 225; public const byte Data = 245; public const byte Channels = 0; public const byte Channel = 1; public const byte Messages = 2; public const byte Message = 3; public const byte Senders = 4; public const byte Sender = 5; public const byte ChannelUserCount = 6; public const byte MsgId = 8; public const byte MsgIds = 9; public const byte SubscribeResults = 15; public const byte Status = 10; public const byte Friends = 11; public const byte SkipMessage = 12; public const byte HistoryLength = 14; public const byte DebugMessage = 17; public const byte WebFlags = 21; public const byte Properties = 22; public const byte ChannelSubscribers = 23; public const byte DebugData = 24; public const byte ExpectedValues = 25; public const byte Broadcast = 26; public const byte UserProperties = 28; public const byte UniqueRoomId = 29; } 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 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; } public object Token { get; protected internal 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<string, object> 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 override string ToString() { return string.Format("AuthenticationValues Type: {3} UserId: {0}, GetParameters: {1} Token available: {2}", UserId, AuthGetParameters, Token != null, AuthType); } public AuthenticationValues CopyTo(AuthenticationValues copy) { copy.AuthType = AuthType; copy.AuthGetParameters = AuthGetParameters; copy.AuthPostData = AuthPostData; copy.UserId = UserId; return copy; } } public class ErrorCode { public const int Ok = 0; public const int OperationNotAllowedInCurrentState = -3; public const int InvalidOperationCode = -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 OperationLimitReached = 32743; } public enum ChatState { Uninitialized, ConnectingToNameServer, ConnectedToNameServer, Authenticating, Authenticated, DisconnectingFromNameServer, ConnectingToFrontEnd, ConnectedToFrontEnd, DisconnectingFromFrontEnd, QueuedComingFromFrontEnd, Disconnecting, Disconnected, ConnectWithFallbackProtocol } public static class ChatUserStatus { public const int Offline = 0; public const int Invisible = 1; public const int Online = 2; public const int Away = 3; public const int DND = 4; public const int LFG = 5; public const int Playing = 6; } public interface IChatClientListener { void DebugReturn(LogLevel level, string message); void OnDisconnected(); void OnConnected(); void OnCustomAuthenticationResponse(Dictionary<string, object> data); void OnCustomAuthenticationFailed(string debugMessage); void OnChatStateChange(ChatState state); void OnGetMessages(string channelName, string[] senders, object[] messages); void OnPrivateMessage(string sender, object message, string channelName); void OnSubscribed(string[] channels, bool[] results); void OnUnsubscribed(string[] channels); void OnStatusUpdate(string user, int status, bool gotMessage, object message); void OnUserSubscribed(string channel, string user); void OnUserUnsubscribed(string channel, string user); }
UserLibs/PhotonClient.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Net; using System.Net.Sockets; using System.Net.WebSockets; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Text; using System.Threading; using System.Threading.Tasks; using Photon.Client.Encryption; using Photon.Client.StructWrapping; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("Exit Games GmbH")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("(c) Exit Games GmbH, http://www.photonengine.com")] [assembly: AssemblyDescription("Photon .Net Client Library.")] [assembly: AssemblyFileVersion("5.1.9.0")] [assembly: AssemblyInformationalVersion("5.1.9")] [assembly: AssemblyProduct("Photon .Net Client Library. Release")] [assembly: AssemblyTitle("PhotonClient")] [assembly: AssemblyVersion("5.1.9.0")] namespace Photon.Client { public class ByteArraySlice : IDisposable { public byte[] Buffer; public int Offset; public int Count; private readonly ByteArraySlicePool returnPool; private readonly int stackIndex; internal ByteArraySlice(ByteArraySlicePool returnPool, int stackIndex) { Buffer = ((stackIndex == 0) ? null : new byte[1 << stackIndex]); this.returnPool = returnPool; this.stackIndex = stackIndex; } public ByteArraySlice(byte[] buffer, int offset = 0, int count = 0) { Buffer = buffer; Count = count; Offset = offset; returnPool = null; stackIndex = -1; } public ByteArraySlice() { returnPool = null; stackIndex = -1; } public void Dispose() { Release(); } public bool Release() { if (stackIndex < 0) { return false; } Count = 0; Offset = 0; return returnPool.Release(this, stackIndex); } public void Reset() { Count = 0; Offset = 0; } } public class ByteArraySlicePool { private int minStackIndex = 7; internal readonly Stack<ByteArraySlice>[] poolTiers = new Stack<ByteArraySlice>[32]; private int allocationCounter; public int MinStackIndex { get { return minStackIndex; } set { minStackIndex = ((value <= 0) ? 1 : ((value < 31) ? value : 31)); } } public int AllocationCounter => allocationCounter; public ByteArraySlicePool() { lock (poolTiers) { poolTiers[0] = new Stack<ByteArraySlice>(); } } public ByteArraySlice Acquire(byte[] buffer, int offset = 0, int count = 0) { ByteArraySlice byteArraySlice; lock (poolTiers) { lock (poolTiers[0]) { byteArraySlice = PopOrCreate(poolTiers[0], 0); } } byteArraySlice.Buffer = buffer; byteArraySlice.Offset = offset; byteArraySlice.Count = count; return byteArraySlice; } public ByteArraySlice Acquire(int minByteCount) { if (minByteCount < 0) { throw new Exception(typeof(ByteArraySlice).Name + " requires a positive minByteCount."); } int i = minStackIndex; if (minByteCount > 0) { for (int num = minByteCount - 1; i < 32 && num >> i != 0; i++) { } } lock (poolTiers) { Stack<ByteArraySlice> stack = poolTiers[i]; if (stack == null) { stack = new Stack<ByteArraySlice>(); poolTiers[i] = stack; } lock (stack) { return PopOrCreate(stack, i); } } } private ByteArraySlice PopOrCreate(Stack<ByteArraySlice> stack, int stackIndex) { lock (stack) { if (stack.Count > 0) { return stack.Pop(); } } ByteArraySlice result = new ByteArraySlice(this, stackIndex); allocationCounter++; return result; } internal bool Release(ByteArraySlice slice, int stackIndex) { if (slice == null || stackIndex < 0) { return false; } if (stackIndex == 0) { slice.Buffer = null; } lock (poolTiers) { lock (poolTiers[stackIndex]) { poolTiers[stackIndex].Push(slice); } } return true; } public void ClearPools(int lower = 0, int upper = int.MaxValue) { _ = minStackIndex; for (int i = 0; i < 32; i++) { int num = 1 << i; if (num < lower || num > upper) { continue; } lock (poolTiers) { if (poolTiers[i] != null) { lock (poolTiers[i]) { poolTiers[i].Clear(); } } } } } } [DebuggerDisplay("{_nodes.Length} nodes, used {Count}")] public class NonAllocDictionary<K, V> : IDictionary<K, V>, ICollection<KeyValuePair<K, V>>, IEnumerable<KeyValuePair<K, V>>, IEnumerable where K : IEquatable<K> { public struct KeyIterator : IEnumerator<K>, IEnumerator, IDisposable { private int _index; private NonAllocDictionary<K, V> _dict; object IEnumerator.Current { get { if (_index == 0) { throw new InvalidOperationException(); } return _dict._nodes[_index].Key; } } public K Current { get { if (_index == 0) { return default(K); } return _dict._nodes[_index].Key; } } public KeyIterator(NonAllocDictionary<K, V> dictionary) { _index = 0; _dict = dictionary; } public KeyIterator GetEnumerator() { return this; } public void Reset() { _index = 0; } public bool MoveNext() { while (++_index < _dict._usedCount) { if (_dict._nodes[_index].Used) { return true; } } _index = 0; return false; } public void Dispose() { } } public struct ValueIterator : IEnumerator<V>, IEnumerator, IDisposable { private int _index; private NonAllocDictionary<K, V> _dict; public V Current { get { if (_index == 0) { return default(V); } return _dict._nodes[_index].Val; } } object IEnumerator.Current { get { if (_index == 0) { throw new InvalidOperationException(); } return _dict._nodes[_index].Val; } } public ValueIterator(NonAllocDictionary<K, V> dictionary) { _index = 0; _dict = dictionary; } public ValueIterator GetEnumerator() { return this; } public void Reset() { _index = 0; } public bool MoveNext() { while (++_index < _dict._usedCount) { if (_dict._nodes[_index].Used) { return true; } } _index = 0; return false; } public void Dispose() { } } public struct PairIterator : IEnumerator<KeyValuePair<K, V>>, IEnumerator, IDisposable { private int _index; private NonAllocDictionary<K, V> _dict; object IEnumerator.Current { get { if (_index == 0) { throw new InvalidOperationException(); } return Current; } } public KeyValuePair<K, V> Current { get { if (_index == 0) { return default(KeyValuePair<K, V>); } return new KeyValuePair<K, V>(_dict._nodes[_index].Key, _dict._nodes[_index].Val); } } public PairIterator(NonAllocDictionary<K, V> dictionary) { _index = 0; _dict = dictionary; } public void Reset() { _index = 0; } public bool MoveNext() { while (++_index < _dict._usedCount) { if (_dict._nodes[_index].Used) { return true; } } _index = 0; return false; } public void Dispose() { } } [DebuggerDisplay("{DebuggerDisplay,nq}")] private struct Node { public bool Used; public K Key; public V Val; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public int Next; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public uint Hash; private string DebuggerDisplay { get { if (!Used) { return "not used"; } return $"{Key}: {Val}"; } } } private static uint[] _primeTableUInt = new uint[30] { 3u, 7u, 17u, 29u, 53u, 97u, 193u, 389u, 769u, 1543u, 3079u, 6151u, 12289u, 24593u, 49157u, 98317u, 196613u, 393241u, 786433u, 1572869u, 3145739u, 6291469u, 12582917u, 25165843u, 50331653u, 100663319u, 201326611u, 402653189u, 805306457u, 1610612741u }; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private int _freeHead; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private int _freeCount; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private int _usedCount; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private uint _capacity; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private int[] _buckets; private Node[] _nodes; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private bool isReadOnly; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ICollection<K> keys; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ICollection<V> values; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public KeyIterator Keys => new KeyIterator(this); [DebuggerBrowsable(DebuggerBrowsableState.Never)] ICollection<V> IDictionary<K, V>.Values => values; [DebuggerBrowsable(DebuggerBrowsableState.Never)] ICollection<K> IDictionary<K, V>.Keys => keys; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public ValueIterator Values => new ValueIterator(this); public int Count => _usedCount - _freeCount - 1; public bool IsReadOnly => isReadOnly; public uint Capacity => _capacity; public V this[K key] { get { int num = FindNode(key); if (num != 0) { return _nodes[num].Val; } K val = key; throw new InvalidOperationException("Key does not exist: " + val); } set { int num = FindNode(key); if (num == 0) { Insert(key, value); return; } Assert(_nodes[num].Key.Equals(key)); _nodes[num].Val = value; } } public NonAllocDictionary(uint capacity = 29u) { _capacity = (IsPrimeFromList(capacity) ? capacity : GetNextPrime(capacity)); _usedCount = 1; _buckets = new int[_capacity]; _nodes = new Node[_capacity]; } public bool ContainsKey(K key) { return FindNode(key) != 0; } public bool Contains(KeyValuePair<K, V> item) { int num = FindNode(item.Key); if (num >= 0 && EqualityComparer<V>.Default.Equals(_nodes[num].Val, item.Value)) { return true; } return false; } public bool TryGetValue(K key, out V val) { int num = FindNode(key); if (num != 0) { val = _nodes[num].Val; return true; } val = default(V); return false; } public void Set(K key, V val) { int num = FindNode(key); if (num == 0) { Insert(key, val); return; } Assert(_nodes[num].Key.Equals(key)); _nodes[num].Val = val; } public void Add(K key, V val) { if (FindNode(key) == 0) { Insert(key, val); return; } K val2 = key; throw new InvalidOperationException("Duplicate key " + val2); } public void Add(KeyValuePair<K, V> item) { if (FindNode(item.Key) == 0) { Insert(item.Key, item.Value); return; } throw new InvalidOperationException("Duplicate key " + item.Key); } public bool Remove(K key) { uint hashCode = (uint)key.GetHashCode(); int num = _buckets[hashCode % _capacity]; int num2 = 0; while (num != 0) { if (_nodes[num].Hash == hashCode && _nodes[num].Key.Equals(key)) { if (num2 == 0) { _buckets[hashCode % _capacity] = _nodes[num].Next; } else { _nodes[num2].Next = _nodes[num].Next; } _nodes[num].Used = false; _nodes[num].Next = _freeHead; _nodes[num].Val = default(V); _freeHead = num; _freeCount++; return true; } num2 = num; num = _nodes[num].Next; } return false; } public bool Remove(KeyValuePair<K, V> item) { if (Contains(item)) { return Remove(item.Key); } return false; } IEnumerator<KeyValuePair<K, V>> IEnumerable<KeyValuePair<K, V>>.GetEnumerator() { return new PairIterator(this); } public PairIterator GetEnumerator() { return new PairIterator(this); } IEnumerator IEnumerable.GetEnumerator() { return new PairIterator(this); } private int FindNode(K key) { uint hashCode = (uint)key.GetHashCode(); for (int num = _buckets[hashCode % _capacity]; num != 0; num = _nodes[num].Next) { if (_nodes[num].Hash == hashCode && _nodes[num].Key.Equals(key)) { return num; } } return 0; } private void Insert(K key, V val) { int num = 0; if (_freeCount > 0) { num = _freeHead; _freeHead = _nodes[num].Next; _freeCount--; } else { if (_usedCount == _capacity) { Expand(); } num = _usedCount++; } uint hashCode = (uint)key.GetHashCode(); uint num2 = hashCode % _capacity; _nodes[num].Used = true; _nodes[num].Hash = hashCode; _nodes[num].Next = _buckets[num2]; _nodes[num].Key = key; _nodes[num].Val = val; _buckets[num2] = num; } private void Expand() { Assert(_buckets.Length == _usedCount); uint nextPrime = GetNextPrime(_capacity); Assert(nextPrime > _capacity); int[] array = new int[nextPrime]; Node[] array2 = new Node[nextPrime]; Array.Copy(_nodes, 0, array2, 0, _nodes.Length); for (int i = 1; i < _nodes.Length; i++) { Assert(array2[i].Used); uint num = array2[i].Hash % nextPrime; array2[i].Next = array[num]; array[num] = i; } _nodes = array2; _buckets = array; _capacity = nextPrime; } public void Clear() { if (_usedCount > 1) { Array.Clear(_nodes, 0, _nodes.Length); Array.Clear(_buckets, 0, _buckets.Length); _freeHead = 0; _freeCount = 0; _usedCount = 1; } } void ICollection<KeyValuePair<K, V>>.CopyTo(KeyValuePair<K, V>[] array, int index) { if (array == null) { throw new ArgumentNullException("array"); } if (index < 0 || index > array.Length) { throw new ArgumentOutOfRangeException(); } if (array.Length - index < Count) { throw new ArgumentException("Array plus offset are too small to fit all items in."); } for (int i = 1; i < _nodes.Length; i++) { if (_nodes[i].Used) { array[index++] = new KeyValuePair<K, V>(_nodes[i].Key, _nodes[i].Val); } } } private static bool IsPrimeFromList(uint value) { for (int i = 0; i < _primeTableUInt.Length; i++) { if (_primeTableUInt[i] == value) { return true; } } return false; } private static uint GetNextPrime(uint value) { for (int i = 0; i < _primeTableUInt.Length; i++) { if (_primeTableUInt[i] > value) { return _primeTableUInt[i]; } } throw new InvalidOperationException("NonAllocDictionary can't get larger than" + _primeTableUInt[_primeTableUInt.Length - 1]); } private static void Assert(bool condition) { if (!condition) { throw new InvalidOperationException("Assert Failed"); } } } public class PhotonHashtable : Dictionary<object, object> { private static readonly object[] boxedByte; public new object this[object key] { get { object value = null; TryGetValue(key, out value); return value; } set { base[key] = value; } } public object this[byte key] { get { object value = null; TryGetValue(boxedByte[key], out value); return value; } set { base[boxedByte[key]] = value; } } public object this[int key] { get { object value = null; TryGetValue(key, out value); return value; } set { base[key] = value; } } public static object GetBoxedByte(byte value) { return boxedByte[value]; } static PhotonHashtable() { int num = 256; boxedByte = new object[num]; for (int i = 0; i < num; i++) { boxedByte[i] = (byte)i; } } public PhotonHashtable() : base(7) { } public PhotonHashtable(int x) : base(x) { } public void Add(byte k, object v) { Add(boxedByte[k], v); } public void Add(int k, object v) { Add((object)k, v); } public void Remove(byte k) { Remove(boxedByte[k]); } public void Remove(int k) { Remove((object)k); } public bool ContainsKey(byte key) { return ContainsKey(boxedByte[key]); } public new DictionaryEntryEnumerator GetEnumerator() { return new DictionaryEntryEnumerator(base.GetEnumerator()); } public override string ToString() { List<string> list = new List<string>(); foreach (object key in base.Keys) { if (key == null || this[key] == null) { list.Add(key?.ToString() + "=" + this[key]); continue; } list.Add("(" + key.GetType()?.ToString() + ")" + key?.ToString() + "=(" + this[key].GetType()?.ToString() + ")" + this[key]); } return string.Join(", ", list.ToArray()); } public object Clone() { return new Dictionary<object, object>(this); } } public struct DictionaryEntryEnumerator : IEnumerator<DictionaryEntry>, IEnumerator, IDisposable { private Dictionary<object, object>.Enumerator enumerator; object IEnumerator.Current => new DictionaryEntry(enumerator.Current.Key, enumerator.Current.Value); public DictionaryEntry Current => new DictionaryEntry(enumerator.Current.Key, enumerator.Current.Value); public object Key => enumerator.Current.Key; public object Value => enumerator.Current.Value; public DictionaryEntryEnumerator(Dictionary<object, object>.Enumerator original) { enumerator = original; } public bool MoveNext() { return enumerator.MoveNext(); } public void Reset() { ((IEnumerator)enumerator).Reset(); } public void Dispose() { } } public class UnknownType { public byte TypeCode; public int Size; public byte[] Data; } internal class EnetChannel { public class ReceiveTrackingValues { internal bool ReceivedReliableCommandSincePreviousAck2; internal HashSet<int> receivedReliableSequenceNumbers = new HashSet<int>(); internal int reliableSequencedNumbersCompletelyReceived; internal int reliableSequencedNumbersHighestReceived; } internal byte ChannelNumber; internal NonAllocDictionary<int, NCommand> incomingReliableCommandsList; internal NonAllocDictionary<int, NCommand> incomingUnreliableCommandsList; internal Queue<NCommand> incomingUnsequencedCommandsList; internal NonAllocDictionary<int, NCommand> incomingUnsequencedFragments; internal List<NCommand> outgoingReliableCommandsList; internal List<NCommand> outgoingUnreliableCommandsList; internal int incomingReliableSequenceNumber; internal int incomingUnreliableSequenceNumber; internal int outgoingReliableSequenceNumber; internal int outgoingUnreliableSequenceNumber; internal int outgoingReliableUnsequencedNumber; private int reliableUnsequencedNumbersCompletelyReceived; private HashSet<int> reliableUnsequencedNumbersReceived = new HashSet<int>(); internal int highestReceivedAck; internal int reliableCommandsInFlight; internal int lowestUnacknowledgedSequenceNumber; private ReceiveTrackingValues SequencedReceived = new ReceiveTrackingValues(); private ReceiveTrackingValues UnsequencedReceived = new ReceiveTrackingValues(); public EnetChannel(byte channelNumber, int commandBufferSize) { ChannelNumber = channelNumber; incomingReliableCommandsList = new NonAllocDictionary<int, NCommand>((uint)commandBufferSize); incomingUnreliableCommandsList = new NonAllocDictionary<int, NCommand>((uint)commandBufferSize); incomingUnsequencedCommandsList = new Queue<NCommand>(); incomingUnsequencedFragments = new NonAllocDictionary<int, NCommand>(); outgoingReliableCommandsList = new List<NCommand>(commandBufferSize); outgoingUnreliableCommandsList = new List<NCommand>(commandBufferSize); } public bool ContainsUnreliableSequenceNumber(int unreliableSequenceNumber) { return incomingUnreliableCommandsList.ContainsKey(unreliableSequenceNumber); } public NCommand FetchUnreliableSequenceNumber(int unreliableSequenceNumber) { return incomingUnreliableCommandsList[unreliableSequenceNumber]; } public bool ContainsReliableSequenceNumber(int reliableSequenceNumber) { return incomingReliableCommandsList.ContainsKey(reliableSequenceNumber); } public bool AddSequencedIfNew(NCommand command) { NonAllocDictionary<int, NCommand> nonAllocDictionary = (command.IsFlaggedReliable ? incomingReliableCommandsList : incomingUnreliableCommandsList); int key = (command.IsFlaggedReliable ? command.reliableSequenceNumber : command.unreliableSequenceNumber); if (nonAllocDictionary.ContainsKey(key)) { return false; } nonAllocDictionary.Add(key, command); return true; } public NCommand FetchReliableSequenceNumber(int reliableSequenceNumber) { return incomingReliableCommandsList[reliableSequenceNumber]; } public bool TryGetFragment(int reliableSequenceNumber, bool isSequenced, out NCommand fragment) { if (isSequenced) { return incomingReliableCommandsList.TryGetValue(reliableSequenceNumber, out fragment); } return incomingUnsequencedFragments.TryGetValue(reliableSequenceNumber, out fragment); } public void RemoveFragment(int reliableSequenceNumber, bool isSequenced) { if (isSequenced) { incomingReliableCommandsList.Remove(reliableSequenceNumber); } else { incomingUnsequencedFragments.Remove(reliableSequenceNumber); } } public void clearAll() { lock (this) { SequencedReceived = new ReceiveTrackingValues(); UnsequencedReceived = new ReceiveTrackingValues(); incomingReliableCommandsList.Clear(); incomingUnreliableCommandsList.Clear(); incomingUnsequencedCommandsList.Clear(); incomingUnsequencedFragments.Clear(); outgoingReliableCommandsList.Clear(); outgoingUnreliableCommandsList.Clear(); } } public bool QueueIncomingReliableUnsequenced(NCommand command) { if (command.reliableSequenceNumber <= reliableUnsequencedNumbersCompletelyReceived) { return false; } if (reliableUnsequencedNumbersReceived.Contains(command.reliableSequenceNumber)) { return false; } if (command.reliableSequenceNumber == reliableUnsequencedNumbersCompletelyReceived + 1) { reliableUnsequencedNumbersCompletelyReceived++; while (reliableUnsequencedNumbersReceived.Contains(reliableUnsequencedNumbersCompletelyReceived + 1)) { reliableUnsequencedNumbersCompletelyReceived++; reliableUnsequencedNumbersReceived.Remove(reliableUnsequencedNumbersCompletelyReceived); } } else { reliableUnsequencedNumbersReceived.Add(command.reliableSequenceNumber); } if (command.commandType == 15) { incomingUnsequencedFragments.Add(command.reliableSequenceNumber, command); } else { incomingUnsequencedCommandsList.Enqueue(command); } return true; } internal void ApplySequenceNumberModifier(int mod) { incomingReliableSequenceNumber += mod; outgoingReliableSequenceNumber += mod; highestReceivedAck += mod; outgoingReliableUnsequencedNumber += mod; reliableUnsequencedNumbersCompletelyReceived += mod; SequencedReceived.reliableSequencedNumbersCompletelyReceived += mod; UnsequencedReceived.reliableSequencedNumbersCompletelyReceived += mod; } public void Received(NCommand inCommand) { int reliableSequenceNumber = inCommand.reliableSequenceNumber; ReceiveTrackingValues receiveTrackingValues = ((!inCommand.IsFlaggedUnsequenced) ? SequencedReceived : UnsequencedReceived); lock (receiveTrackingValues) { receiveTrackingValues.ReceivedReliableCommandSincePreviousAck2 = true; if (reliableSequenceNumber > receiveTrackingValues.reliableSequencedNumbersHighestReceived) { receiveTrackingValues.reliableSequencedNumbersHighestReceived = reliableSequenceNumber; } if (reliableSequenceNumber == receiveTrackingValues.reliableSequencedNumbersCompletelyReceived + 1) { receiveTrackingValues.reliableSequencedNumbersCompletelyReceived++; while (receiveTrackingValues.receivedReliableSequenceNumbers.Contains(receiveTrackingValues.reliableSequencedNumbersCompletelyReceived + 1)) { receiveTrackingValues.reliableSequencedNumbersCompletelyReceived++; receiveTrackingValues.receivedReliableSequenceNumbers.Remove(receiveTrackingValues.reliableSequencedNumbersCompletelyReceived); } } else if (reliableSequenceNumber > receiveTrackingValues.reliableSequencedNumbersCompletelyReceived) { receiveTrackingValues.receivedReliableSequenceNumbers.Add(reliableSequenceNumber); } } } public bool GetGapBlock(out int completeSequenceNumber, int[] blocks, bool isSequenced = true) { ReceiveTrackingValues receiveTrackingValues = (isSequenced ? SequencedReceived : UnsequencedReceived); lock (receiveTrackingValues) { completeSequenceNumber = receiveTrackingValues.reliableSequencedNumbersCompletelyReceived; bool receivedReliableCommandSincePreviousAck = receiveTrackingValues.ReceivedReliableCommandSincePreviousAck2; receiveTrackingValues.ReceivedReliableCommandSincePreviousAck2 = false; if (!receivedReliableCommandSincePreviousAck) { return false; } if (blocks == null) { blocks = new int[4]; } int num = completeSequenceNumber + 1; int num2 = 0; for (int i = 0; i < blocks.Length; i++) { blocks[i] = 0; int num3 = 0; int num4 = num + 32 * i; for (int j = 0; j < 32; j++) { int num5 = num4 + j; if (receiveTrackingValues.receivedReliableSequenceNumbers.Contains(num5)) { num3 |= 1 << j; num2++; if (num2 >= receiveTrackingValues.receivedReliableSequenceNumbers.Count || num5 > receiveTrackingValues.reliableSequencedNumbersHighestReceived) { break; } } } blocks[i] = num3; } return receivedReliableCommandSincePreviousAck; } } } internal class EnetPeer : PeerBase { internal struct GapBlock { internal byte Offset; internal int Block; } private const int CRC_LENGTH = 4; private const int EncryptedDataGramHeaderSize = 7; private const int EncryptedHeaderSize = 5; internal Pool<NCommand> nCommandPool = new Pool<NCommand>(() => new NCommand(), delegate(NCommand cmd) { cmd.Reset(); }, 16); private List<NCommand> sentReliableCommands = new List<NCommand>(); private int sendWindowUpdateRequiredBackValue; private StreamBuffer outgoingAcknowledgementsPool; internal const int UnsequencedWindowSize = 128; internal readonly int[] unsequencedWindow = new int[4]; internal int outgoingUnsequencedGroupNumber; internal int incomingUnsequencedGroupNumber; private byte udpCommandCount; private byte[] udpBuffer; private int udpBufferIndex; private byte[] bufferForEncryption; private int commandBufferSize = 100; internal int challenge; internal int serverSentTime; internal static readonly byte[] udpHeader0xF3 = new byte[2] { 243, 2 }; private int datagramEncryptedConnectionBackValue; private EnetChannel[] channelArray = new EnetChannel[0]; private const byte ControlChannelNumber = byte.MaxValue; protected internal const short PeerIdForConnect = -1; protected internal const short PeerIdForConnectTrace = -2; private Queue<int> commandsToRemove = new Queue<int>(); private ConcurrentQueue<NCommand> CommandQueue = new ConcurrentQueue<NCommand>(); private int fragmentLength; private int fragmentLengthDatagramEncrypt; private int fragmentLengthMtuValue; private readonly HashSet<byte> channelsToUpdateLowestSent = new HashSet<byte>(); private int[] lowestSentSequenceNumber; private int[] gapBlocks = new int[4]; private List<NCommand> toRemove = new List<NCommand>(32); internal override int QueuedIncomingCommandsCount { get { int num = 0; lock (channelArray) { for (int i = 0; i < channelArray.Length; i++) { EnetChannel enetChannel = channelArray[i]; num += enetChannel.incomingReliableCommandsList.Count; num += enetChannel.incomingUnreliableCommandsList.Count; } return num; } } } internal override int QueuedOutgoingCommandsCount { get { int num = 0; lock (channelArray) { for (int i = 0; i < channelArray.Length; i++) { EnetChannel enetChannel = channelArray[i]; lock (enetChannel) { num += enetChannel.outgoingReliableCommandsList.Count; num += enetChannel.outgoingUnreliableCommandsList.Count; } } return num; } } } private bool SendWindowUpdateRequired { get { return Interlocked.CompareExchange(ref sendWindowUpdateRequiredBackValue, 1, 1) == 1; } set { if (value) { Interlocked.CompareExchange(ref sendWindowUpdateRequiredBackValue, 1, 0); } else { Interlocked.CompareExchange(ref sendWindowUpdateRequiredBackValue, 0, 1); } } } private bool DatagramEncryptedConnection { get { return Interlocked.CompareExchange(ref datagramEncryptedConnectionBackValue, 1, 1) == 1; } set { if (value) { Interlocked.CompareExchange(ref datagramEncryptedConnectionBackValue, 1, 0); } else { Interlocked.CompareExchange(ref datagramEncryptedConnectionBackValue, 0, 1); } } } private bool useAck2 { get { if (photonPeer.UseAck2) { return base.serverFeatureAck2Available; } return false; } } internal EnetPeer() { messageHeader = udpHeader0xF3; } internal override bool IsTransportEncrypted() { return DatagramEncryptedConnection; } internal override void Reset() { base.Reset(); if (photonPeer.PayloadEncryptionSecret != null && usedTransportProtocol == ConnectionProtocol.Udp) { InitEncryption(photonPeer.PayloadEncryptionSecret); } if (photonPeer.Encryptor != null) { isEncryptionAvailable = true; } peerID = (short)(photonPeer.EnableServerTracing ? (-2) : (-1)); challenge = SupportClass.ThreadSafeRandom.Next(); if (udpBuffer == null || udpBuffer.Length != base.mtu) { udpBuffer = new byte[base.mtu]; } NCommand result = null; while (CommandQueue.TryDequeue(out result)) { } timeoutInt = 0; base.bestRoundtripTimeout = 0; outgoingUnsequencedGroupNumber = 0; incomingUnsequencedGroupNumber = 0; for (int i = 0; i < unsequencedWindow.Length; i++) { unsequencedWindow[i] = 0; } lock (channelArray) { EnetChannel[] array = channelArray; if (array.Length != base.ChannelCount + 1) { array = new EnetChannel[base.ChannelCount + 1]; } for (byte b = 0; b < base.ChannelCount; b++) { array[b] = new EnetChannel(b, commandBufferSize); } array[base.ChannelCount] = new EnetChannel(byte.MaxValue, commandBufferSize); channelArray = array; } lock (sentReliableCommands) { sentReliableCommands.Clear(); } outgoingAcknowledgementsPool = new StreamBuffer(); } internal void ApplyRandomizedSequenceNumbers() { lock (channelArray) { for (int i = 0; i < channelArray.Length; i++) { EnetChannel obj = channelArray[i]; int mod = photonPeer.RandomizedSequenceNumbers[i % photonPeer.RandomizedSequenceNumbers.Length]; obj.ApplySequenceNumberModifier(mod); } } } private EnetChannel GetChannel(byte channelNumber) { if (channelNumber != byte.MaxValue) { return channelArray[channelNumber]; } return channelArray[channelArray.Length - 1]; } internal override bool Connect(string ipport, string proxyServerAddress, string appID, object photonToken) { if (PhotonSocket.Connect()) { peerConnectionState = ConnectionStateValue.Connecting; NCommand nCommand = nCommandPool.Acquire(); nCommand.Initialize(this, 2, null, byte.MaxValue); QueueOutgoingReliableCommand(nCommand); return true; } return false; } internal override void Disconnect(bool queueStatusChangeCallback = true) { if (peerConnectionState == ConnectionStateValue.Disconnected || peerConnectionState == ConnectionStateValue.Disconnecting) { return; } if (sentReliableCommands != null) { lock (sentReliableCommands) { sentReliableCommands.Clear(); } } lock (channelArray) { EnetChannel[] array = channelArray; for (int i = 0; i < array.Length; i++) { array[i].clearAll(); } } bool isSimulationEnabled = base.NetworkSimulationSettings.IsSimulationEnabled; base.NetworkSimulationSettings.IsSimulationEnabled = false; NCommand nCommand = nCommandPool.Acquire(); nCommand.Initialize(this, 4, null, byte.MaxValue); peerConnectionState = ConnectionStateValue.Disconnecting; QueueOutgoingReliableCommand(nCommand); SendOutgoingCommands(); base.NetworkSimulationSettings.IsSimulationEnabled = isSimulationEnabled; if (PhotonSocket != null) { PhotonSocket.Disconnect(); } DatagramEncryptedConnection = false; peerConnectionState = ConnectionStateValue.Disconnected; if (queueStatusChangeCallback) { EnqueueStatusCallback(StatusCode.Disconnect); } else { base.Listener.OnStatusChanged(StatusCode.Disconnect); } } internal override void SimulateTimeoutDisconnect(bool queueStatusChangeCallback = true) { if (peerConnectionState == ConnectionStateValue.Disconnected || peerConnectionState == ConnectionStateValue.Disconnecting) { return; } if (sentReliableCommands != null) { lock (sentReliableCommands) { sentReliableCommands.Clear(); } } lock (channelArray) { EnetChannel[] array = channelArray; for (int i = 0; i < array.Length; i++) { array[i].clearAll(); } } peerConnectionState = ConnectionStateValue.Disconnecting; if (PhotonSocket != null) { PhotonSocket.Disconnect(); } DatagramEncryptedConnection = false; peerConnectionState = ConnectionStateValue.Disconnected; if (queueStatusChangeCallback) { EnqueueStatusCallback(StatusCode.TimeoutDisconnect); EnqueueStatusCallback(StatusCode.Disconnect); } else { base.Listener.OnStatusChanged(StatusCode.TimeoutDisconnect); base.Listener.OnStatusChanged(StatusCode.Disconnect); } } internal override void FetchServerTimestamp() { if (peerConnectionState != ConnectionStateValue.Connected || !ApplicationIsInitialized) { if ((int)base.LogLevel >= 3) { EnqueueDebugReturn(LogLevel.Info, $"FetchServerTimestamp() was skipped, as the client is not connected. Current ConnectionState: {peerConnectionState}"); } } else { CreateAndEnqueueCommand(12, null, byte.MaxValue); } } private void DispatchCommandQueue() { NCommand result = null; while (CommandQueue.TryDequeue(out result)) { ExecuteCommand(result); } } internal override bool DispatchIncomingCommands() { DispatchCommandQueue(); if (SendWindowUpdateRequired) { base.Stats.UdpReliableCommandsInFlight = sentReliableCommands.Count; } while (true) { MyAction myAction; lock (ActionQueue) { if (ActionQueue.Count <= 0) { break; } myAction = ActionQueue.Dequeue(); goto IL_005d; } IL_005d: myAction(); } NCommand val = null; lock (channelArray) { for (int i = 0; i < channelArray.Length; i++) { EnetChannel enetChannel = channelArray[i]; if (enetChannel.incomingUnsequencedCommandsList.Count > 0) { val = enetChannel.incomingUnsequencedCommandsList.Dequeue(); break; } if (enetChannel.incomingUnreliableCommandsList.Count > 0) { int num = int.MaxValue; foreach (int key2 in enetChannel.incomingUnreliableCommandsList.Keys) { NCommand nCommand = enetChannel.incomingUnreliableCommandsList[key2]; if (key2 < enetChannel.incomingUnreliableSequenceNumber || nCommand.reliableSequenceNumber < enetChannel.incomingReliableSequenceNumber) { photonPeer.CountDiscarded++; commandsToRemove.Enqueue(key2); } else if (key2 < num && nCommand.reliableSequenceNumber <= enetChannel.incomingReliableSequenceNumber) { num = key2; } } NonAllocDictionary<int, NCommand> incomingUnreliableCommandsList = enetChannel.incomingUnreliableCommandsList; while (commandsToRemove.Count > 0) { int key = commandsToRemove.Dequeue(); NCommand nCommand2 = incomingUnreliableCommandsList[key]; incomingUnreliableCommandsList.Remove(key); nCommand2.FreePayload(); nCommandPool.Release(nCommand2); } if (num < int.MaxValue) { photonPeer.DeltaUnreliableNumber = num - enetChannel.incomingUnreliableSequenceNumber; val = enetChannel.incomingUnreliableCommandsList[num]; } if (val != null) { enetChannel.incomingUnreliableCommandsList.Remove(val.unreliableSequenceNumber); enetChannel.incomingUnreliableSequenceNumber = val.unreliableSequenceNumber; break; } } if (val != null || enetChannel.incomingReliableCommandsList.Count <= 0) { continue; } lock (enetChannel) { enetChannel.incomingReliableCommandsList.TryGetValue(enetChannel.incomingReliableSequenceNumber + 1, out val); if (val == null) { continue; } if (val.commandType != 8) { enetChannel.incomingReliableSequenceNumber = val.reliableSequenceNumber; enetChannel.incomingReliableCommandsList.Remove(val.reliableSequenceNumber); } else if (val.fragmentsRemaining > 0) { val = null; } else { enetChannel.incomingReliableSequenceNumber = val.reliableSequenceNumber + val.fragmentCount - 1; enetChannel.incomingReliableCommandsList.Remove(val.reliableSequenceNumber); } break; } } } if (val != null && val.Payload != null) { ByteCountCurrentDispatch = val.Size; DeserializeMessageAndCallback(val.Payload); val.FreePayload(); nCommandPool.Release(val); return true; } return false; } private int GetFragmentLength() { if (fragmentLength == 0 || base.mtu != fragmentLengthMtuValue) { fragmentLengthMtuValue = base.mtu; fragmentLength = base.mtu - 12 - 36; fragmentLengthDatagramEncrypt = ((photonPeer.Encryptor != null) ? photonPeer.Encryptor.CalculateFragmentLength() : 0); } if (!DatagramEncryptedConnection) { return fragmentLength; } return fragmentLengthDatagramEncrypt; } private int CalculatePacketSize(int inSize) { if (DatagramEncryptedConnection) { return photonPeer.Encryptor.CalculateEncryptedSize(inSize + 7); } return inSize; } private int CalculateInitialOffset() { if (DatagramEncryptedConnection) { return 5; } int num = 12; if (photonPeer.CrcEnabled) { num += 4; } return num; } internal override bool SendAcksOnly() { return SendOutgoingCommands(sendAcksOnly: true); } internal override bool SendOutgoingCommands() { return SendOutgoingCommands(sendAcksOnly: false); } internal bool SendOutgoingCommands(bool sendAcksOnly) { if (peerConnectionState == ConnectionStateValue.Disconnected) { return false; } if (PhotonSocket == null || !PhotonSocket.Connected) { return false; } int num = 0; udpBufferIndex = CalculateInitialOffset(); udpCommandCount = 0; timeIntCurrentSend = base.timeInt; lock (outgoingAcknowledgementsPool) { if (outgoingAcknowledgementsPool.Length > 0 || useAck2) { num = SerializeAckToBuffer(); base.Stats.LastSendAckTimestamp = timeIntCurrentSend; } } DispatchCommandQueue(); if (timeIntCurrentSend > timeoutInt && sentReliableCommands.Count > 0) { int num2 = timeIntCurrentSend + 50; lock (sentReliableCommands) { int num3 = 0; for (int i = 0; i < sentReliableCommands.Count; i++) { NCommand nCommand = sentReliableCommands[i]; int num4 = nCommand.commandSentTime + nCommand.roundTripTimeout; if (timeIntCurrentSend > num4) { if (!sendAcksOnly && (nCommand.commandSentCount > photonPeer.MaxResends || timeIntCurrentSend > nCommand.timeoutTime)) { if ((int)base.LogLevel >= 3) { base.Listener.DebugReturn(LogLevel.Info, $"Timeout-disconnect! Command: {nCommand.ToString(full: true)} now: {timeIntCurrentSend} challenge: {Convert.ToString(challenge, 16)}"); } peerConnectionState = ConnectionStateValue.Zombie; EnqueueStatusCallback(StatusCode.TimeoutDisconnect); Disconnect(); nCommandPool.Release(nCommand); return false; } _ = nCommand.commandSentTime; _ = nCommand.roundTripTimeout; if (SerializeCommandToBuffer(nCommand, commandIsInSentQueue: true)) { if ((int)base.LogLevel >= 4) { base.Listener.DebugReturn(LogLevel.Debug, $"Resending: {nCommand.ToString(full: true)}. repeat after: {nCommand.roundTripTimeout} rtt/var: {roundTripTime}/{roundTripTimeVariance} last recv: {base.timeInt - photonPeer.Stats.LastReceiveTimestamp} now: {timeIntCurrentSend}"); } base.Stats.UdpReliableCommandsResent++; } else { num3++; num2 = timeoutInt; if (base.mtu - udpBufferIndex < 80) { break; } } } else if (num4 < num2) { num2 = num4; } } num += num3; timeoutInt = num2; } } if (!sendAcksOnly) { if (peerConnectionState == ConnectionStateValue.Connected && base.PingInterval > 0 && sentReliableCommands.Count == 0 && timeIntCurrentSend - timeLastAckReceive > base.PingInterval && CalculatePacketSize(udpBufferIndex + 12) <= base.mtu) { NCommand nCommand2 = nCommandPool.Acquire(); nCommand2.Initialize(this, 5, null, byte.MaxValue); QueueOutgoingReliableCommand(nCommand2); } base.Stats.LastSendOutgoingTimestamp = base.timeInt; if (SendWindowUpdateRequired) { UpdateSendWindow(); } lock (channelArray) { for (int j = 0; j < channelArray.Length; j++) { EnetChannel enetChannel = channelArray[j]; lock (enetChannel) { int channelSequenceLimit = enetChannel.lowestUnacknowledgedSequenceNumber + photonPeer.SendWindowSize; num += SerializeToBuffer(enetChannel.outgoingReliableCommandsList, channelSequenceLimit); num += SerializeToBuffer(enetChannel.outgoingUnreliableCommandsList, channelSequenceLimit); } } } } if (udpCommandCount <= 0) { return false; } SendData(udpBuffer, udpBufferIndex); base.Stats.UdpReliableCommandsInFlight = sentReliableCommands.Count; return num > 0; } private void UpdateSendWindow() { SendWindowUpdateRequired = false; if (photonPeer.SendWindowSize <= 0) { return; } if (sentReliableCommands.Count == 0) { lock (channelArray) { for (int i = 0; i < channelArray.Length; i++) { EnetChannel obj = channelArray[i]; obj.reliableCommandsInFlight = 0; obj.lowestUnacknowledgedSequenceNumber = obj.highestReceivedAck + 1; } return; } } channelsToUpdateLowestSent.Clear(); lock (channelArray) { for (int j = 0; j < channelArray.Length; j++) { EnetChannel enetChannel = channelArray[j]; if (enetChannel.ChannelNumber != byte.MaxValue && enetChannel.reliableCommandsInFlight > 0) { channelsToUpdateLowestSent.Add(enetChannel.ChannelNumber); } } } if (lowestSentSequenceNumber == null || lowestSentSequenceNumber.Length != channelArray.Length) { lowestSentSequenceNumber = new int[channelArray.Length]; } else { for (int k = 0; k < lowestSentSequenceNumber.Length; k++) { lowestSentSequenceNumber[k] = 0; } } lock (sentReliableCommands) { for (int l = 0; l < sentReliableCommands.Count; l++) { NCommand nCommand = sentReliableCommands[l]; if (nCommand.IsFlaggedUnsequenced || nCommand.commandChannelID == byte.MaxValue) { continue; } int commandChannelID = nCommand.commandChannelID; if (channelsToUpdateLowestSent.Contains(nCommand.commandChannelID)) { if (lowestSentSequenceNumber[commandChannelID] == 0) { lowestSentSequenceNumber[commandChannelID] = nCommand.reliableSequenceNumber; } channelsToUpdateLowestSent.Remove(nCommand.commandChannelID); if (channelsToUpdateLowestSent.Count == 0) { break; } } } } lock (channelArray) { for (int m = 0; m < channelArray.Length; m++) { EnetChannel enetChannel2 = channelArray[m]; enetChannel2.lowestUnacknowledgedSequenceNumber = ((lowestSentSequenceNumber[m] > 0) ? lowestSentSequenceNumber[m] : (enetChannel2.highestReceivedAck + 1)); } } } internal override bool EnqueuePhotonMessage(StreamBuffer opBytes, SendOptions sendParams) { byte commandType = 7; if (sendParams.DeliveryMode == DeliveryMode.UnreliableUnsequenced) { commandType = 11; } else if (sendParams.DeliveryMode == DeliveryMode.ReliableUnsequenced) { commandType = 14; } else if (sendParams.DeliveryMode == DeliveryMode.Reliable) { commandType = 6; } return CreateAndEnqueueCommand(commandType, opBytes, sendParams.Channel); } internal bool CreateAndEnqueueCommand(byte commandType, StreamBuffer payload, byte channelNumber) { EnetChannel channel = GetChannel(channelNumber); ByteCountLastOperation = 0; int num = GetFragmentLength(); if (num == 0) { num = 1000; EnqueueDebugReturn(LogLevel.Warning, "Value of currentFragmentSize should not be 0. Corrected to 1000."); } if (payload == null || payload.Length <= num) { NCommand nCommand = nCommandPool.Acquire(); nCommand.Initialize(this, commandType, payload, channel.ChannelNumber); if (nCommand.IsFlaggedReliable) { QueueOutgoingReliableCommand(nCommand); } else { QueueOutgoingUnreliableCommand(nCommand); } ByteCountLastOperation = nCommand.Size; } else { bool flag = commandType == 14 || commandType == 11; int fragmentCount = (payload.Length + num - 1) / num; int startSequenceNumber = (flag ? channel.outgoingReliableUnsequencedNumber : channel.outgoingReliableSequenceNumber) + 1; byte[] buffer = payload.GetBuffer(); int num2 = 0; for (int i = 0; i < payload.Length; i += num) { if (payload.Length - i < num) { num = payload.Length - i; } StreamBuffer streamBuffer = PeerBase.MessageBufferPool.Acquire(); streamBuffer.Write(buffer, i, num); NCommand nCommand2 = nCommandPool.Acquire(); nCommand2.Initialize(this, (byte)(flag ? 15 : 8), streamBuffer, channel.ChannelNumber); nCommand2.fragmentNumber = num2; nCommand2.startSequenceNumber = startSequenceNumber; nCommand2.fragmentCount = fragmentCount; nCommand2.totalLength = payload.Length; nCommand2.fragmentOffset = i; QueueOutgoingReliableCommand(nCommand2); ByteCountLastOperation += nCommand2.Size; base.Stats.UdpFragmentsOut++; num2++; } PeerBase.MessageBufferPool.Release(payload); } return true; } internal int SerializeAckToBuffer() { if (useAck2) { if (peerConnectionState != ConnectionStateValue.Connected) { return 0; } lock (channelArray) { for (int i = 0; i < channelArray.Length; i++) { EnetChannel enetChannel = channelArray[i]; int completeSequenceNumber = 0; bool isSequenced = true; if (enetChannel.GetGapBlock(out completeSequenceNumber, gapBlocks, isSequenced)) { for (int j = 0; j < gapBlocks.Length; j++) { int num = gapBlocks[j]; if (num != 0 || j <= 0) { NCommand.CreateAck2(udpBuffer, udpBufferIndex, enetChannel.ChannelNumber, completeSequenceNumber, num, (byte)j, serverSentTime, isSequenced); udpBufferIndex += 20; udpCommandCount++; } } } isSequenced = false; if (!enetChannel.GetGapBlock(out completeSequenceNumber, gapBlocks, isSequenced)) { continue; } for (int k = 0; k < gapBlocks.Length; k++) { int num2 = gapBlocks[k]; if (num2 != 0 || k <= 0) { NCommand.CreateAck2(udpBuffer, udpBufferIndex, enetChannel.ChannelNumber, completeSequenceNumber, num2, (byte)k, serverSentTime, isSequenced); udpBufferIndex += 20; udpCommandCount++; } } } } return 0; } outgoingAcknowledgementsPool.Seek(0L, SeekOrigin.Begin); while (outgoingAcknowledgementsPool.Position + 20 <= outgoingAcknowledgementsPool.Length && CalculatePacketSize(udpBufferIndex + 20) <= base.mtu) { Buffer.BlockCopy(outgoingAcknowledgementsPool.GetBufferAndAdvance(20, out var offset), offset, udpBuffer, udpBufferIndex, 20); udpBufferIndex += 20; udpCommandCount++; } outgoingAcknowledgementsPool.Compact(); outgoingAcknowledgementsPool.Position = outgoingAcknowledgementsPool.Length; return outgoingAcknowledgementsPool.Length / 20; } internal int SerializeToBuffer(List<NCommand> commandList, int channelSequenceLimit) { if (commandList.Count == 0) { return 0; } int num = 0; int num2 = 0; while (num < commandList.Count) { NCommand nCommand = commandList[num]; if (nCommand.IsFlaggedReliable && !nCommand.IsFlaggedUnsequenced && photonPeer.SendWindowSize > 0 && nCommand.commandChannelID != byte.MaxValue && ((nCommand.startSequenceNumber == 0 && nCommand.reliableSequenceNumber >= channelSequenceLimit) || (nCommand.startSequenceNumber > 0 && nCommand.startSequenceNumber >= channelSequenceLimit))) { num++; num2++; continue; } if (!SerializeCommandToBuffer(nCommand)) { break; } commandList.RemoveAt(num); } throttledBySendWindow += num2; return commandList.Count - num2; } private bool SerializeCommandToBuffer(NCommand command, bool commandIsInSentQueue = false) { if (command == null) { return true; } if (CalculatePacketSize(udpBufferIndex + command.Size) > base.mtu) { return false; } command.SerializeHeader(udpBuffer, ref udpBufferIndex); if (command.SizeOfPayload > 0) { Buffer.BlockCopy(command.Serialize(), 0, udpBuffer, udpBufferIndex, command.SizeOfPayload); udpBufferIndex += command.SizeOfPayload; } udpCommandCount++; if (command.IsFlaggedReliable) { if (command.commandSentCount == 0) { base.Stats.UdpReliableCommandsSent++; } QueueSentCommand(command, commandIsInSentQueue); } else { base.Stats.UdpUnreliableCommandsSent++; command.FreePayload(); nCommandPool.Release(command); } return true; } internal void SendData(byte[] data, int length) { try { if (DatagramEncryptedConnection) { SendDataEncrypted(data, length); return; } int targetOffset = 0; MessageProtocol.Serialize(peerID, data, ref targetOffset); data[2] = (byte)(photonPeer.CrcEnabled ? 204 : 0); data[3] = udpCommandCount; targetOffset = 4; MessageProtocol.Serialize(timeIntCurrentSend, data, ref targetOffset); MessageProtocol.Serialize(challenge, data, ref targetOffset); if (photonPeer.CrcEnabled) { MessageProtocol.Serialize(0, data, ref targetOffset); uint value = SupportClass.CalculateCrc(data, length); targetOffset -= 4; MessageProtocol.Serialize((int)value, data, ref targetOffset); } SendToSocket(data, length); } catch (Exception ex) { if ((int)base.LogLevel >= 1) { base.Listener.DebugReturn(LogLevel.Error, "SendData() caught exception: " + ex.ToString()); } SupportClass.WriteStackTrace(ex); } } private void SendToSocket(byte[] data, int length) { ITrafficRecorder trafficRecorder = photonPeer.TrafficRecorder; if (trafficRecorder != null && trafficRecorder.Enabled) { trafficRecorder.Record(data, length, incoming: false, peerID, PhotonSocket); } base.Stats.BytesOut = base.Stats.BytesOut + length; base.Stats.PackagesOut++; PhotonSocket.Send(data, length); } private void SendDataEncrypted(byte[] data, int length) { if (bufferForEncryption == null || bufferForEncryption.Length != base.mtu) { bufferForEncryption = new byte[base.mtu]; } byte[] array = bufferForEncryption; int targetOffset = 0; MessageProtocol.Serialize(peerID, array, ref targetOffset); array[2] = 1; targetOffset++; MessageProtocol.Serialize(challenge, array, ref targetOffset); data[0] = udpCommandCount; int targetOffset2 = 1; MessageProtocol.Serialize(timeIntCurrentSend, data, ref targetOffset2); int outSize = array.Length - targetOffset; photonPeer.Encryptor.Encrypt2(data, length, array, array, targetOffset, ref outSize); SendToSocket(array, outSize + targetOffset); } internal void QueueSentCommand(NCommand command, bool commandIsAlreadyInSentQueue = false) { command.commandSentTime = timeIntCurrentSend; if (command.roundTripTimeout == 0) { command.roundTripTimeout = Math.Min(roundTripTime + 4 * roundTripTimeVariance, photonPeer.InitialResendTimeMax); base.bestRoundtripTimeout = command.roundTripTimeout; command.timeoutTime = timeIntCurrentSend + base.DisconnectTimeout; } else if (command.commandSentCount >= photonPeer.QuickResendAttempts || sentReliableCommands.Count >= photonPeer.SendWindowSize) { int val = command.roundTripTimeout * 2; command.roundTripTimeout = Math.Min(val, photonPeer.InitialResendTimeMax * 2); } command.commandSentCount++; int num = command.commandSentTime + command.roundTripTimeout; if (num < timeoutInt) { timeoutInt = num; } if (!commandIsAlreadyInSentQueue) { GetChannel(command.commandChannelID).reliableCommandsInFlight++; lock (sentReliableCommands) { sentReliableCommands.Add(command); } } } internal void QueueOutgoingReliableCommand(NCommand command) { EnetChannel channel = GetChannel(command.commandChannelID); lock (channel) { if (command.reliableSequenceNumber == 0) { if (command.IsFlaggedUnsequenced) { command.reliableSequenceNumber = ++channel.outgoingReliableUnsequencedNumber; } else { command.reliableSequenceNumber = ++channel.outgoingReliableSequenceNumber; } } channel.outgoingReliableCommandsList.Add(command); } } internal void QueueOutgoingUnreliableCommand(NCommand command) { EnetChannel channel = GetChannel(command.commandChannelID); lock (channel) { if (command.IsFlaggedUnsequenced) { command.reliableSequenceNumber = 0; command.unsequencedGroupNumber = ++outgoingUnsequencedGroupNumber; } else { command.reliableSequenceNumber = channel.outgoingReliableSequenceNumber; command.unreliableSequenceNumber = ++channel.outgoingUnreliableSequenceNumber; } if (!photonPeer.SendInCreationOrder) { channel.outgoingUnreliableCommandsList.Add(command); } else { channel.outgoingReliableCommandsList.Add(command); } } } internal void QueueOutgoingAcknowledgement(NCommand readCommand, int sendTime) { if (useAck2) { lock (channelArray) { EnetChannel channel = GetChannel(readCommand.commandChannelID); if (channel != null) { lock (channel) { channel.Received(readCommand); return; } } return; } } lock (outgoingAcknowledgementsPool) { NCommand.CreateAck(outgoingAcknowledgementsPool.GetBufferAndAdvance(20, out var offset), offset, readCommand, sendTime); } } internal override void ReceiveIncomingCommands(byte[] inBuff, int inDataLength) { int num = base.timeInt; photonPeer.Stats.LastReceiveTimestamp = num; base.Stats.BytesIn += inDataLength; base.Stats.PackagesIn++; if (peerConnectionState == ConnectionStateValue.Disconnected) { return; } try { int offset = 0; MessageProtocol.Deserialize(out short _, inBuff, ref offset); byte b = inBuff[offset++]; int value2; byte b2; if (b == 1) { if (photonPeer.Encryptor == null) { return; } MessageProtocol.Deserialize(out value2, inBuff, ref offset); if (value2 != challenge) { packetLossByChallenge++; return; } inBuff = photonPeer.Encryptor.Decrypt2(inBuff, offset, inDataLength - offset, inBuff, out var _); if (!DatagramEncryptedConnection) { DatagramEncryptedConnection = true; fragmentLength = 0; } offset = 0; b2 = inBuff[offset++]; MessageProtocol.Deserialize(out serverSentTime, inBuff, ref offset); } else { if (DatagramEncryptedConnection) { if ((int)base.LogLevel >= 2) { EnqueueDebugReturn(LogLevel.Warning, "Ignored received package. Connection requires Datagram Encryption but received unencrypted datagram."); } return; } b2 = inBuff[offset++]; MessageProtocol.Deserialize(out serverSentTime, inBuff, ref offset); MessageProtocol.Deserialize(out value2, inBuff, ref offset); if (value2 != challenge) { packetLossByChallenge++; if (peerConnectionState != 0 && (int)base.LogLevel >= 4) { EnqueueDebugReturn(LogLevel.Debug, $"Ignored received package. Wrong challenge. Received: {value2} local: {challenge}"); } return; } if (b == 204) { MessageProtocol.Deserialize(out int value3, inBuff, ref offset); offset -= 4; MessageProtocol.Serialize(0, inBuff, ref offset); uint num2 = SupportClass.CalculateCrc(inBuff, inDataLength); if (value3 != (int)num2) { packetLossByCrc++; if (peerConnectionState != 0 && (int)base.LogLevel >= 4) { EnqueueDebugReturn(LogLevel.Debug, $"Ignored received package. Wrong CRC. Incoming: {(uint)value3:X} local: {num2:X}"); } return; } } } if (b2 <= 0) { if ((int)base.LogLevel >= 4) { EnqueueDebugReturn(LogLevel.Debug, $"Ignored received package. No commands in package: {b2}."); } return; } for (int i = 0; i < b2; i++) { NCommand nCommand = nCommandPool.Acquire(); nCommand.Initialize(this, inBuff, ref offset, num); CommandQueue.Enqueue(nCommand); if (nCommand.IsFlaggedReliable) { QueueOutgoingAcknowledgement(nCommand, serverSentTime); } } } catch (Exception ex) { if ((int)base.LogLevel >= 1) { EnqueueDebugReturn(LogLevel.Error, $"ReceiveIncomingCommands caught exception: {ex}"); } SupportClass.WriteStackTrace(ex); } } internal void ExecuteCommand(NCommand command) { switch (command.commandType) { case 2: case 5: nCommandPool.Release(command); break; case 4: { StatusCode statusValue = StatusCode.DisconnectByServerReasonUnknown; if (command.reservedByte == 1) { statusValue = StatusCode.DisconnectByServerLogic; } else if (command.reservedByte == 2) { statusValue = StatusCode.DisconnectByServerTimeout; } else if (command.reservedByte == 3) { statusValue = StatusCode.DisconnectByServerUserLimit; } if ((int)base.LogLevel >= 4) { base.Listener.DebugReturn(LogLevel.Debug, $"Disconnect received. Server: {base.ServerAddress} PeerId: {(ushort)peerID} rtt(var): {base.rttVarString} Reason byte: {command.reservedByte} peerConnectionState: {peerConnectionState}"); } if (peerConnectionState != 0 && peerConnectionState != ConnectionStateValue.Disconnecting) { EnqueueStatusCallback(statusValue); Disconnect(); } nCommandPool.Release(command); break; } case 1: case 16: { timeLastAckReceive = command.TimeOfReceive; SendWindowUpdateRequired = true; lastRoundTripTime = command.TimeOfReceive - command.ackReceivedSentTime; if (lastRoundTripTime < 0 || lastRoundTripTime > 10000) { if ((int)base.LogLevel >= 3) { EnqueueDebugReturn(LogLevel.Info, $"Measured lastRoundtripTime is suspicious: {lastRoundTripTime} for command: {command}"); } lastRoundTripTime = roundTripTime * 4; } NCommand nCommand = RemoveSentReliableCommand(command.ackReceivedReliableSequenceNumber, command.commandChannelID, command.commandType == 16); nCommandPool.Release(command); if (nCommand == null) { break; } nCommand.FreePayload(); EnetChannel channel2 = GetChannel(nCommand.commandChannelID); lock (channel2) { if (nCommand.reliableSequenceNumber > channel2.highestReceivedAck) { channel2.highestReceivedAck = nCommand.reliableSequenceNumber; } channel2.reliableCommandsInFlight--; } if (nCommand.commandType == 12) { if (lastRoundTripTime <= roundTripTime) { serverTimeOffset = serverSentTime + (lastRoundTripTime >> 1) - base.timeInt; serverTimeOffsetIsAvailable = true; } else { FetchServerTimestamp(); } } else { UpdateRoundTripTimeAndVariance(lastRoundTripTime); if (nCommand.commandType == 4 && peerConnectionState == ConnectionStateValue.Disconnecting) { if ((int)base.LogLevel >= 4) { EnqueueDebugReturn(LogLevel.Debug, "Server ACKd this client's Disconnect command."); } EnqueueActionForDispatch(delegate { PhotonSocket.Disconnect(); }); } else if (nCommand.commandType == 2 && lastRoundTripTime >= 0) { if (lastRoundTripTime <= 15) { roundTripTime = 15; roundTripTimeVariance = 5; } else { roundTripTime = lastRoundTripTime; base.bestRoundtripTimeout = (int)((float)roundTripTime * 1.5f); } } } nCommandPool.Release(nCommand); break; } case 17: case 18: { timeLastAckReceive = command.TimeOfReceive; SendWindowUpdateRequired = true; lastRoundTripTime = command.TimeOfReceive - command.ackReceivedSentTime; if (lastRoundTripTime < 0 || lastRoundTripTime > 10000) { if ((int)base.LogLevel >= 3) { EnqueueDebugReturn(LogLevel.Info, $"Measured lastRoundtripTime is suspicious: {lastRoundTripTime} for command: {command}"); } lastRoundTripTime = roundTripTime * 4; } UpdateRoundTripTimeAndVariance(lastRoundTripTime); int ackReceivedReliableSequenceNumber = command.ackReceivedReliableSequenceNumber; uint reliableSequenceNumber = (uint)command.reliableSequenceNumber; byte commandFlags = command.commandFlags; byte commandChannelID = command.commandChannelID; bool flag3 = command.commandType == 18; EnetChannel channel3 = GetChannel(command.commandChannelID); _ = channel3.highestReceivedAck; int num2 = ackReceivedReliableSequenceNumber + 1 + commandFlags * 32; int num3 = num2; lock (sentReliableCommands) { toRemove.Clear(); foreach (NCommand sentReliableCommand in sentReliableCommands) { if (sentReliableCommand.commandChannelID != commandChannelID || sentReliableCommand.IsFlaggedUnsequenced != flag3) { continue; } if (sentReliableCommand.reliableSequenceNumber <= ackReceivedReliableSequenceNumber) { toRemove.Add(sentReliableCommand); continue; } int num4 = sentReliableCommand.reliableSequenceNumber - num2; if (num4 < 0 || num4 >= 32) { continue; } if (((reliableSequenceNumber >> num4) & 1) == 1) { toRemove.Add(sentReliableCommand); } else if (sentReliableCommand.reliableSequenceNumber < num3) { if (sentReliableCommand.commandSentCount <= 3) { sentReliableCommand.roundTripTimeout = base.bestRoundtripTimeout; } timeoutInt = 0; } } foreach (NCommand item in toRemove) { sentReliableCommands.Remove(item); if (item.commandType != 2 && item.commandType != 4) { _ = item.commandType; _ = 12; } item.FreePayload(); nCommandPool.Release(item); } if (ackReceivedReliableSequenceNumber > channel3.highestReceivedAck) { channel3.highestReceivedAck = ackReceivedReliableSequenceNumber; } break; } } case 6: case 7: case 11: case 14: if (peerConnectionState != ConnectionStateValue.Connected || !QueueIncomingCommand(command)) { nCommandPool.Release(command); } break; case 8: case 15: { if (peerConnectionState != ConnectionStateValue.Connected) { nCommandPool.Release(command); break; } if (command.fragmentNumber > command.fragmentCount || command.fragmentOffset >= command.totalLength || command.fragmentOffset + command.Payload.Length > command.totalLength) { if ((int)base.LogLevel >= 1) { base.Listener.DebugReturn(LogLevel.Error, $"Received fragment has bad size: {command}"); } nCommandPool.Release(command); break; } bool flag = command.commandType == 8; EnetChannel channel = GetChannel(command.commandChannelID); NCommand fragment = null; lock (channel) { bool flag2 = channel.TryGetFragment(command.startSequenceNumber, flag, out fragment); if (flag2 && fragment.fragmentsRemaining <= 0) { nCommandPool.Release(command); break; } if (!QueueIncomingCommand(command)) { nCommandPool.Release(command); break; } base.Stats.UdpFragmentsIn++; if (command.reliableSequenceNumber != command.startSequenceNumber) { if (flag2) { fragment.fragmentsRemaining--; } } else { fragment = command; fragment.fragmentsRemaining--; NCommand fragment2 = null; int num = command.startSequenceNumber + 1; while (fragment.fragmentsRemaining > 0 && num < fragment.startSequenceNumber + fragment.fragmentCount) { if (channel.TryGetFragment(num++, flag, out fragment2)) { fragment.fragmentsRemaining--; } } } if (fragment == null || fragment.fragmentsRemaining > 0) { break; } StreamBuffer streamBuffer = PeerBase.MessageBufferPool.Acquire(); streamBuffer.Position = 0; streamBuffer.SetCapacityMinimum(fragment.totalLength); byte[] buffer = streamBuffer.GetBuffer(); for (int i = fragment.startSequenceNumber; i < fragment.startSequenceNumber + fragment.fragmentCount; i++) { if (channel.TryGetFragment(i, flag, out var fragment3)) { Buffer.BlockCopy(fragment3.Payload.GetBuffer(), 0, buffer, fragment3.fragmentOffset, fragment3.Payload.Length); fragment3.FreePayload(); channel.RemoveFragment(fragment3.reliableSequenceNumber, flag); if (fragment3.fragmentNumber > 0) { nCommandPool.Release(fragment3); } continue; } throw new Exception("startCommand.fragmentsRemaining was 0 but not all fragments were found to be combined!"); } streamBuffer.SetLength(fragment.totalLength); fragment.FreePayload(); fragment.Payload = streamBuffer; fragment.Size = 12 * fragment.fragmentCount + fragment.totalLength; if (flag) { channel.incomingReliableCommandsList.Add(fragment.startSequenceNumber, fragment); } else { channel.incomingUnsequencedCommandsList.Enqueue(fragment); } break; } } case 3: if (peerConnectionState == ConnectionStateValue.Connecting) { if (base.serverFeatureSyncReliableQueue && base.ServerMaxQueueableReliableCommands != 0) { photonPeer.SendWindowSize = base.ServerMaxQueueableReliableCommands; } byte[] buf = WriteInitRequest(); CreateAndEnqueueCommand(6, new StreamBuffer(buf), 0); if (photonPeer.RandomizeSequenceNumbers) { ApplyRandomizedSequenceNumbers(); } peerConnectionState = ConnectionStateValue.Connected; } nCommandPool.Release(command); break; case 9: case 10: case 12: case 13: case 19: break; } } internal bool QueueIncomingCommand(NCommand command) { EnetChannel channel = GetChannel(command.commandChannelID); if (channel == null) { if ((int)base.LogLevel >= 1) { base.Listener.DebugReturn(LogLevel.Error, $"Received command for non-existing channel: {command.commandChannelID}"); } return false; } if (command.IsFlaggedUnsequenced) { if (command.IsFlaggedReliable) { lock (channel) { return channel.QueueIncomingReliableUnsequenced(command); } } int unsequencedGroupNumber = command.unsequencedGroupNumber; int num = command.unsequencedGroupNumber % 128; if (unsequencedGroupNumber >= incomingUnsequencedGroupNumber + 128) { incomingUnsequencedGroupNumber = unsequencedGroupNumber - num; for (int i = 0; i < unsequencedWindow.Length; i++) { unsequencedWindow[i] = 0; } } else if (unsequencedGroupNumber < incomingUnsequencedGroupNumber || (unsequencedWindow[num / 32] & (1 << num % 32)) != 0) { return false; } unsequencedWindow[num / 32] |= 1 << num % 32; channel.incomingUnsequencedCommandsList.Enqueue(command); return true; } if (command.IsFlaggedReliable) { if (command.reliableSequenceNumber <= channel.incomingReliableSequenceNumber) { if ((int)base.LogLevel >= 4) { base.Listener.DebugReturn(LogLevel.Debug, $"Command {command} outdated. Sequence number is less than dispatched incomingReliableSequenceNumber: {channel.incomingReliableSequenceNumber}"); } return false; } bool flag = false; lock (channel) { flag = channel.AddSequencedIfNew(command); } if (!flag) { if ((int)base.LogLevel >= 4) { base.Listener.DebugReturn(LogLevel.Debug, $"Command was received before! New: {command} inReliableSeq#: {channel.incomingReliableSequenceNumber}"); } return false; } } else { if (command.reliableSequenceNumber < channel.incomingReliableSequenceNumber) { photonPeer.CountDiscarded++; if ((int)base.LogLevel >= 4) { base.Listener.DebugReturn(LogLevel.Debug, "Incoming reliable-seq# < Dispatched-rel-seq#. not saved."); } return false; } if (command.unreliableSequenceNumber <= channel.incomingUnreliableSequenceNumber) { photonPeer.CountDiscarded++; if ((int)base.LogLevel >= 4) { base.Listener.DebugReturn(LogLevel.Debug, "Incoming unreliable-seq# < Dispatched-unrel-seq#. not saved."); } return false; } bool flag2 = false; lock (channel) { flag2 = channel.AddSequencedIfNew(command); } if (!flag2) { if ((int)base.LogLevel >= 4) { base.Listener.DebugReturn(LogLevel.Debug, $"Command was received before! New: {command} inReliableSeq#: {channel.incomingReliableSequenceNumber}"); } return false; } } return true; } internal NCommand RemoveSentReliableCommand(int ackReceivedReliableSequenceNumber, int ackReceivedChannel, bool isUnsequenced) { NCommand nCommand = null; lock (sentReliableCommands) { foreach (NCommand sentReliableCommand in sentReliableCommands) { if (sentReliableCommand != null && sentReliableCommand.reliableSequenceNumber == ackReceivedReliableSequenceNumber && sentReliableCommand.commandChannelID == ackReceivedChannel && sentReliableCommand.IsFlaggedUnsequenced == isUnsequenced) { nCommand = sentReliableCommand; break; } } if (nCommand != null) { sentReliableCommands.Remove(nCommand); } else if ((int)base.LogLevel >= 4 && peerConnectionState != ConnectionStateValue.Connected && peerConnectionState != ConnectionStateValue.Disconnecting) { EnqueueDebugReturn(LogLevel.Debug, $"No sent command for ACK (Ch: {ackReceivedReliableSequenceNumber} Sq#: {ackReceivedChannel}). PeerState: {peerConnectionState}."); } } return nCommand; } internal string CommandListToString(NCommand[] list) { if ((int)base.LogLevel < 4) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < list.Length; i++) { stringBuilder.Append(i + "="); stringBuilder.Append(list[i]); stringBuilder.Append(" # "); } return stringBuilder.ToString(); } } public enum PeerStateValue : byte { Disconnected = 0, Connecting = 1, InitializingApplication = 10, Connected = 3, Disconnecting = 4 } public enum ConnectionProtocol : byte { Udp = 0, Tcp = 1, WebSocket = 4, WebSocketSecure = 5 } public enum LogLevel : byte { Off = 0, [Obsolete] OFF = 0, Error = 1, [Obsolete] ERROR = 1, Warning = 2, [Obsolete] WARNING = 2, Info = 3, [Obsolete] INFO = 3, Debug = 4, [Obsolete] ALL = 4 } public enum TargetFrameworks { Unknown, Net35, NetStandard20, Metro, NetStandard21 } public enum StatusCode { Connect = 1024, Disconnect = 1025, Exception = 1026, ExceptionOnConnect = 1023, ServerAddressInvalid = 1050, DnsExceptionOnConnect = 1051, SecurityExceptionOnConnect = 1022, SendError = 1030, ExceptionOnReceive = 1039, TimeoutDisconnect = 1040, DisconnectByServerTimeout = 1041, DisconnectByServerUserLimit = 1042, DisconnectByServerLogic = 1043, DisconnectByServerReasonUnknown = 1044, EncryptionEstablished = 1048, EncryptionFailedToEstablish = 1049 } public interface IPhotonPeerListener { void DebugReturn(LogLevel level, string message); void OnOperationResponse(OperationResponse operationResponse); void OnStatusChanged(StatusCode statusCode); void OnEvent(EventData eventData); void OnMessage(bool isRawMessage, object message); void OnDisconnectMessage(DisconnectMessage dm); } public interface ITrafficRecorder { bool Enabled { get; set; } void Record(byte[] inBuffer, int length, bool incoming, short peerId, PhotonSocket connection); } internal class NCommand : IComparable<NCommand> { internal const byte Ack2FeatureFlag = 0; internal const byte ReliableSendWindowFeatureFlag = 2; internal const byte FeatureFlagsLow = 2; internal const byte FV_UNRELIABLE = 0; internal const byte FV_RELIABLE = 1; internal const byte FV_UNRELIABLE_UNSEQUENCED = 2; internal const byte FV_RELIBALE_UNSEQUENCED = 3; internal const byte CT_NONE = 0; internal const byte CT_ACK = 1; internal const byte CT_CONNECT = 2; internal const byte CT_VERIFYCONNECT = 3; internal const byte CT_DISCONNECT = 4; internal const byte CT_PING = 5; internal const byte CT_SENDRELIABLE = 6; internal const byte CT_SENDUNRELIABLE = 7; internal const byte CT_SENDFRAGMENT = 8; internal const byte CT_SENDUNSEQUENCED = 11; internal const byte CT_EG_SERVERTIME = 12; internal const byte CT_EG_SEND_UNRELIABLE_PROCESSED = 13; internal const byte CT_EG_SEND_RELIABLE_UNSEQUENCED = 14; internal const byte CT_EG_SEND_FRAGMENT_UNSEQUENCED = 15; internal const byte CT_EG_ACK_UNSEQUENCED = 16; internal const byte CT_EG_ACK_2 = 17; internal const byte CT_EG_ACK_2_UNSEQUENCED = 18; internal const byte CT_EG_ACK_2_NULL = 19; internal const int HEADER_UDP_PACK_LENGTH = 12; internal const int CmdSizeMinimum = 12; internal const int CmdSizeAck = 20; internal const int CmdSizeConnect = 44; internal const int CmdSizeVerifyConnect = 44; internal const int CmdSizeDisconnect = 12; internal const int CmdSizePing = 12; internal const int CmdSizeReliableHeader = 12; internal const int CmdSizeUnreliableHeader = 16; internal const int CmdSizeUnsequensedHeader = 16; internal const int CmdSizeFragmentHeader = 32; internal const int CmdSizeMaxHeader = 36; internal byte commandFlags; internal byte commandType; internal byte commandChannelID; internal int reliableSequenceNumber; internal int unreliableSequenceNumber; internal int unsequencedGroupNumber; internal byte reservedByte = 4; internal int startSequenceNumber; internal int fragmentCount; internal int fragmentNumber; internal int totalLength; internal int fragmentOffset; internal int fragmentsRemaining; internal int commandSentTime; internal byte commandSentCount; internal int roundTripTimeout; internal int timeoutTime; internal int ackReceivedReliableSequenceNumber; internal int ackReceivedSentTime; internal int TimeOfReceive; internal int Size; internal StreamBuffer Payload; protected internal int SizeOfPayload { get { if (Payload == null) { return 0; } return Payload.Length; } } protected internal bool IsFlaggedUnsequenced => (commandFlags & 2) > 0; protected internal bool IsFlaggedReliable { get { if ((commandFlags & 1) > 0) { return commandType < 17; } return false; } } internal static void CreateAck(byte[] buffer, int offset, NCommand commandToAck, int sentTime) { buffer[offset++] = (byte)((!commandToAck.IsFlaggedUnsequenced) ? 1 : 16); buffer[offset++] = commandToAck.commandChannelID; buffer[offset++] = 0; buffer[offset++] = 4; MessageProtocol.Serialize(20, buffer, ref offset); MessageProtocol.Serialize(0, buffer, ref offset); MessageProtocol.Serialize(commandToAck.reliableSequenceNumber, buffer, ref offset); MessageProtocol.Serialize(sentTime, buffer, ref offset); } internal static void CreateAck2(byte[] buffer, int offset, byte channelId, int completeSequence, int gapBlock, byte gapBlockOffset, int sentTime, bool isSequenced) { buffer[offset++] = (byte)(isSequenced ? 17 : 18); buffer[offset++] = channelId; buffer[offset++] = gapBlockOffset; buffer[offset++] = 4; MessageProtocol.Serialize(20, buffer, ref offset); MessageProtocol.Serialize(gapBlock, buffer, ref offset); MessageProtocol.Serialize(completeSequence, buffer, ref offset); MessageProtocol.Serialize(sentTime, buffer, ref offset); } internal void Initialize(EnetPeer peer, byte commandType, StreamBuffer payload, byte channel) { this.commandType = commandType; commandFlags = 1; commandChannelID = channel; Payload = payload; Size = 12; switch (this.commandType) { case 2: { Size = 44; byte[] array = new byte[32]; array[0] = 0; array[1] = 0; int targetOffset = 2; MessageProtocol.Serialize((short)peer.mtu, array, ref targetOffset); array[4] = 0; array[5] = 2; array[6] = 128; array[7] = 0; array[8] = 0; array[9] = 0; array[10] = 0; array[11] = peer.ChannelCount; array[12] = byte.MaxValue; array[13] = byte.MaxValue; array[22] = 19; array[23] = 136; array[27] = 2; array[31] = 2; Payload = new StreamBuffer(array); break; } case 4: Size = 12; if (peer.peerConnectionState != ConnectionStateValue.Connected) { commandFlags = 2; reservedByte = (byte)((peer.peerConnectionState == ConnectionStateValue.Zombie) ? 2 : 4); } break; case 6: Size = 12 + payload.Length; break; case 14: Size = 12 + payload.Length; commandFlags = 3; break; case 7: Size = 16 + payload.Length; commandFlags = 0; break; case 11: Size = 16 + payload.Length; commandFlags = 2; break; case 8: Size = 32 + payload.Length; break; case 15: Size = 32 + payload.Length; commandFlags = 3; break; case 3: case 5: case 9: case 10: case 12: case 13: break; } } internal void Initialize(EnetPeer peer, byte[] inBuff, ref int readingOffset, int timeOfReceive) { commandType = inBuff[readingOffset++]; commandChannelID = inBuff[readingOffset++]; commandFlags = inBuff[readingOffset++]; reservedByte = inBuff[readingOffset++]; MessageProtocol.Deserialize(out Size, inBuff, ref readingOffset); MessageProtocol.Deserialize(out reliableSequenceNumber, inBuff, ref readingOffset); int num = 0; TimeOfReceive = timeOfReceive; switch (commandType) { case 1: case 16: case 17: case 18: MessageProtocol.Deserialize(out ackReceivedReliableSequenceNumber, inBuff, ref readingOffset); MessageProtocol.Deserialize(out ackReceivedSentTime, inBuff, ref readingOffset); break; case 6: case 14: num = Size - 12; break; case 7: MessageProtocol.Deserialize(out unreliableSequenceNumber, inBuff, ref readingOffset); num = Size - 16; break; case 11: MessageProtocol.Deserialize(out unsequencedGroupNumber, inBuff, ref readingOffset); num = Size - 16; break; case 8: case 15: MessageProtocol.Deserialize(out startSequenceNumber, inBuff, ref readingOffset); MessageProtocol.Deserialize(out fragmentCount, inBuff, ref readingOffset); MessageProtocol.Deserialize(out fragmentNumber, inBuff, ref readingOffset); MessageProtocol.Deserialize(out totalLength, inBuff, ref readingOffset); MessageProtocol.Deserialize(out fragmentOffset, inBuff, ref readingOffset); num = Size - 32; fragmentsRemaining = fragmentCount; break; case 3: { MessageProtocol.Deserialize(out short value, inBuff, ref readingOffset); MessageProtocol.Deserialize(out short _, inBuff, ref readingOffset); MessageProtocol.Deserialize(out short value3, inBuff, ref readingOffset); MessageProtocol.Deserialize(out short _, inBuff, ref readingOffset); readingOffset += 3; _ = inBuff[readingOffset++]; MessageProtocol.Deserialize(out short value5, inBuff, ref readingOffset); readingOffset += 20; if (peer.peerID == -1 || peer.peerID == -2) { peer.peerID = value; } peer.ServerFeatureFlags = (ushort)value3; if (peer.serverFeatureFlagsAvailable && peer.serverFeatureSyncReliableQueue) { peer.ServerMaxQueueableReliableCommands = (ushort)value5; } break; } default: readingOffset += Size - 12; break; } if (num != 0) { StreamBuffer streamBuffer = PeerBase.MessageBufferPool.Acquire(); streamBuffer.Write(inBuff, readingOffset, num); Payload = streamBuffer; Payload.Position = 0; readingOffset += num; } } public void Reset() { commandFlags = 0; commandType = 0; commandChannelID = 0; reliableSequenceNumber = 0; unreliableSequenceNumber = 0; unsequencedGroupNumber = 0; reservedByte = 4; startSequenceNumber = 0; fragmentCount = 0; fragmentNumber = 0; totalLength = 0; fragmentOffset = 0; fragmentsRemaining = 0; commandSentTime = 0; commandSentCount = 0; roundTripTimeout = 0; timeoutTime = 0; ackReceivedReliableSequenceNumber = 0; ackReceivedSentTime = 0; Size = 0; } internal void SerializeHeader(byte[] buffer, ref int bufferIndex) { buffer[bufferIndex++] = commandType; buffer[bufferIndex++] = commandChannelID; buffer[bufferIndex++] = commandFlags; buffer[bufferIndex++] = reservedByte; MessageProtocol.Serialize(Size, buffer, ref bufferIndex); MessageProtocol.Serialize(reliableSequenceNumber, buffer, ref bufferIndex); if (commandType == 7) { MessageProtocol.Serialize(unreliableSequenceNumber, buffer, ref bufferIndex); } else if (commandType == 11) { MessageProtocol.Serialize(unsequencedGroupNumber, buffer, ref bufferIndex); } else if (commandType == 8 || commandType == 15) { MessageProtocol.Serialize(startSequenceNumber, buffer, ref bufferIndex); MessageProtocol.Serialize(fragmentCount, buffer, ref bufferIndex); MessageProtocol.Serialize(fragmentNumber, buffer, ref bufferIndex); MessageProtocol.Serialize(totalLength, buffer, ref bufferIndex); MessageProtocol.Serialize(fragmentOffset, buffer, ref bufferIndex); } } internal byte[] Serialize() { return Payload.GetBuffer(); } public void FreePayload() { if (Payload != null) { PeerBase.MessageBufferPool.Release(Payload); } Payload = null; } public int CompareTo(NCommand other) { if (other == null) { return 1; } int num = reliableSequenceNumber - other.reliableSequenceNumber; if (IsFlaggedReliable || num != 0) { return num; } return unreliableSequenceNumber - other.unreliableSequenceNumber; } public override string ToString() { return ToString(); } public string ToString(bool full = false) { string text = (IsFlaggedUnsequenced ? "u" : ""); if (unreliableSequenceNumber == 0) { if (full) { return $"{text}{reliableSequenceNumber}/{commandChannelID}x{commandSentCount} (CMD {commandType} sent {commandSentTime} timeout {timeoutTime})"; } return $"{text}{reliableSequenceNumber}/{commandChannelID}"; } if (full) { return $"{text}{reliableSequenceNumber}.{unreliableSequenceNumber}/{commandChannelID}x{commandSentCount} (CMD {commandType} sent {commandSentTime} timeout {timeoutTime})"; } return $"{text}{reliableSequenceNumber}.{unreliableSequenceNumber}/{commandChannelID}"; } } internal class SimulationItem { internal readonly Stopwatch stopw; public int TimeToExecute; public byte[] DelayedData; public int Delay { get; internal set; } public SimulationItem() { stopw = new Stopwatch(); stopw.Start(); } } public class NetworkSimulationSet { private bool isSimulationEnabled; private int outgoingLag = 100; private int outgoingJitter; private int outgoingLossPercentage = 1; private int incomingLag = 100; private int incomingJitter; private int incomingLossPercentage = 1; internal PeerBase peerBase; private Thread netSimThread; protected internal readonly ManualResetEvent NetSimManualResetEvent = new ManualResetEvent(initialState: false); protected internal bool IsSimulationEnabled { get { return isSimulationEnabled; } set { lock (NetSimManualResetEvent) { if (value == isSimulationEnabled) { return; } if (!value) { lock (peerBase.NetSimListIncoming) { foreach (SimulationItem item in peerBase.NetSimListIncoming) { if (peerBase.PhotonSocket != null && peerBase.PhotonSocket.Connected) { peerBase.ReceiveIncomingCommands(item.DelayedData, item.DelayedData.Length); } } peerBase.NetSimListIncoming.Clear(); } lock (peerBase.NetSimListOutgoing) { foreach (SimulationItem item2 in peerBase.NetSimListOutgoing) { if (peerBase.PhotonSocket != null && peerBase.PhotonSocket.Connected) { peerBase.PhotonSocket.Send(item2.DelayedData, item2.DelayedData.Length); } } peerBase.NetSimListOutgoing.Clear(); } } isSimulationEnabled = value; if (isSimulationEnabled) { if (netSimThread == null) { netSimThread = new Thread(peerBase.NetworkSimRun); netSimThread.IsBackground = true; netSimThread.Name = "netSim"; netSimThread.Start(); } NetSimManualResetEvent.Set(); } else { NetSimManualResetEvent.Reset(); } } } } public int OutgoingLag { get { return outgoingLag; } set { outgoingLag = value; } } public int OutgoingJitter { get { return outgoingJitter; } set { outgoingJitter = value; } } public int OutgoingLossPercentage { get { return outgoingLossPercentage; } set { outgoingLossPercentage = value; } } public int IncomingLag { get { return incomingLag; } set { incomingLag = value; } } public int IncomingJitter { get { return incomingJitter; } set { incomingJitter = value; } } public int IncomingLossPercentage { get { return incomingLossPercentage; } set { incomingLossPercentage = value; } } public int LostPackagesOut { get; internal set; } public int LostPackagesIn { get; internal set; } public override string ToString() { return string.Format("NetworkSimulationSet {6}. Lag in={0} out={1}. Jitter in={2} out={3}. Loss in={4} out={5}.", incomingLag, outgoingLag, incomingJitter, outgoingJitter, incomingLossPercentage, outgoingLossPercentage, IsSimulationEnabled); } } [DebuggerDisplay("Parameter count: {Count}")] public class ParameterDictionary : IEnumerable<KeyValuePair<byte, object>>, IEnumerable { public readonly NonAllocDictionary<byte, object> paramDict; public readonly StructWrapperPools wrapperPools = new StructWrapperPools(); public object this[byte key] { get { object obj = paramDict[key]; if (!(obj is StructWrapper<object> result)) { return obj; } return result; } set { paramDict[key] = value; } } public int Count => paramDict.Count; public ParameterDictionary() { paramDict = new NonAllocDictionary<byte, object>(); } public ParameterDictionary(int capacity) { paramDict = new NonAllocDictionary<byte, object>((uint)capacity); } public static implicit operator NonAllocDictionary<byte, object>(ParameterDictionary value) { return value.paramDict; } IEnumerator<KeyValuePair<byte, object>> IEnumerable<KeyValuePair<byte, object>>.GetEnumerator() { return ((IEnumerable<KeyValuePair<byte, object>>)paramDict).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<KeyValuePair<byte, object>>)paramDict).GetEnumerator(); } public NonAllocDictionary<byte, object>.PairIterator GetEnumerator() { return paramDict.GetEnumerator(); } public void Clear() { wrapperPools.Clear(); paramDict.Clear(); } public void Add(byte code, string value) { paramDict[code] = value; } public void Add(byte code, PhotonHashtable value) { paramDict[code] = value; } public void Add(byte code, byte value) { StructWrapper<byte> value2 = StructWrapperPools.mappedByteWrappers[value]; paramDict[code] = value2; } public void Add(byte code, bool value) { StructWrapper<bool> value2 = StructWrapperPools.mappedBoolWrappers[value ? 1u : 0u]; paramDict[code] = value2; } public void Add(byte code, short value) { paramDict[code] = value; } public void Add(byte code, int value) { paramDict[code] = value; } public void Add(byte code, long value) { paramDict[code] = value; } public void Add(byte code, object value) { paramDict[code] = value; } public T Unwrap<T>(byte key) { return paramDict[key].Unwrap<T>(); } public T Get<T>(byte key) { return paramDict[key].Get<T>(); } public bool ContainsKey(byte key) { return paramDict.ContainsKey(key); } public object TryGetObject(byte key) { if (paramDict.TryGetValue(key, out var val)) { return val; } return null; } public bool TryGetValue(byte key, out object value) { return paramDict.TryGetValue(key, out value); } public bool TryGetValue<T>(byte key, out T value) where T : struct { object val; bool flag = paramDict.TryGetValue(key, out val); if (!flag) { value = default(T); return false; } if (val is StructWrapper<T> structWrapper) { value = structWrapper.value; } else if (val is StructWrapper<object> structWrapper2) { value = (T)structWrapper2.value; } else { value = (T)val; } return flag; } public string ToStringFull(bool includeTypes = true) { if (includeTypes) { return $"(ParameterDictionary){SupportClass.DictionaryToString(paramDict, includeTypes)}"; } return SupportClass.DictionaryToString(paramDict, includeTypes); } } internal static class PhotonCodes { internal static byte ClientKey = 1; internal static byte ModeKey = 2; internal static byte ServerKey = 1; internal static byte InitEncryption = 0; internal static byte Ping = 1; public const byte Ok = 0; } public enum ConnectionStateValue : byte { Disconnected = 0, Connecting = 1, Connected = 3, Disconnecting = 4, AcknowledgingDisconnect = 5, Zombie = 6 } internal enum EgMessageType : byte { Init, InitResponse, Operation, OperationResponse, Event, DisconnectReason, InternalOperationRequest, InternalOperationResponse, Message, RawMessage } [Flags] internal enum InitV3Flags : short { NoFlags = 0, EncryptionFlag = 1, IPv6Flag = 2, ReleaseSdkFlag = 4 } public abstract class PeerBase { internal delegate void MyAction(); private static class GpBinaryV3Parameters { public const byte CustomObject = 0; public const byte ExtraPlatformParams = 1; } private int bestRoundtripTimeoutIntern; internal PhotonPeer photonPeer; public Protocol SerializationProtocol; internal ConnectionProtocol usedTransportProtocol; internal PhotonSocket PhotonSocket; internal ConnectionStateValue peerConnectionState; internal int ByteCountLastOperation; internal int ByteCountCurrentDispatch; internal NCommand CommandInCurrentDispatch; internal int packetLossByCrc; internal int packetLossByChallenge; internal int throttledBySendWindow; internal readonly Queue<MyAction> ActionQueue = new Queue<MyAction>(); internal short peerID = -1; internal static short peerCount; internal int serverTimeOffset; internal bool serverTimeOffsetIsAvailable; internal int roundTripTime; internal int roundTripTimeVariance; internal int lastRoundTripTime; internal int lowestRoundTripTime; internal int highestRoundTripTimeVariance; internal object PhotonToken; internal object CustomInitData; public string AppId; internal EventData reusableEventData; internal Stopwatch watch = Stopwatch.StartNew(); internal int timeoutInt; internal int timeLastAckReceive; internal int longestSendCall; internal int timeIntCurrentSend; internal bool ApplicationIsInitialized; internal bool isEncryptionAvailable; private ushort serverFeatureFlags; protected internal static Pool<StreamBuffer> MessageBufferPool = new Pool<StreamBuffer>(() => new StreamBuffer(PhotonPeer.OutgoingStreamBufferSize), delegate(StreamBuffer buffer) { buffer.Reset(); }, 16); internal byte[] messageHeader; private volatile int prepareWebSocketUrlCount = -1; private StringBuilder prepareWebSocketUrlSB; internal ICryptoProvider CryptoProvider; private readonly Random lagRandomizer = new Random(); internal readonly LinkedList<SimulationItem> NetSimListOutgoing = new LinkedList<SimulationItem>(); internal readonly LinkedList<SimulationItem> NetSimListIncoming = new LinkedList<SimulationItem>(); private readonly NetworkSimulationSet networkSimulationSettings = new NetworkSimulationSet(); internal int bestRoundtripTimeout { get { return bestRoundtripTimeoutIntern; } set { if (bestRoundtripTimeoutIntern <= 0 || value < bestRoundtripTimeoutIntern) { bestRoundtripTimeoutIntern = value; } } } internal TrafficStats Stats => photonPeer.Stats; internal IPhotonPeerListener Listener => photonPeer.Listener; internal LogLevel LogLevel => photonPeer.LogLevel; public string ServerAddress { get; internal set; } public string ProxyServerAddress { get; internal set; } internal string rttVarString => $"{roundTripTime}({roundTripTimeVariance})"; internal int DisconnectTimeout => photonPeer.DisconnectTimeout; internal int PingInterval => photonPeer.PingInterval; internal byte ChannelCount => photonPeer.ChannelCount; internal abstract int QueuedIncomingCommandsCount { get; } internal abstract int QueuedOutgoingCommandsCount { get; } public virtual string PeerID => ((ushort)peerID).ToString(); internal int timeInt => (int)watch.ElapsedMilliseconds; public ushort ServerFeatureFlags { get { return serverFeatureFlags; } internal set { serverFeatureFlags = value; serverFeatureFlagsAvailable = serverFeatureFlags > 0; serverFeatureAck2Available = (serverFeatureFlags & 1) > 0; serverFeatureSyncReliableQueue = (serverFeatureFlags & 2) > 0; if (!serverFeatureFlagsAvailable) { ServerMaxQueueableReliableCommands = 0; } } } internal bool serverFeatureFlagsAvailable { get; private set; } internal bool serverFeatureAck2Available { get; private set; } internal bool serverFeatureSyncReliableQueue { get; private set; } public ushort ServerMaxQueueableReliableCommands { get; internal set; } internal int mtu => photonPeer.MaximumTransferUnit; protected internal bool IsIpv6 { get { if (PhotonSocket != null) { return PhotonSocket.AddressResolvedAsIpv6; } return false; } } public NetworkSimulationSet NetworkSimulationSettings => networkSimulationSettings; protected PeerBase() { networkSimulationSettings.peerBase = this; peerCount++; } internal virtual void Reset() { SerializationProtocol = SerializationProtocolFactory.Create(photonPeer.SerializationProtocolType); ByteCountLastOperation = 0; ByteCountCurrentDispatch = 0; Stats.BytesIn = 0L; Stats.BytesOut = 0L; packetLossByCrc = 0; packetLossByChallenge = 0; networkSimulationSettings.LostPackagesIn = 0; networkSimulationSettings.LostPackagesOut = 0; throttledBySendWindow = 0; lock (NetSimListOutgoing) { NetSimListOutgoing.Clear(); } lock (NetSimListIncoming) { NetSimListIncoming.Clear(); } lock (ActionQueue) { ActionQueue.Clear(); } peerConnectionState = ConnectionStateValue.Disconnected; watch.Reset(); watch.Start(); isEncryptionAvailable = false; ServerFeatureFlags = 0; ApplicationIsInitialized = false; CryptoProvider = null; roundTripTime = 200; roundTripTimeVariance = 5; serverTimeOffsetIsAvailable = false; serverTimeOffset = 0; } internal abstract bool Connect(string serverAddress, string proxyServerAddress, string appID, object photonToken); private string GetHttpKeyValueString(Dictionary<string, string> dic) { StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair<string, string> item in dic) { stringBuilder.Append(item.Key).Append("=").Append(item.Value) .Append("&"); } return stringBuilder.ToString(); } internal byte[] WriteInitRequest() { if (photonPeer.UseInitV3) { return WriteInitV3(); } if (PhotonToken == null) { byte[] array = new byte[41]; byte[] clientVersion = Version.clientVersion; array[0] = 243; array[1] = 0; array[2] = SerializationProtocol.VersionBytes[0]; array[3] = SerializationProtocol.VersionBytes[1]; array[4] = photonPeer.ClientSdkIdShifted; array[5] = (byte)((byte)(clientVersion[0] << 4) | clientVersion[1]); array[6] = clientVersion[2]; array[7] = clientVersion[3]; array[8] = 0; if (string.IsNullOrEmpty(AppId)) { AppId = "Realtime"; } for (int i = 0; i < 32; i++) { array[i + 9] = (byte)((i < AppId.Length) ? ((byte)AppId[i]) : 0); } if (IsIpv6) { array[5] |= 128; } else { array[5] &= 127; } return array; } if (PhotonToken != null) { byte[] array2 = null; Dictionary<string, string> dictionary = new Dictionary<string, string>(); dictionary["init"] = null; dictionary["app"] = AppId; dictionary["clientversion"] = PhotonPeer.Version; dictionary["protocol"] = SerializationProtocol.ProtocolType; dictionary["sid"] = photonPeer.ClientSdkIdShifted.ToString(); byte[] array3 = null; int num = 0; if (PhotonToken != null) { array3 = SerializationProtocol.Serialize(PhotonToken); num += array3.Length; } string text = GetHttpKeyValueString(dictionary); if (IsIpv6) { text += "&IPv6"; } string text2 = $"POST /?{text} HTTP/1.1\r\nHost: {ServerAddress}\r\nContent-Length: {num}\r\n\r\n"; array2 = new byte[text2.Length + num]; if (array3 != null) { Buffer.BlockCopy(array3, 0, array2, text2.Length, array3.Length); } Buffer.BlockCopy(Encoding.UTF8.GetBytes(text2), 0, array2, 0, text2.Length); return array2; } return null; } private byte[] WriteInitV3() { StreamBuffer streamBuffer = new StreamBuffer(); streamBuffer.WriteByte(245); InitV3Flags initV3Flags = InitV3Flags.NoFlags; if (IsIpv6) { initV3Flags |= InitV3Flags.IPv6Flag; } initV3Flags |= InitV3Flags.ReleaseSdkFlag; IPhotonEncryptor encryptor = photonPeer.Encryptor; if (encryptor != null) { initV3Flags |= InitV3Flags.EncryptionFlag; } streamBuffer.WriteBytes((byte)((int)initV3Flags >> 8), (byte)initV3Flags); switch (SerializationProtocol.VersionBytes[1]) { case 6: streamBuffer.WriteByte(16); break; case 8: streamBuffer.WriteByte(18); break; default: throw new Exception("Unknown protocol version: " + SerializationProtocol.VersionBytes[1]); } streamBuffer.Write(Version.clientVersion,
UserLibs/PhotonRealtime.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
#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<OnConnectedToMasterMsg>(delegate { handler.SetResult(0); })); return handler.Task; }).Unwrap(); } public static Task<short> 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<short>(new OperationStartException("Client still connected")); } if (!client.ReconnectAndRejoin(ticket)) { return Task.FromException<short>(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<OnJoinedRoomMsg>(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<short> 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<OnConnectedToMasterMsg>(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<OnDisconnectedMsg>(delegate { handler.SetResult(0); })); if (client.State != ClientState.Disconnecting) { client.Disconnect(); } return handler.Task; }).Unwrap(); } public static Task<RegionHandler> 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<RegionHandler>(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<RegionHandler> task = handler.Task.ContinueWith((Task<short> c) => client.RegionHandler, asyncConfig.TaskScheduler); task.ContinueWith((Task<RegionHandler> c) => client.DisconnectAsync(), asyncConfig.TaskScheduler); return task; }).Unwrap(); } public static Task<short> 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<short>(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<OnJoinedRoomMsg>(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<short> 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<short>(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<OnJoinedRoomMsg>(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<short> 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<short>(new OperationStartException("Must be connected to master server")); } if (!client.OpRejoinRoom(roomName, ticket)) { return Task.FromException<short>(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<OnJoinedRoomMsg>(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<short> 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<short>(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<OnJoinedRoomMsg>(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<short> 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<short>(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<OnJoinedRoomMsg>(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<short> 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<short>(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<OnJoinedRoomMsg>(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<OnConnectedToMasterMsg>(delegate { handler.SetResult(0); })); return handler.Task; }).Unwrap(); } public static Task<short> 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<short>(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<OnJoinedLobbyMsg>(delegate { handler.SetResult(0); })); return handler.Task; }).Unwrap(); } public static Task<short> 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<short>(new OperationStartException("Must be inside a lobby")); } return config.Resolve().TaskFactory.StartNew(delegate { if (!client.OpLeaveLobby()) { return Task.FromException<short>(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<OnLeftLobbyMsg>(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<short> completionSource = null, IDisposable disposable = null, AsyncConfig config = null) { DateTime now = DateTime.Now; config.Resolve().TaskFactory.StartNew((Func<Task>)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<OnDisconnectedMsg>(delegate { handler.SetResult(0); })); return handler.Task; }).Unwrap(); } } public class AsyncOperationHandler : IDisposable { private TaskCompletionSource<short> _result; private CancellationTokenSource _cancellation; public Task<short> Task => _result.Task; public TaskCompletionSource<short> CompletionSource => _result; public CancellationToken Token => (_cancellation == null) ? CancellationToken.None : _cancellation.Token; public bool IsCancellationRequested => _cancellation != null && _cancellation.IsCancellationRequested; public ConcurrentQueue<IDisposable> Disposables { get; private set; } public string Name { get; set; } public AsyncOperationHandler(float operationTimeoutSec) { _result = new TaskCompletionSource<short>(); _cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(operationTimeoutSec)); _cancellation.Token.Register(delegate { SetException(new OperationTimeoutException("Operation timed out " + Name)); }); Disposables = new ConcurrentQueue<IDisposable>(); } public AsyncOperationHandler() { _result = new TaskCompletionSource<short>(); Disposables = new ConcurrentQueue<IDisposable>(); } 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<string, object> 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<string, object> 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<string, object> data; } public class OnCustomAuthenticationFailedMsg { public string debugMessage; } public interface ILobbyCallbacks { void OnJoinedLobby(); void OnLeftLobby(); void OnRoomListUpdate(List<RoomInfo> roomList); void OnLobbyStatisticsUpdate(List<TypedLobbyInfo> lobbyStatistics); } public class OnJoinedLobbyMsg { } public class OnLeftLobbyMsg { } public class OnRoomListUpdateMsg { public List<RoomInfo> roomList; internal OnRoomListUpdateMsg(List<RoomInfo> roomList) { this.roomList = roomList; } } public class OnLobbyStatisticsUpdateMsg { public List<TypedLobbyInfo> lobbyStatistics; internal OnLobbyStatisticsUpdateMsg(List<TypedLobbyInfo> lobbyStatistics) { this.lobbyStatistics = lobbyStatistics; } } public interface IMatchmakingCallbacks { void OnFriendListUpdate(List<FriendInfo> 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<FriendInfo> friendList; internal OnFriendListUpdateMsg(List<FriendInfo> 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>, 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<string, object> 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>, 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<FriendInfo> 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>, 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>, 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<RoomInfo> 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<TypedLobbyInfo> 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>, 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<MessageType> : IEnumerator, IDisposable where MessageType : class { private Delegate handler; internal EventBetter EventBetterInstance; internal List<MessageType> 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<MessageType>(); } 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<object> listeners = new List<object>(); public readonly List<Delegate> handlers = new List<Delegate>(); public readonly List<HandlerFlags> flags = new List<HandlerFlags>(); 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<Type, EventEntry> s_entries = new ConcurrentDictionary<Type, EventEntry>(); private List<EventEntry> s_entriesList = new List<EventEntry>(); private EventBetterWorker s_worker; public IDisposable ListenManual<MessageType>(Action<MessageType> handler) { Delegate handler2 = RegisterInternal<object, MessageType>((object)s_entries, (Action<MessageType>)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>(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<MessageType> ListenWait<MessageType>() where MessageType : class { return new YieldListener<MessageType>(); } private bool RaiseInternal<T>(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<T>)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, MessageType>(ListenerType listener, Action<MessageType> handler, HandlerFlags flags) { return RegisterInternal((object)listener, handler, flags); } private Delegate RegisterInternal<T>(object listener, Action<T> 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<ParamType>(Type messageType, ParamType param, Func<EventEntry, int, ParamType, bool> predicate) { if (!s_entries.TryGetValue(messageType, out var value)) { return false; } return UnregisterInternal(value, param, predicate); } private bool UnregisterInternal<ParamType>(EventEntry entry, ParamType param, Func<EventEntry, int, ParamType, bool> 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<object> keysWithNullValue = new List<object>(); 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<T>(this List<T> 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<byte> 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, object>)(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, object>)(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, object>)(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<string> onError; private static Action<string> onWarn; private static Action<string> onInfo; private static Action<string> onDebug; private static Action<Exception, string> 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<string> error, Action<string> warn, Action<string> info, Action<string> debug, Action<Exception, string> 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<RealtimeClient> ConnectToRoomAsync(this RealtimeClient client, MatchmakingArguments arguments) { return ConnectToRoomAsync(arguments, client); } public static Task<RealtimeClient> 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<RealtimeClient> ReconnectToRoomAsync(this RealtimeClient client, MatchmakingArguments arguments) { return ReconnectToRoomAsync(arguments, client); } public static Task<RealtimeClient> 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