Decompiled source of Multiside v1.0.2

Mods/Multiside.dll

Decompiled a day ago
using 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 ago
using 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 ago
using 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
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
#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