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[] poolTiers = new Stack[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(); } } 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 stack = poolTiers[i]; if (stack == null) { stack = new Stack(); poolTiers[i] = stack; } lock (stack) { return PopOrCreate(stack, i); } } } private ByteArraySlice PopOrCreate(Stack 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 : IDictionary, ICollection>, IEnumerable>, IEnumerable where K : IEquatable { public struct KeyIterator : IEnumerator, IEnumerator, IDisposable { private int _index; private NonAllocDictionary _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 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, IEnumerator, IDisposable { private int _index; private NonAllocDictionary _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 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>, IEnumerator, IDisposable { private int _index; private NonAllocDictionary _dict; object IEnumerator.Current { get { if (_index == 0) { throw new InvalidOperationException(); } return Current; } } public KeyValuePair Current { get { if (_index == 0) { return default(KeyValuePair); } return new KeyValuePair(_dict._nodes[_index].Key, _dict._nodes[_index].Val); } } public PairIterator(NonAllocDictionary 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 keys; [DebuggerBrowsable(DebuggerBrowsableState.Never)] private ICollection values; [DebuggerBrowsable(DebuggerBrowsableState.Never)] public KeyIterator Keys => new KeyIterator(this); [DebuggerBrowsable(DebuggerBrowsableState.Never)] ICollection IDictionary.Values => values; [DebuggerBrowsable(DebuggerBrowsableState.Never)] ICollection IDictionary.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 item) { int num = FindNode(item.Key); if (num >= 0 && EqualityComparer.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 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 item) { if (Contains(item)) { return Remove(item.Key); } return false; } IEnumerator> IEnumerable>.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>.CopyTo(KeyValuePair[] 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(_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 { 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 list = new List(); 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(this); } } public struct DictionaryEntryEnumerator : IEnumerator, IEnumerator, IDisposable { private Dictionary.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.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 receivedReliableSequenceNumbers = new HashSet(); internal int reliableSequencedNumbersCompletelyReceived; internal int reliableSequencedNumbersHighestReceived; } internal byte ChannelNumber; internal NonAllocDictionary incomingReliableCommandsList; internal NonAllocDictionary incomingUnreliableCommandsList; internal Queue incomingUnsequencedCommandsList; internal NonAllocDictionary incomingUnsequencedFragments; internal List outgoingReliableCommandsList; internal List outgoingUnreliableCommandsList; internal int incomingReliableSequenceNumber; internal int incomingUnreliableSequenceNumber; internal int outgoingReliableSequenceNumber; internal int outgoingUnreliableSequenceNumber; internal int outgoingReliableUnsequencedNumber; private int reliableUnsequencedNumbersCompletelyReceived; private HashSet reliableUnsequencedNumbersReceived = new HashSet(); 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((uint)commandBufferSize); incomingUnreliableCommandsList = new NonAllocDictionary((uint)commandBufferSize); incomingUnsequencedCommandsList = new Queue(); incomingUnsequencedFragments = new NonAllocDictionary(); outgoingReliableCommandsList = new List(commandBufferSize); outgoingUnreliableCommandsList = new List(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 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 nCommandPool = new Pool(() => new NCommand(), delegate(NCommand cmd) { cmd.Reset(); }, 16); private List sentReliableCommands = new List(); 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 commandsToRemove = new Queue(); private ConcurrentQueue CommandQueue = new ConcurrentQueue(); private int fragmentLength; private int fragmentLengthDatagramEncrypt; private int fragmentLengthMtuValue; private readonly HashSet channelsToUpdateLowestSent = new HashSet(); private int[] lowestSentSequenceNumber; private int[] gapBlocks = new int[4]; private List toRemove = new List(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 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 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 { 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>, IEnumerable { public readonly NonAllocDictionary paramDict; public readonly StructWrapperPools wrapperPools = new StructWrapperPools(); public object this[byte key] { get { object obj = paramDict[key]; if (!(obj is StructWrapper result)) { return obj; } return result; } set { paramDict[key] = value; } } public int Count => paramDict.Count; public ParameterDictionary() { paramDict = new NonAllocDictionary(); } public ParameterDictionary(int capacity) { paramDict = new NonAllocDictionary((uint)capacity); } public static implicit operator NonAllocDictionary(ParameterDictionary value) { return value.paramDict; } IEnumerator> IEnumerable>.GetEnumerator() { return ((IEnumerable>)paramDict).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable>)paramDict).GetEnumerator(); } public NonAllocDictionary.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 value2 = StructWrapperPools.mappedByteWrappers[value]; paramDict[code] = value2; } public void Add(byte code, bool value) { StructWrapper 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(byte key) { return paramDict[key].Unwrap(); } public T Get(byte key) { return paramDict[key].Get(); } 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(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 structWrapper) { value = structWrapper.value; } else if (val is StructWrapper 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 ActionQueue = new Queue(); 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 MessageBufferPool = new Pool(() => 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 NetSimListOutgoing = new LinkedList(); internal readonly LinkedList NetSimListIncoming = new LinkedList(); 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 dic) { StringBuilder stringBuilder = new StringBuilder(); foreach (KeyValuePair 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 dictionary = new Dictionary(); 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, 0, 4); streamBuffer.WriteByte(photonPeer.ClientSdkIdShifted); streamBuffer.WriteByte(0); if (string.IsNullOrEmpty(AppId)) { AppId = "Master"; } byte[] bytes = Encoding.UTF8.GetBytes(AppId); int num = bytes.Length; if (num > 255) { throw new Exception("AppId is too long. Limited by 255 symbols."); } streamBuffer.WriteByte((byte)num); streamBuffer.Write(bytes, 0, bytes.Length); if (PhotonToken is byte[] array) { num = array.Length; streamBuffer.WriteBytes((byte)(num >> 8), (byte)num); streamBuffer.Write(array, 0, num); } else { streamBuffer.WriteBytes(0, 0); } Dictionary dictionary = new Dictionary(); if (CustomInitData != null) { dictionary.Add(0, CustomInitData); } if (encryptor != null) { throw new NotImplementedException("InitV3 with encryption is not implemented yet."); } SerializationProtocol.Serialize(streamBuffer, dictionary, setType: true); return streamBuffer.ToArray(); } internal string PrepareWebSocketUrl(string serverAddress, string appId, object photonToken) { if (prepareWebSocketUrlSB == null) { prepareWebSocketUrlSB = new StringBuilder(256); } prepareWebSocketUrlSB.Clear(); prepareWebSocketUrlCount++; prepareWebSocketUrlSB.Append(serverAddress); prepareWebSocketUrlSB.AppendFormat("/?libversion={0}", PhotonPeer.Version); prepareWebSocketUrlSB.AppendFormat("&sid={0}", photonPeer.ClientSdkIdShifted); prepareWebSocketUrlSB.AppendFormat("&peerId={0}_{1}", peerID, prepareWebSocketUrlCount); if (!photonPeer.RemoveAppIdFromWebSocketPath && appId != null && appId.Length >= 8) { prepareWebSocketUrlSB.AppendFormat("&app={0}", appId.Substring(0, 8)); } if (IsIpv6) { prepareWebSocketUrlSB.Append("&IPv6"); } if (photonToken != null) { prepareWebSocketUrlSB.Append("&xInit="); } return prepareWebSocketUrlSB.ToString(); } [Obsolete("This callback is no longer required by PhotonSocket implementations.")] public void OnConnect() { } internal void OnInitResponse() { if (peerConnectionState == ConnectionStateValue.Connecting) { peerConnectionState = ConnectionStateValue.Connected; } ApplicationIsInitialized = true; FetchServerTimestamp(); Listener.OnStatusChanged(StatusCode.Connect); } internal abstract void Disconnect(bool queueStatusChangeCallback = true); internal abstract void SimulateTimeoutDisconnect(bool queueStatusChangeCallback = true); internal abstract void FetchServerTimestamp(); internal abstract bool IsTransportEncrypted(); internal abstract bool EnqueuePhotonMessage(StreamBuffer opBytes, SendOptions sendParams); internal StreamBuffer SerializeOperationToMessage(byte opCode, ParameterDictionary parameters, EgMessageType messageType, bool encrypt) { bool num = encrypt && !IsTransportEncrypted(); StreamBuffer streamBuffer = MessageBufferPool.Acquire(); streamBuffer.SetLength(0L); if (!num) { streamBuffer.Write(messageHeader, 0, messageHeader.Length); } SerializationProtocol.SerializeOperationRequest(streamBuffer, opCode, parameters, setType: false); if (num) { byte[] array = CryptoProvider.Encrypt(streamBuffer.GetBuffer(), 0, streamBuffer.Length); streamBuffer.SetLength(0L); streamBuffer.Write(messageHeader, 0, messageHeader.Length); streamBuffer.Write(array, 0, array.Length); } byte[] buffer = streamBuffer.GetBuffer(); if (messageType != EgMessageType.Operation) { buffer[messageHeader.Length - 1] = (byte)messageType; } if (num || (encrypt && photonPeer.EnableEncryptedFlag)) { buffer[messageHeader.Length - 1] = (byte)(buffer[messageHeader.Length - 1] | 0x80u); } return streamBuffer; } internal StreamBuffer SerializeMessageToMessage(object message, bool encrypt) { bool num = encrypt && !IsTransportEncrypted(); StreamBuffer streamBuffer = MessageBufferPool.Acquire(); streamBuffer.SetLength(0L); if (!num) { streamBuffer.Write(messageHeader, 0, messageHeader.Length); } bool flag = message is byte[]; if (flag) { byte[] array = message as byte[]; streamBuffer.Write(array, 0, array.Length); } else { SerializationProtocol.SerializeMessage(streamBuffer, message); } if (num) { byte[] array2 = CryptoProvider.Encrypt(streamBuffer.GetBuffer(), 0, streamBuffer.Length); streamBuffer.SetLength(0L); streamBuffer.Write(messageHeader, 0, messageHeader.Length); streamBuffer.Write(array2, 0, array2.Length); } byte[] buffer = streamBuffer.GetBuffer(); buffer[messageHeader.Length - 1] = (byte)(flag ? 9 : 8); if (num || (encrypt && photonPeer.EnableEncryptedFlag)) { buffer[messageHeader.Length - 1] = (byte)(buffer[messageHeader.Length - 1] | 0x80u); } return streamBuffer; } internal abstract bool SendOutgoingCommands(); internal virtual bool SendAcksOnly() { return false; } internal abstract void ReceiveIncomingCommands(byte[] inBuff, int dataLength); internal abstract bool DispatchIncomingCommands(); internal virtual bool DeserializeMessageAndCallback(StreamBuffer stream) { if (stream.Length < 2) { if ((int)LogLevel >= 4) { Listener.DebugReturn(LogLevel.Debug, $"Discarding message: Less than 2 bytes. Length: {stream.Length}"); } return false; } byte b = stream.ReadByte(); if (b != 243 && b != 253) { if ((int)LogLevel >= 4) { Listener.DebugReturn(LogLevel.Debug, $"Discarding message: Unknown magic byte: {b}"); } return false; } byte num = stream.ReadByte(); byte b2 = (byte)(num & 0x7Fu); bool flag = (num & 0x80) > 0; if (b2 != 1) { try { if (flag) { stream = new StreamBuffer(CryptoProvider.Decrypt(stream.GetBuffer(), 2, stream.Length - 2)); } else { stream.Seek(2L, SeekOrigin.Begin); } } catch (Exception ex) { if ((int)LogLevel >= 1) { Listener.DebugReturn(LogLevel.Error, $"Decryption caught exception handling msgType: {b2} exception: {ex}"); } SupportClass.WriteStackTrace(ex); return false; } } Protocol.DeserializationFlags flags = (photonPeer.UseByteArraySlicePoolForEvents ? Protocol.DeserializationFlags.AllowPooledByteArray : Protocol.DeserializationFlags.None) | (photonPeer.WrapIncomingStructs ? Protocol.DeserializationFlags.WrapIncomingStructs : Protocol.DeserializationFlags.None); int num2 = 0; switch (b2) { case 3: { OperationResponse operationResponse = null; try { operationResponse = SerializationProtocol.DeserializeOperationResponse(stream, flags); } catch (Exception arg4) { if ((int)LogLevel >= 1) { EnqueueDebugReturn(LogLevel.Error, $"Deserialization caught exception for Operation Response: {arg4}"); } return false; } num2 = timeInt; Listener.OnOperationResponse(operationResponse); Stats.LastDispatchDuration = timeInt - num2; break; } case 4: { EventData eventData = null; try { eventData = SerializationProtocol.DeserializeEventData(stream, reusableEventData, flags); } catch (Exception arg) { if ((int)LogLevel >= 1) { EnqueueDebugReturn(LogLevel.Error, $"Deserialization caught exception for Event: {arg}"); } return false; } num2 = timeInt; Listener.OnEvent(eventData); Stats.LastDispatchDuration = timeInt - num2; if (photonPeer.ReuseEventInstance) { reusableEventData = eventData; } break; } case 5: try { DisconnectMessage dm = SerializationProtocol.DeserializeDisconnectMessage(stream); Listener.OnDisconnectMessage(dm); } catch (Exception arg3) { if ((int)LogLevel >= 1) { EnqueueDebugReturn(LogLevel.Error, $"Deserialization caught exception for Disconnect Message: {arg3}"); } return false; } break; case 1: OnInitResponse(); break; case 7: { OperationResponse operationResponse; try { operationResponse = SerializationProtocol.DeserializeOperationResponse(stream); } catch (Exception arg2) { if ((int)LogLevel >= 1) { EnqueueDebugReturn(LogLevel.Error, $"Deserialization caught exception for Internal Operation Response: {arg2}"); } return false; } num2 = timeInt; if (operationResponse.OperationCode == PhotonCodes.InitEncryption) { DeriveSharedKey(operationResponse); } else if (operationResponse.OperationCode == PhotonCodes.Ping) { if (peerConnectionState == ConnectionStateValue.Connecting && (usedTransportProtocol == ConnectionProtocol.WebSocket || usedTransportProtocol == ConnectionProtocol.WebSocketSecure)) { photonPeer.PingUsedAsInit = true; OnInitResponse(); } if (this is TPeer tPeer) { tPeer.ReadPingResult(operationResponse); } } else if ((int)LogLevel >= 1) { EnqueueDebugReturn(LogLevel.Error, "Deserialization failed for unknown Internal Operation Response Code: " + operationResponse.ToStringFull()); } Stats.LastDispatchDuration = timeInt - num2; break; } case 8: { object message = SerializationProtocol.DeserializeMessage(stream); num2 = timeInt; Listener.OnMessage(isRawMessage: false, message); Stats.LastDispatchDuration = timeInt - num2; break; } case 9: num2 = timeInt; Listener.OnMessage(isRawMessage: true, stream); Stats.LastDispatchDuration = timeInt - num2; break; default: if ((int)LogLevel >= 1) { EnqueueDebugReturn(LogLevel.Error, $"Deserialization failed for unexpected msgType: {b2}"); } break; } return true; } internal void UpdateRoundTripTimeAndVariance(int lastRoundtripTime) { if (lastRoundtripTime >= 0) { roundTripTimeVariance -= roundTripTimeVariance / 4; if (lastRoundtripTime >= roundTripTime) { roundTripTime += (lastRoundtripTime - roundTripTime) / 8; roundTripTimeVariance += (lastRoundtripTime - roundTripTime) / 4; } else { roundTripTime += (lastRoundtripTime - roundTripTime) / 8; roundTripTimeVariance -= (lastRoundtripTime - roundTripTime) / 4; } if (roundTripTime < lowestRoundTripTime) { lowestRoundTripTime = roundTripTime; } if (roundTripTimeVariance > highestRoundTripTimeVariance) { highestRoundTripTimeVariance = roundTripTimeVariance; } Stats.RoundtripTime = roundTripTime; Stats.RoundtripTimeVariance = roundTripTimeVariance; Stats.LastRoundtripTime = lastRoundTripTime; } } internal bool ExchangeKeysForEncryption(object lockObject) { if (lockObject == null) { throw new NotSupportedException("Parameter lockObject must be non-Null."); } isEncryptionAvailable = false; if (CryptoProvider != null) { CryptoProvider.Dispose(); CryptoProvider = null; } if (photonPeer.PayloadEncryptorType != null) { try { CryptoProvider = (ICryptoProvider)Activator.CreateInstance(photonPeer.PayloadEncryptorType); if (CryptoProvider == null) { Listener.DebugReturn(LogLevel.Warning, $"Payload encryptor creation by type failed, Activator.CreateInstance() returned null for: {photonPeer.PayloadEncryptorType}"); } } catch (Exception arg) { Listener.DebugReturn(LogLevel.Warning, $"Payload encryptor creation by type failed. Caught: {arg}"); } } if (CryptoProvider == null) { CryptoProvider = new DiffieHellmanCryptoProvider(); } ParameterDictionary parameterDictionary = new ParameterDictionary(1); parameterDictionary[PhotonCodes.ClientKey] = CryptoProvider.PublicKey; lock (lockObject) { SendOptions sendOptions = default(SendOptions); sendOptions.Channel = 0; sendOptions.Encrypt = false; sendOptions.Reliability = true; SendOptions sendParams = sendOptions; StreamBuffer opBytes = SerializeOperationToMessage(PhotonCodes.InitEncryption, parameterDictionary, EgMessageType.InternalOperationRequest, sendParams.Encrypt); return EnqueuePhotonMessage(opBytes, sendParams); } } internal void DeriveSharedKey(OperationResponse operationResponse) { if (operationResponse.ReturnCode != 0) { EnqueueDebugReturn(LogLevel.Error, "Establishing encryption keys failed. ReturnCode != OK: " + operationResponse.ToStringFull()); EnqueueStatusCallback(StatusCode.EncryptionFailedToEstablish); return; } byte[] array = (byte[])operationResponse.Parameters[PhotonCodes.ServerKey]; if (array == null || array.Length == 0) { EnqueueDebugReturn(LogLevel.Error, "Establishing encryption keys failed. Server public key is null or empty: " + operationResponse.ToStringFull()); EnqueueStatusCallback(StatusCode.EncryptionFailedToEstablish); } else { CryptoProvider.DeriveSharedKey(array); isEncryptionAvailable = true; EnqueueStatusCallback(StatusCode.EncryptionEstablished); } } internal virtual void InitEncryption(byte[] secret) { if (photonPeer.PayloadEncryptorType != null) { try { CryptoProvider = (ICryptoProvider)Activator.CreateInstance(photonPeer.PayloadEncryptorType, secret); if (CryptoProvider == null) { if ((int)LogLevel >= 2) { Listener.DebugReturn(LogLevel.Warning, $"Payload encryptor creation by type failed, Activator.CreateInstance() returned null for: {photonPeer.PayloadEncryptorType}"); } } else { isEncryptionAvailable = true; } } catch (Exception arg) { if ((int)LogLevel >= 2) { Listener.DebugReturn(LogLevel.Warning, $"Payload encryptor creation by type failed: {arg}"); } } } if (CryptoProvider == null) { CryptoProvider = new DiffieHellmanCryptoProvider(secret); isEncryptionAvailable = true; } } internal void EnqueueActionForDispatch(MyAction action) { lock (ActionQueue) { ActionQueue.Enqueue(action); } } internal void EnqueueDebugReturn(LogLevel level, string debugReturn) { lock (ActionQueue) { ActionQueue.Enqueue(delegate { Listener.DebugReturn(level, debugReturn); }); } } internal void EnqueueStatusCallback(StatusCode statusValue) { lock (ActionQueue) { ActionQueue.Enqueue(delegate { Listener.OnStatusChanged(statusValue); }); } } internal void SendNetworkSimulated(byte[] dataToSend) { if (!NetworkSimulationSettings.IsSimulationEnabled) { throw new NotImplementedException("SendNetworkSimulated was called, despite NetworkSimulationSettings.IsSimulationEnabled == false."); } if (usedTransportProtocol == ConnectionProtocol.Udp && NetworkSimulationSettings.OutgoingLossPercentage > 0 && lagRandomizer.Next(101) < NetworkSimulationSettings.OutgoingLossPercentage) { networkSimulationSettings.LostPackagesOut++; return; } int num = ((networkSimulationSettings.OutgoingJitter > 0) ? (lagRandomizer.Next(networkSimulationSettings.OutgoingJitter * 2) - networkSimulationSettings.OutgoingJitter) : 0); int num2 = networkSimulationSettings.OutgoingLag + num; int num3 = timeInt + num2; SimulationItem value = new SimulationItem { DelayedData = dataToSend, TimeToExecute = num3, Delay = num2 }; lock (NetSimListOutgoing) { if (NetSimListOutgoing.Count == 0 || usedTransportProtocol == ConnectionProtocol.Tcp) { NetSimListOutgoing.AddLast(value); return; } LinkedListNode linkedListNode = NetSimListOutgoing.First; while (linkedListNode != null && linkedListNode.Value.TimeToExecute < num3) { linkedListNode = linkedListNode.Next; } if (linkedListNode == null) { NetSimListOutgoing.AddLast(value); } else { NetSimListOutgoing.AddBefore(linkedListNode, value); } } } internal void ReceiveNetworkSimulated(byte[] dataReceived) { if (!networkSimulationSettings.IsSimulationEnabled) { throw new NotImplementedException("ReceiveNetworkSimulated was called, despite NetworkSimulationSettings.IsSimulationEnabled == false."); } if (usedTransportProtocol == ConnectionProtocol.Udp && networkSimulationSettings.IncomingLossPercentage > 0 && lagRandomizer.Next(101) < networkSimulationSettings.IncomingLossPercentage) { networkSimulationSettings.LostPackagesIn++; return; } int num = ((networkSimulationSettings.IncomingJitter > 0) ? (lagRandomizer.Next(networkSimulationSettings.IncomingJitter * 2) - networkSimulationSettings.IncomingJitter) : 0); int num2 = networkSimulationSettings.IncomingLag + num; int num3 = timeInt + num2; SimulationItem value = new SimulationItem { DelayedData = dataReceived, TimeToExecute = num3, Delay = num2 }; lock (NetSimListIncoming) { if (NetSimListIncoming.Count == 0 || usedTransportProtocol == ConnectionProtocol.Tcp) { NetSimListIncoming.AddLast(value); return; } LinkedListNode linkedListNode = NetSimListIncoming.First; while (linkedListNode != null && linkedListNode.Value.TimeToExecute < num3) { linkedListNode = linkedListNode.Next; } if (linkedListNode == null) { NetSimListIncoming.AddLast(value); } else { NetSimListIncoming.AddBefore(linkedListNode, value); } } } protected internal void NetworkSimRun() { while (true) { bool flag = false; lock (networkSimulationSettings.NetSimManualResetEvent) { flag = networkSimulationSettings.IsSimulationEnabled; } if (!flag) { networkSimulationSettings.NetSimManualResetEvent.WaitOne(); continue; } lock (NetSimListIncoming) { SimulationItem simulationItem = null; while (NetSimListIncoming.First != null) { simulationItem = NetSimListIncoming.First.Value; if (simulationItem.stopw.ElapsedMilliseconds < simulationItem.Delay) { break; } ReceiveIncomingCommands(simulationItem.DelayedData, simulationItem.DelayedData.Length); NetSimListIncoming.RemoveFirst(); } } lock (NetSimListOutgoing) { SimulationItem simulationItem2 = null; while (NetSimListOutgoing.First != null) { simulationItem2 = NetSimListOutgoing.First.Value; if (simulationItem2.stopw.ElapsedMilliseconds < simulationItem2.Delay) { break; } if (PhotonSocket != null && PhotonSocket.Connected) { PhotonSocket.Send(simulationItem2.DelayedData, simulationItem2.DelayedData.Length); } NetSimListOutgoing.RemoveFirst(); } } Thread.Sleep(0); } } } public class PhotonClientWebSocket : PhotonSocket { private ClientWebSocket clientWebSocket; private Task sendTask; [Preserve] public PhotonClientWebSocket(PeerBase peerBase) : base(peerBase) { if (ReportDebugOfLevel(LogLevel.Info)) { EnqueueDebugReturn(LogLevel.Info, "PhotonClientWebSocket"); } } public override bool Connect() { if (!base.Connect()) { return false; } base.State = PhotonSocketState.Connecting; Thread thread = new Thread(AsyncConnectAndReceive); thread.IsBackground = true; thread.Start(); return true; } private void AsyncConnectAndReceive() { Uri uri = null; try { uri = new Uri(ConnectAddress); } catch (Exception arg) { if (ReportDebugOfLevel(LogLevel.Error)) { base.Listener.DebugReturn(LogLevel.Error, $"Failed to create a URI from ConnectAddress ({ConnectAddress}). Exception: {arg}"); } } if (uri != null && uri.HostNameType == UriHostNameType.Dns) { try { IPAddress[] hostAddresses = Dns.GetHostAddresses(uri.Host); for (int i = 0; i < hostAddresses.Length; i++) { if (hostAddresses[i].AddressFamily == AddressFamily.InterNetworkV6) { base.AddressResolvedAsIpv6 = true; ConnectAddress += "&IPv6"; break; } } } catch (Exception arg2) { if (ReportDebugOfLevel(LogLevel.Error)) { base.Listener.DebugReturn(LogLevel.Error, $"AsyncConnectAndReceive() failed. Dns.GetHostAddresses({uri.Host}) caught: {arg2}"); } } } clientWebSocket = new ClientWebSocket(); clientWebSocket.Options.AddSubProtocol(base.SerializationProtocol); CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(10000); Task task = clientWebSocket.ConnectAsync(new Uri(ConnectAddress), cancellationTokenSource.Token); try { task.Wait(); } catch (Exception arg3) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, $"AsyncConnectAndReceive() caught exception on {ConnectAddress}: {arg3}"); } } if (task.IsFaulted) { EnqueueDebugReturn(LogLevel.Error, "ClientWebSocket IsFaulted: " + task.Exception); } if (clientWebSocket.State != WebSocketState.Open) { base.SocketErrorCode = (int)(clientWebSocket.CloseStatus.HasValue ? clientWebSocket.CloseStatus.Value : ((WebSocketCloseStatus)0)); if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, $"ClientWebSocket is not open. State: {clientWebSocket.State} CloseStatus: {clientWebSocket.CloseStatus} Description: {clientWebSocket.CloseStatusDescription}"); } HandleException(StatusCode.ExceptionOnConnect); return; } base.State = PhotonSocketState.Connected; MemoryStream memoryStream = new MemoryStream(base.MTU); bool flag = false; ArraySegment buffer = new ArraySegment(new byte[base.MTU]); while (clientWebSocket.State == WebSocketState.Open) { Task task2 = null; try { task2 = clientWebSocket.ReceiveAsync(buffer, CancellationToken.None); while (!task2.IsCompleted) { task2.Wait(50); } } catch (Exception) { } if (!task2.IsCompleted || clientWebSocket.State != WebSocketState.Open) { continue; } if (task2.IsCanceled) { EnqueueDebugReturn(LogLevel.Error, $"PhotonClientWebSocket readTask.IsCanceled: {task2.Status} {base.ServerAddress}:{base.ServerPort} {clientWebSocket.CloseStatusDescription}"); continue; } if (task2.Result.Count == 0) { if (ReportDebugOfLevel(LogLevel.Info)) { EnqueueDebugReturn(LogLevel.Info, $"PhotonClientWebSocket received 0 bytes. this.State: {base.State} clientWebSocket.State: {clientWebSocket.State} readTask.Status: {task2.Status}"); } continue; } if (!task2.Result.EndOfMessage) { flag = true; memoryStream.Write(buffer.Array, 0, task2.Result.Count); continue; } int length; byte[] inBuffer; if (flag) { memoryStream.Write(buffer.Array, 0, task2.Result.Count); length = (int)memoryStream.Length; inBuffer = memoryStream.GetBuffer(); memoryStream.SetLength(0L); memoryStream.Position = 0L; flag = false; } else { length = task2.Result.Count; inBuffer = buffer.Array; } HandleReceivedDatagram(inBuffer, length, willBeReused: true); } if (base.State != PhotonSocketState.Disconnecting && base.State != 0) { if (ReportDebugOfLevel(LogLevel.Info)) { EnqueueDebugReturn(LogLevel.Info, $"PhotonSocket.State is {base.State} but can't receive anymore. ClientWebSocket.State: {clientWebSocket.State}"); } if (clientWebSocket.State == WebSocketState.CloseReceived) { HandleException(StatusCode.DisconnectByServerLogic); } if (clientWebSocket.State == WebSocketState.Aborted) { HandleException(StatusCode.DisconnectByServerReasonUnknown); } } Disconnect(); } public override bool Disconnect() { if (clientWebSocket != null && clientWebSocket.State == WebSocketState.CloseReceived) { try { clientWebSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "CloseAsync due to state CloseReceived", CancellationToken.None); } catch (Exception arg) { if (ReportDebugOfLevel(LogLevel.Debug)) { EnqueueDebugReturn(LogLevel.Debug, $"Caught exception in clientWebSocket.CloseAsync(): {arg}"); } } base.State = PhotonSocketState.Disconnected; return true; } if (clientWebSocket != null && clientWebSocket.State != WebSocketState.Closed && clientWebSocket.State != WebSocketState.CloseSent) { base.State = PhotonSocketState.Disconnecting; try { clientWebSocket.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "ws close", CancellationToken.None); } catch (Exception arg2) { if (ReportDebugOfLevel(LogLevel.Debug)) { EnqueueDebugReturn(LogLevel.Debug, $"Caught exception in clientWebSocket.CloseOutputAsync(): {arg2}"); } } } base.State = PhotonSocketState.Disconnected; return true; } public override PhotonSocketError Send(byte[] data, int length) { if (clientWebSocket != null && clientWebSocket.State != WebSocketState.Open && base.State != PhotonSocketState.Disconnecting && base.State != 0) { if (clientWebSocket.State == WebSocketState.CloseReceived) { HandleException(StatusCode.DisconnectByServerLogic); return PhotonSocketError.Exception; } if (clientWebSocket.State == WebSocketState.Aborted) { HandleException(StatusCode.DisconnectByServerReasonUnknown); return PhotonSocketError.Exception; } } if (clientWebSocket == null) { if (base.State == PhotonSocketState.Disconnecting || base.State == PhotonSocketState.Disconnected) { return PhotonSocketError.Skipped; } if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, "PhotonClientWebSocket.Send() failed, as this.clientWebSocket is null."); } return PhotonSocketError.Exception; } if (sendTask != null && !sendTask.IsCompleted && !sendTask.Wait(5)) { return PhotonSocketError.Busy; } sendTask = clientWebSocket.SendAsync(new ArraySegment(data, 0, length), WebSocketMessageType.Binary, endOfMessage: true, CancellationToken.None); if (sendTask != null && !sendTask.IsCompleted && !sendTask.Wait(5)) { return PhotonSocketError.PendingSend; } sendTask = null; return PhotonSocketError.Success; } public override PhotonSocketError Receive(out byte[] data) { throw new NotImplementedException(); } } public class PhotonPeer { public const bool NoSocket = false; public const bool DebugBuild = false; public const int NativeEncryptorApiVersion = 2; public TargetFrameworks TargetFramework = TargetFrameworks.NetStandard20; public byte ClientSdkId = 15; private static string clientVersion; public static bool NoNativeCallbacks; public bool RemoveAppIdFromWebSocketPath; internal bool UseInitV3; public bool UseAck2; public bool EnableEncryptedFlag; public Dictionary SocketImplementationConfig; public LogLevel LogLevel = LogLevel.Error; private bool reuseEventInstance = true; private bool useByteArraySlicePoolForEvents; private bool wrapIncomingStructs; public bool SendInCreationOrder = true; public int SendWindowSize = 50; private byte quickResendAttempts = 2; public int MaxResends = 15; public int InitialResendTimeMax = 400; private int disconnectTimeout = 10000; private bool crcEnabled; public int PingInterval = 1000; public byte ChannelCount = 2; public static int OutgoingStreamBufferSize = 1200; private int mtu = 1200; public static bool AsyncKeyExchange = false; internal bool RandomizeSequenceNumbers; internal byte[] RandomizedSequenceNumbers; private Type payloadEncryptorType; protected internal byte[] PayloadEncryptionSecret; private Type encryptorType; protected internal IPhotonEncryptor Encryptor; public ITrafficRecorder TrafficRecorder; public bool PingUsedAsInit; [Obsolete("Not used anymore.")] public bool TrafficStatsEnabled; internal PeerBase peerBase; private readonly object sendOutgoingLockObject = new object(); private readonly object dispatchLockObject = new object(); private readonly object enqueueLock = new object(); protected internal byte ClientSdkIdShifted => (byte)((uint)(ClientSdkId << 1) | 1u); public static string Version { get { if (string.IsNullOrEmpty(clientVersion)) { clientVersion = $"{Photon.Client.Version.clientVersion[0]}.{Photon.Client.Version.clientVersion[1]}.{Photon.Client.Version.clientVersion[2]}.{Photon.Client.Version.clientVersion[3]}"; } return clientVersion; } } public bool IsAck2Available => false; public SerializationProtocol SerializationProtocolType { get; set; } public Type SocketImplementation { get; internal set; } public int SocketErrorCode { get { if (peerBase == null || peerBase.PhotonSocket == null) { return 0; } return peerBase.PhotonSocket.SocketErrorCode; } } [Obsolete("Use LogLevel instead.")] public LogLevel DebugOut { get { return LogLevel; } set { LogLevel = value; } } public IPhotonPeerListener Listener { get; protected set; } public PeerStateValue PeerState { get { if (peerBase.peerConnectionState == ConnectionStateValue.Connected && !peerBase.ApplicationIsInitialized) { return PeerStateValue.InitializingApplication; } return (PeerStateValue)peerBase.peerConnectionState; } } public string PeerID => peerBase.PeerID; public bool ReuseEventInstance { get { return reuseEventInstance; } set { lock (dispatchLockObject) { reuseEventInstance = value; if (!value) { peerBase.reusableEventData = null; } } } } public bool UseByteArraySlicePoolForEvents { get { return useByteArraySlicePoolForEvents; } set { useByteArraySlicePoolForEvents = value; } } public bool WrapIncomingStructs { get { return wrapIncomingStructs; } set { wrapIncomingStructs = value; } } public ByteArraySlicePool ByteArraySlicePool => peerBase.SerializationProtocol.ByteArraySlicePool; public static Pool MessageBufferPool => PeerBase.MessageBufferPool; [Obsolete("Use SendWindowSize instead.")] public int SequenceDeltaLimitSends { get { return SendWindowSize; } set { SendWindowSize = value; } } public byte QuickResendAttempts { get { return quickResendAttempts; } set { quickResendAttempts = value; if (quickResendAttempts > 4) { quickResendAttempts = 4; } else if (quickResendAttempts < 1) { quickResendAttempts = 1; } } } [Obsolete("Use MaxResends instead.")] public int SentCountAllowance { get { return MaxResends; } set { MaxResends = value; } } public int DisconnectTimeout { get { return disconnectTimeout; } set { if (value < 0) { disconnectTimeout = 10000; } disconnectTimeout = value; } } public bool CrcEnabled { get { return crcEnabled; } set { if (crcEnabled != value) { if (peerBase.peerConnectionState != 0) { throw new Exception("CrcEnabled can only be set while disconnected."); } crcEnabled = value; } } } [Obsolete("Use PingInterval instead.")] public int TimePingInterval { get { return PingInterval; } set { PingInterval = value; } } public string ServerAddress => peerBase.ServerAddress; public string ServerIpAddress { get { if (peerBase != null && peerBase.PhotonSocket != null) { return peerBase.PhotonSocket.ServerIpAddress; } return string.Empty; } } public ConnectionProtocol UsedProtocol => peerBase.usedTransportProtocol; public ConnectionProtocol TransportProtocol { get; set; } public virtual bool IsSimulationEnabled { get { return NetworkSimulationSettings.IsSimulationEnabled; } set { if (value == NetworkSimulationSettings.IsSimulationEnabled) { return; } lock (sendOutgoingLockObject) { NetworkSimulationSettings.IsSimulationEnabled = value; } } } public NetworkSimulationSet NetworkSimulationSettings => peerBase.NetworkSimulationSettings; public int MaximumTransferUnit { get { return mtu; } set { if (PeerState != 0) { throw new Exception("MaximumTransferUnit is only settable while disconnected. State: " + PeerState); } if (value < 576) { value = 576; } mtu = value; } } public bool IsEncryptionAvailable => peerBase.isEncryptionAvailable; public Type PayloadEncryptorType { get { return payloadEncryptorType; } set { if (value == null || typeof(ICryptoProvider).IsAssignableFrom(value)) { payloadEncryptorType = value; } else if ((int)LogLevel >= 1) { Listener.DebugReturn(LogLevel.Error, "Failed to set PayloadEncryptorType. Must implement ICryptoProvider."); } } } public Type EncryptorType { get { return encryptorType; } set { if (value == null || typeof(IPhotonEncryptor).IsAssignableFrom(value)) { encryptorType = value; } else if ((int)LogLevel >= 1) { Listener.DebugReturn(LogLevel.Error, "Failed to set PhotonPeer.EncryptorType. Type '" + value?.ToString() + "' does not implement IPhotonEncryptor."); } } } public int ServerTimeInMilliseconds { get { if (!peerBase.serverTimeOffsetIsAvailable) { return 0; } return peerBase.serverTimeOffset + ConnectionTime; } } public bool EnableServerTracing { get; set; } public int ConnectionTime => peerBase.timeInt; [Obsolete("Use Stats.RoundtripTime instead.")] public int RoundTripTime => peerBase.roundTripTime; [Obsolete("Use Stats.RoundTripTimeVariance instead.")] public int RoundTripTimeVariance => peerBase.roundTripTimeVariance; [Obsolete("Use Stats.LastRoundTripTime instead.")] public int LastRoundTripTime => peerBase.lastRoundTripTime; public long BytesIn => Stats.BytesIn; public long BytesOut => Stats.BytesOut; public int ByteCountCurrentDispatch => peerBase.ByteCountCurrentDispatch; public string CommandInfoCurrentDispatch { get { if (peerBase.CommandInCurrentDispatch == null) { return string.Empty; } return peerBase.CommandInCurrentDispatch.ToString(); } } public int ByteCountLastOperation => peerBase.ByteCountLastOperation; public int PacketLossByCrc => peerBase.packetLossByCrc; public int PacketLossByChallenge => peerBase.packetLossByChallenge; [Obsolete("Use Stats.UdpReliableCommandsResent instead.")] public int ResentReliableCommands => Stats.UdpReliableCommandsResent; [Obsolete("Use Stats.LastSendAckTimestamp instead.")] public int LastSendAckTime => Stats.LastSendAckTimestamp; public int LastSendAckDeltaTime => peerBase.timeInt - Stats.LastSendAckTimestamp; [Obsolete("Use Stats.LastSendOutgoingTimestamp instead.")] public int LastSendOutgoingTime => Stats.LastSendOutgoingTimestamp; public int LastSendOutgoingDeltaTime => peerBase.timeInt - Stats.LastSendOutgoingTimestamp; [Obsolete("Use Stats.LastReceiveTimestamp instead.")] public int TimestampOfLastSocketReceive => Stats.LastReceiveTimestamp; public int LastReceiveDeltaTime => peerBase.timeInt - Stats.LastReceiveTimestamp; public int LongestSendCall { get { return peerBase.longestSendCall; } set { peerBase.longestSendCall = value; } } public int CountDiscarded { get; set; } public int DeltaUnreliableNumber { get; set; } public int QueuedIncomingCommands => peerBase.QueuedIncomingCommandsCount; public int QueuedOutgoingCommands => peerBase.QueuedOutgoingCommandsCount; [Obsolete("Use Stats.UdpReliableCommandsInFlight.")] public int ReliableCommandsInFlight => Stats.UdpReliableCommandsInFlight; [Obsolete("Use Stats.UdpReliableCommandsInFlight instead. Check reference doc to make sure this is what you want to check.")] public int SentReliableCommandsCount => Stats.UdpReliableCommandsInFlight; public TrafficStats Stats { get; internal set; } public string VitalStatsToString(bool all = true) { float num = (float)peerBase.timeInt / 1000f; long num2 = (BytesIn + BytesOut) / 1000; int num3 = ((!(num <= 0f)) ? ((int)((float)num2 / num)) : 0); string text = $"Stats duration: {num:F2} sec. rtt(var): {Stats.RoundtripTime}({Stats.RoundtripTimeVariance})ms. min-to: {peerBase.bestRoundtripTimeout}ms. {num2:N0} kB -> {num3:N0} kB/sec. throttled: {peerBase.throttledBySendWindow}. out-queue: {peerBase.QueuedOutgoingCommandsCount}."; if (!all) { return text; } return $"{text}\n{Stats}"; } public PhotonPeer(ConnectionProtocol protocolType) { TransportProtocol = protocolType; SocketImplementationConfig = new Dictionary(5); SocketImplementationConfig[ConnectionProtocol.Udp] = typeof(SocketUdp); SocketImplementationConfig[ConnectionProtocol.Tcp] = typeof(SocketTcp); SocketImplementationConfig[ConnectionProtocol.WebSocket] = typeof(PhotonClientWebSocket); SocketImplementationConfig[ConnectionProtocol.WebSocketSecure] = typeof(PhotonClientWebSocket); CreatePeerBase(); Stats = new TrafficStats(peerBase.watch); } public PhotonPeer(IPhotonPeerListener listener, ConnectionProtocol protocolType) : this(protocolType) { Listener = listener; } [Obsolete("Use new overload with updated parameter order.")] public virtual bool Connect(string serverAddress, string proxyServerAddress, string appId, object photonToken, object customInitData = null) { return Connect(serverAddress, appId, photonToken, customInitData, proxyServerAddress); } public virtual bool Connect(string serverAddress, string appId, object photonToken, object customInitData = null, string proxyServerAddress = null) { lock (dispatchLockObject) { lock (sendOutgoingLockObject) { if (peerBase != null && peerBase.peerConnectionState != 0) { if ((int)LogLevel >= 2) { Listener.DebugReturn(LogLevel.Warning, $"Connect() failed. Peer is not Disconnected. peerConnectionState: {peerBase.peerConnectionState}."); } return false; } if (photonToken == null) { Encryptor = null; RandomizedSequenceNumbers = null; RandomizeSequenceNumbers = false; } CreatePeerBase(); peerBase.Reset(); Stats = new TrafficStats(peerBase.watch); PingUsedAsInit = false; peerBase.ServerAddress = serverAddress; peerBase.ProxyServerAddress = proxyServerAddress; peerBase.AppId = appId; peerBase.PhotonToken = photonToken; peerBase.CustomInitData = customInitData; Type value = null; if (!SocketImplementationConfig.TryGetValue(TransportProtocol, out value)) { peerBase.EnqueueDebugReturn(LogLevel.Error, $"Connect() failed. SocketImplementationConfig is not set for protocol {TransportProtocol}: {SupportClass.DictionaryToString(SocketImplementationConfig, includeTypes: false)}"); return false; } SocketImplementation = value; try { peerBase.PhotonSocket = (PhotonSocket)Activator.CreateInstance(SocketImplementation, peerBase); } catch (Exception arg) { if ((int)LogLevel >= 1) { Listener.DebugReturn(LogLevel.Error, $"Connect() failed to create a PhotonSocket instance for {TransportProtocol}. SocketImplementationConfig: {SupportClass.DictionaryToString(SocketImplementationConfig, includeTypes: false)} Exception: {arg}"); } return false; } return peerBase.Connect(serverAddress, proxyServerAddress, appId, photonToken); } } } private void CreatePeerBase() { ConnectionProtocol transportProtocol = TransportProtocol; if (transportProtocol == ConnectionProtocol.Tcp || transportProtocol - 4 <= ConnectionProtocol.Tcp) { TPeer tPeer = peerBase as TPeer; if (tPeer == null) { tPeer = (TPeer)(peerBase = new TPeer()); } tPeer.DoFraming = TransportProtocol == ConnectionProtocol.Tcp; } else if (!(peerBase is EnetPeer)) { peerBase = new EnetPeer(); } peerBase.photonPeer = this; peerBase.usedTransportProtocol = TransportProtocol; } public virtual void Disconnect() { lock (dispatchLockObject) { lock (sendOutgoingLockObject) { peerBase.Disconnect(); } } } public virtual void SimulateTimeoutDisconnect() { } public virtual void FetchServerTimestamp() { peerBase.FetchServerTimestamp(); } public bool EstablishEncryption() { if (AsyncKeyExchange) { ThreadPool.QueueUserWorkItem(delegate { peerBase.ExchangeKeysForEncryption(sendOutgoingLockObject); }); return true; } return peerBase.ExchangeKeysForEncryption(sendOutgoingLockObject); } [Obsolete("Use InitDatagramEncryption(byte[] encryptionSecret, byte[] hmacSecret).")] public bool InitDatagramEncryption(byte[] encryptionSecret, byte[] hmacSecret, bool randomizedSequenceNumbers, bool chainingModeGCM) { if (!randomizedSequenceNumbers || !chainingModeGCM) { if ((int)LogLevel >= 1) { Listener.DebugReturn(LogLevel.Error, "InitDatagramEncryption now requires randomizedSequenceNumbers and chainingModeGCM being true."); } return false; } return InitDatagramEncryption(encryptionSecret, hmacSecret); } public bool InitDatagramEncryption(byte[] encryptionSecret, byte[] hmacSecret) { if (encryptionSecret == null) { Listener.DebugReturn(LogLevel.Error, "InitDatagramEncryption() failed. Parameter encryptionSecret can not be null."); peerBase.EnqueueStatusCallback(StatusCode.EncryptionFailedToEstablish); return false; } if (EncryptorType == null) { Listener.DebugReturn(LogLevel.Error, "InitDatagramEncryption() failed. PhotonPeer.EncryptorType must be set to non-null value to initialize Datagram Encryption."); peerBase.EnqueueStatusCallback(StatusCode.EncryptionFailedToEstablish); return false; } try { Encryptor = (IPhotonEncryptor)Activator.CreateInstance(EncryptorType); } catch (Exception arg) { if ((int)LogLevel >= 2) { Listener.DebugReturn(LogLevel.Warning, $"InitDatagramEncryption() failed in CreateInstance({EncryptorType}). Caught exception: {arg}"); } } if (Encryptor == null) { Listener.DebugReturn(LogLevel.Error, "InitDatagramEncryption() failed. Could not create an encryptor instance."); peerBase.EnqueueStatusCallback(StatusCode.EncryptionFailedToEstablish); return false; } try { Encryptor.LogLevel = (int)LogLevel; Encryptor.Init(encryptionSecret, hmacSecret, null, chainingModeGCM: true, mtu); if ((int)LogLevel >= 3) { Listener.DebugReturn(LogLevel.Info, $"Datagram Encryptor ({Encryptor.GetType()}) successfully initialized."); } } catch (Exception arg2) { Listener.DebugReturn(LogLevel.Error, $"InitDatagramEncryption() failed in {Encryptor}.Init(). Caught exception: {arg2}"); peerBase.EnqueueStatusCallback(StatusCode.EncryptionFailedToEstablish); return false; } RandomizedSequenceNumbers = encryptionSecret; RandomizeSequenceNumbers = true; return true; } public void InitPayloadEncryption(byte[] secret) { PayloadEncryptionSecret = secret; } public virtual void Service() { while (DispatchIncomingCommands()) { } while (SendOutgoingCommands()) { } } public virtual bool SendOutgoingCommands() { Stats.SendOutgoingCommandsCalled(peerBase.timeInt); lock (sendOutgoingLockObject) { return peerBase.SendOutgoingCommands(); } } public virtual bool SendAcksOnly() { lock (sendOutgoingLockObject) { return peerBase.SendAcksOnly(); } } public virtual bool DispatchIncomingCommands() { Stats.DispatchIncomingCommandsCalled(peerBase.timeInt); lock (dispatchLockObject) { peerBase.ByteCountCurrentDispatch = 0; return peerBase.DispatchIncomingCommands(); } } public virtual bool SendOperation(byte operationCode, ParameterDictionary operationParameters, SendOptions sendOptions) { if (sendOptions.Encrypt && !IsEncryptionAvailable && peerBase.usedTransportProtocol != ConnectionProtocol.WebSocketSecure) { throw new ArgumentException("Can't use encryption yet. Exchange keys first."); } if (peerBase.peerConnectionState != ConnectionStateValue.Connected) { if ((int)LogLevel >= 1) { Listener.DebugReturn(LogLevel.Error, $"SendOperation failed. Not connected. Failed operation: {operationCode} PeerState: {peerBase.peerConnectionState}"); } Listener.OnStatusChanged(StatusCode.SendError); return false; } if (sendOptions.Channel >= ChannelCount) { if ((int)LogLevel >= 1) { Listener.DebugReturn(LogLevel.Error, $"SendOperation failed. Channel unavailable: ({sendOptions.Channel} >= channelCount {ChannelCount}). Failed operation: {operationCode}"); } Listener.OnStatusChanged(StatusCode.SendError); return false; } lock (enqueueLock) { StreamBuffer opBytes = peerBase.SerializeOperationToMessage(operationCode, operationParameters, EgMessageType.Operation, sendOptions.Encrypt); return peerBase.EnqueuePhotonMessage(opBytes, sendOptions); } } public virtual bool SendMessage(object message, SendOptions sendOptions) { if (sendOptions.Encrypt && !IsEncryptionAvailable && peerBase.usedTransportProtocol != ConnectionProtocol.WebSocketSecure) { throw new ArgumentException("Can't use encryption yet. Exchange keys first."); } if (peerBase.peerConnectionState != ConnectionStateValue.Connected) { if ((int)LogLevel >= 1) { Listener.DebugReturn(LogLevel.Error, $"SendMessage failed. Not connected. PeerState: {peerBase.peerConnectionState}"); } Listener.OnStatusChanged(StatusCode.SendError); return false; } if (sendOptions.Channel >= ChannelCount) { if ((int)LogLevel >= 1) { Listener.DebugReturn(LogLevel.Error, $"SendMessage failed. Channel unavailable: ({sendOptions.Channel} >= channelCount {ChannelCount}). Failed message: {message}"); } Listener.OnStatusChanged(StatusCode.SendError); return false; } lock (enqueueLock) { StreamBuffer opBytes = peerBase.SerializeMessageToMessage(message, sendOptions.Encrypt); return peerBase.EnqueuePhotonMessage(opBytes, sendOptions); } } public static bool RegisterType(Type customType, byte code, SerializeStreamMethod serializeMethod, DeserializeStreamMethod deserializeMethod) { return Protocol.TryRegisterType(customType, code, serializeMethod, deserializeMethod); } } public enum PhotonSocketState { Disconnected, Connecting, Connected, Disconnecting } public enum PhotonSocketError { Success, Skipped, NoData, Exception, Busy, PendingSend } public abstract class PhotonSocket { protected internal PeerBase peerBase; protected readonly ConnectionProtocol Protocol; public bool PollReceive; public string ConnectAddress; protected IPhotonPeerListener Listener => peerBase.Listener; protected internal int MTU => peerBase.mtu; public PhotonSocketState State { get; protected set; } public int SocketErrorCode { get; protected set; } public bool Connected => State == PhotonSocketState.Connected; public string ServerAddress { get; protected set; } public string ProxyServerAddress { get; protected set; } public string ServerIpAddress { get; protected set; } public int ServerPort { get; protected set; } public bool AddressResolvedAsIpv6 { get; protected internal set; } public string UrlProtocol { get; protected set; } public string UrlPath { get; protected set; } protected internal string SerializationProtocol { get { if (peerBase == null || peerBase.photonPeer == null) { return "GpBinaryV18"; } return Enum.GetName(typeof(SerializationProtocol), peerBase.photonPeer.SerializationProtocolType); } } public PhotonSocket(PeerBase peerBase) { if (peerBase == null) { throw new Exception("Can't init without peer"); } Protocol = peerBase.usedTransportProtocol; this.peerBase = peerBase; ConnectAddress = this.peerBase.ServerAddress; } public virtual bool Connect() { if (State != 0) { if ((int)peerBase.LogLevel >= 1) { peerBase.Listener.DebugReturn(LogLevel.Error, $"Connect() failed: connection in State: {State}"); } return false; } if (peerBase == null || Protocol != peerBase.usedTransportProtocol) { return false; } if (!TryParseAddress(peerBase.ServerAddress, out var host, out var port, out var scheme, out var absolutePath)) { if ((int)peerBase.LogLevel >= 1) { peerBase.Listener.DebugReturn(LogLevel.Error, "Failed parsing address: " + peerBase.ServerAddress); } return false; } ServerIpAddress = string.Empty; ServerAddress = host; ServerPort = port; UrlProtocol = scheme; UrlPath = absolutePath; if ((int)peerBase.LogLevel >= 4) { Listener.DebugReturn(LogLevel.Debug, $"PhotonSocket.Connect() {ServerAddress}:{ServerPort} this.Protocol: {Protocol}"); } return true; } public abstract bool Disconnect(); public abstract PhotonSocketError Send(byte[] data, int length); public abstract PhotonSocketError Receive(out byte[] data); public void HandleReceivedDatagram(byte[] inBuffer, int length, bool willBeReused) { peerBase.ReceiveIncomingCommands(inBuffer, length); } public bool ReportDebugOfLevel(LogLevel levelOfMessage) { return (int)peerBase.LogLevel >= (int)levelOfMessage; } public void EnqueueDebugReturn(LogLevel logLevel, string message) { peerBase.EnqueueDebugReturn(logLevel, message); } protected internal void HandleException(StatusCode statusCode) { State = PhotonSocketState.Disconnecting; peerBase.EnqueueStatusCallback(statusCode); peerBase.EnqueueActionForDispatch(delegate { peerBase.Disconnect(); }); } protected internal bool TryParseAddress(string url, out string host, out ushort port, out string scheme, out string absolutePath) { host = string.Empty; port = 0; scheme = string.Empty; absolutePath = string.Empty; if (string.IsNullOrEmpty(url)) { return false; } bool flag = url.Contains("://"); Uri result; bool num = Uri.TryCreate(flag ? url : ("net.tcp://" + url), UriKind.Absolute, out result); if (num) { host = result.Host; port = (ushort)((flag || url.Contains($":{result.Port}")) ? ((ushort)result.Port) : 0); scheme = (flag ? result.Scheme : string.Empty); absolutePath = ("/".Equals(result.AbsolutePath) ? string.Empty : result.AbsolutePath); } return num; } private bool IpAddressTryParse(string strIP, out IPAddress address) { address = null; if (string.IsNullOrEmpty(strIP)) { return false; } string[] array = strIP.Split(new char[1] { '.' }); if (array.Length != 4) { return false; } byte[] array2 = new byte[4]; for (int i = 0; i < array.Length; i++) { string s = array[i]; byte result = 0; if (!byte.TryParse(s, out result)) { return false; } array2[i] = result; } if (array2[0] == 0) { return false; } address = new IPAddress(array2); return true; } protected internal IPAddress[] GetIpAddresses(string hostname) { IPAddress address = null; if (IPAddress.TryParse(hostname, out address)) { if (address.AddressFamily == AddressFamily.InterNetworkV6 || IpAddressTryParse(hostname, out address)) { return new IPAddress[1] { address }; } HandleException(StatusCode.ServerAddressInvalid); return null; } IPAddress[] array; try { array = Dns.GetHostAddresses(ServerAddress); } catch (Exception arg) { try { array = Dns.GetHostByName(ServerAddress).AddressList; } catch (Exception arg2) { if (ReportDebugOfLevel(LogLevel.Warning)) { EnqueueDebugReturn(LogLevel.Warning, $"GetHostAddresses and GetHostEntry() failed for: {ServerAddress}. Caught and handled exceptions:\n{arg}\n{arg2}"); } HandleException(StatusCode.DnsExceptionOnConnect); return null; } } Array.Sort(array, AddressSortComparer); if (ReportDebugOfLevel(LogLevel.Info)) { string[] array2 = array.Select((IPAddress x) => $"{x} ({x.AddressFamily}({(int)x.AddressFamily}))").ToArray(); string arg3 = string.Join(", ", array2); if (ReportDebugOfLevel(LogLevel.Info)) { EnqueueDebugReturn(LogLevel.Info, $"{ServerAddress} resolved to {array2.Length} address(es): {arg3}"); } } return array; } private int AddressSortComparer(IPAddress x, IPAddress y) { if (x.AddressFamily == y.AddressFamily) { return 0; } if (x.AddressFamily != AddressFamily.InterNetworkV6) { return 1; } return -1; } } public enum SerializationProtocol { GpBinaryV16, GpBinaryV18 } internal static class SerializationProtocolFactory { internal static Protocol Create(SerializationProtocol serializationProtocol) { if (serializationProtocol == SerializationProtocol.GpBinaryV18) { return new Protocol18(); } return new Protocol16(); } } public delegate short SerializeStreamMethod(StreamBuffer outStream, object customObject); public delegate object DeserializeStreamMethod(StreamBuffer inStream, short length); internal class CustomType { public readonly byte Code; public readonly Type Type; public readonly SerializeStreamMethod SerializeStreamFunction; public readonly DeserializeStreamMethod DeserializeStreamFunction; public CustomType(Type type, byte code, SerializeStreamMethod serializeFunction, DeserializeStreamMethod deserializeFunction) { Type = type; Code = code; SerializeStreamFunction = serializeFunction; DeserializeStreamFunction = deserializeFunction; } } public abstract class Protocol { public enum DeserializationFlags { None, AllowPooledByteArray, WrapIncomingStructs } public readonly ByteArraySlicePool ByteArraySlicePool = new ByteArraySlicePool(); internal static readonly Dictionary TypeDict = new Dictionary(); internal static readonly Dictionary CodeDict = new Dictionary(); public abstract string ProtocolType { get; } public abstract byte[] VersionBytes { get; } public abstract void Serialize(StreamBuffer dout, object serObject, bool setType); public abstract void SerializeShort(StreamBuffer dout, short serObject, bool setType); public abstract void SerializeString(StreamBuffer dout, string serObject, bool setType); public abstract void SerializeEventData(StreamBuffer stream, EventData serObject, bool setType); public abstract void SerializeOperationRequest(StreamBuffer stream, byte operationCode, ParameterDictionary parameters, bool setType); public abstract void SerializeOperationResponse(StreamBuffer stream, OperationResponse serObject, bool setType); public abstract object Deserialize(StreamBuffer din, byte type, DeserializationFlags flags = DeserializationFlags.None); public abstract short DeserializeShort(StreamBuffer din); public abstract byte DeserializeByte(StreamBuffer din); public abstract EventData DeserializeEventData(StreamBuffer din, EventData target = null, DeserializationFlags flags = DeserializationFlags.None); public abstract OperationRequest DeserializeOperationRequest(StreamBuffer din, DeserializationFlags flags = DeserializationFlags.None); public abstract OperationResponse DeserializeOperationResponse(StreamBuffer stream, DeserializationFlags flags = DeserializationFlags.None); public abstract DisconnectMessage DeserializeDisconnectMessage(StreamBuffer stream); public byte[] Serialize(object obj) { StreamBuffer streamBuffer = new StreamBuffer(64); Serialize(streamBuffer, obj, setType: true); return streamBuffer.ToArray(); } public object Deserialize(StreamBuffer stream) { return Deserialize(stream, stream.ReadByte()); } public object Deserialize(byte[] serializedData) { StreamBuffer streamBuffer = new StreamBuffer(serializedData); return Deserialize(streamBuffer, streamBuffer.ReadByte()); } public object DeserializeMessage(StreamBuffer stream) { return Deserialize(stream, stream.ReadByte()); } internal void SerializeMessage(StreamBuffer ms, object msg) { Serialize(ms, msg, setType: true); } public static bool TryRegisterType(Type type, byte typeCode, SerializeStreamMethod serializeFunction, DeserializeStreamMethod deserializeFunction) { if (CodeDict.ContainsKey(typeCode) || type == null || TypeDict.ContainsKey(type)) { return false; } if (serializeFunction == null || deserializeFunction == null) { return false; } CustomType value = new CustomType(type, typeCode, serializeFunction, deserializeFunction); CodeDict.Add(typeCode, value); TypeDict.Add(type, value); return true; } } public class Protocol16 : Protocol { public enum GpType : byte { Unknown = 0, Array = 121, Boolean = 111, Byte = 98, ByteArray = 120, ObjectArray = 122, Short = 107, Float = 102, Dictionary = 68, Double = 100, Hashtable = 104, Integer = 105, IntegerArray = 110, Long = 108, String = 115, StringArray = 97, Custom = 99, Null = 42, EventData = 101, OperationRequest = 113, OperationResponse = 112 } private readonly byte[] versionBytes = new byte[2] { 1, 6 }; private readonly byte[] memShort = new byte[2]; private readonly long[] memLongBlock = new long[1]; private readonly byte[] memLongBlockBytes = new byte[8]; private static readonly float[] memFloatBlock = new float[1]; private static readonly byte[] memFloatBlockBytes = new byte[4]; private readonly double[] memDoubleBlock = new double[1]; private readonly byte[] memDoubleBlockBytes = new byte[8]; private readonly byte[] memInteger = new byte[4]; private readonly byte[] memLong = new byte[8]; private readonly byte[] memFloat = new byte[4]; private readonly byte[] memDouble = new byte[8]; public override string ProtocolType => "GpBinaryV16"; public override byte[] VersionBytes => versionBytes; private bool SerializeCustom(StreamBuffer dout, object serObject) { Type key = ((serObject is StructWrapper structWrapper) ? structWrapper.ttype : serObject.GetType()); if (Protocol.TypeDict.TryGetValue(key, out var value)) { if (value.SerializeStreamFunction == null) { throw new NullReferenceException($"Custom Type serialization failed. SerializeStreamFunction is null for Type: {value.Type} Code: {value.Code}."); } dout.WriteByte(99); dout.WriteByte(value.Code); int position = dout.Position; dout.Position += 2; short num = value.SerializeStreamFunction(dout, serObject); long num2 = dout.Position; dout.Position = position; byte code = value.Code; SerializeLengthAsShort(dout, num, "Custom Type " + code); dout.Position += num; if (dout.Position != num2) { throw new Exception($"Serialization failed. Stream position corrupted. Should be {num2} is now: {dout.Position} serializedLength: {num}"); } return true; } return false; } private object DeserializeCustom(StreamBuffer din, byte customTypeCode, DeserializationFlags flags = DeserializationFlags.None) { short num = DeserializeShort(din); if (num < 0) { throw new InvalidDataException($"DeserializeCustom() read negative length value: {num} before position: {din.Position}"); } if (num <= din.Available && Protocol.CodeDict.TryGetValue(customTypeCode, out var value)) { if (value.DeserializeStreamFunction == null) { throw new NullReferenceException($"Custom Type deserialization failed. DeserializeStreamFunction is null for Type: {value.Type} Code: {value.Code}."); } int position = din.Position; object result = value.DeserializeStreamFunction(din, num); if (din.Position - position != num) { din.Position = position + num; } return result; } int num2 = ((num <= din.Available) ? num : ((short)din.Available)); byte[] array = new byte[num2]; din.Read(array, 0, num2); return array; } private Type GetTypeOfCode(byte typeCode) { switch (typeCode) { case 105: return typeof(int); case 115: return typeof(string); case 97: return typeof(string[]); case 120: return typeof(byte[]); case 110: return typeof(int[]); case 104: return typeof(PhotonHashtable); case 68: return typeof(IDictionary); case 111: return typeof(bool); case 107: return typeof(short); case 108: return typeof(long); case 98: return typeof(byte); case 102: return typeof(float); case 100: return typeof(double); case 121: return typeof(Array); case 99: return typeof(CustomType); case 122: return typeof(object[]); case 101: return typeof(EventData); case 113: return typeof(OperationRequest); case 112: return typeof(OperationResponse); case 0: case 42: return typeof(object); default: throw new Exception("GetTypeOfCode failed for typeCode: " + typeCode); } } private GpType GetCodeOfType(Type type) { switch (Type.GetTypeCode(type)) { case TypeCode.Byte: return GpType.Byte; case TypeCode.String: return GpType.String; case TypeCode.Boolean: return GpType.Boolean; case TypeCode.Int16: return GpType.Short; case TypeCode.Int32: return GpType.Integer; case TypeCode.Int64: return GpType.Long; case TypeCode.Single: return GpType.Float; case TypeCode.Double: return GpType.Double; default: if (type.IsArray) { if (type == typeof(byte[])) { return GpType.ByteArray; } return GpType.Array; } if (type == typeof(PhotonHashtable)) { return GpType.Hashtable; } if (type == typeof(List)) { return GpType.ObjectArray; } if (type.IsGenericType && typeof(Dictionary<, >) == type.GetGenericTypeDefinition()) { return GpType.Dictionary; } if (type == typeof(EventData)) { return GpType.EventData; } if (type == typeof(OperationRequest)) { return GpType.OperationRequest; } if (type == typeof(OperationResponse)) { return GpType.OperationResponse; } return GpType.Unknown; } } private Array CreateArrayByType(byte arrayType, short length) { return Array.CreateInstance(GetTypeOfCode(arrayType), length); } public void SerializeOperationRequest(StreamBuffer stream, OperationRequest operation, bool setType) { SerializeOperationRequest(stream, operation.OperationCode, operation.Parameters, setType); } public override void SerializeOperationRequest(StreamBuffer stream, byte operationCode, ParameterDictionary parameters, bool setType) { if (setType) { stream.WriteByte(113); } stream.WriteByte(operationCode); SerializeParameterTable(stream, parameters); } public override OperationRequest DeserializeOperationRequest(StreamBuffer din, DeserializationFlags flags) { OperationRequest operationRequest = new OperationRequest(); operationRequest.OperationCode = DeserializeByte(din); operationRequest.Parameters = DeserializeParameterDictionary(din, operationRequest.Parameters, flags); return operationRequest; } public override void SerializeOperationResponse(StreamBuffer stream, OperationResponse serObject, bool setType) { if (setType) { stream.WriteByte(112); } stream.WriteByte(serObject.OperationCode); SerializeShort(stream, serObject.ReturnCode, setType: false); if (string.IsNullOrEmpty(serObject.DebugMessage)) { stream.WriteByte(42); } else { SerializeString(stream, serObject.DebugMessage, setType: false); } SerializeParameterTable(stream, serObject.Parameters); } public override DisconnectMessage DeserializeDisconnectMessage(StreamBuffer stream) { return new DisconnectMessage { Code = DeserializeShort(stream), DebugMessage = (Deserialize(stream, DeserializeByte(stream)) as string), Parameters = DeserializeParameterDictionary(stream) }; } public override OperationResponse DeserializeOperationResponse(StreamBuffer stream, DeserializationFlags flags = DeserializationFlags.None) { return new OperationResponse { OperationCode = DeserializeByte(stream), ReturnCode = DeserializeShort(stream), DebugMessage = (Deserialize(stream, DeserializeByte(stream)) as string), Parameters = DeserializeParameterDictionary(stream) }; } public override void SerializeEventData(StreamBuffer stream, EventData serObject, bool setType) { if (setType) { stream.WriteByte(101); } stream.WriteByte(serObject.Code); SerializeParameterTable(stream, serObject.Parameters); } public override EventData DeserializeEventData(StreamBuffer din, EventData target = null, DeserializationFlags flags = DeserializationFlags.None) { EventData eventData; if (target != null) { target.Reset(); eventData = target; } else { eventData = new EventData(); } eventData.Code = DeserializeByte(din); DeserializeParameterDictionary(din, eventData.Parameters); return eventData; } [Obsolete("Use ParameterDictionary instead of Dictionary.")] private void SerializeParameterTable(StreamBuffer stream, Dictionary parameters) { if (parameters == null || parameters.Count == 0) { SerializeShort(stream, 0, setType: false); return; } SerializeLengthAsShort(stream, parameters.Count, "ParameterTable"); foreach (KeyValuePair parameter in parameters) { stream.WriteByte(parameter.Key); Serialize(stream, parameter.Value, setType: true); } } private void SerializeParameterTable(StreamBuffer stream, ParameterDictionary parameters) { if (parameters == null || parameters.Count == 0) { SerializeShort(stream, 0, setType: false); return; } SerializeLengthAsShort(stream, parameters.Count, "Array"); foreach (KeyValuePair parameter in parameters) { stream.WriteByte(parameter.Key); Serialize(stream, parameter.Value, setType: true); } } private Dictionary DeserializeParameterTable(StreamBuffer stream, Dictionary target = null) { short num = DeserializeShort(stream); Dictionary dictionary = ((target != null) ? target : new Dictionary(num)); for (int i = 0; i < num; i++) { byte key = stream.ReadByte(); object value = Deserialize(stream, stream.ReadByte()); dictionary[key] = value; } return dictionary; } private ParameterDictionary DeserializeParameterDictionary(StreamBuffer stream, ParameterDictionary target = null, DeserializationFlags flags = DeserializationFlags.None) { short num = DeserializeShort(stream); ParameterDictionary parameterDictionary = ((target != null) ? target : new ParameterDictionary(num)); for (int i = 0; i < num; i++) { byte code = stream.ReadByte(); object value = Deserialize(stream, stream.ReadByte(), flags); parameterDictionary.Add(code, value); } return parameterDictionary; } public override void Serialize(StreamBuffer dout, object serObject, bool setType) { if (serObject == null) { if (setType) { dout.WriteByte(42); } return; } Type type = ((serObject is StructWrapper structWrapper) ? structWrapper.ttype : serObject.GetType()); switch (GetCodeOfType(type)) { case GpType.Byte: SerializeByte(dout, serObject.Get(), setType); return; case GpType.String: SerializeString(dout, (string)serObject, setType); return; case GpType.Boolean: SerializeBoolean(dout, serObject.Get(), setType); return; case GpType.Short: SerializeShort(dout, serObject.Get(), setType); return; case GpType.Integer: SerializeInteger(dout, serObject.Get(), setType); return; case GpType.Long: SerializeLong(dout, serObject.Get(), setType); return; case GpType.Float: SerializeFloat(dout, serObject.Get(), setType); return; case GpType.Double: SerializeDouble(dout, serObject.Get(), setType); return; case GpType.Hashtable: SerializeHashTable(dout, (PhotonHashtable)serObject, setType); return; case GpType.ByteArray: SerializeByteArray(dout, (byte[])serObject, setType); return; case GpType.ObjectArray: SerializeObjectArray(dout, (IList)serObject, setType); return; case GpType.Array: if (serObject is int[]) { SerializeIntArrayOptimized(dout, (int[])serObject, setType); } else if (type.GetElementType() == typeof(object)) { SerializeObjectArray(dout, serObject as object[], setType); } else { SerializeArray(dout, (Array)serObject, setType); } return; case GpType.Dictionary: SerializeDictionary(dout, (IDictionary)serObject, setType); return; case GpType.EventData: SerializeEventData(dout, (EventData)serObject, setType); return; case GpType.OperationResponse: SerializeOperationResponse(dout, (OperationResponse)serObject, setType); return; case GpType.OperationRequest: SerializeOperationRequest(dout, (OperationRequest)serObject, setType); return; } if (serObject is ArraySegment arraySegment) { SerializeByteArraySegment(dout, arraySegment.Array, arraySegment.Offset, arraySegment.Count, setType); } else if (!SerializeCustom(dout, serObject)) { if (serObject is StructWrapper) { throw new Exception("cannot serialize(): StructWrapper<" + (serObject as StructWrapper).ttype.Name + ">"); } throw new Exception("cannot serialize(): " + type); } } private void SerializeByte(StreamBuffer dout, byte serObject, bool setType) { if (setType) { dout.WriteByte(98); } dout.WriteByte(serObject); } private void SerializeBoolean(StreamBuffer dout, bool serObject, bool setType) { if (setType) { dout.WriteByte(111); } dout.WriteByte(serObject ? ((byte)1) : ((byte)0)); } public override void SerializeShort(StreamBuffer dout, short serObject, bool setType) { if (setType) { dout.WriteByte(107); } lock (memShort) { byte[] array = memShort; array[0] = (byte)(serObject >> 8); array[1] = (byte)serObject; dout.Write(array, 0, 2); } } public void SerializeLengthAsShort(StreamBuffer dout, int serObject, string type) { if (serObject > 32767 || serObject < 0) { throw new NotSupportedException($"Exceeding 32767 (short.MaxValue) entries are not supported. Failed writing {type}. Length: {serObject}"); } lock (memShort) { byte[] array = memShort; array[0] = (byte)(serObject >> 8); array[1] = (byte)serObject; dout.Write(array, 0, 2); } } private void SerializeInteger(StreamBuffer dout, int serObject, bool setType) { if (setType) { dout.WriteByte(105); } lock (memInteger) { byte[] array = memInteger; array[0] = (byte)(serObject >> 24); array[1] = (byte)(serObject >> 16); array[2] = (byte)(serObject >> 8); array[3] = (byte)serObject; dout.Write(array, 0, 4); } } private void SerializeLong(StreamBuffer dout, long serObject, bool setType) { if (setType) { dout.WriteByte(108); } lock (memLongBlock) { memLongBlock[0] = serObject; Buffer.BlockCopy(memLongBlock, 0, memLongBlockBytes, 0, 8); byte[] array = memLongBlockBytes; if (BitConverter.IsLittleEndian) { byte b = array[0]; byte b2 = array[1]; byte b3 = array[2]; byte b4 = array[3]; array[0] = array[7]; array[1] = array[6]; array[2] = array[5]; array[3] = array[4]; array[4] = b4; array[5] = b3; array[6] = b2; array[7] = b; } dout.Write(array, 0, 8); } } private void SerializeFloat(StreamBuffer dout, float serObject, bool setType) { if (setType) { dout.WriteByte(102); } lock (memFloatBlockBytes) { memFloatBlock[0] = serObject; Buffer.BlockCopy(memFloatBlock, 0, memFloatBlockBytes, 0, 4); if (BitConverter.IsLittleEndian) { byte b = memFloatBlockBytes[0]; byte b2 = memFloatBlockBytes[1]; memFloatBlockBytes[0] = memFloatBlockBytes[3]; memFloatBlockBytes[1] = memFloatBlockBytes[2]; memFloatBlockBytes[2] = b2; memFloatBlockBytes[3] = b; } dout.Write(memFloatBlockBytes, 0, 4); } } private void SerializeDouble(StreamBuffer dout, double serObject, bool setType) { if (setType) { dout.WriteByte(100); } lock (memDoubleBlockBytes) { memDoubleBlock[0] = serObject; Buffer.BlockCopy(memDoubleBlock, 0, memDoubleBlockBytes, 0, 8); byte[] array = memDoubleBlockBytes; if (BitConverter.IsLittleEndian) { byte b = array[0]; byte b2 = array[1]; byte b3 = array[2]; byte b4 = array[3]; array[0] = array[7]; array[1] = array[6]; array[2] = array[5]; array[3] = array[4]; array[4] = b4; array[5] = b3; array[6] = b2; array[7] = b; } dout.Write(array, 0, 8); } } public override void SerializeString(StreamBuffer stream, string value, bool setType) { if (setType) { stream.WriteByte(115); } int byteCount = Encoding.UTF8.GetByteCount(value); if (byteCount > 32767) { throw new NotSupportedException("Strings that exceed a UTF8-encoded byte-length of 32767 (short.MaxValue) are not supported. Yours is: " + byteCount); } SerializeLengthAsShort(stream, byteCount, "String"); int offset = 0; byte[] bufferAndAdvance = stream.GetBufferAndAdvance(byteCount, out offset); Encoding.UTF8.GetBytes(value, 0, value.Length, bufferAndAdvance, offset); } private void SerializeArray(StreamBuffer dout, Array serObject, bool setType) { if (setType) { dout.WriteByte(121); } SerializeLengthAsShort(dout, serObject.Length, "Array"); Type elementType = serObject.GetType().GetElementType(); GpType codeOfType = GetCodeOfType(elementType); if (codeOfType != 0) { dout.WriteByte((byte)codeOfType); if (codeOfType == GpType.Dictionary) { SerializeDictionaryHeader(dout, serObject, out var setKeyType, out var setValueType); for (int i = 0; i < serObject.Length; i++) { object value = serObject.GetValue(i); SerializeDictionaryElements(dout, value, setKeyType, setValueType); } } else { for (int j = 0; j < serObject.Length; j++) { object value2 = serObject.GetValue(j); Serialize(dout, value2, setType: false); } } return; } if (Protocol.TypeDict.TryGetValue(elementType, out var value3)) { dout.WriteByte(99); dout.WriteByte(value3.Code); for (int k = 0; k < serObject.Length; k++) { object value4 = serObject.GetValue(k); if (value3.SerializeStreamFunction == null) { throw new NullReferenceException($"Custom Type array serialization failed. SerializeStreamFunction is null for Type: {value3.Type} Code: {value3.Code}."); } int position = dout.Position; dout.Position += 2; short num = value3.SerializeStreamFunction(dout, value4); long num2 = dout.Position; dout.Position = position; short serObject2 = num; byte code = value3.Code; SerializeLengthAsShort(dout, serObject2, "Custom Type " + code); dout.Position += num; if (dout.Position != num2) { throw new Exception("Serialization failed. Stream position corrupted. Should be " + num2 + " is now: " + dout.Position + " serializedLength: " + num); } } return; } throw new NotSupportedException("cannot serialize array of type " + elementType); } private void SerializeByteArray(StreamBuffer dout, byte[] serObject, bool setType) { if (setType) { dout.WriteByte(120); } SerializeInteger(dout, serObject.Length, setType: false); dout.Write(serObject, 0, serObject.Length); } private void SerializeByteArraySegment(StreamBuffer dout, byte[] serObject, int offset, int count, bool setType) { if (setType) { dout.WriteByte(120); } SerializeInteger(dout, count, setType: false); dout.Write(serObject, offset, count); } private void SerializeIntArrayOptimized(StreamBuffer inWriter, int[] serObject, bool setType) { if (setType) { inWriter.WriteByte(121); } SerializeLengthAsShort(inWriter, serObject.Length, "int[]"); inWriter.WriteByte(105); byte[] array = new byte[serObject.Length * 4]; int num = 0; for (int i = 0; i < serObject.Length; i++) { array[num++] = (byte)(serObject[i] >> 24); array[num++] = (byte)(serObject[i] >> 16); array[num++] = (byte)(serObject[i] >> 8); array[num++] = (byte)serObject[i]; } inWriter.Write(array, 0, array.Length); } private void SerializeStringArray(StreamBuffer dout, string[] serObject, bool setType) { if (setType) { dout.WriteByte(97); } SerializeLengthAsShort(dout, serObject.Length, "string[]"); for (int i = 0; i < serObject.Length; i++) { SerializeString(dout, serObject[i], setType: false); } } private void SerializeObjectArray(StreamBuffer dout, IList objects, bool setType) { if (setType) { dout.WriteByte(122); } SerializeLengthAsShort(dout, objects.Count, "object[]"); for (int i = 0; i < objects.Count; i++) { object serObject = objects[i]; Serialize(dout, serObject, setType: true); } } private void SerializeHashTable(StreamBuffer dout, PhotonHashtable serObject, bool setType) { if (setType) { dout.WriteByte(104); } SerializeLengthAsShort(dout, serObject.Count, "PhotonHashtable"); foreach (object key in serObject.Keys) { Serialize(dout, key, setType: true); Serialize(dout, serObject[key], setType: true); } } private void SerializeDictionary(StreamBuffer dout, IDictionary serObject, bool setType) { if (setType) { dout.WriteByte(68); } SerializeDictionaryHeader(dout, serObject, out var setKeyType, out var setValueType); SerializeDictionaryElements(dout, serObject, setKeyType, setValueType); } private void SerializeDictionaryHeader(StreamBuffer writer, Type dictType) { SerializeDictionaryHeader(writer, dictType, out var _, out var _); } private void SerializeDictionaryHeader(StreamBuffer writer, object dict, out bool setKeyType, out bool setValueType) { Type[] genericArguments = dict.GetType().GetGenericArguments(); setKeyType = genericArguments[0] == typeof(object); setValueType = genericArguments[1] == typeof(object); if (setKeyType) { writer.WriteByte(0); } else { GpType codeOfType = GetCodeOfType(genericArguments[0]); if (codeOfType == GpType.Unknown || codeOfType == GpType.Dictionary) { throw new Exception("Unexpected - cannot serialize Dictionary with key type: " + genericArguments[0]); } writer.WriteByte((byte)codeOfType); } if (setValueType) { writer.WriteByte(0); return; } GpType codeOfType2 = GetCodeOfType(genericArguments[1]); if (codeOfType2 == GpType.Unknown) { throw new Exception("Unexpected - cannot serialize Dictionary with value type: " + genericArguments[1]); } writer.WriteByte((byte)codeOfType2); if (codeOfType2 == GpType.Dictionary) { SerializeDictionaryHeader(writer, genericArguments[1]); } } private void SerializeDictionaryElements(StreamBuffer writer, object dict, bool setKeyType, bool setValueType) { IDictionary dictionary = (IDictionary)dict; SerializeLengthAsShort(writer, dictionary.Count, "Dictionary elements"); foreach (DictionaryEntry item in dictionary) { if (!setValueType && item.Value == null) { throw new Exception("Can't serialize null in Dictionary with specific value-type."); } if (!setKeyType && item.Key == null) { throw new Exception("Can't serialize null in Dictionary with specific key-type."); } Serialize(writer, item.Key, setKeyType); Serialize(writer, item.Value, setValueType); } } public override object Deserialize(StreamBuffer din, byte type, DeserializationFlags flags = DeserializationFlags.None) { switch (type) { case 105: return DeserializeInteger(din); case 115: return DeserializeString(din); case 97: return DeserializeStringArray(din); case 120: return DeserializeByteArray(din); case 110: return DeserializeIntArray(din); case 104: return DeserializeHashTable(din); case 68: return DeserializeDictionary(din); case 111: return DeserializeBoolean(din); case 107: return DeserializeShort(din); case 108: return DeserializeLong(din); case 98: return DeserializeByte(din); case 102: return DeserializeFloat(din); case 100: return DeserializeDouble(din); case 121: return DeserializeArray(din); case 99: { byte customTypeCode = din.ReadByte(); return DeserializeCustom(din, customTypeCode); } case 122: return DeserializeObjectArray(din); case 101: return DeserializeEventData(din); case 113: return DeserializeOperationRequest(din, flags); case 112: return DeserializeOperationResponse(din, flags); case 0: case 42: return null; default: throw new Exception("Deserialize(): " + type + " pos: " + din.Position + " bytes: " + din.Length + ". " + SupportClass.ByteArrayToString(din.GetBuffer())); } } public override byte DeserializeByte(StreamBuffer din) { return din.ReadByte(); } private bool DeserializeBoolean(StreamBuffer din) { return din.ReadByte() != 0; } public override short DeserializeShort(StreamBuffer din) { lock (memShort) { byte[] array = memShort; din.Read(array, 0, 2); return (short)((array[0] << 8) | array[1]); } } private int DeserializeInteger(StreamBuffer din) { lock (memInteger) { byte[] array = memInteger; din.Read(array, 0, 4); return (array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]; } } private long DeserializeLong(StreamBuffer din) { lock (memLong) { byte[] array = memLong; din.Read(array, 0, 8); if (BitConverter.IsLittleEndian) { return (long)(((ulong)array[0] << 56) | ((ulong)array[1] << 48) | ((ulong)array[2] << 40) | ((ulong)array[3] << 32) | ((ulong)array[4] << 24) | ((ulong)array[5] << 16) | ((ulong)array[6] << 8) | array[7]); } return BitConverter.ToInt64(array, 0); } } private float DeserializeFloat(StreamBuffer din) { lock (memFloat) { byte[] array = memFloat; din.Read(array, 0, 4); if (BitConverter.IsLittleEndian) { byte b = array[0]; byte b2 = array[1]; array[0] = array[3]; array[1] = array[2]; array[2] = b2; array[3] = b; } return BitConverter.ToSingle(array, 0); } } private double DeserializeDouble(StreamBuffer din) { lock (memDouble) { byte[] array = memDouble; din.Read(array, 0, 8); if (BitConverter.IsLittleEndian) { byte b = array[0]; byte b2 = array[1]; byte b3 = array[2]; byte b4 = array[3]; array[0] = array[7]; array[1] = array[6]; array[2] = array[5]; array[3] = array[4]; array[4] = b4; array[5] = b3; array[6] = b2; array[7] = b; } return BitConverter.ToDouble(array, 0); } } private string DeserializeString(StreamBuffer din) { short num = DeserializeShort(din); if (num == 0) { return string.Empty; } if (num < 0) { throw new NotSupportedException("Received string type with unsupported length: " + num); } int offset = 0; byte[] bufferAndAdvance = din.GetBufferAndAdvance(num, out offset); return Encoding.UTF8.GetString(bufferAndAdvance, offset, num); } private Array DeserializeArray(StreamBuffer din) { short num = DeserializeShort(din); byte b = din.ReadByte(); Array array = null; switch (b) { case 121: { Array array2 = DeserializeArray(din); array = Array.CreateInstance(array2.GetType(), num); array.SetValue(array2, 0); for (short num4 = 1; num4 < num; num4++) { array2 = DeserializeArray(din); array.SetValue(array2, num4); } break; } case 120: { array = Array.CreateInstance(typeof(byte[]), num); for (short num5 = 0; num5 < num; num5++) { Array value3 = DeserializeByteArray(din); array.SetValue(value3, num5); } break; } case 98: array = DeserializeByteArray(din, num); break; case 105: array = DeserializeIntArray(din, num); break; case 99: { byte b2 = din.ReadByte(); if (Protocol.CodeDict.TryGetValue(b2, out var value)) { array = Array.CreateInstance(value.Type, num); for (int i = 0; i < num; i++) { short num3 = DeserializeShort(din); if (num3 < 0) { throw new InvalidDataException($"DeserializeArray read negative objLength value: {num3} before position: {din.Position}"); } if (value.DeserializeStreamFunction == null) { throw new NullReferenceException($"Custom Type array deserialization failed. DeserializeStreamFunction is null for Type: {value.Type} Code: {value.Code}."); } int position = din.Position; object value2 = value.DeserializeStreamFunction(din, num3); if (din.Position - position != num3) { din.Position = position + num3; } array.SetValue(value2, i); } break; } throw new Exception($"Cannot find deserializer for custom type: {b2}"); } case 68: { Array arrayResult = null; DeserializeDictionaryArray(din, num, out arrayResult); return arrayResult; } default: { array = CreateArrayByType(b, num); for (short num2 = 0; num2 < num; num2++) { array.SetValue(Deserialize(din, b), num2); } break; } } return array; } private byte[] DeserializeByteArray(StreamBuffer din, int size = -1) { if (size == -1) { size = DeserializeInteger(din); } byte[] array = new byte[size]; din.Read(array, 0, size); return array; } private int[] DeserializeIntArray(StreamBuffer din, int size = -1) { if (size == -1) { size = DeserializeInteger(din); } int[] array = new int[size]; for (int i = 0; i < size; i++) { array[i] = DeserializeInteger(din); } return array; } private string[] DeserializeStringArray(StreamBuffer din) { int num = DeserializeShort(din); string[] array = new string[num]; for (int i = 0; i < num; i++) { array[i] = DeserializeString(din); } return array; } private object[] DeserializeObjectArray(StreamBuffer din) { short num = DeserializeShort(din); object[] array = new object[num]; for (int i = 0; i < num; i++) { byte type = din.ReadByte(); array[i] = Deserialize(din, type); } return array; } private PhotonHashtable DeserializeHashTable(StreamBuffer din) { int num = DeserializeShort(din); PhotonHashtable photonHashtable = new PhotonHashtable(num); for (int i = 0; i < num; i++) { object obj = Deserialize(din, din.ReadByte()); object value = Deserialize(din, din.ReadByte()); if (obj != null) { photonHashtable[obj] = value; } } return photonHashtable; } private IDictionary DeserializeDictionary(StreamBuffer din) { byte b = din.ReadByte(); byte b2 = din.ReadByte(); if (b == 68 || b == 121) { throw new NotSupportedException("Client serialization protocol 1.6 does not support nesting Dictionary or Arrays into Dictionary keys."); } if (b2 == 68 || b2 == 121) { throw new NotSupportedException("Client serialization protocol 1.6 does not support nesting Dictionary or Arrays into Dictionary values."); } int num = DeserializeShort(din); bool flag = b == 0 || b == 42; bool flag2 = b2 == 0 || b2 == 42; Type typeOfCode = GetTypeOfCode(b); Type typeOfCode2 = GetTypeOfCode(b2); IDictionary dictionary = Activator.CreateInstance(typeof(Dictionary<, >).MakeGenericType(typeOfCode, typeOfCode2)) as IDictionary; for (int i = 0; i < num; i++) { object obj = Deserialize(din, flag ? din.ReadByte() : b); object value = Deserialize(din, flag2 ? din.ReadByte() : b2); if (obj != null) { dictionary.Add(obj, value); } } return dictionary; } private bool DeserializeDictionaryArray(StreamBuffer din, short size, out Array arrayResult) { byte keyTypeCode; byte valTypeCode; Type type = DeserializeDictionaryType(din, out keyTypeCode, out valTypeCode); arrayResult = Array.CreateInstance(type, size); for (short num = 0; num < size; num++) { if (!(Activator.CreateInstance(type) is IDictionary dictionary)) { return false; } short num2 = DeserializeShort(din); for (int i = 0; i < num2; i++) { object obj; if (keyTypeCode != 0) { obj = Deserialize(din, keyTypeCode); } else { byte type2 = din.ReadByte(); obj = Deserialize(din, type2); } object value; if (valTypeCode != 0) { value = Deserialize(din, valTypeCode); } else { byte type3 = din.ReadByte(); value = Deserialize(din, type3); } if (obj != null) { dictionary.Add(obj, value); } } arrayResult.SetValue(dictionary, num); } return true; } private Type DeserializeDictionaryType(StreamBuffer reader, out byte keyTypeCode, out byte valTypeCode) { keyTypeCode = reader.ReadByte(); valTypeCode = reader.ReadByte(); GpType gpType = (GpType)keyTypeCode; GpType gpType2 = (GpType)valTypeCode; Type type; switch (gpType) { case GpType.Unknown: type = typeof(object); break; case GpType.Dictionary: case GpType.Array: throw new NotSupportedException("Client serialization protocol 1.6 does not support nesting Dictionary or Arrays into Dictionary keys."); default: type = GetTypeOfCode(keyTypeCode); break; } Type type2; switch (gpType2) { case GpType.Unknown: type2 = typeof(object); break; case GpType.Dictionary: case GpType.Array: throw new NotSupportedException("Client serialization protocol 1.6 does not support nesting Dictionary or Arrays into Dictionary values."); default: type2 = GetTypeOfCode(valTypeCode); break; } return typeof(Dictionary<, >).MakeGenericType(type, type2); } } public class InvalidDataException : Exception { public InvalidDataException(string message) : base(message) { } } public class Protocol18 : Protocol { public enum GpType : byte { Unknown = 0, Boolean = 2, Byte = 3, Short = 4, Float = 5, Double = 6, String = 7, Null = 8, CompressedInt = 9, CompressedLong = 10, Int1 = 11, Int1_ = 12, Int2 = 13, Int2_ = 14, L1 = 15, L1_ = 16, L2 = 17, L2_ = 18, Custom = 19, CustomTypeSlim = 128, Dictionary = 20, Hashtable = 21, ObjectArray = 23, OperationRequest = 24, OperationResponse = 25, EventData = 26, BooleanFalse = 27, BooleanTrue = 28, ShortZero = 29, IntZero = 30, LongZero = 31, FloatZero = 32, DoubleZero = 33, ByteZero = 34, Array = 64, BooleanArray = 66, ByteArray = 67, ShortArray = 68, DoubleArray = 70, FloatArray = 69, StringArray = 71, HashtableArray = 85, DictionaryArray = 84, CustomTypeArray = 83, CompressedIntArray = 73, CompressedLongArray = 74 } private readonly byte[] versionBytes = new byte[2] { 1, 8 }; private static readonly byte[] boolMasks = new byte[8] { 1, 2, 4, 8, 16, 32, 64, 128 }; private readonly double[] memDoubleBlock = new double[1]; private readonly float[] memFloatBlock = new float[1]; private readonly byte[] memCustomTypeBodyLengthSerialized = new byte[5]; private readonly byte[] memCompressedUInt32 = new byte[5]; private byte[] memCompressedUInt64 = new byte[10]; public override string ProtocolType => "GpBinaryV18"; public override byte[] VersionBytes => versionBytes; public override void Serialize(StreamBuffer dout, object serObject, bool setType) { Write(dout, serObject, setType); } public override void SerializeShort(StreamBuffer dout, short serObject, bool setType) { WriteInt16(dout, serObject, setType); } public override void SerializeString(StreamBuffer dout, string serObject, bool setType) { WriteString(dout, serObject, setType); } public override object Deserialize(StreamBuffer din, byte type, DeserializationFlags flags = DeserializationFlags.None) { return Read(din, type); } public override short DeserializeShort(StreamBuffer din) { return ReadInt16(din); } public override byte DeserializeByte(StreamBuffer din) { return ReadByte(din); } private static Type GetAllowedDictionaryKeyTypes(GpType gpType) { switch (gpType) { case GpType.Byte: case GpType.ByteZero: return typeof(byte); case GpType.Short: case GpType.ShortZero: return typeof(short); case GpType.Float: case GpType.FloatZero: return typeof(float); case GpType.Double: case GpType.DoubleZero: return typeof(double); case GpType.String: return typeof(string); case GpType.CompressedInt: case GpType.Int1: case GpType.Int1_: case GpType.Int2: case GpType.Int2_: case GpType.IntZero: return typeof(int); case GpType.CompressedLong: case GpType.L1: case GpType.L1_: case GpType.L2: case GpType.L2_: case GpType.LongZero: return typeof(long); default: throw new Exception($"{gpType} is not a valid Type as Dictionary key."); } } private static Type GetClrArrayType(GpType gpType) { switch (gpType) { case GpType.Boolean: case GpType.BooleanFalse: case GpType.BooleanTrue: return typeof(bool); case GpType.Byte: case GpType.ByteZero: return typeof(byte); case GpType.Short: case GpType.ShortZero: return typeof(short); case GpType.Float: case GpType.FloatZero: return typeof(float); case GpType.Double: case GpType.DoubleZero: return typeof(double); case GpType.String: return typeof(string); case GpType.CompressedInt: case GpType.Int1: case GpType.Int1_: case GpType.Int2: case GpType.Int2_: case GpType.IntZero: return typeof(int); case GpType.CompressedLong: case GpType.L1: case GpType.L1_: case GpType.L2: case GpType.L2_: case GpType.LongZero: return typeof(long); case GpType.Hashtable: return typeof(PhotonHashtable); case GpType.OperationRequest: return typeof(OperationRequest); case GpType.OperationResponse: return typeof(OperationResponse); case GpType.EventData: return typeof(EventData); case GpType.BooleanArray: return typeof(bool[]); case GpType.ByteArray: return typeof(byte[]); case GpType.ShortArray: return typeof(short[]); case GpType.DoubleArray: return typeof(double[]); case GpType.FloatArray: return typeof(float[]); case GpType.StringArray: return typeof(string[]); case GpType.HashtableArray: return typeof(PhotonHashtable[]); case GpType.CompressedIntArray: return typeof(int[]); case GpType.CompressedLongArray: return typeof(long[]); default: return null; } } private GpType GetCodeOfType(Type type) { if (type == null) { return GpType.Null; } if (type == typeof(StructWrapper<>)) { return GpType.Unknown; } if (type.IsPrimitive || type.IsEnum) { TypeCode typeCode = Type.GetTypeCode(type); return GetCodeOfTypeCode(typeCode); } if (type == typeof(string)) { return GpType.String; } if (type.IsArray) { Type elementType = type.GetElementType(); if (elementType == null) { throw new InvalidDataException($"Arrays of type {type} are not supported"); } if (elementType.IsPrimitive) { switch (Type.GetTypeCode(elementType)) { case TypeCode.Byte: return GpType.ByteArray; case TypeCode.Int16: return GpType.ShortArray; case TypeCode.Int32: return GpType.CompressedIntArray; case TypeCode.Int64: return GpType.CompressedLongArray; case TypeCode.Boolean: return GpType.BooleanArray; case TypeCode.Single: return GpType.FloatArray; case TypeCode.Double: return GpType.DoubleArray; } } if (elementType.IsArray) { return GpType.Array; } if (elementType == typeof(string)) { return GpType.StringArray; } if (elementType == typeof(object) || elementType == typeof(StructWrapper)) { return GpType.ObjectArray; } if (elementType == typeof(PhotonHashtable)) { return GpType.HashtableArray; } if (elementType.IsGenericType && typeof(Dictionary<, >) == elementType.GetGenericTypeDefinition()) { return GpType.DictionaryArray; } return GpType.CustomTypeArray; } if (type == typeof(PhotonHashtable)) { return GpType.Hashtable; } if (type == typeof(List)) { return GpType.ObjectArray; } if (type.IsGenericType && typeof(Dictionary<, >) == type.GetGenericTypeDefinition()) { return GpType.Dictionary; } if (type == typeof(EventData)) { return GpType.EventData; } if (type == typeof(OperationRequest)) { return GpType.OperationRequest; } if (type == typeof(OperationResponse)) { return GpType.OperationResponse; } return GpType.Unknown; } private GpType GetCodeOfTypeCode(TypeCode type) { return type switch { TypeCode.Byte => GpType.Byte, TypeCode.String => GpType.String, TypeCode.Boolean => GpType.Boolean, TypeCode.Int16 => GpType.Short, TypeCode.Int32 => GpType.CompressedInt, TypeCode.Int64 => GpType.CompressedLong, TypeCode.Single => GpType.Float, TypeCode.Double => GpType.Double, _ => GpType.Unknown, }; } private object Read(StreamBuffer stream, DeserializationFlags flags, ParameterDictionary parameters) { return Read(stream, ReadByte(stream), flags, parameters); } private object Read(StreamBuffer stream, byte gpType, DeserializationFlags flags = DeserializationFlags.None, ParameterDictionary parameters = null) { int num = ((gpType >= 128) ? (gpType - 128) : gpType); num = ((num >= 64) ? (num - 64) : num); bool flag = (flags & DeserializationFlags.WrapIncomingStructs) == DeserializationFlags.WrapIncomingStructs; if (gpType >= 128 && gpType <= 228) { return ReadCustomType(stream, gpType); } switch ((GpType)gpType) { case GpType.Boolean: { bool flag2 = ReadBoolean(stream); if (!flag) { return flag2; } return parameters.wrapperPools.Acquire(flag2); } case GpType.BooleanTrue: { bool flag4 = true; if (!flag) { return flag4; } return parameters.wrapperPools.Acquire(flag4); } case GpType.BooleanFalse: { bool flag3 = false; if (!flag) { return flag3; } return parameters.wrapperPools.Acquire(flag3); } case GpType.Byte: { byte b = ReadByte(stream); if (!flag) { return b; } return parameters.wrapperPools.Acquire(b); } case GpType.ByteZero: { byte b2 = 0; if (!flag) { return b2; } return parameters.wrapperPools.Acquire(b2); } case GpType.Short: { short num11 = ReadInt16(stream); if (!flag) { return num11; } return parameters.wrapperPools.Acquire(num11); } case GpType.ShortZero: { short num19 = 0; if (!flag) { return num19; } return parameters.wrapperPools.Acquire(num19); } case GpType.Float: { float num15 = ReadSingle(stream); if (!flag) { return num15; } return parameters.wrapperPools.Acquire(num15); } case GpType.FloatZero: { float num8 = 0f; if (!flag) { return num8; } return parameters.wrapperPools.Acquire(num8); } case GpType.Double: { double num4 = ReadDouble(stream); if (!flag) { return num4; } return parameters.wrapperPools.Acquire(num4); } case GpType.DoubleZero: { double num17 = 0.0; if (!flag) { return num17; } return parameters.wrapperPools.Acquire(num17); } case GpType.String: return ReadString(stream); case GpType.Int1: { int num13 = ReadInt1(stream, signNegative: false); if (!flag) { return num13; } return parameters.wrapperPools.Acquire(num13); } case GpType.Int2: { int num10 = ReadInt2(stream, signNegative: false); if (!flag) { return num10; } return parameters.wrapperPools.Acquire(num10); } case GpType.Int1_: { int num6 = ReadInt1(stream, signNegative: true); if (!flag) { return num6; } return parameters.wrapperPools.Acquire(num6); } case GpType.Int2_: { int num3 = ReadInt2(stream, signNegative: true); if (!flag) { return num3; } return parameters.wrapperPools.Acquire(num3); } case GpType.CompressedInt: { int num18 = ReadCompressedInt32(stream); if (!flag) { return num18; } return parameters.wrapperPools.Acquire(num18); } case GpType.IntZero: { int num16 = 0; if (!flag) { return num16; } return parameters.wrapperPools.Acquire(num16); } case GpType.L1: { long num14 = ReadInt1(stream, signNegative: false); if (!flag) { return num14; } return parameters.wrapperPools.Acquire(num14); } case GpType.L2: { long num12 = ReadInt2(stream, signNegative: false); if (!flag) { return num12; } return parameters.wrapperPools.Acquire(num12); } case GpType.L1_: { long num9 = ReadInt1(stream, signNegative: true); if (!flag) { return num9; } return parameters.wrapperPools.Acquire(num9); } case GpType.L2_: { long num7 = ReadInt2(stream, signNegative: true); if (!flag) { return num7; } return parameters.wrapperPools.Acquire(num7); } case GpType.CompressedLong: { long num5 = ReadCompressedInt64(stream); if (!flag) { return num5; } return parameters.wrapperPools.Acquire(num5); } case GpType.LongZero: { long num2 = 0L; if (!flag) { return num2; } return parameters.wrapperPools.Acquire(num2); } case GpType.Hashtable: return ReadHashtable(stream, flags, parameters); case GpType.Dictionary: return ReadDictionary(stream, flags, parameters); case GpType.Custom: return ReadCustomType(stream, 0); case GpType.OperationRequest: return DeserializeOperationRequest(stream); case GpType.OperationResponse: return DeserializeOperationResponse(stream, flags); case GpType.EventData: return DeserializeEventData(stream); case GpType.ObjectArray: return ReadObjectArray(stream, flags, parameters); case GpType.BooleanArray: return ReadBooleanArray(stream); case GpType.ByteArray: return ReadByteArray(stream); case GpType.ShortArray: return ReadInt16Array(stream); case GpType.DoubleArray: return ReadDoubleArray(stream); case GpType.FloatArray: return ReadSingleArray(stream); case GpType.StringArray: return ReadStringArray(stream); case GpType.HashtableArray: return ReadHashtableArray(stream, flags, parameters); case GpType.DictionaryArray: return ReadDictionaryArray(stream, flags, parameters); case GpType.CustomTypeArray: return ReadCustomTypeArray(stream); case GpType.CompressedIntArray: return ReadCompressedInt32Array(stream); case GpType.CompressedLongArray: return ReadCompressedInt64Array(stream); case GpType.Array: return ReadArrayInArray(stream, flags, parameters); case GpType.Null: return null; default: throw new InvalidDataException(string.Format("GpTypeCode not found: {0}(0x{0:X}). Is not a CustomType either. Pos: {1} Available: {2}", gpType, stream.Position, stream.Available)); } } internal bool ReadBoolean(StreamBuffer stream) { return stream.ReadByte() > 0; } internal byte ReadByte(StreamBuffer stream) { return stream.ReadByte(); } internal short ReadInt16(StreamBuffer stream) { int offset; byte[] bufferAndAdvance = stream.GetBufferAndAdvance(2, out offset); return (short)(bufferAndAdvance[offset++] | (bufferAndAdvance[offset] << 8)); } internal ushort ReadUShort(StreamBuffer stream) { int offset; byte[] bufferAndAdvance = stream.GetBufferAndAdvance(2, out offset); return (ushort)(bufferAndAdvance[offset++] | (bufferAndAdvance[offset] << 8)); } internal int ReadInt32(StreamBuffer stream) { int offset; byte[] bufferAndAdvance = stream.GetBufferAndAdvance(4, out offset); return (bufferAndAdvance[offset++] << 24) | (bufferAndAdvance[offset++] << 16) | (bufferAndAdvance[offset++] << 8) | bufferAndAdvance[offset]; } internal long ReadInt64(StreamBuffer stream) { int offset; byte[] bufferAndAdvance = stream.GetBufferAndAdvance(4, out offset); return (long)(((ulong)bufferAndAdvance[offset++] << 56) | ((ulong)bufferAndAdvance[offset++] << 48) | ((ulong)bufferAndAdvance[offset++] << 40) | ((ulong)bufferAndAdvance[offset++] << 32) | ((ulong)bufferAndAdvance[offset++] << 24) | ((ulong)bufferAndAdvance[offset++] << 16) | ((ulong)bufferAndAdvance[offset++] << 8) | bufferAndAdvance[offset]); } internal float ReadSingle(StreamBuffer stream) { int offset; return BitConverter.ToSingle(stream.GetBufferAndAdvance(4, out offset), offset); } internal double ReadDouble(StreamBuffer stream) { int offset; return BitConverter.ToDouble(stream.GetBufferAndAdvance(8, out offset), offset); } internal ByteArraySlice ReadNonAllocByteArray(StreamBuffer stream) { uint num = ReadCompressedUInt32(stream); ByteArraySlice byteArraySlice = ByteArraySlicePool.Acquire((int)num); stream.Read(byteArraySlice.Buffer, 0, (int)num); byteArraySlice.Count = (int)num; return byteArraySlice; } internal byte[] ReadByteArray(StreamBuffer stream) { uint num = ReadCompressedUInt32(stream); byte[] array = new byte[num]; stream.Read(array, 0, (int)num); return array; } public object ReadCustomType(StreamBuffer stream, byte gpType = 0) { byte b = 0; b = ((gpType != 0) ? ((byte)(gpType - 128)) : stream.ReadByte()); int num = (int)ReadCompressedUInt32(stream); if (num < 0) { throw new InvalidDataException($"ReadCustomType read negative size value: {num} before position: {stream.Position}"); } bool flag = num <= stream.Available; if (!flag || num > 32767 || !Protocol.CodeDict.TryGetValue(b, out var value)) { UnknownType unknownType = new UnknownType { TypeCode = b, Size = num }; int num2 = (flag ? num : stream.Available); if (num2 > 0) { byte[] array = new byte[num2]; stream.Read(array, 0, num2); unknownType.Data = array; } return unknownType; } if (value.DeserializeStreamFunction == null) { throw new NullReferenceException($"Custom Type deserialization failed. DeserializeStreamFunction is null for Type: {value.Type} Code: {value.Code}."); } int position = stream.Position; object result = value.DeserializeStreamFunction(stream, (short)num); if (stream.Position - position != num) { stream.Position = position + num; } return result; } public override EventData DeserializeEventData(StreamBuffer din, EventData target = null, DeserializationFlags flags = DeserializationFlags.None) { EventData eventData; if (target != null) { target.Reset(); eventData = target; } else { eventData = new EventData(); } eventData.Code = ReadByte(din); short num = ReadByte(din); bool flag = (flags & DeserializationFlags.AllowPooledByteArray) == DeserializationFlags.AllowPooledByteArray; for (uint num2 = 0u; num2 < num; num2++) { byte b = din.ReadByte(); byte b2 = din.ReadByte(); if (b == eventData.SenderKey) { switch ((GpType)b2) { case GpType.Int1: eventData.Sender = ReadInt1(din, signNegative: false); break; case GpType.Int2: eventData.Sender = ReadInt2(din, signNegative: false); break; case GpType.Int1_: eventData.Sender = ReadInt1(din, signNegative: true); break; case GpType.Int2_: eventData.Sender = ReadInt2(din, signNegative: true); break; case GpType.CompressedInt: eventData.Sender = ReadCompressedInt32(din); break; case GpType.IntZero: eventData.Sender = 0; break; default: { object obj = Read(din, b2, flags, eventData.Parameters); break; } } } else { object obj = ((!flag) ? Read(din, b2, flags, eventData.Parameters) : ((b2 != 67) ? Read(din, b2, flags, eventData.Parameters) : ReadNonAllocByteArray(din))); eventData.Parameters.Add(b, obj); } } return eventData; } private ParameterDictionary ReadParameterDictionary(StreamBuffer stream, ParameterDictionary target = null, DeserializationFlags flags = DeserializationFlags.None) { short num = ReadByte(stream); ParameterDictionary parameterDictionary = ((target != null) ? target : new ParameterDictionary(num)); bool flag = (flags & DeserializationFlags.AllowPooledByteArray) == DeserializationFlags.AllowPooledByteArray; for (uint num2 = 0u; num2 < num; num2++) { byte code = stream.ReadByte(); byte b = stream.ReadByte(); object value = ((!flag || b != 67) ? Read(stream, b, flags, parameterDictionary) : ReadNonAllocByteArray(stream)); parameterDictionary.Add(code, value); } return parameterDictionary; } public PhotonHashtable ReadHashtable(StreamBuffer stream, DeserializationFlags flags, ParameterDictionary parameters) { int num = (int)ReadCompressedUInt32(stream); PhotonHashtable photonHashtable = new PhotonHashtable(num); for (uint num2 = 0u; num2 < num; num2++) { object obj = Read(stream, flags, parameters); object value = Read(stream, flags, parameters); if (obj != null) { if (!(obj is StructWrapper obj2)) { photonHashtable[obj] = value; } else { photonHashtable[obj2.Unwrap()] = value; } } } return photonHashtable; } public int[] ReadIntArray(StreamBuffer stream) { int num = ReadInt32(stream); int[] array = new int[num]; for (uint num2 = 0u; num2 < num; num2++) { array[num2] = ReadInt32(stream); } return array; } public override OperationRequest DeserializeOperationRequest(StreamBuffer din, DeserializationFlags flags = DeserializationFlags.None) { OperationRequest operationRequest = new OperationRequest(); operationRequest.OperationCode = ReadByte(din); operationRequest.Parameters = ReadParameterDictionary(din, operationRequest.Parameters, flags); return operationRequest; } public override OperationResponse DeserializeOperationResponse(StreamBuffer stream, DeserializationFlags flags = DeserializationFlags.None) { OperationResponse operationResponse = new OperationResponse(); operationResponse.OperationCode = ReadByte(stream); operationResponse.ReturnCode = ReadInt16(stream); operationResponse.DebugMessage = Read(stream, ReadByte(stream), flags, operationResponse.Parameters) as string; operationResponse.Parameters = ReadParameterDictionary(stream, operationResponse.Parameters, flags); return operationResponse; } public override DisconnectMessage DeserializeDisconnectMessage(StreamBuffer stream) { DisconnectMessage disconnectMessage = new DisconnectMessage(); disconnectMessage.Code = ReadInt16(stream); disconnectMessage.DebugMessage = Read(stream, ReadByte(stream)) as string; disconnectMessage.Parameters = ReadParameterDictionary(stream, disconnectMessage.Parameters); return disconnectMessage; } internal string ReadString(StreamBuffer stream) { int num = (int)ReadCompressedUInt32(stream); if (num == 0) { return string.Empty; } int offset = 0; byte[] bufferAndAdvance = stream.GetBufferAndAdvance(num, out offset); return Encoding.UTF8.GetString(bufferAndAdvance, offset, num); } private object ReadCustomTypeArray(StreamBuffer stream) { uint num = ReadCompressedUInt32(stream); byte b = stream.ReadByte(); if (!Protocol.CodeDict.TryGetValue(b, out var value)) { int position = stream.Position; for (uint num2 = 0u; num2 < num; num2++) { int num3 = (int)ReadCompressedUInt32(stream); int available = stream.Available; int num4 = ((num3 > available) ? available : num3); stream.Position += num4; } return new UnknownType[1] { new UnknownType { TypeCode = b, Size = stream.Position - position } }; } Array array = Array.CreateInstance(value.Type, (int)num); for (uint num5 = 0u; num5 < num; num5++) { int num6 = (int)ReadCompressedUInt32(stream); if (num6 < 0) { throw new InvalidDataException($"ReadCustomTypeArray read negative size value: {num6} before position: {stream.Position}"); } if (num6 > stream.Available || num6 > 32767) { stream.Position = stream.Length; throw new InvalidDataException($"ReadCustomTypeArray read size value: {num6} larger than short.MaxValue or available data: {stream.Available}"); } if (value.DeserializeStreamFunction == null) { throw new NullReferenceException($"Custom Type array deserialization failed. DeserializeStreamFunction is null for Type: {value.Type} Code: {value.Code}."); } int position2 = stream.Position; object obj = value.DeserializeStreamFunction(stream, (short)num6); if (stream.Position - position2 != num6) { stream.Position = position2 + num6; } if (obj != null && value.Type.IsAssignableFrom(obj.GetType())) { array.SetValue(obj, num5); } } return array; } private Type ReadDictionaryType(StreamBuffer stream, out GpType keyReadType, out GpType valueReadType) { keyReadType = (GpType)stream.ReadByte(); GpType gpType = (valueReadType = (GpType)stream.ReadByte()); Type type = ((keyReadType != 0) ? GetAllowedDictionaryKeyTypes(keyReadType) : typeof(object)); Type type2; switch (gpType) { case GpType.Unknown: type2 = typeof(object); break; case GpType.Dictionary: type2 = ReadDictionaryType(stream); break; case GpType.Array: type2 = GetDictArrayType(stream); valueReadType = GpType.Unknown; break; case GpType.ObjectArray: type2 = typeof(object[]); break; case GpType.HashtableArray: type2 = typeof(PhotonHashtable[]); break; default: type2 = GetClrArrayType(gpType); break; } return typeof(Dictionary<, >).MakeGenericType(type, type2); } private Type ReadDictionaryType(StreamBuffer stream) { GpType gpType = (GpType)stream.ReadByte(); GpType gpType2 = (GpType)stream.ReadByte(); Type type = ((gpType != 0) ? GetAllowedDictionaryKeyTypes(gpType) : typeof(object)); Type type2 = gpType2 switch { GpType.Unknown => typeof(object), GpType.Dictionary => ReadDictionaryType(stream), GpType.Array => GetDictArrayType(stream), _ => GetClrArrayType(gpType2), }; return typeof(Dictionary<, >).MakeGenericType(type, type2); } private Type GetDictArrayType(StreamBuffer stream) { GpType gpType = (GpType)stream.ReadByte(); int num = 0; while (gpType == GpType.Array) { num++; gpType = (GpType)stream.ReadByte(); } Type type = GetClrArrayType(gpType).MakeArrayType(); for (uint num2 = 0u; num2 < num; num2++) { type = type.MakeArrayType(); } return type; } private IDictionary ReadDictionary(StreamBuffer stream, DeserializationFlags flags, ParameterDictionary parameters) { GpType keyReadType; GpType valueReadType; Type type = ReadDictionaryType(stream, out keyReadType, out valueReadType); if (type == null) { return null; } if (!(Activator.CreateInstance(type) is IDictionary dictionary)) { return null; } ReadDictionaryElements(stream, keyReadType, valueReadType, dictionary, flags, parameters); return dictionary; } private bool ReadDictionaryElements(StreamBuffer stream, GpType keyReadType, GpType valueReadType, IDictionary dictionary, DeserializationFlags flags, ParameterDictionary parameters) { uint num = ReadCompressedUInt32(stream); for (uint num2 = 0u; num2 < num; num2++) { object obj = ((keyReadType == GpType.Unknown) ? Read(stream, flags, parameters) : Read(stream, (byte)keyReadType)); object value = ((valueReadType == GpType.Unknown) ? Read(stream, flags, parameters) : Read(stream, (byte)valueReadType)); if (obj != null) { dictionary.Add(obj, value); } } return true; } private object[] ReadObjectArray(StreamBuffer stream, DeserializationFlags flags, ParameterDictionary parameters) { uint num = ReadCompressedUInt32(stream); object[] array = new object[num]; for (uint num2 = 0u; num2 < num; num2++) { object obj = Read(stream, flags, parameters); array[num2] = obj; } return array; } private StructWrapper[] ReadWrapperArray(StreamBuffer stream, DeserializationFlags flags, ParameterDictionary parameters) { uint num = ReadCompressedUInt32(stream); StructWrapper[] array = new StructWrapper[num]; for (uint num2 = 0u; num2 < num; num2++) { object obj = Read(stream, flags, parameters); array[num2] = obj as StructWrapper; _ = array[num2]; } return array; } private bool[] ReadBooleanArray(StreamBuffer stream) { uint num = ReadCompressedUInt32(stream); bool[] array = new bool[num]; int num2 = (int)num / 8; int num3 = 0; while (num2 > 0) { byte b = stream.ReadByte(); array[num3++] = (b & 1) == 1; array[num3++] = (b & 2) == 2; array[num3++] = (b & 4) == 4; array[num3++] = (b & 8) == 8; array[num3++] = (b & 0x10) == 16; array[num3++] = (b & 0x20) == 32; array[num3++] = (b & 0x40) == 64; array[num3++] = (b & 0x80) == 128; num2--; } if (num3 < num) { byte b2 = stream.ReadByte(); int num4 = 0; while (num3 < num) { array[num3++] = (b2 & boolMasks[num4]) == boolMasks[num4]; num4++; } } return array; } internal short[] ReadInt16Array(StreamBuffer stream) { short[] array = new short[ReadCompressedUInt32(stream)]; for (uint num = 0u; num < array.Length; num++) { array[num] = ReadInt16(stream); } return array; } private float[] ReadSingleArray(StreamBuffer stream) { uint num = ReadCompressedUInt32(stream); int num2 = (int)(num * 4); float[] array = new float[num]; Buffer.BlockCopy(stream.GetBufferAndAdvance(num2, out var offset), offset, array, 0, num2); return array; } private double[] ReadDoubleArray(StreamBuffer stream) { uint num = ReadCompressedUInt32(stream); int num2 = (int)(num * 8); double[] array = new double[num]; Buffer.BlockCopy(stream.GetBufferAndAdvance(num2, out var offset), offset, array, 0, num2); return array; } internal string[] ReadStringArray(StreamBuffer stream) { string[] array = new string[ReadCompressedUInt32(stream)]; for (uint num = 0u; num < array.Length; num++) { array[num] = ReadString(stream); } return array; } private PhotonHashtable[] ReadHashtableArray(StreamBuffer stream, DeserializationFlags flags, ParameterDictionary parameters) { uint num = ReadCompressedUInt32(stream); PhotonHashtable[] array = new PhotonHashtable[num]; for (uint num2 = 0u; num2 < num; num2++) { array[num2] = ReadHashtable(stream, flags, parameters); } return array; } private IDictionary[] ReadDictionaryArray(StreamBuffer stream, DeserializationFlags flags, ParameterDictionary parameters) { GpType keyReadType; GpType valueReadType; Type type = ReadDictionaryType(stream, out keyReadType, out valueReadType); uint num = ReadCompressedUInt32(stream); IDictionary[] array = (IDictionary[])Array.CreateInstance(type, (int)num); for (uint num2 = 0u; num2 < num; num2++) { array[num2] = (IDictionary)Activator.CreateInstance(type); ReadDictionaryElements(stream, keyReadType, valueReadType, array[num2], flags, parameters); } return array; } private Array ReadArrayInArray(StreamBuffer stream, DeserializationFlags flags, ParameterDictionary parameters) { uint num = ReadCompressedUInt32(stream); Array array = null; Type type = null; for (uint num2 = 0u; num2 < num; num2++) { if (Read(stream, flags, parameters) is Array array2) { if (array == null) { type = array2.GetType(); array = Array.CreateInstance(type, (int)num); } if (type.IsAssignableFrom(array2.GetType())) { array.SetValue(array2, num2); } } } return array; } internal int ReadInt1(StreamBuffer stream, bool signNegative) { if (signNegative) { return -stream.ReadByte(); } return stream.ReadByte(); } internal int ReadInt2(StreamBuffer stream, bool signNegative) { if (signNegative) { return -ReadUShort(stream); } return ReadUShort(stream); } internal int ReadCompressedInt32(StreamBuffer stream) { uint value = ReadCompressedUInt32(stream); return DecodeZigZag32(value); } private uint ReadCompressedUInt32(StreamBuffer stream) { uint num = 0u; int num2 = 0; byte[] buffer = stream.GetBuffer(); int num3 = stream.Position; while (num2 != 35) { if (num3 >= stream.Length) { stream.Position = stream.Length; throw new EndOfStreamException("Failed to read full uint. offset: " + num3 + " stream.Length: " + stream.Length + " data.Length: " + buffer.Length + " stream.Available: " + stream.Available); } byte b = buffer[num3]; num3++; num |= (uint)((b & 0x7F) << num2); num2 += 7; if ((b & 0x80) == 0) { break; } } stream.Position = num3; return num; } internal long ReadCompressedInt64(StreamBuffer stream) { ulong value = ReadCompressedUInt64(stream); return DecodeZigZag64(value); } private ulong ReadCompressedUInt64(StreamBuffer stream) { ulong num = 0uL; int num2 = 0; byte[] buffer = stream.GetBuffer(); int num3 = stream.Position; while (num2 != 70) { if (num3 >= buffer.Length) { throw new EndOfStreamException("Failed to read full ulong."); } byte b = buffer[num3]; num3++; num |= (ulong)((long)(b & 0x7F) << num2); num2 += 7; if ((b & 0x80) == 0) { break; } } stream.Position = num3; return num; } internal int[] ReadCompressedInt32Array(StreamBuffer stream) { int[] array = new int[ReadCompressedUInt32(stream)]; for (uint num = 0u; num < array.Length; num++) { array[num] = ReadCompressedInt32(stream); } return array; } internal long[] ReadCompressedInt64Array(StreamBuffer stream) { long[] array = new long[ReadCompressedUInt32(stream)]; for (uint num = 0u; num < array.Length; num++) { array[num] = ReadCompressedInt64(stream); } return array; } private int DecodeZigZag32(uint value) { return (int)((value >> 1) ^ (0L - (long)(value & 1))); } private long DecodeZigZag64(ulong value) { return (long)((value >> 1) ^ (0L - (value & 1))); } internal void Write(StreamBuffer stream, object value, bool writeType) { if (value == null) { Write(stream, value, GpType.Null, writeType); } else { Write(stream, value, GetCodeOfType(value.GetType()), writeType); } } private void Write(StreamBuffer stream, object value, GpType gpType, bool writeType) { switch (gpType) { case GpType.Unknown: if (value is ByteArraySlice) { ByteArraySlice buffer = (ByteArraySlice)value; WriteByteArraySlice(stream, buffer, writeType); break; } if (value is ArraySegment seg) { WriteArraySegmentByte(stream, seg, writeType); break; } if (value is StructWrapper structWrapper) { switch (structWrapper.wrappedType) { case WrappedType.Bool: WriteBoolean(stream, value.Get(), writeType); break; case WrappedType.Byte: WriteByte(stream, value.Get(), writeType); break; case WrappedType.Int16: WriteInt16(stream, value.Get(), writeType); break; case WrappedType.Int32: WriteCompressedInt32(stream, value.Get(), writeType); break; case WrappedType.Int64: WriteCompressedInt64(stream, value.Get(), writeType); break; case WrappedType.Single: WriteSingle(stream, value.Get(), writeType); break; case WrappedType.Double: WriteDouble(stream, value.Get(), writeType); break; default: WriteCustomType(stream, value, writeType); break; } break; } goto case GpType.Custom; case GpType.Custom: WriteCustomType(stream, value, writeType); break; case GpType.CustomTypeArray: WriteCustomTypeArray(stream, value, writeType); break; case GpType.Array: WriteArrayInArray(stream, value, writeType); break; case GpType.CompressedInt: WriteCompressedInt32(stream, (int)value, writeType); break; case GpType.CompressedLong: WriteCompressedInt64(stream, (long)value, writeType); break; case GpType.Dictionary: WriteDictionary(stream, (IDictionary)value, writeType); break; case GpType.Byte: WriteByte(stream, (byte)value, writeType); break; case GpType.Double: WriteDouble(stream, (double)value, writeType); break; case GpType.EventData: SerializeEventData(stream, (EventData)value, writeType); break; case GpType.Float: WriteSingle(stream, (float)value, writeType); break; case GpType.Hashtable: WriteHashtable(stream, (PhotonHashtable)value, writeType); break; case GpType.Short: WriteInt16(stream, (short)value, writeType); break; case GpType.CompressedIntArray: WriteInt32ArrayCompressed(stream, (int[])value, writeType); break; case GpType.CompressedLongArray: WriteInt64ArrayCompressed(stream, (long[])value, writeType); break; case GpType.Boolean: WriteBoolean(stream, (bool)value, writeType); break; case GpType.OperationResponse: SerializeOperationResponse(stream, (OperationResponse)value, writeType); break; case GpType.OperationRequest: SerializeOperationRequest(stream, (OperationRequest)value, writeType); break; case GpType.String: WriteString(stream, (string)value, writeType); break; case GpType.ByteArray: WriteByteArray(stream, (byte[])value, writeType); break; case GpType.ObjectArray: WriteObjectArray(stream, (IList)value, writeType); break; case GpType.DictionaryArray: WriteDictionaryArray(stream, (IDictionary[])value, writeType); break; case GpType.DoubleArray: WriteDoubleArray(stream, (double[])value, writeType); break; case GpType.FloatArray: WriteSingleArray(stream, (float[])value, writeType); break; case GpType.HashtableArray: WriteHashtableArray(stream, value, writeType); break; case GpType.ShortArray: WriteInt16Array(stream, (short[])value, writeType); break; case GpType.BooleanArray: WriteBoolArray(stream, (bool[])value, writeType); break; case GpType.StringArray: WriteStringArray(stream, value, writeType); break; case GpType.Null: if (writeType) { stream.WriteByte(8); } break; } } public override void SerializeEventData(StreamBuffer stream, EventData serObject, bool setType) { if (setType) { stream.WriteByte(26); } stream.WriteByte(serObject.Code); WriteParameterTable(stream, serObject.Parameters); } private void WriteParameterTable(StreamBuffer stream, Dictionary parameters) { if (parameters == null || parameters.Count == 0) { WriteByte(stream, 0, writeType: false); return; } WriteByte(stream, (byte)parameters.Count, writeType: false); foreach (KeyValuePair parameter in parameters) { stream.WriteByte(parameter.Key); Write(stream, parameter.Value, writeType: true); } } private void WriteParameterTable(StreamBuffer stream, ParameterDictionary parameters) { if (parameters == null || parameters.Count == 0) { WriteByte(stream, 0, writeType: false); return; } WriteByte(stream, (byte)parameters.Count, writeType: false); foreach (KeyValuePair parameter in parameters) { stream.WriteByte(parameter.Key); Write(stream, parameter.Value, writeType: true); } } private void SerializeOperationRequest(StreamBuffer stream, OperationRequest operation, bool setType) { SerializeOperationRequest(stream, operation.OperationCode, operation.Parameters, setType); } public override void SerializeOperationRequest(StreamBuffer stream, byte operationCode, ParameterDictionary parameters, bool setType) { if (setType) { stream.WriteByte(24); } stream.WriteByte(operationCode); WriteParameterTable(stream, parameters); } public override void SerializeOperationResponse(StreamBuffer stream, OperationResponse serObject, bool setType) { if (setType) { stream.WriteByte(25); } stream.WriteByte(serObject.OperationCode); WriteInt16(stream, serObject.ReturnCode, writeType: false); if (string.IsNullOrEmpty(serObject.DebugMessage)) { stream.WriteByte(8); } else { stream.WriteByte(7); WriteString(stream, serObject.DebugMessage, writeType: false); } WriteParameterTable(stream, serObject.Parameters); } internal void WriteByte(StreamBuffer stream, byte value, bool writeType) { if (writeType) { if (value == 0) { stream.WriteByte(34); return; } stream.WriteByte(3); } stream.WriteByte(value); } internal void WriteBoolean(StreamBuffer stream, bool value, bool writeType) { if (writeType) { if (value) { stream.WriteByte(28); } else { stream.WriteByte(27); } } else { stream.WriteByte(value ? ((byte)1) : ((byte)0)); } } internal void WriteUShort(StreamBuffer stream, ushort value) { stream.WriteBytes((byte)value, (byte)(value >> 8)); } internal void WriteInt16(StreamBuffer stream, short value, bool writeType) { if (writeType) { if (value == 0) { stream.WriteByte(29); return; } stream.WriteByte(4); } stream.WriteBytes((byte)value, (byte)(value >> 8)); } internal void WriteDouble(StreamBuffer stream, double value, bool writeType) { if (writeType) { stream.WriteByte(6); } int offset; byte[] bufferAndAdvance = stream.GetBufferAndAdvance(8, out offset); lock (memDoubleBlock) { memDoubleBlock[0] = value; Buffer.BlockCopy(memDoubleBlock, 0, bufferAndAdvance, offset, 8); } } internal void WriteSingle(StreamBuffer stream, float value, bool writeType) { if (writeType) { stream.WriteByte(5); } int offset; byte[] bufferAndAdvance = stream.GetBufferAndAdvance(4, out offset); lock (memFloatBlock) { memFloatBlock[0] = value; Buffer.BlockCopy(memFloatBlock, 0, bufferAndAdvance, offset, 4); } } internal void WriteString(StreamBuffer stream, string value, bool writeType) { if (writeType) { stream.WriteByte(7); } int byteCount = Encoding.UTF8.GetByteCount(value); if (byteCount > 32767) { throw new NotSupportedException("Strings that exceed a UTF8-encoded byte-length of 32767 (short.MaxValue) are not supported. Yours is: " + byteCount); } WriteIntLength(stream, byteCount); int offset = 0; byte[] bufferAndAdvance = stream.GetBufferAndAdvance(byteCount, out offset); Encoding.UTF8.GetBytes(value, 0, value.Length, bufferAndAdvance, offset); } private void WriteHashtable(StreamBuffer stream, object value, bool writeType) { PhotonHashtable photonHashtable = (PhotonHashtable)value; if (writeType) { stream.WriteByte(21); } WriteIntLength(stream, photonHashtable.Count); foreach (DictionaryEntry item in photonHashtable) { Write(stream, item.Key, writeType: true); Write(stream, item.Value, writeType: true); } } internal void WriteByteArray(StreamBuffer stream, byte[] value, bool writeType) { if (writeType) { stream.WriteByte(67); } WriteIntLength(stream, value.Length); stream.Write(value, 0, value.Length); } private void WriteArraySegmentByte(StreamBuffer stream, ArraySegment seg, bool writeType) { if (writeType) { stream.WriteByte(67); } int count = seg.Count; WriteIntLength(stream, count); if (count > 0) { stream.Write(seg.Array, seg.Offset, count); } } private void WriteByteArraySlice(StreamBuffer stream, ByteArraySlice buffer, bool writeType) { if (writeType) { stream.WriteByte(67); } int count = buffer.Count; WriteIntLength(stream, count); stream.Write(buffer.Buffer, buffer.Offset, count); buffer.Release(); } internal void WriteInt32ArrayCompressed(StreamBuffer stream, int[] value, bool writeType) { if (writeType) { stream.WriteByte(73); } WriteIntLength(stream, value.Length); for (int i = 0; i < value.Length; i++) { WriteCompressedInt32(stream, value[i], writeType: false); } } private void WriteInt64ArrayCompressed(StreamBuffer stream, long[] values, bool setType) { if (setType) { stream.WriteByte(74); } WriteIntLength(stream, values.Length); for (int i = 0; i < values.Length; i++) { WriteCompressedInt64(stream, values[i], writeType: false); } } internal void WriteBoolArray(StreamBuffer stream, bool[] value, bool writeType) { if (writeType) { stream.WriteByte(66); } WriteIntLength(stream, value.Length); int num = value.Length >> 3; byte[] array = new byte[num + 1]; int num2 = 0; int i = 0; while (num > 0) { byte b = 0; if (value[i++]) { b = (byte)(b | 1u); } if (value[i++]) { b = (byte)(b | 2u); } if (value[i++]) { b = (byte)(b | 4u); } if (value[i++]) { b = (byte)(b | 8u); } if (value[i++]) { b = (byte)(b | 0x10u); } if (value[i++]) { b = (byte)(b | 0x20u); } if (value[i++]) { b = (byte)(b | 0x40u); } if (value[i++]) { b = (byte)(b | 0x80u); } array[num2] = b; num--; num2++; } if (i < value.Length) { byte b2 = 0; int num3 = 0; for (; i < value.Length; i++) { if (value[i]) { b2 |= (byte)(1 << num3); } num3++; } array[num2] = b2; num2++; } stream.Write(array, 0, num2); } internal void WriteInt16Array(StreamBuffer stream, short[] value, bool writeType) { if (writeType) { stream.WriteByte(68); } WriteIntLength(stream, value.Length); for (int i = 0; i < value.Length; i++) { WriteInt16(stream, value[i], writeType: false); } } internal void WriteSingleArray(StreamBuffer stream, float[] values, bool setType) { if (setType) { stream.WriteByte(69); } WriteIntLength(stream, values.Length); int num = values.Length * 4; int offset; byte[] bufferAndAdvance = stream.GetBufferAndAdvance(num, out offset); Buffer.BlockCopy(values, 0, bufferAndAdvance, offset, num); } internal void WriteDoubleArray(StreamBuffer stream, double[] values, bool setType) { if (setType) { stream.WriteByte(70); } WriteIntLength(stream, values.Length); int num = values.Length * 8; int offset; byte[] bufferAndAdvance = stream.GetBufferAndAdvance(num, out offset); Buffer.BlockCopy(values, 0, bufferAndAdvance, offset, num); } internal void WriteStringArray(StreamBuffer stream, object value0, bool writeType) { string[] array = (string[])value0; if (writeType) { stream.WriteByte(71); } WriteIntLength(stream, array.Length); for (int i = 0; i < array.Length; i++) { if (array[i] == null) { throw new InvalidDataException("Unexpected - cannot serialize string array with null element " + i); } WriteString(stream, array[i], writeType: false); } } private void WriteObjectArray(StreamBuffer stream, object array, bool writeType) { WriteObjectArray(stream, (IList)array, writeType); } private void WriteObjectArray(StreamBuffer stream, IList array, bool writeType) { if (writeType) { stream.WriteByte(23); } WriteIntLength(stream, array.Count); for (int i = 0; i < array.Count; i++) { object value = array[i]; Write(stream, value, writeType: true); } } private void WriteArrayInArray(StreamBuffer stream, object value, bool writeType) { object[] array = (object[])value; stream.WriteByte(64); WriteIntLength(stream, array.Length); object[] array2 = array; foreach (object value2 in array2) { Write(stream, value2, writeType: true); } } private void WriteCustomTypeBody(CustomType customType, StreamBuffer stream, object value) { if (customType.SerializeStreamFunction == null) { throw new NullReferenceException($"Custom Type serialization failed. SerializeStreamFunction is null for Type: {customType.Type} Code: {customType.Code}."); } int position = stream.Position; stream.Position++; uint num = (uint)customType.SerializeStreamFunction(stream, value); int num2 = stream.Position - position - 1; _ = num2; _ = num; int num3 = WriteCompressedUInt32(memCustomTypeBodyLengthSerialized, (uint)num2); if (num3 == 1) { stream.GetBuffer()[position] = memCustomTypeBodyLengthSerialized[0]; return; } for (int i = 0; i < num3 - 1; i++) { stream.WriteByte(0); } Buffer.BlockCopy(stream.GetBuffer(), position + 1, stream.GetBuffer(), position + num3, num2); Buffer.BlockCopy(memCustomTypeBodyLengthSerialized, 0, stream.GetBuffer(), position, num3); stream.Position = position + num3 + num2; } private void WriteCustomType(StreamBuffer stream, object value, bool writeType) { Type type = ((!(value is StructWrapper structWrapper)) ? value.GetType() : structWrapper.ttype); if (Protocol.TypeDict.TryGetValue(type, out var value2)) { if (writeType) { if (value2.Code < 100) { stream.WriteByte((byte)(128 + value2.Code)); } else { stream.WriteByte(19); stream.WriteByte(value2.Code); } } else { stream.WriteByte(value2.Code); } WriteCustomTypeBody(value2, stream, value); return; } throw new Exception("Write failed. Custom type not found: " + type); } private void WriteCustomTypeArray(StreamBuffer stream, object value, bool writeType) { IList list = (IList)value; Type elementType = value.GetType().GetElementType(); if (Protocol.TypeDict.TryGetValue(elementType, out var value2)) { if (writeType) { stream.WriteByte(83); } WriteIntLength(stream, list.Count); stream.WriteByte(value2.Code); { foreach (object item in list) { WriteCustomTypeBody(value2, stream, item); } return; } } throw new Exception("Write failed. Custom type of element not found: " + elementType); } private bool WriteArrayHeader(StreamBuffer stream, Type type) { Type elementType = type.GetElementType(); while (elementType.IsArray) { stream.WriteByte(64); elementType = elementType.GetElementType(); } GpType codeOfType = GetCodeOfType(elementType); if (codeOfType == GpType.Unknown) { return false; } stream.WriteByte((byte)(codeOfType | GpType.CustomTypeSlim)); return true; } private void WriteDictionaryElements(StreamBuffer stream, IDictionary dictionary, GpType keyWriteType, GpType valueWriteType) { WriteIntLength(stream, dictionary.Count); foreach (DictionaryEntry item in dictionary) { Write(stream, item.Key, keyWriteType == GpType.Unknown); Write(stream, item.Value, valueWriteType == GpType.Unknown); } } private void WriteDictionary(StreamBuffer stream, object dict, bool setType) { if (setType) { stream.WriteByte(20); } WriteDictionaryHeader(stream, dict.GetType(), out var keyWriteType, out var valueWriteType); IDictionary dictionary = (IDictionary)dict; WriteDictionaryElements(stream, dictionary, keyWriteType, valueWriteType); } private void WriteDictionaryHeader(StreamBuffer stream, Type type, out GpType keyWriteType, out GpType valueWriteType) { Type[] genericArguments = type.GetGenericArguments(); if (genericArguments[0] == typeof(object)) { stream.WriteByte(0); keyWriteType = GpType.Unknown; } else { if (!genericArguments[0].IsPrimitive && genericArguments[0] != typeof(string)) { throw new InvalidDataException("Unexpected - cannot serialize Dictionary with key type: " + genericArguments[0]); } keyWriteType = GetCodeOfType(genericArguments[0]); if (keyWriteType == GpType.Unknown) { throw new InvalidDataException("Unexpected - cannot serialize Dictionary with key type: " + genericArguments[0]); } stream.WriteByte((byte)keyWriteType); } if (genericArguments[1] == typeof(object)) { stream.WriteByte(0); valueWriteType = GpType.Unknown; return; } if (genericArguments[1].IsArray) { if (WriteArrayType(stream, genericArguments[1], out valueWriteType)) { return; } throw new InvalidDataException("Unexpected - cannot serialize Dictionary with value type: " + genericArguments[1]); } valueWriteType = GetCodeOfType(genericArguments[1]); if (valueWriteType == GpType.Unknown) { throw new InvalidDataException("Unexpected - cannot serialize Dictionary with value type: " + genericArguments[1]); } if (valueWriteType == GpType.Array) { if (!WriteArrayHeader(stream, genericArguments[1])) { throw new InvalidDataException("Unexpected - cannot serialize Dictionary with value type: " + genericArguments[1]); } } else if (valueWriteType == GpType.Dictionary) { stream.WriteByte((byte)valueWriteType); WriteDictionaryHeader(stream, genericArguments[1], out var _, out var _); } else { stream.WriteByte((byte)valueWriteType); } } private bool WriteArrayType(StreamBuffer stream, Type type, out GpType writeType) { Type elementType = type.GetElementType(); if (elementType == null) { throw new InvalidDataException("Unexpected - cannot serialize array with type: " + type); } if (elementType.IsArray) { while (elementType != null && elementType.IsArray) { stream.WriteByte(64); elementType = elementType.GetElementType(); } byte value = (byte)(GetCodeOfType(elementType) | GpType.Array); stream.WriteByte(value); writeType = GpType.Array; return true; } if (elementType.IsPrimitive) { byte b = (byte)(GetCodeOfType(elementType) | GpType.Array); if (b == 226) { b = 67; } stream.WriteByte(b); if (Enum.IsDefined(typeof(GpType), b)) { writeType = (GpType)b; return true; } writeType = GpType.Unknown; return false; } if (elementType == typeof(string)) { stream.WriteByte(71); writeType = GpType.StringArray; return true; } if (elementType == typeof(object)) { stream.WriteByte(23); writeType = GpType.ObjectArray; return true; } if (elementType == typeof(PhotonHashtable)) { stream.WriteByte(85); writeType = GpType.HashtableArray; return true; } writeType = GpType.Unknown; return false; } private void WriteHashtableArray(StreamBuffer stream, object value, bool writeType) { PhotonHashtable[] array = (PhotonHashtable[])value; if (writeType) { stream.WriteByte(85); } WriteIntLength(stream, array.Length); PhotonHashtable[] array2 = array; foreach (PhotonHashtable value2 in array2) { WriteHashtable(stream, value2, writeType: false); } } private void WriteDictionaryArray(StreamBuffer stream, IDictionary[] dictArray, bool writeType) { stream.WriteByte(84); WriteDictionaryHeader(stream, dictArray.GetType().GetElementType(), out var keyWriteType, out var valueWriteType); WriteIntLength(stream, dictArray.Length); foreach (IDictionary dictionary in dictArray) { WriteDictionaryElements(stream, dictionary, keyWriteType, valueWriteType); } } private void WriteIntLength(StreamBuffer stream, int value) { WriteCompressedUInt32(stream, (uint)value); } private void WriteVarInt32(StreamBuffer stream, int value, bool writeType) { WriteCompressedInt32(stream, value, writeType); } private void WriteCompressedInt32(StreamBuffer stream, int value, bool writeType) { if (writeType) { if (value == 0) { stream.WriteByte(30); return; } if (value > 0) { if (value <= 255) { stream.WriteByte(11); stream.WriteByte((byte)value); return; } if (value <= 65535) { stream.WriteByte(13); WriteUShort(stream, (ushort)value); return; } } else if (value >= -65535) { if (value >= -255) { stream.WriteByte(12); stream.WriteByte((byte)(-value)); return; } if (value >= -65535) { stream.WriteByte(14); WriteUShort(stream, (ushort)(-value)); return; } } } if (writeType) { stream.WriteByte(9); } uint value2 = EncodeZigZag32(value); WriteCompressedUInt32(stream, value2); } private void WriteCompressedInt64(StreamBuffer stream, long value, bool writeType) { if (writeType) { if (value == 0L) { stream.WriteByte(31); return; } if (value > 0) { if (value <= 255) { stream.WriteByte(15); stream.WriteByte((byte)value); return; } if (value <= 65535) { stream.WriteByte(17); WriteUShort(stream, (ushort)value); return; } } else if (value >= -65535) { if (value >= -255) { stream.WriteByte(16); stream.WriteByte((byte)(-value)); return; } if (value >= -65535) { stream.WriteByte(18); WriteUShort(stream, (ushort)(-value)); return; } } } if (writeType) { stream.WriteByte(10); } ulong value2 = EncodeZigZag64(value); WriteCompressedUInt64(stream, value2); } private void WriteCompressedUInt32(StreamBuffer stream, uint value) { lock (memCompressedUInt32) { stream.Write(memCompressedUInt32, 0, WriteCompressedUInt32(memCompressedUInt32, value)); } } private int WriteCompressedUInt32(byte[] buffer, uint value) { int num = 0; buffer[num] = (byte)(value & 0x7Fu); for (value >>= 7; value != 0; value >>= 7) { buffer[num] |= 128; buffer[++num] = (byte)(value & 0x7Fu); } return num + 1; } private void WriteCompressedUInt64(StreamBuffer stream, ulong value) { int num = 0; lock (memCompressedUInt64) { memCompressedUInt64[num] = (byte)(value & 0x7F); for (value >>= 7; value != 0; value >>= 7) { memCompressedUInt64[num] |= 128; memCompressedUInt64[++num] = (byte)(value & 0x7F); } num++; stream.Write(memCompressedUInt64, 0, num); } } private uint EncodeZigZag32(int value) { return (uint)((value << 1) ^ (value >> 31)); } private ulong EncodeZigZag64(long value) { return (ulong)((value << 1) ^ (value >> 63)); } } public class OperationRequest { public byte OperationCode; public ParameterDictionary Parameters; } public class OperationResponse { public byte OperationCode; public short ReturnCode; public string DebugMessage; public ParameterDictionary Parameters; public object this[byte parameterCode] { get { Parameters.TryGetValue(parameterCode, out var value); return value; } set { Parameters.Add(parameterCode, value); } } public override string ToString() { if (string.IsNullOrEmpty(DebugMessage)) { return $"OperationResponse {OperationCode}: ReturnCode: {ReturnCode}."; } return $"OperationResponse {OperationCode}: ReturnCode: {ReturnCode}. Msg: \"{DebugMessage}\""; } public string ToStringFull() { return string.Format("OperationResponse {0}: ReturnCode: {1} ({3}). Parameters: {2}", OperationCode, ReturnCode, SupportClass.DictionaryToString(Parameters), DebugMessage); } } public class DisconnectMessage { public short Code; public string DebugMessage; public ParameterDictionary Parameters; } public class EventData { public byte Code; public readonly ParameterDictionary Parameters; public byte SenderKey = 254; private int sender = -1; public byte CustomDataKey = 245; private object customData; public object this[byte key] { get { Parameters.TryGetValue(key, out var value); return value; } internal set { Parameters.Add(key, value); } } public int Sender { get { if (sender == -1) { int value; bool flag = Parameters.TryGetValue(SenderKey, out value); sender = (flag ? value : (-1)); } return sender; } internal set { sender = value; } } public object CustomData { get { if (customData == null) { Parameters.TryGetValue(CustomDataKey, out customData); } return customData; } internal set { customData = value; } } public EventData() { Parameters = new ParameterDictionary(); } internal void Reset() { Code = 0; Parameters.Clear(); sender = -1; customData = null; } public override string ToString() { return $"Event {Code.ToString()}."; } public string ToStringFull() { return $"Event {Code}: {SupportClass.DictionaryToString(Parameters)}"; } } public class MessageProtocol { private static readonly float[] memFloatBlock = new float[1]; private static readonly byte[] memDeserialize = new byte[4]; public static void Serialize(short value, byte[] target, ref int targetOffset) { target[targetOffset++] = (byte)(value >> 8); target[targetOffset++] = (byte)value; } public static void Deserialize(out short value, byte[] source, ref int offset) { value = (short)((source[offset++] << 8) | source[offset++]); } public static void Serialize(int value, byte[] target, ref int targetOffset) { target[targetOffset++] = (byte)(value >> 24); target[targetOffset++] = (byte)(value >> 16); target[targetOffset++] = (byte)(value >> 8); target[targetOffset++] = (byte)value; } public static void Deserialize(out int value, byte[] source, ref int offset) { value = (source[offset++] << 24) | (source[offset++] << 16) | (source[offset++] << 8) | source[offset++]; } public static void Serialize(float value, byte[] target, ref int targetOffset) { lock (memFloatBlock) { memFloatBlock[0] = value; Buffer.BlockCopy(memFloatBlock, 0, target, targetOffset, 4); } if (BitConverter.IsLittleEndian) { byte b = target[targetOffset]; byte b2 = target[targetOffset + 1]; target[targetOffset] = target[targetOffset + 3]; target[targetOffset + 1] = target[targetOffset + 2]; target[targetOffset + 2] = b2; target[targetOffset + 3] = b; } targetOffset += 4; } public static void Deserialize(out float value, byte[] source, ref int offset) { if (BitConverter.IsLittleEndian) { lock (memDeserialize) { byte[] array = memDeserialize; array[3] = source[offset++]; array[2] = source[offset++]; array[1] = source[offset++]; array[0] = source[offset++]; value = BitConverter.ToSingle(array, 0); return; } } value = BitConverter.ToSingle(source, offset); offset += 4; } } public enum DeliveryMode { Unreliable, Reliable, UnreliableUnsequenced, ReliableUnsequenced } public struct SendOptions { public static readonly SendOptions SendReliable = new SendOptions { Reliability = true }; public static readonly SendOptions SendUnreliable = new SendOptions { Reliability = false }; public DeliveryMode DeliveryMode; public bool Encrypt; public byte Channel; public bool Reliability { get { return DeliveryMode == DeliveryMode.Reliable; } set { DeliveryMode = (value ? DeliveryMode.Reliable : DeliveryMode.Unreliable); } } } public class SocketTcp : PhotonSocket, IDisposable { private Socket sock; private readonly object syncer = new object(); [Preserve] public SocketTcp(PeerBase npeer) : base(npeer) { if (ReportDebugOfLevel(LogLevel.Info)) { base.Listener.DebugReturn(LogLevel.Info, "SocketTcp, .Net, Unity."); } PollReceive = false; } ~SocketTcp() { Dispose(); } public void Dispose() { base.State = PhotonSocketState.Disconnecting; if (sock != null) { try { if (sock.Connected) { sock.Close(); } } catch (Exception arg) { if (ReportDebugOfLevel(LogLevel.Info)) { EnqueueDebugReturn(LogLevel.Info, $"Exception caught in Dispose(): {arg}"); } } } sock = null; base.State = PhotonSocketState.Disconnected; } public override bool Connect() { lock (syncer) { if (!base.Connect()) { return false; } base.State = PhotonSocketState.Connecting; } Thread thread = new Thread(DnsAndConnect); thread.IsBackground = true; thread.Start(); return true; } public override bool Disconnect() { if (ReportDebugOfLevel(LogLevel.Info)) { EnqueueDebugReturn(LogLevel.Info, "SocketTcp.Disconnect()"); } lock (syncer) { base.State = PhotonSocketState.Disconnecting; if (sock != null) { try { sock.Close(); } catch (Exception arg) { if (ReportDebugOfLevel(LogLevel.Info)) { EnqueueDebugReturn(LogLevel.Info, $"Exception caught in Disconnect(): {arg}"); } } } base.State = PhotonSocketState.Disconnected; } return true; } public override PhotonSocketError Send(byte[] data, int length) { try { if (sock == null || !sock.Connected) { return PhotonSocketError.Skipped; } sock.Send(data, 0, length, SocketFlags.None); } catch (Exception ex) { if (base.State != PhotonSocketState.Disconnecting && base.State != 0) { if (ReportDebugOfLevel(LogLevel.Info)) { SocketException ex2 = ex as SocketException; string text = ""; string text2 = ""; text = ((ex2 != null) ? $"ErrorCode {ex2.ErrorCode} Message {ex2.Message}" : ex.ToString()); if (sock != null) { text2 = $"Local: {sock.LocalEndPoint} Remote: {sock.RemoteEndPoint}."; } EnqueueDebugReturn(LogLevel.Info, "Caught exception sending to: " + base.ServerAddress + ". " + text2 + " Exception: " + text); } HandleException(StatusCode.SendError); } return PhotonSocketError.Exception; } return PhotonSocketError.Success; } public override PhotonSocketError Receive(out byte[] data) { data = null; return PhotonSocketError.NoData; } internal void DnsAndConnect() { IPAddress[] ipAddresses = GetIpAddresses(base.ServerAddress); if (ipAddresses == null) { return; } string text = string.Empty; IPAddress[] array = ipAddresses; foreach (IPAddress iPAddress in array) { try { sock = new Socket(iPAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); sock.NoDelay = true; sock.ReceiveTimeout = peerBase.DisconnectTimeout; sock.SendTimeout = peerBase.DisconnectTimeout; sock.Connect(iPAddress, base.ServerPort); if (sock != null && sock.Connected) { break; } } catch (SecurityException ex) { if (ReportDebugOfLevel(LogLevel.Error)) { text = text + ex?.ToString() + " "; EnqueueDebugReturn(LogLevel.Warning, $"SecurityException caught: {ex}"); } } catch (SocketException ex2) { if (ReportDebugOfLevel(LogLevel.Warning)) { text = text + ex2?.ToString() + " " + ex2.ErrorCode + "; "; EnqueueDebugReturn(LogLevel.Warning, $"SocketException caught: {ex2} ErrorCode: {ex2.ErrorCode}"); } } catch (Exception ex3) { if (ReportDebugOfLevel(LogLevel.Warning)) { text = text + ex3?.ToString() + "; "; EnqueueDebugReturn(LogLevel.Warning, $"Exception caught: {ex3}"); } } } if (sock == null || !sock.Connected) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, $"Failed to connect to server {base.ServerAddress} after testing each known IP ({ipAddresses.Length}). Error(s): {text}"); } HandleException(StatusCode.ExceptionOnConnect); } else { base.AddressResolvedAsIpv6 = sock.AddressFamily == AddressFamily.InterNetworkV6; base.ServerIpAddress = sock.RemoteEndPoint.ToString(); base.State = PhotonSocketState.Connected; Thread thread = new Thread(ReceiveLoop); thread.IsBackground = true; thread.Start(); } } public void ReceiveLoop() { StreamBuffer streamBuffer = new StreamBuffer(base.MTU); byte[] array = new byte[9]; while (base.State == PhotonSocketState.Connected) { streamBuffer.SetLength(0L); try { int num = 0; int num2 = 0; while (num < 9) { try { num2 = sock.Receive(array, num, 9 - num, SocketFlags.None); } catch (SocketException ex) { if (base.State != PhotonSocketState.Disconnecting && base.State > PhotonSocketState.Disconnected && ex.SocketErrorCode == SocketError.WouldBlock) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, "ReceiveLoop() got a WouldBlock exception. This is non-fatal. Going to continue."); } continue; } throw; } num += num2; if (num2 == 0) { throw new SocketException(10054); } } if (array[0] == 240) { HandleReceivedDatagram(array, array.Length, willBeReused: true); continue; } int num3 = (array[1] << 24) | (array[2] << 16) | (array[3] << 8) | array[4]; if (ReportDebugOfLevel(LogLevel.Debug)) { EnqueueDebugReturn(LogLevel.Debug, $"TCP < {num3}"); } streamBuffer.SetCapacityMinimum(num3 - 7); streamBuffer.Write(array, 7, num - 7); num = 0; num3 -= 9; while (num < num3) { try { num2 = sock.Receive(streamBuffer.GetBuffer(), streamBuffer.Position, num3 - num, SocketFlags.None); } catch (SocketException ex2) { if (base.State != PhotonSocketState.Disconnecting && base.State > PhotonSocketState.Disconnected && ex2.SocketErrorCode == SocketError.WouldBlock) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, "ReceiveLoop() got a WouldBlock exception. This is non-fatal. Going to continue."); } continue; } throw; } streamBuffer.Position += num2; num += num2; if (num2 == 0) { throw new SocketException(10054); } } HandleReceivedDatagram(streamBuffer.ToArray(), streamBuffer.Length, willBeReused: false); if (ReportDebugOfLevel(LogLevel.Debug)) { EnqueueDebugReturn(LogLevel.Debug, string.Format("TCP < {0}{1}", streamBuffer.Length, (streamBuffer.Length == num3 + 2) ? " OK" : " BAD")); } } catch (SocketException ex3) { if (base.State != PhotonSocketState.Disconnecting && base.State != 0) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, $"Receiving failed. SocketException: {ex3.SocketErrorCode}"); } if (ex3.SocketErrorCode == SocketError.ConnectionReset || ex3.SocketErrorCode == SocketError.ConnectionAborted) { HandleException(StatusCode.DisconnectByServerTimeout); } else { HandleException(StatusCode.ExceptionOnReceive); } } } catch (Exception arg) { if (base.State != PhotonSocketState.Disconnecting && base.State != 0) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, $"Receive issue. State: {base.State}. Server: '{base.ServerAddress}' Exception: {arg}"); } HandleException(StatusCode.ExceptionOnReceive); } } } lock (syncer) { if (base.State != PhotonSocketState.Disconnecting && base.State != 0) { Disconnect(); } } } } public class SocketTcpAsync : PhotonSocket, IDisposable { private class ReceiveContext { public Socket workSocket; public int ReceivedHeaderBytes; public byte[] HeaderBuffer; public int ExpectedMessageBytes; public int ReceivedMessageBytes; public byte[] MessageBuffer; public bool ReadingHeader => ExpectedMessageBytes == 0; public bool ReadingMessage => ExpectedMessageBytes != 0; public byte[] CurrentBuffer { get { if (!ReadingHeader) { return MessageBuffer; } return HeaderBuffer; } } public int CurrentOffset { get { if (!ReadingHeader) { return ReceivedMessageBytes; } return ReceivedHeaderBytes; } } public int CurrentExpected { get { if (!ReadingHeader) { return ExpectedMessageBytes; } return 9; } } public ReceiveContext(Socket socket, byte[] headerBuffer, byte[] messageBuffer) { HeaderBuffer = headerBuffer; MessageBuffer = messageBuffer; workSocket = socket; } public void Reset() { ReceivedHeaderBytes = 0; ExpectedMessageBytes = 0; ReceivedMessageBytes = 0; } } private Socket sock; private readonly object syncer = new object(); [Preserve] public SocketTcpAsync(PeerBase npeer) : base(npeer) { if (ReportDebugOfLevel(LogLevel.Info)) { base.Listener.DebugReturn(LogLevel.Info, "SocketTcpAsync, .Net, Unity."); } PollReceive = false; } ~SocketTcpAsync() { Dispose(); } public void Dispose() { base.State = PhotonSocketState.Disconnecting; if (sock != null) { try { if (sock.Connected) { sock.Close(); } } catch (Exception ex) { EnqueueDebugReturn(LogLevel.Info, "Exception in Dispose(): " + ex); } } sock = null; base.State = PhotonSocketState.Disconnected; } public override bool Connect() { lock (syncer) { if (!base.Connect()) { return false; } base.State = PhotonSocketState.Connecting; } Thread thread = new Thread(DnsAndConnect); thread.IsBackground = true; thread.Start(); return true; } public override bool Disconnect() { if (ReportDebugOfLevel(LogLevel.Info)) { EnqueueDebugReturn(LogLevel.Info, "SocketTcpAsync.Disconnect()"); } lock (syncer) { base.State = PhotonSocketState.Disconnecting; if (sock != null) { try { sock.Close(); } catch (Exception ex) { if (ReportDebugOfLevel(LogLevel.Info)) { EnqueueDebugReturn(LogLevel.Info, "Exception in Disconnect(): " + ex); } } } base.State = PhotonSocketState.Disconnected; } return true; } public override PhotonSocketError Send(byte[] data, int length) { try { if (sock == null || !sock.Connected) { return PhotonSocketError.Skipped; } sock.Send(data, 0, length, SocketFlags.None); } catch (Exception ex) { if (base.State != PhotonSocketState.Disconnecting && base.State != 0) { if (ReportDebugOfLevel(LogLevel.Info)) { string text = ""; if (sock != null) { text = string.Format(" Local: {0} Remote: {1} ({2}, {3})", sock.LocalEndPoint, sock.RemoteEndPoint, sock.Connected ? "connected" : "not connected", sock.IsBound ? "bound" : "not bound"); } EnqueueDebugReturn(LogLevel.Info, string.Format("Cannot send to: {0} ({4}). Uptime: {1} ms. {2} {3}", base.ServerAddress, peerBase.timeInt, base.AddressResolvedAsIpv6 ? " IPv6" : string.Empty, text, ex)); } HandleException(StatusCode.SendError); } return PhotonSocketError.Exception; } return PhotonSocketError.Success; } public override PhotonSocketError Receive(out byte[] data) { data = null; return PhotonSocketError.NoData; } internal void DnsAndConnect() { IPAddress[] ipAddresses = GetIpAddresses(base.ServerAddress); if (ipAddresses == null) { return; } string text = string.Empty; IPAddress[] array = ipAddresses; foreach (IPAddress iPAddress in array) { try { sock = new Socket(iPAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); sock.NoDelay = true; sock.ReceiveTimeout = peerBase.DisconnectTimeout; sock.SendTimeout = peerBase.DisconnectTimeout; sock.Connect(iPAddress, base.ServerPort); if (sock != null && sock.Connected) { break; } } catch (SecurityException ex) { if (ReportDebugOfLevel(LogLevel.Error)) { text = text + ex?.ToString() + " "; EnqueueDebugReturn(LogLevel.Warning, "SecurityException catched: " + ex); } } catch (SocketException ex2) { if (ReportDebugOfLevel(LogLevel.Warning)) { text = text + ex2?.ToString() + " " + ex2.ErrorCode + "; "; EnqueueDebugReturn(LogLevel.Warning, "SocketException catched: " + ex2?.ToString() + " ErrorCode: " + ex2.ErrorCode); } } catch (Exception ex3) { if (ReportDebugOfLevel(LogLevel.Warning)) { text = text + ex3?.ToString() + "; "; EnqueueDebugReturn(LogLevel.Warning, "Exception catched: " + ex3); } } } if (sock == null || !sock.Connected) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, "Failed to connect to server after testing each known IP. Error(s): " + text); } HandleException(StatusCode.ExceptionOnConnect); } else { base.AddressResolvedAsIpv6 = sock.AddressFamily == AddressFamily.InterNetworkV6; base.ServerIpAddress = sock.RemoteEndPoint.ToString(); base.State = PhotonSocketState.Connected; ReceiveAsync(); } } private void ReceiveAsync(ReceiveContext context = null) { if (context == null) { context = new ReceiveContext(sock, new byte[9], new byte[base.MTU]); } try { sock.BeginReceive(context.CurrentBuffer, context.CurrentOffset, context.CurrentExpected - context.CurrentOffset, SocketFlags.None, ReceiveAsync, context); } catch (Exception ex) { if (base.State != PhotonSocketState.Disconnecting && base.State != 0) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, "SocketTcpAsync.ReceiveAsync Exception. State: " + base.State.ToString() + ". Server: '" + base.ServerAddress + "' Exception: " + ex); } HandleException(StatusCode.ExceptionOnReceive); } } } private void ReceiveAsync(IAsyncResult ar) { if (base.State == PhotonSocketState.Disconnecting || base.State == PhotonSocketState.Disconnected) { return; } int num = 0; try { num = sock.EndReceive(ar); if (num == 0) { throw new SocketException(10054); } } catch (SocketException ex) { if (base.State != PhotonSocketState.Disconnecting && base.State != 0) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, "SocketTcpAsync.EndReceive SocketException. State: " + base.State.ToString() + ". Server: '" + base.ServerAddress + "' ErrorCode: " + ex.ErrorCode + " SocketErrorCode: " + ex.SocketErrorCode.ToString() + " Message: " + ex.Message + " " + ex); } HandleException(StatusCode.ExceptionOnReceive); return; } } catch (Exception ex2) { if (base.State != PhotonSocketState.Disconnecting && base.State != 0) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, "SocketTcpAsync.EndReceive Exception. State: " + base.State.ToString() + ". Server: '" + base.ServerAddress + "' Exception: " + ex2); } HandleException(StatusCode.ExceptionOnReceive); return; } } ReceiveContext receiveContext = (ReceiveContext)ar.AsyncState; if (num + receiveContext.CurrentOffset != receiveContext.CurrentExpected) { if (receiveContext.ReadingHeader) { receiveContext.ReceivedHeaderBytes += num; } else { receiveContext.ReceivedMessageBytes += num; } ReceiveAsync(receiveContext); } else if (receiveContext.ReadingHeader) { byte[] headerBuffer = receiveContext.HeaderBuffer; if (headerBuffer[0] == 240) { HandleReceivedDatagram(headerBuffer, headerBuffer.Length, willBeReused: true); receiveContext.Reset(); ReceiveAsync(receiveContext); return; } int num2 = (headerBuffer[1] << 24) | (headerBuffer[2] << 16) | (headerBuffer[3] << 8) | headerBuffer[4]; receiveContext.ExpectedMessageBytes = num2 - 7; if (receiveContext.ExpectedMessageBytes > receiveContext.MessageBuffer.Length) { receiveContext.MessageBuffer = new byte[receiveContext.ExpectedMessageBytes]; } receiveContext.MessageBuffer[0] = headerBuffer[7]; receiveContext.MessageBuffer[1] = headerBuffer[8]; receiveContext.ReceivedMessageBytes = 2; ReceiveAsync(receiveContext); } else { HandleReceivedDatagram(receiveContext.MessageBuffer, receiveContext.ExpectedMessageBytes, willBeReused: true); receiveContext.Reset(); ReceiveAsync(receiveContext); } } } public class SocketUdp : PhotonSocket, IDisposable { private Socket sock; private readonly object syncer = new object(); [Preserve] public SocketUdp(PeerBase npeer) : base(npeer) { if (ReportDebugOfLevel(LogLevel.Info)) { base.Listener.DebugReturn(LogLevel.Info, "SocketUdp, .Net, Unity."); } PollReceive = false; } ~SocketUdp() { Dispose(); } public void Dispose() { base.State = PhotonSocketState.Disconnecting; if (sock != null) { try { if (sock.Connected) { sock.Close(1); } } catch (Exception ex) { EnqueueDebugReturn(LogLevel.Info, "Exception in Dispose(): " + ex); } } sock = null; base.State = PhotonSocketState.Disconnected; } public override bool Connect() { lock (syncer) { if (!base.Connect()) { return false; } base.State = PhotonSocketState.Connecting; } Thread thread = new Thread(DnsAndConnect); thread.IsBackground = true; thread.Start(); return true; } public override bool Disconnect() { if (ReportDebugOfLevel(LogLevel.Info)) { EnqueueDebugReturn(LogLevel.Info, "SocketUdp.Disconnect()"); } lock (syncer) { base.State = PhotonSocketState.Disconnecting; if (sock != null) { try { sock.Close(1); } catch (Exception ex) { if (ReportDebugOfLevel(LogLevel.Info)) { EnqueueDebugReturn(LogLevel.Info, "Exception in Disconnect(): " + ex); } } } base.State = PhotonSocketState.Disconnected; } return true; } public override PhotonSocketError Send(byte[] data, int length) { try { if (sock == null || !sock.Connected) { return PhotonSocketError.Skipped; } sock.Send(data, 0, length, SocketFlags.None); } catch (SocketException ex) { if (ex.SocketErrorCode == SocketError.WouldBlock) { return PhotonSocketError.Busy; } if (base.State == PhotonSocketState.Disconnecting || base.State == PhotonSocketState.Disconnected) { return PhotonSocketError.Exception; } base.SocketErrorCode = (int)ex.SocketErrorCode; if (ReportDebugOfLevel(LogLevel.Info)) { string text = ""; if (sock != null) { text = string.Format(" Local: {0} Remote: {1} ({2}, {3})", sock.LocalEndPoint, sock.RemoteEndPoint, sock.Connected ? "connected" : "not connected", sock.IsBound ? "bound" : "not bound"); } EnqueueDebugReturn(LogLevel.Info, string.Format("Cannot send to: {0}. Uptime: {1} ms. {2} {3}\n{4}", base.ServerAddress, peerBase.timeInt, base.AddressResolvedAsIpv6 ? " IPv6" : string.Empty, text, ex)); } HandleException(StatusCode.SendError); return PhotonSocketError.Exception; } catch (Exception ex2) { if (base.State == PhotonSocketState.Disconnecting || base.State == PhotonSocketState.Disconnected) { return PhotonSocketError.Exception; } if (ReportDebugOfLevel(LogLevel.Info)) { string text2 = ""; if (sock != null) { text2 = string.Format(" Local: {0} Remote: {1} ({2}, {3})", sock.LocalEndPoint, sock.RemoteEndPoint, sock.Connected ? "connected" : "not connected", sock.IsBound ? "bound" : "not bound"); } EnqueueDebugReturn(LogLevel.Info, string.Format("Cannot send to: {0}. Uptime: {1} ms. {2} {3}\n{4}", base.ServerAddress, peerBase.timeInt, base.AddressResolvedAsIpv6 ? " IPv6" : string.Empty, text2, ex2)); } if (!sock.Connected) { EnqueueDebugReturn(LogLevel.Info, "Caught Exception in Send(). Ending connection with StatusCode.SendError."); HandleException(StatusCode.SendError); } return PhotonSocketError.Exception; } return PhotonSocketError.Success; } public override PhotonSocketError Receive(out byte[] data) { data = null; return PhotonSocketError.NoData; } internal void DnsAndConnect() { IPAddress[] ipAddresses = GetIpAddresses(base.ServerAddress); if (ipAddresses == null) { return; } string text = string.Empty; IPAddress[] array = ipAddresses; foreach (IPAddress iPAddress in array) { try { sock = new Socket(iPAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp); sock.Blocking = false; sock.Connect(iPAddress, base.ServerPort); if (sock != null && sock.Connected) { break; } } catch (SocketException ex) { if (ReportDebugOfLevel(LogLevel.Warning)) { text = text + ex?.ToString() + " " + ex.ErrorCode + "; "; EnqueueDebugReturn(LogLevel.Warning, "SocketException caught: " + ex?.ToString() + " ErrorCode: " + ex.ErrorCode); } } catch (Exception ex2) { if (ReportDebugOfLevel(LogLevel.Warning)) { text = text + ex2?.ToString() + "; "; EnqueueDebugReturn(LogLevel.Warning, "Exception caught: " + ex2); } } } if (sock == null || !sock.Connected) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, "Failed to connect to server after testing each known IP. Error(s): " + text); } HandleException(StatusCode.ExceptionOnConnect); } else { base.AddressResolvedAsIpv6 = sock.AddressFamily == AddressFamily.InterNetworkV6; base.ServerIpAddress = sock.RemoteEndPoint.ToString(); base.State = PhotonSocketState.Connected; Thread thread = new Thread(ReceiveLoop); thread.IsBackground = true; thread.Start(); } } public void ReceiveLoop() { byte[] array = new byte[base.MTU]; while (base.State == PhotonSocketState.Connected) { try { if (sock.Poll(5000, SelectMode.SelectRead)) { int length = sock.Receive(array); HandleReceivedDatagram(array, length, willBeReused: true); } } catch (SocketException ex) { if (ex.ErrorCode != 10055 && ex.ErrorCode != 10040 && base.State != PhotonSocketState.Disconnecting && base.State != 0) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, "Receive issue. State: " + base.State.ToString() + ". Server: '" + base.ServerAddress + "' ErrorCode: " + ex.ErrorCode + " SocketErrorCode: " + ex.SocketErrorCode.ToString() + " Message: " + ex.Message + " " + ex); } HandleException(StatusCode.ExceptionOnReceive); } } catch (Exception ex2) { if (base.State != PhotonSocketState.Disconnecting && base.State != 0) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, "Receive issue. State: " + base.State.ToString() + ". Server: '" + base.ServerAddress + "' Message: " + ex2.Message + " Exception: " + ex2); } HandleException(StatusCode.ExceptionOnReceive); } } } lock (syncer) { if (base.State != PhotonSocketState.Disconnecting && base.State != 0) { Disconnect(); } } } } public class SocketUdpAsync : PhotonSocket, IDisposable { private Socket sock; private readonly object syncer = new object(); [Preserve] public SocketUdpAsync(PeerBase npeer) : base(npeer) { if (ReportDebugOfLevel(LogLevel.Info)) { base.Listener.DebugReturn(LogLevel.Info, "SocketUdpAsync, .Net, Unity."); } PollReceive = false; } ~SocketUdpAsync() { Dispose(); } public void Dispose() { base.State = PhotonSocketState.Disconnecting; if (sock != null) { try { if (sock.Connected) { sock.Close(); } } catch (Exception ex) { EnqueueDebugReturn(LogLevel.Info, "Exception in Dispose(): " + ex); } } sock = null; base.State = PhotonSocketState.Disconnected; } public override bool Connect() { lock (syncer) { if (!base.Connect()) { return false; } base.State = PhotonSocketState.Connecting; } Thread thread = new Thread(DnsAndConnect); thread.IsBackground = true; thread.Start(); return true; } public override bool Disconnect() { if (ReportDebugOfLevel(LogLevel.Info)) { EnqueueDebugReturn(LogLevel.Info, "SocketUdpAsync.Disconnect()"); } lock (syncer) { base.State = PhotonSocketState.Disconnecting; if (sock != null) { try { sock.Close(); } catch (Exception ex) { if (ReportDebugOfLevel(LogLevel.Info)) { EnqueueDebugReturn(LogLevel.Info, "Exception in Disconnect(): " + ex); } } } base.State = PhotonSocketState.Disconnected; } return true; } public override PhotonSocketError Send(byte[] data, int length) { try { if (sock == null || !sock.Connected) { return PhotonSocketError.Skipped; } sock.Send(data, 0, length, SocketFlags.None); } catch (Exception ex) { if (base.State != PhotonSocketState.Disconnecting && base.State != 0) { if (ReportDebugOfLevel(LogLevel.Info)) { string text = ""; if (sock != null) { text = string.Format(" Local: {0} Remote: {1} ({2}, {3})", sock.LocalEndPoint, sock.RemoteEndPoint, sock.Connected ? "connected" : "not connected", sock.IsBound ? "bound" : "not bound"); } EnqueueDebugReturn(LogLevel.Info, string.Format("Cannot send to: {0}. Uptime: {1} ms. {2} {3}\n{4}", base.ServerAddress, peerBase.timeInt, base.AddressResolvedAsIpv6 ? " IPv6" : string.Empty, text, ex)); } if (!sock.Connected) { EnqueueDebugReturn(LogLevel.Info, "Socket got closed by the local system. Disconnecting from within Send with StatusCode.Disconnect."); HandleException(StatusCode.SendError); } } return PhotonSocketError.Exception; } return PhotonSocketError.Success; } public override PhotonSocketError Receive(out byte[] data) { data = null; return PhotonSocketError.NoData; } internal void DnsAndConnect() { IPAddress[] ipAddresses = GetIpAddresses(base.ServerAddress); if (ipAddresses == null) { return; } string text = string.Empty; IPAddress[] array = ipAddresses; foreach (IPAddress iPAddress in array) { try { sock = new Socket(iPAddress.AddressFamily, SocketType.Dgram, ProtocolType.Udp); sock.Connect(iPAddress, base.ServerPort); if (sock != null && sock.Connected) { break; } } catch (SocketException ex) { if (ReportDebugOfLevel(LogLevel.Warning)) { text = text + ex?.ToString() + " " + ex.ErrorCode + "; "; EnqueueDebugReturn(LogLevel.Warning, "SocketException catched: " + ex?.ToString() + " ErrorCode: " + ex.ErrorCode); } } catch (Exception ex2) { if (ReportDebugOfLevel(LogLevel.Warning)) { text = text + ex2?.ToString() + "; "; EnqueueDebugReturn(LogLevel.Warning, "Exception catched: " + ex2); } } } if (sock == null || !sock.Connected) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, "Failed to connect to server after testing each known IP. Error(s): " + text); } HandleException(StatusCode.ExceptionOnConnect); } else { base.AddressResolvedAsIpv6 = sock.AddressFamily == AddressFamily.InterNetworkV6; base.ServerIpAddress = sock.RemoteEndPoint.ToString(); base.State = PhotonSocketState.Connected; StartReceive(); } } public void StartReceive() { byte[] array = new byte[base.MTU]; try { sock.BeginReceive(array, 0, array.Length, SocketFlags.None, OnReceive, array); } catch (Exception ex) { if (base.State != PhotonSocketState.Disconnecting && base.State != 0) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, "Receive issue. State: " + base.State.ToString() + ". Server: '" + base.ServerAddress + "' Exception: " + ex); } HandleException(StatusCode.ExceptionOnReceive); } } } private void OnReceive(IAsyncResult ar) { if (base.State == PhotonSocketState.Disconnecting || base.State == PhotonSocketState.Disconnected) { return; } int length = 0; try { length = sock.EndReceive(ar); } catch (SocketException ex) { if (base.State != PhotonSocketState.Disconnecting && base.State != 0) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, "SocketException in EndReceive. State: " + base.State.ToString() + ". Server: '" + base.ServerAddress + "' ErrorCode: " + ex.ErrorCode + " SocketErrorCode: " + ex.SocketErrorCode.ToString() + " Message: " + ex.Message + " " + ex); } HandleException(StatusCode.ExceptionOnReceive); } } catch (Exception ex2) { if (base.State != PhotonSocketState.Disconnecting && base.State != 0) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, "Exception in EndReceive. State: " + base.State.ToString() + ". Server: '" + base.ServerAddress + "' Exception: " + ex2); } HandleException(StatusCode.ExceptionOnReceive); } } if (base.State == PhotonSocketState.Disconnecting || base.State == PhotonSocketState.Disconnected) { return; } byte[] array = (byte[])ar.AsyncState; HandleReceivedDatagram(array, length, willBeReused: true); try { sock.BeginReceive(array, 0, array.Length, SocketFlags.None, OnReceive, array); } catch (SocketException ex3) { if (base.State != PhotonSocketState.Disconnecting && base.State != 0) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, "SocketException in BeginReceive. State: " + base.State.ToString() + ". Server: '" + base.ServerAddress + "' ErrorCode: " + ex3.ErrorCode + " SocketErrorCode: " + ex3.SocketErrorCode.ToString() + " Message: " + ex3.Message + " " + ex3); } HandleException(StatusCode.ExceptionOnReceive); } } catch (Exception ex4) { if (base.State != PhotonSocketState.Disconnecting && base.State != 0) { if (ReportDebugOfLevel(LogLevel.Error)) { EnqueueDebugReturn(LogLevel.Error, "Exception in BeginReceive. State: " + base.State.ToString() + ". Server: '" + base.ServerAddress + "' Exception: " + ex4); } HandleException(StatusCode.ExceptionOnReceive); } } } } public class StreamBuffer { private const int DefaultInitialSize = 0; private int pos; private int len; private byte[] buf; public bool CanRead => true; public bool CanSeek => true; public bool CanWrite => true; public int Length => len; public int Position { get { return pos; } set { pos = value; if (len < pos) { len = pos; CheckSize(len); } } } public int Available { get { int num = len - pos; if (num >= 0) { return num; } return 0; } } public StreamBuffer(int size = 0) { buf = new byte[size]; } public StreamBuffer(byte[] buf) { this.buf = buf; len = buf.Length; } public byte[] ToArray() { byte[] array = new byte[len]; Buffer.BlockCopy(buf, 0, array, 0, len); return array; } public byte[] ToArrayFromPos() { int num = len - pos; if (num <= 0) { return new byte[0]; } byte[] array = new byte[num]; Buffer.BlockCopy(buf, pos, array, 0, num); return array; } public ArraySegment ToArraySegmentFromPos() { int count = len - pos; return new ArraySegment(buf, pos, count); } public void Compact() { long num = Length - Position; if (num > 0) { Buffer.BlockCopy(buf, Position, buf, 0, (int)num); } Position = 0; SetLength(num); } public byte[] GetBuffer() { return buf; } public byte[] GetBufferAndAdvance(int length, out int offset) { offset = Position; Position += length; return buf; } public void Flush() { } public void Reset() { pos = 0; len = 0; } public long Seek(long offset, SeekOrigin origin) { int num = 0; num = origin switch { SeekOrigin.Begin => (int)offset, SeekOrigin.Current => pos + (int)offset, SeekOrigin.End => len + (int)offset, _ => throw new ArgumentException("Invalid seek origin"), }; if (num < 0) { throw new ArgumentException("Seek before begin"); } if (num > len) { throw new ArgumentException("Seek after end"); } pos = num; return pos; } public void SetLength(long value) { len = (int)value; CheckSize(len); if (pos > len) { pos = len; } } public void SetCapacityMinimum(int neededSize) { CheckSize(neededSize); } public int Read(byte[] buffer, int dstOffset, int count) { int num = len - pos; if (num <= 0) { return 0; } if (count > num) { count = num; } Buffer.BlockCopy(buf, pos, buffer, dstOffset, count); pos += count; return count; } public void Write(byte[] buffer, int srcOffset, int count) { int num = pos + count; CheckSize(num); if (num > len) { len = num; } Buffer.BlockCopy(buffer, srcOffset, buf, pos, count); pos = num; } public byte ReadByte() { if (pos >= len) { throw new EndOfStreamException("SteamBuffer.ReadByte() failed. pos:" + pos + " len:" + len); } return buf[pos++]; } public void WriteByte(byte value) { if (pos >= len) { len = pos + 1; CheckSize(len); } buf[pos++] = value; } public void WriteBytes(byte v0, byte v1) { int num = pos + 2; if (len < num) { len = num; CheckSize(len); } buf[pos++] = v0; buf[pos++] = v1; } public void WriteBytes(byte v0, byte v1, byte v2) { int num = pos + 3; if (len < num) { len = num; CheckSize(len); } buf[pos++] = v0; buf[pos++] = v1; buf[pos++] = v2; } public void WriteBytes(byte v0, byte v1, byte v2, byte v3) { int num = pos + 4; if (len < num) { len = num; CheckSize(len); } buf[pos++] = v0; buf[pos++] = v1; buf[pos++] = v2; buf[pos++] = v3; } public void WriteBytes(byte v0, byte v1, byte v2, byte v3, byte v4, byte v5, byte v6, byte v7) { int num = pos + 8; if (len < num) { len = num; CheckSize(len); } buf[pos++] = v0; buf[pos++] = v1; buf[pos++] = v2; buf[pos++] = v3; buf[pos++] = v4; buf[pos++] = v5; buf[pos++] = v6; buf[pos++] = v7; } private bool CheckSize(int size) { if (size <= buf.Length) { return false; } int num = buf.Length; if (num == 0) { num = 1; } while (size > num) { num *= 2; } byte[] dst = new byte[num]; Buffer.BlockCopy(buf, 0, dst, 0, buf.Length); buf = dst; return true; } } public class SupportClass { public class ThreadSafeRandom { private static readonly Random _r = new Random(); public static int Next() { lock (_r) { return _r.Next(); } } } private static uint[] crcLookupTable; public static List GetMethods(Type type, Type attribute) { List list = new List(); if (type == null) { return list; } MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (attribute == null || methodInfo.IsDefined(attribute, inherit: false)) { list.Add(methodInfo); } } return list; } public static void WriteStackTrace(Exception throwable, TextWriter stream = null) { if (stream != null) { stream.WriteLine(throwable.ToString()); stream.WriteLine(throwable.StackTrace); stream.Flush(); } } public static string DictionaryToString(IDictionary dictionary, bool includeTypes = true) { if (dictionary == null) { return "null"; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("{"); foreach (object key in dictionary.Keys) { if (stringBuilder.Length > 1) { stringBuilder.Append(", "); } Type type; string text; if (dictionary[key] == null) { type = typeof(object); text = "null"; } else { type = dictionary[key].GetType(); text = dictionary[key].ToString(); } if (type == typeof(IDictionary) || type == typeof(PhotonHashtable)) { text = DictionaryToString((IDictionary)dictionary[key]); } else if (type == typeof(NonAllocDictionary)) { text = DictionaryToString((NonAllocDictionary)dictionary[key]); } else if (type == typeof(string[])) { text = string.Format("{{{0}}}", string.Join(",", (string[])dictionary[key])); } else if (type == typeof(byte[])) { text = $"byte[{((byte[])dictionary[key]).Length}]"; } else if (dictionary[key] is StructWrapper structWrapper) { stringBuilder.AppendFormat("{0}={1}", key, structWrapper.ToString(includeTypes)); continue; } if (includeTypes) { stringBuilder.AppendFormat("({0}){1}=({2}){3}", key.GetType().Name, key, type.Name, text); } else { stringBuilder.AppendFormat("{0}={1}", key, text); } } stringBuilder.Append("}"); return stringBuilder.ToString(); } public static string DictionaryToString(NonAllocDictionary dictionary, bool includeTypes = true) { if (dictionary == null) { return "null"; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("{"); foreach (byte key in dictionary.Keys) { if (stringBuilder.Length > 1) { stringBuilder.Append(", "); } Type type; string text; if (dictionary[key] == null) { type = typeof(object); text = "null"; } else { type = dictionary[key].GetType(); text = dictionary[key].ToString(); } if (type == typeof(IDictionary) || type == typeof(PhotonHashtable)) { text = DictionaryToString((IDictionary)dictionary[key]); } else if (type == typeof(NonAllocDictionary)) { text = DictionaryToString((NonAllocDictionary)dictionary[key]); } else if (type == typeof(string[])) { text = string.Format("{{{0}}}", string.Join(",", (string[])dictionary[key])); } else if (type == typeof(byte[])) { text = $"byte[{((byte[])dictionary[key]).Length}]"; } else if (dictionary[key] is StructWrapper structWrapper) { stringBuilder.AppendFormat("{0}={1}", key, structWrapper.ToString(includeTypes)); continue; } if (includeTypes) { stringBuilder.AppendFormat("({0}){1}=({2}){3}", key.GetType().Name, key, type.Name, text); } else { stringBuilder.AppendFormat("{0}={1}", key, text); } } stringBuilder.Append("}"); return stringBuilder.ToString(); } public static string ByteArrayToString(byte[] list, int length = -1) { if (list == null) { return string.Empty; } if (length < 0 || length > list.Length) { length = list.Length; } return BitConverter.ToString(list, 0, length); } private static uint[] InitializeTable(uint polynomial) { uint[] array = new uint[256]; for (int i = 0; i < 256; i++) { uint num = (uint)i; for (int j = 0; j < 8; j++) { num = (((num & 1) != 1) ? (num >> 1) : ((num >> 1) ^ polynomial)); } array[i] = num; } return array; } public static uint CalculateCrc(byte[] buffer, int offset, int length) { uint num = uint.MaxValue; uint polynomial = 3988292384u; if (crcLookupTable == null) { crcLookupTable = InitializeTable(polynomial); } for (int i = 0; i < length; i++) { num = (num >> 8) ^ crcLookupTable[buffer[offset + i] ^ (num & 0xFF)]; } return num; } [Obsolete("Use overloaded CalculateCrc version with offset.")] public static uint CalculateCrc(byte[] buffer, int length) { return CalculateCrc(buffer, 0, length); } } public class Pool where T : class { private readonly Func createFunction; private readonly Queue pool; private readonly Action resetFunction; public int Count { get { lock (pool) { return pool.Count; } } } public Pool(Func createFunction, Action resetFunction, int poolCapacity) { this.createFunction = createFunction; this.resetFunction = resetFunction; pool = new Queue(); CreatePoolItems(poolCapacity); } public Pool(Func createFunction, int poolCapacity) : this(createFunction, (Action)null, poolCapacity) { } private void CreatePoolItems(int numItems) { for (int i = 0; i < numItems; i++) { T item = createFunction(); pool.Enqueue(item); } } public void Release(T item) { if (item == null) { throw new ArgumentNullException("Pushing null as item is not allowed."); } if (resetFunction != null) { resetFunction(item); } lock (pool) { pool.Enqueue(item); } } public T Acquire() { lock (pool) { if (pool.Count == 0) { return createFunction(); } return pool.Dequeue(); } } } public class PreserveAttribute : Attribute { } internal class TPeer : PeerBase { internal const int TCP_HEADER_BYTES = 7; internal const int MSG_HEADER_BYTES = 2; public const int ALL_HEADER_BYTES = 9; private Queue incomingList = new Queue(32); internal List outgoingStream; private int lastPingActivity; private readonly byte[] pingRequest = new byte[5] { 240, 0, 0, 0, 0 }; private readonly ParameterDictionary pingParamDict = new ParameterDictionary(); internal static readonly byte[] tcpFramedMessageHead = new byte[9] { 251, 0, 0, 0, 0, 0, 0, 243, 2 }; internal static readonly byte[] tcpMsgHead = new byte[2] { 243, 2 }; protected internal bool DoFraming = true; internal override int QueuedIncomingCommandsCount => incomingList.Count; internal override int QueuedOutgoingCommandsCount => outgoingStream.Count; internal TPeer() { } internal override bool IsTransportEncrypted() { return usedTransportProtocol == ConnectionProtocol.WebSocketSecure; } internal override void Reset() { base.Reset(); peerID = (short)(SupportClass.ThreadSafeRandom.Next() % 32767); if (photonPeer.PayloadEncryptionSecret != null && usedTransportProtocol != ConnectionProtocol.WebSocketSecure) { InitEncryption(photonPeer.PayloadEncryptionSecret); } incomingList = new Queue(32); base.Stats.LastReceiveTimestamp = base.timeInt; } internal override bool Connect(string serverAddress, string proxyServerAddress, string appID, object photonToken) { outgoingStream = new List(8); messageHeader = (DoFraming ? tcpFramedMessageHead : tcpMsgHead); if (usedTransportProtocol == ConnectionProtocol.WebSocket || usedTransportProtocol == ConnectionProtocol.WebSocketSecure) { PhotonSocket.ConnectAddress = PrepareWebSocketUrl(serverAddress, appID, photonToken); } if (PhotonSocket.Connect()) { peerConnectionState = ConnectionStateValue.Connecting; lastPingActivity = base.timeInt; if (DoFraming || PhotonToken != null) { byte[] initRequestBytes = WriteInitRequest(); EnqueueInit(initRequestBytes); } return true; } return false; } private void Disconnect() { Disconnect(queueStatusChangeCallback: true); } internal override void Disconnect(bool queueStatusChangeCallback = true) { if (peerConnectionState != 0 && peerConnectionState != ConnectionStateValue.Disconnecting) { if ((int)base.LogLevel >= 4) { base.Listener.DebugReturn(LogLevel.Debug, "TPeer.Disconnect()"); } peerConnectionState = ConnectionStateValue.Disconnecting; if (PhotonSocket != null) { PhotonSocket.Disconnect(); } lock (incomingList) { incomingList.Clear(); } peerConnectionState = ConnectionStateValue.Disconnected; if (queueStatusChangeCallback) { EnqueueStatusCallback(StatusCode.Disconnect); } else { base.Listener.OnStatusChanged(StatusCode.Disconnect); } } } internal override void SimulateTimeoutDisconnect(bool queueStatusChangeCallback = true) { if (peerConnectionState != 0 && peerConnectionState != ConnectionStateValue.Disconnecting) { if ((int)base.LogLevel >= 4) { base.Listener.DebugReturn(LogLevel.Debug, "TPeer.Disconnect()"); } peerConnectionState = ConnectionStateValue.Disconnecting; if (PhotonSocket != null) { PhotonSocket.Disconnect(); } lock (incomingList) { incomingList.Clear(); } peerConnectionState = ConnectionStateValue.Disconnected; if (queueStatusChangeCallback) { EnqueueStatusCallback(StatusCode.TimeoutDisconnect); } else { base.Listener.OnStatusChanged(StatusCode.TimeoutDisconnect); } } } internal override void FetchServerTimestamp() { if (peerConnectionState != ConnectionStateValue.Connected || !ApplicationIsInitialized) { if ((int)base.LogLevel >= 3) { base.Listener.DebugReturn(LogLevel.Info, $"FetchServerTimestamp() skipped. Client is not connected. Current ConnectionState: {peerConnectionState}"); } } else { SendPing(); serverTimeOffsetIsAvailable = false; } } private void EnqueueInit(byte[] initRequestBytes) { StreamBuffer streamBuffer = new StreamBuffer(initRequestBytes.Length + 32); byte[] array = new byte[7] { 251, 0, 0, 0, 0, 0, 1 }; int targetOffset = 1; MessageProtocol.Serialize(initRequestBytes.Length + array.Length, array, ref targetOffset); streamBuffer.Write(array, 0, array.Length); streamBuffer.Write(initRequestBytes, 0, initRequestBytes.Length); EnqueueMessageAsPayload(DeliveryMode.Reliable, streamBuffer, 0); } internal override bool DispatchIncomingCommands() { if (peerConnectionState == ConnectionStateValue.Connected && base.timeInt - base.Stats.LastReceiveTimestamp > base.DisconnectTimeout) { EnqueueStatusCallback(StatusCode.TimeoutDisconnect); EnqueueActionForDispatch(Disconnect); } while (true) { MyAction myAction; lock (ActionQueue) { if (ActionQueue.Count <= 0) { break; } myAction = ActionQueue.Dequeue(); goto IL_0079; } IL_0079: myAction(); } StreamBuffer streamBuffer; lock (incomingList) { if (incomingList.Count <= 0) { return false; } streamBuffer = incomingList.Dequeue(); } ByteCountCurrentDispatch = streamBuffer.Length + 3; bool result = DeserializeMessageAndCallback(streamBuffer); PeerBase.MessageBufferPool.Release(streamBuffer); return result; } internal override bool SendOutgoingCommands() { if (peerConnectionState == ConnectionStateValue.Disconnected) { return false; } if (!PhotonSocket.Connected) { return false; } base.Stats.LastSendOutgoingTimestamp = base.timeInt; timeIntCurrentSend = base.timeInt; if (base.timeInt - lastPingActivity > base.PingInterval) { SendPing(); } lock (outgoingStream) { int num = 0; int num2 = 0; PhotonSocketError photonSocketError = PhotonSocketError.Success; for (int i = 0; i < outgoingStream.Count; i++) { StreamBuffer streamBuffer = outgoingStream[i]; photonSocketError = SendData(streamBuffer.GetBuffer(), streamBuffer.Length); if (photonSocketError == PhotonSocketError.Busy) { break; } num2 += streamBuffer.Length; num++; if (photonSocketError != PhotonSocketError.PendingSend) { PeerBase.MessageBufferPool.Release(streamBuffer); } if (num2 >= base.mtu || photonSocketError == PhotonSocketError.PendingSend) { break; } } outgoingStream.RemoveRange(0, num); if (photonSocketError == PhotonSocketError.Busy || photonSocketError == PhotonSocketError.PendingSend) { return false; } return outgoingStream.Count > 0; } } internal override bool SendAcksOnly() { if (PhotonSocket == null || !PhotonSocket.Connected) { return false; } if (peerConnectionState == ConnectionStateValue.Connected && base.timeInt - lastPingActivity > base.PingInterval) { SendPing(sendImmediately: true); } return false; } internal override bool EnqueuePhotonMessage(StreamBuffer opBytes, SendOptions sendParams) { return EnqueueMessageAsPayload(sendParams.DeliveryMode, opBytes, sendParams.Channel); } internal bool EnqueueMessageAsPayload(DeliveryMode deliveryMode, StreamBuffer opMessage, byte channelId) { if (opMessage == null) { return false; } if (DoFraming) { byte[] buffer = opMessage.GetBuffer(); int targetOffset = 1; MessageProtocol.Serialize(opMessage.Length, buffer, ref targetOffset); buffer[5] = channelId; switch (deliveryMode) { case DeliveryMode.Unreliable: buffer[6] = 0; break; case DeliveryMode.Reliable: buffer[6] = 1; break; case DeliveryMode.UnreliableUnsequenced: buffer[6] = 2; break; case DeliveryMode.ReliableUnsequenced: buffer[6] = 3; break; default: throw new ArgumentOutOfRangeException("DeliveryMode", deliveryMode, null); } } lock (outgoingStream) { outgoingStream.Add(opMessage); } ByteCountLastOperation = opMessage.Length; return true; } internal void SendPing(bool sendImmediately = false) { int num = (lastPingActivity = base.timeInt); StreamBuffer streamBuffer; if (!DoFraming) { lock (pingParamDict) { pingParamDict[1] = num; streamBuffer = SerializeOperationToMessage(PhotonCodes.Ping, pingParamDict, EgMessageType.InternalOperationRequest, encrypt: false); } } else { int targetOffset = 1; MessageProtocol.Serialize(num, pingRequest, ref targetOffset); streamBuffer = PeerBase.MessageBufferPool.Acquire(); streamBuffer.Write(pingRequest, 0, pingRequest.Length); } if (!sendImmediately) { EnqueuePhotonMessage(streamBuffer, SendOptions.SendReliable); } else if (SendData(streamBuffer.GetBuffer(), streamBuffer.Length) == PhotonSocketError.Success) { PeerBase.MessageBufferPool.Release(streamBuffer); } } internal PhotonSocketError SendData(byte[] data, int length) { PhotonSocketError photonSocketError = PhotonSocketError.Success; try { int num = base.timeInt; photonSocketError = PhotonSocket.Send(data, length); int num2 = base.timeInt - num; if (num2 > longestSendCall) { longestSendCall = num2; } if (photonSocketError == PhotonSocketError.Success) { base.Stats.BytesOut += length; base.Stats.PackagesOut++; } } catch (Exception arg) { if ((int)base.LogLevel >= 1) { base.Listener.DebugReturn(LogLevel.Error, $"Caught exception in TPeer.SendData(): {arg}"); } } return photonSocketError; } internal override void ReceiveIncomingCommands(byte[] inbuff, int dataLength) { if (inbuff == null) { if ((int)base.LogLevel >= 1) { EnqueueDebugReturn(LogLevel.Error, "checkAndQueueIncomingCommands() inBuff: null"); } return; } base.Stats.LastReceiveTimestamp = base.timeInt; base.Stats.BytesIn += dataLength; base.Stats.PackagesIn++; if (inbuff[0] == 243) { if (DoFraming) { base.Stats.BytesIn += 7L; } byte num = (byte)(inbuff[1] & 0x7F); byte b = inbuff[2]; if (num != 7 || b != PhotonCodes.Ping) { StreamBuffer streamBuffer = PeerBase.MessageBufferPool.Acquire(); streamBuffer.Write(inbuff, 0, dataLength); streamBuffer.Position = 0; lock (incomingList) { incomingList.Enqueue(streamBuffer); return; } } DeserializeMessageAndCallback(new StreamBuffer(inbuff)); } else if (inbuff[0] == 240) { ReadPingResult(inbuff); } else if ((int)base.LogLevel >= 1 && dataLength > 0) { EnqueueDebugReturn(LogLevel.Error, $"ReceiveIncomingCommands MagicNumber should be 0xF0 or 0xF3. Is: {inbuff[0]} dataLength: {dataLength}"); } } private void ReadPingResult(byte[] inbuff) { int value = 0; int value2 = 0; int offset = 1; MessageProtocol.Deserialize(out value, inbuff, ref offset); MessageProtocol.Deserialize(out value2, inbuff, ref offset); lastRoundTripTime = base.timeInt - value2; if (!serverTimeOffsetIsAvailable) { roundTripTime = lastRoundTripTime; } UpdateRoundTripTimeAndVariance(lastRoundTripTime); if (!serverTimeOffsetIsAvailable) { serverTimeOffset = value + (lastRoundTripTime >> 1) - base.timeInt; serverTimeOffsetIsAvailable = true; } } protected internal void ReadPingResult(OperationResponse operationResponse) { int num = (int)operationResponse.Parameters[2]; int num2 = (int)operationResponse.Parameters[1]; lastRoundTripTime = base.timeInt - num2; if (!serverTimeOffsetIsAvailable) { roundTripTime = lastRoundTripTime; } UpdateRoundTripTimeAndVariance(lastRoundTripTime); if (!serverTimeOffsetIsAvailable) { serverTimeOffset = num + (lastRoundTripTime >> 1) - base.timeInt; serverTimeOffsetIsAvailable = true; } } } public class TrafficStatsBase { public long BytesIn { get; internal set; } public long BytesOut { get; internal set; } public int PackagesIn { get; internal set; } public int PackagesOut { get; internal set; } public int UdpFragmentsIn { get; internal set; } public int UdpFragmentsOut { get; internal set; } public int UdpUnreliableCommandsSent { get; internal set; } public int UdpReliableCommandsSent { get; internal set; } public int UdpReliableCommandsResent { get; internal set; } public int UdpReliableCommandsInFlight { get; internal set; } public int DispatchIncomingCommandsCalls { get; internal set; } public int SendOutgoingCommandsCalls { get; internal set; } public virtual long RoundtripTime { get; internal set; } public virtual long RoundtripTimeVariance { get; internal set; } public virtual long LastRoundtripTime { get; internal set; } public TrafficStatsBase() { } public TrafficStatsBase(TrafficStatsBase origin = null) { BytesIn = origin.BytesIn; BytesOut = origin.BytesOut; PackagesIn = origin.PackagesIn; PackagesOut = origin.PackagesOut; UdpFragmentsIn = origin.UdpFragmentsIn; UdpFragmentsOut = origin.UdpFragmentsOut; UdpUnreliableCommandsSent = origin.UdpUnreliableCommandsSent; UdpReliableCommandsSent = origin.UdpReliableCommandsSent; UdpReliableCommandsResent = origin.UdpReliableCommandsResent; UdpReliableCommandsInFlight = origin.UdpReliableCommandsInFlight; DispatchIncomingCommandsCalls = origin.DispatchIncomingCommandsCalls; SendOutgoingCommandsCalls = origin.SendOutgoingCommandsCalls; RoundtripTime = origin.RoundtripTime; RoundtripTimeVariance = origin.RoundtripTimeVariance; LastRoundtripTime = origin.LastRoundtripTime; } } public class TrafficStatsDelta : TrafficStatsBase { public long DeltaTime; public override long RoundtripTime { get; internal set; } public override long RoundtripTimeVariance { get; internal set; } public override long LastRoundtripTime { get; internal set; } public long ReferenceRoundtripTime { get; set; } public long ReferenceRoundtripTimeVariance { get; set; } public long LaterRoundtripTime { get; set; } public long LaterRoundtripTimeVariance { get; set; } internal TrafficStatsDelta(TrafficStatsBase reference, TrafficStatsBase now) { base.BytesIn = now.BytesIn - reference.BytesIn; base.BytesOut = now.BytesOut - reference.BytesOut; base.PackagesIn = now.PackagesIn - reference.PackagesIn; base.PackagesOut = now.PackagesOut - reference.PackagesOut; base.UdpFragmentsIn = now.UdpFragmentsIn - reference.UdpFragmentsIn; base.UdpFragmentsOut = now.UdpFragmentsOut - reference.UdpFragmentsOut; base.UdpUnreliableCommandsSent = now.UdpUnreliableCommandsSent - reference.UdpUnreliableCommandsSent; base.UdpReliableCommandsSent = now.UdpReliableCommandsSent - reference.UdpReliableCommandsSent; base.UdpReliableCommandsResent = now.UdpReliableCommandsResent - reference.UdpReliableCommandsResent; base.UdpReliableCommandsInFlight = now.UdpReliableCommandsInFlight - reference.UdpReliableCommandsInFlight; base.DispatchIncomingCommandsCalls = now.DispatchIncomingCommandsCalls - reference.DispatchIncomingCommandsCalls; base.SendOutgoingCommandsCalls = now.SendOutgoingCommandsCalls - reference.SendOutgoingCommandsCalls; ReferenceRoundtripTime = reference.RoundtripTime; ReferenceRoundtripTimeVariance = reference.RoundtripTimeVariance; LaterRoundtripTime = now.RoundtripTime; LaterRoundtripTimeVariance = now.RoundtripTimeVariance; RoundtripTime = now.RoundtripTime - reference.RoundtripTime; RoundtripTimeVariance = now.RoundtripTimeVariance - reference.RoundtripTimeVariance; LastRoundtripTime = now.LastRoundtripTime - reference.LastRoundtripTime; } public TrafficStatsDelta(TrafficStatsSnapshot reference, TrafficStatsSnapshot now) : this((TrafficStatsBase)reference, (TrafficStatsBase)now) { DeltaTime = now.SnapshotTimestamp - reference.SnapshotTimestamp; } public TrafficStatsDelta(TrafficStatsSnapshot reference, TrafficStats now) : this((TrafficStatsBase)reference, (TrafficStatsBase)now) { DeltaTime = now.connectionStopwatch.ElapsedMilliseconds - reference.SnapshotTimestamp; } public override string ToString() { return ToString(udpValues: true, rttValues: true, callValues: true); } public string ToString(bool udpValues = true, bool rttValues = false, bool callValues = false) { float num = (float)DeltaTime / 1000f; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine($"Delta elapsed: {num:F3} sec. Out: {base.BytesOut:N0} bytes -> {base.BytesOut / DeltaTime:N0} kB/sec. In: {base.BytesIn:N0} bytes -> {base.BytesIn / DeltaTime:N0} kB/sec.\nPackages Out: {base.PackagesOut:N0} In: {base.PackagesIn:N0}"); if (udpValues) { if (base.UdpReliableCommandsSent > 0) { stringBuilder.AppendLine($"Reliable commands out: {base.UdpReliableCommandsSent:N0} resent: {base.UdpReliableCommandsResent:N0} in flight: {base.UdpReliableCommandsInFlight:N0}."); } if (base.UdpUnreliableCommandsSent > 0) { stringBuilder.AppendLine($"Unreliable commands out: {base.UdpUnreliableCommandsSent:N0}."); } if (base.UdpFragmentsIn > 0 || base.UdpFragmentsOut > 0) { stringBuilder.AppendLine($"Fragments out: {base.UdpFragmentsOut:N0} in: {base.UdpFragmentsIn:N0}."); } } if (rttValues) { stringBuilder.AppendLine($"RTT/Variance from: {ReferenceRoundtripTime}/{LaterRoundtripTimeVariance} to: {LaterRoundtripTime}/{LaterRoundtripTimeVariance}"); } if (callValues && (base.DispatchIncomingCommandsCalls > 0 || base.SendOutgoingCommandsCalls > 0)) { stringBuilder.AppendLine($"DispatchIncomingCommands calls: {base.DispatchIncomingCommandsCalls:N0} SendOutgoingCommands calls: {base.SendOutgoingCommandsCalls:N0}."); } return stringBuilder.ToString(); } } public class TrafficStatsSnapshot : TrafficStatsBase { public long SnapshotTimestamp; [Obsolete("Use SnapshotTimestamp (without incorrect uppercase Stamp).")] public long SnapshotTimeStamp { get { return SnapshotTimestamp; } set { SnapshotTimestamp = value; } } public TrafficStatsSnapshot(TrafficStats ts, long timestamp) : base(ts) { SnapshotTimestamp = timestamp; } } public class TrafficStats : TrafficStatsBase { internal readonly Stopwatch connectionStopwatch; public int LastSendOutgoingTimestamp; public int LastSendAckTimestamp; public int LastSendOutgoingDeltaTime => (int)(connectionStopwatch.ElapsedMilliseconds - LastSendOutgoingTimestamp); public int LastSendAckDeltaTime => (int)(connectionStopwatch.ElapsedMilliseconds - LastSendAckTimestamp); public int LastReceiveTimestamp { get; internal set; } public int LastReceiveDeltaTime => (int)(connectionStopwatch.ElapsedMilliseconds - LastReceiveTimestamp); public int LastDispatchTimestamp { get; internal set; } public int LastDispatchDeltaTime => (int)(connectionStopwatch.ElapsedMilliseconds - LastDispatchTimestamp); public int LongestDeltaBetweenDispatchCalls { get; internal set; } public int LastDispatchDuration { get; internal set; } public int LongestDeltaBetweenSendOutgoingCalls { get; internal set; } public TrafficStats(Stopwatch connectionTimeSw) { connectionStopwatch = connectionTimeSw; } public TrafficStatsSnapshot ToSnapshot() { long timestamp = ((connectionStopwatch == null) ? 0 : connectionStopwatch.ElapsedMilliseconds); return new TrafficStatsSnapshot(this, timestamp); } public TrafficStatsDelta ToDelta(TrafficStatsSnapshot reference) { return new TrafficStatsDelta(reference, this); } internal void DispatchIncomingCommandsCalled(int timestamp) { if (LastDispatchTimestamp != 0) { int num = timestamp - LastDispatchTimestamp; if (num > LongestDeltaBetweenDispatchCalls) { LongestDeltaBetweenDispatchCalls = num; } } base.DispatchIncomingCommandsCalls++; LastDispatchTimestamp = timestamp; } internal void SendOutgoingCommandsCalled(int timestamp) { if (LastSendOutgoingTimestamp != 0) { int num = timestamp - LastSendOutgoingTimestamp; if (num > LongestDeltaBetweenSendOutgoingCalls) { LongestDeltaBetweenSendOutgoingCalls = num; } } base.SendOutgoingCommandsCalls++; LastSendOutgoingTimestamp = timestamp; } public void ResetMaximumCounters() { LongestDeltaBetweenDispatchCalls = 0; LongestDeltaBetweenSendOutgoingCalls = 0; LastDispatchTimestamp = 0; LastSendOutgoingTimestamp = 0; } public override string ToString() { return ToString(extended: true); } public string ToString(bool extended) { string text = ((base.UdpFragmentsIn <= 0 && base.UdpFragmentsOut <= 0) ? $"In: {base.BytesIn} bytes, {base.PackagesIn} packages.\nOut: {base.BytesOut} bytes, {base.PackagesOut} packages." : $"In: {base.BytesIn} bytes, {base.PackagesIn} packages, {base.UdpFragmentsIn} fragments.\nOut: {base.BytesOut} bytes, {base.PackagesOut} packages, {base.UdpFragmentsOut} fragments."); if (!extended) { return text; } return text + "\n" + $"Max time between Send: {LongestDeltaBetweenSendOutgoingCalls}ms, " + $"Dispatch: {LongestDeltaBetweenDispatchCalls}ms. " + $"Send calls: {base.SendOutgoingCommandsCalls}. " + $"Dispatch calls: {base.DispatchIncomingCommandsCalls}."; } } internal static class Version { internal static readonly byte[] clientVersion = new byte[4] { 5, 1, 9, 0 }; } } namespace Photon.Client.StructWrapping { public enum WrappedType { Unknown, Bool, Byte, Int16, Int32, Int64, Single, Double } public enum Pooling { Disconnected = 0, Connected = 1, ReleaseOnUnwrap = 2, Readonly = 4, CheckedOut = 8 } public abstract class StructWrapper : IDisposable { public readonly WrappedType wrappedType; public readonly Type ttype; public StructWrapper(Type ttype, WrappedType wrappedType) { this.ttype = ttype; this.wrappedType = wrappedType; } public abstract object Box(); public abstract void DisconnectFromPool(); public abstract void Dispose(); public abstract string ToString(bool writeType); public static implicit operator StructWrapper(bool value) { return value.Wrap(); } public static implicit operator StructWrapper(byte value) { return value.Wrap(); } public static implicit operator StructWrapper(float value) { return value.Wrap(); } public static implicit operator StructWrapper(double value) { return value.Wrap(); } public static implicit operator StructWrapper(short value) { return value.Wrap(); } public static implicit operator StructWrapper(int value) { return value.Wrap(); } public static implicit operator StructWrapper(long value) { return value.Wrap(); } public static implicit operator bool(StructWrapper wrapper) { return (wrapper as StructWrapper).Unwrap(); } public static implicit operator byte(StructWrapper wrapper) { return (wrapper as StructWrapper).Unwrap(); } public static implicit operator float(StructWrapper wrapper) { return (wrapper as StructWrapper).Unwrap(); } public static implicit operator double(StructWrapper wrapper) { return (wrapper as StructWrapper).Unwrap(); } public static implicit operator short(StructWrapper wrapper) { return (wrapper as StructWrapper).Unwrap(); } public static implicit operator int(StructWrapper wrapper) { return (wrapper as StructWrapper).Unwrap(); } public static implicit operator long(StructWrapper wrapper) { return (wrapper as StructWrapper).Unwrap(); } } public class StructWrapper : StructWrapper { internal Pooling pooling; internal T value; internal static StructWrapperPool staticPool = new StructWrapperPool(isStaticPool: true); public StructWrapperPool ReturnPool { get; internal set; } public StructWrapper(Pooling releasing) : base(typeof(T), StructWrapperPool.GetWrappedType(typeof(T))) { pooling = releasing; } public StructWrapper(Pooling releasing, Type tType, WrappedType wType) : base(tType, wType) { pooling = releasing; } public StructWrapper Poke(byte value) { if (pooling == Pooling.Readonly) { throw new InvalidOperationException("Trying to Poke the value of a readonly StructWrapper. Value cannot be modified."); } return this; } public StructWrapper Poke(bool value) { if (pooling == Pooling.Readonly) { throw new InvalidOperationException("Trying to Poke the value of a readonly StructWrapper. Value cannot be modified."); } return this; } public StructWrapper Poke(T value) { this.value = value; return this; } public T Unwrap() { T result = value; if (pooling != Pooling.Readonly) { ReturnPool.Release(this); } return result; } public T Peek() { return value; } public override object Box() { T val = value; if (ReturnPool != null) { ReturnPool.Release(this); } return val; } public override void Dispose() { if ((pooling & Pooling.CheckedOut) == Pooling.CheckedOut && ReturnPool != null) { ReturnPool.Release(this); } } public override void DisconnectFromPool() { if (pooling != Pooling.Readonly) { pooling = Pooling.Disconnected; ReturnPool = null; } } public override string ToString() { return Unwrap().ToString(); } public override string ToString(bool writeTypeInfo) { if (writeTypeInfo) { return $"(StructWrapper<{wrappedType}>){Unwrap().ToString()}"; } return Unwrap().ToString(); } public static implicit operator StructWrapper(T value) { return staticPool.Acquire(value); } } public class StructWrapperPool { public static WrappedType GetWrappedType(Type type) { if (type == typeof(bool)) { return WrappedType.Bool; } if (type == typeof(byte)) { return WrappedType.Byte; } if (type == typeof(short)) { return WrappedType.Int16; } if (type == typeof(int)) { return WrappedType.Int32; } if (type == typeof(long)) { return WrappedType.Int64; } if (type == typeof(float)) { return WrappedType.Single; } if (type == typeof(double)) { return WrappedType.Double; } return WrappedType.Unknown; } } public class StructWrapperPool : StructWrapperPool { public const int GROWBY = 4; public readonly Type tType = typeof(T); public readonly WrappedType wType = StructWrapperPool.GetWrappedType(typeof(T)); public Stack> pool; public readonly bool isStaticPool; public int Count => pool.Count; public StructWrapperPool(bool isStaticPool) { pool = new Stack>(); this.isStaticPool = isStaticPool; } public StructWrapper Acquire() { StructWrapper structWrapper; if (pool.Count == 0) { int num = 1; while (true) { structWrapper = new StructWrapper((!isStaticPool) ? Pooling.Connected : ((Pooling)3), tType, wType); structWrapper.ReturnPool = this; if (num == 4) { break; } pool.Push(structWrapper); num++; } } else { structWrapper = pool.Pop(); } structWrapper.pooling |= Pooling.CheckedOut; return structWrapper; } public StructWrapper Acquire(T value) { StructWrapper structWrapper = Acquire(); structWrapper.value = value; return structWrapper; } internal void Release(StructWrapper obj) { obj.pooling &= (Pooling)(-9); pool.Push(obj); } } public class StructWrapperPools { public static readonly StructWrapper[] mappedByteWrappers = new StructWrapper[256] { new StructWrapper(Pooling.Readonly) { value = 0 }, new StructWrapper(Pooling.Readonly) { value = 1 }, new StructWrapper(Pooling.Readonly) { value = 2 }, new StructWrapper(Pooling.Readonly) { value = 3 }, new StructWrapper(Pooling.Readonly) { value = 4 }, new StructWrapper(Pooling.Readonly) { value = 5 }, new StructWrapper(Pooling.Readonly) { value = 6 }, new StructWrapper(Pooling.Readonly) { value = 7 }, new StructWrapper(Pooling.Readonly) { value = 8 }, new StructWrapper(Pooling.Readonly) { value = 9 }, new StructWrapper(Pooling.Readonly) { value = 10 }, new StructWrapper(Pooling.Readonly) { value = 11 }, new StructWrapper(Pooling.Readonly) { value = 12 }, new StructWrapper(Pooling.Readonly) { value = 13 }, new StructWrapper(Pooling.Readonly) { value = 14 }, new StructWrapper(Pooling.Readonly) { value = 15 }, new StructWrapper(Pooling.Readonly) { value = 16 }, new StructWrapper(Pooling.Readonly) { value = 17 }, new StructWrapper(Pooling.Readonly) { value = 18 }, new StructWrapper(Pooling.Readonly) { value = 19 }, new StructWrapper(Pooling.Readonly) { value = 20 }, new StructWrapper(Pooling.Readonly) { value = 21 }, new StructWrapper(Pooling.Readonly) { value = 22 }, new StructWrapper(Pooling.Readonly) { value = 23 }, new StructWrapper(Pooling.Readonly) { value = 24 }, new StructWrapper(Pooling.Readonly) { value = 25 }, new StructWrapper(Pooling.Readonly) { value = 26 }, new StructWrapper(Pooling.Readonly) { value = 27 }, new StructWrapper(Pooling.Readonly) { value = 28 }, new StructWrapper(Pooling.Readonly) { value = 29 }, new StructWrapper(Pooling.Readonly) { value = 30 }, new StructWrapper(Pooling.Readonly) { value = 31 }, new StructWrapper(Pooling.Readonly) { value = 32 }, new StructWrapper(Pooling.Readonly) { value = 33 }, new StructWrapper(Pooling.Readonly) { value = 34 }, new StructWrapper(Pooling.Readonly) { value = 35 }, new StructWrapper(Pooling.Readonly) { value = 36 }, new StructWrapper(Pooling.Readonly) { value = 37 }, new StructWrapper(Pooling.Readonly) { value = 38 }, new StructWrapper(Pooling.Readonly) { value = 39 }, new StructWrapper(Pooling.Readonly) { value = 40 }, new StructWrapper(Pooling.Readonly) { value = 41 }, new StructWrapper(Pooling.Readonly) { value = 42 }, new StructWrapper(Pooling.Readonly) { value = 43 }, new StructWrapper(Pooling.Readonly) { value = 44 }, new StructWrapper(Pooling.Readonly) { value = 45 }, new StructWrapper(Pooling.Readonly) { value = 46 }, new StructWrapper(Pooling.Readonly) { value = 47 }, new StructWrapper(Pooling.Readonly) { value = 48 }, new StructWrapper(Pooling.Readonly) { value = 49 }, new StructWrapper(Pooling.Readonly) { value = 50 }, new StructWrapper(Pooling.Readonly) { value = 51 }, new StructWrapper(Pooling.Readonly) { value = 52 }, new StructWrapper(Pooling.Readonly) { value = 53 }, new StructWrapper(Pooling.Readonly) { value = 54 }, new StructWrapper(Pooling.Readonly) { value = 55 }, new StructWrapper(Pooling.Readonly) { value = 56 }, new StructWrapper(Pooling.Readonly) { value = 57 }, new StructWrapper(Pooling.Readonly) { value = 58 }, new StructWrapper(Pooling.Readonly) { value = 59 }, new StructWrapper(Pooling.Readonly) { value = 60 }, new StructWrapper(Pooling.Readonly) { value = 61 }, new StructWrapper(Pooling.Readonly) { value = 62 }, new StructWrapper(Pooling.Readonly) { value = 63 }, new StructWrapper(Pooling.Readonly) { value = 64 }, new StructWrapper(Pooling.Readonly) { value = 65 }, new StructWrapper(Pooling.Readonly) { value = 66 }, new StructWrapper(Pooling.Readonly) { value = 67 }, new StructWrapper(Pooling.Readonly) { value = 68 }, new StructWrapper(Pooling.Readonly) { value = 69 }, new StructWrapper(Pooling.Readonly) { value = 70 }, new StructWrapper(Pooling.Readonly) { value = 71 }, new StructWrapper(Pooling.Readonly) { value = 72 }, new StructWrapper(Pooling.Readonly) { value = 73 }, new StructWrapper(Pooling.Readonly) { value = 74 }, new StructWrapper(Pooling.Readonly) { value = 75 }, new StructWrapper(Pooling.Readonly) { value = 76 }, new StructWrapper(Pooling.Readonly) { value = 77 }, new StructWrapper(Pooling.Readonly) { value = 78 }, new StructWrapper(Pooling.Readonly) { value = 79 }, new StructWrapper(Pooling.Readonly) { value = 80 }, new StructWrapper(Pooling.Readonly) { value = 81 }, new StructWrapper(Pooling.Readonly) { value = 82 }, new StructWrapper(Pooling.Readonly) { value = 83 }, new StructWrapper(Pooling.Readonly) { value = 84 }, new StructWrapper(Pooling.Readonly) { value = 85 }, new StructWrapper(Pooling.Readonly) { value = 86 }, new StructWrapper(Pooling.Readonly) { value = 87 }, new StructWrapper(Pooling.Readonly) { value = 88 }, new StructWrapper(Pooling.Readonly) { value = 89 }, new StructWrapper(Pooling.Readonly) { value = 90 }, new StructWrapper(Pooling.Readonly) { value = 91 }, new StructWrapper(Pooling.Readonly) { value = 92 }, new StructWrapper(Pooling.Readonly) { value = 93 }, new StructWrapper(Pooling.Readonly) { value = 94 }, new StructWrapper(Pooling.Readonly) { value = 95 }, new StructWrapper(Pooling.Readonly) { value = 96 }, new StructWrapper(Pooling.Readonly) { value = 97 }, new StructWrapper(Pooling.Readonly) { value = 98 }, new StructWrapper(Pooling.Readonly) { value = 99 }, new StructWrapper(Pooling.Readonly) { value = 100 }, new StructWrapper(Pooling.Readonly) { value = 101 }, new StructWrapper(Pooling.Readonly) { value = 102 }, new StructWrapper(Pooling.Readonly) { value = 103 }, new StructWrapper(Pooling.Readonly) { value = 104 }, new StructWrapper(Pooling.Readonly) { value = 105 }, new StructWrapper(Pooling.Readonly) { value = 106 }, new StructWrapper(Pooling.Readonly) { value = 107 }, new StructWrapper(Pooling.Readonly) { value = 108 }, new StructWrapper(Pooling.Readonly) { value = 109 }, new StructWrapper(Pooling.Readonly) { value = 110 }, new StructWrapper(Pooling.Readonly) { value = 111 }, new StructWrapper(Pooling.Readonly) { value = 112 }, new StructWrapper(Pooling.Readonly) { value = 113 }, new StructWrapper(Pooling.Readonly) { value = 114 }, new StructWrapper(Pooling.Readonly) { value = 115 }, new StructWrapper(Pooling.Readonly) { value = 116 }, new StructWrapper(Pooling.Readonly) { value = 117 }, new StructWrapper(Pooling.Readonly) { value = 118 }, new StructWrapper(Pooling.Readonly) { value = 119 }, new StructWrapper(Pooling.Readonly) { value = 120 }, new StructWrapper(Pooling.Readonly) { value = 121 }, new StructWrapper(Pooling.Readonly) { value = 122 }, new StructWrapper(Pooling.Readonly) { value = 123 }, new StructWrapper(Pooling.Readonly) { value = 124 }, new StructWrapper(Pooling.Readonly) { value = 125 }, new StructWrapper(Pooling.Readonly) { value = 126 }, new StructWrapper(Pooling.Readonly) { value = 127 }, new StructWrapper(Pooling.Readonly) { value = 128 }, new StructWrapper(Pooling.Readonly) { value = 129 }, new StructWrapper(Pooling.Readonly) { value = 130 }, new StructWrapper(Pooling.Readonly) { value = 131 }, new StructWrapper(Pooling.Readonly) { value = 132 }, new StructWrapper(Pooling.Readonly) { value = 133 }, new StructWrapper(Pooling.Readonly) { value = 134 }, new StructWrapper(Pooling.Readonly) { value = 135 }, new StructWrapper(Pooling.Readonly) { value = 136 }, new StructWrapper(Pooling.Readonly) { value = 137 }, new StructWrapper(Pooling.Readonly) { value = 138 }, new StructWrapper(Pooling.Readonly) { value = 139 }, new StructWrapper(Pooling.Readonly) { value = 140 }, new StructWrapper(Pooling.Readonly) { value = 141 }, new StructWrapper(Pooling.Readonly) { value = 142 }, new StructWrapper(Pooling.Readonly) { value = 143 }, new StructWrapper(Pooling.Readonly) { value = 144 }, new StructWrapper(Pooling.Readonly) { value = 145 }, new StructWrapper(Pooling.Readonly) { value = 146 }, new StructWrapper(Pooling.Readonly) { value = 147 }, new StructWrapper(Pooling.Readonly) { value = 148 }, new StructWrapper(Pooling.Readonly) { value = 149 }, new StructWrapper(Pooling.Readonly) { value = 150 }, new StructWrapper(Pooling.Readonly) { value = 151 }, new StructWrapper(Pooling.Readonly) { value = 152 }, new StructWrapper(Pooling.Readonly) { value = 153 }, new StructWrapper(Pooling.Readonly) { value = 154 }, new StructWrapper(Pooling.Readonly) { value = 155 }, new StructWrapper(Pooling.Readonly) { value = 156 }, new StructWrapper(Pooling.Readonly) { value = 157 }, new StructWrapper(Pooling.Readonly) { value = 158 }, new StructWrapper(Pooling.Readonly) { value = 159 }, new StructWrapper(Pooling.Readonly) { value = 160 }, new StructWrapper(Pooling.Readonly) { value = 161 }, new StructWrapper(Pooling.Readonly) { value = 162 }, new StructWrapper(Pooling.Readonly) { value = 163 }, new StructWrapper(Pooling.Readonly) { value = 164 }, new StructWrapper(Pooling.Readonly) { value = 165 }, new StructWrapper(Pooling.Readonly) { value = 166 }, new StructWrapper(Pooling.Readonly) { value = 167 }, new StructWrapper(Pooling.Readonly) { value = 168 }, new StructWrapper(Pooling.Readonly) { value = 169 }, new StructWrapper(Pooling.Readonly) { value = 170 }, new StructWrapper(Pooling.Readonly) { value = 171 }, new StructWrapper(Pooling.Readonly) { value = 172 }, new StructWrapper(Pooling.Readonly) { value = 173 }, new StructWrapper(Pooling.Readonly) { value = 174 }, new StructWrapper(Pooling.Readonly) { value = 175 }, new StructWrapper(Pooling.Readonly) { value = 176 }, new StructWrapper(Pooling.Readonly) { value = 177 }, new StructWrapper(Pooling.Readonly) { value = 178 }, new StructWrapper(Pooling.Readonly) { value = 179 }, new StructWrapper(Pooling.Readonly) { value = 180 }, new StructWrapper(Pooling.Readonly) { value = 181 }, new StructWrapper(Pooling.Readonly) { value = 182 }, new StructWrapper(Pooling.Readonly) { value = 183 }, new StructWrapper(Pooling.Readonly) { value = 184 }, new StructWrapper(Pooling.Readonly) { value = 185 }, new StructWrapper(Pooling.Readonly) { value = 186 }, new StructWrapper(Pooling.Readonly) { value = 187 }, new StructWrapper(Pooling.Readonly) { value = 188 }, new StructWrapper(Pooling.Readonly) { value = 189 }, new StructWrapper(Pooling.Readonly) { value = 190 }, new StructWrapper(Pooling.Readonly) { value = 191 }, new StructWrapper(Pooling.Readonly) { value = 192 }, new StructWrapper(Pooling.Readonly) { value = 193 }, new StructWrapper(Pooling.Readonly) { value = 194 }, new StructWrapper(Pooling.Readonly) { value = 195 }, new StructWrapper(Pooling.Readonly) { value = 196 }, new StructWrapper(Pooling.Readonly) { value = 197 }, new StructWrapper(Pooling.Readonly) { value = 198 }, new StructWrapper(Pooling.Readonly) { value = 199 }, new StructWrapper(Pooling.Readonly) { value = 200 }, new StructWrapper(Pooling.Readonly) { value = 201 }, new StructWrapper(Pooling.Readonly) { value = 202 }, new StructWrapper(Pooling.Readonly) { value = 203 }, new StructWrapper(Pooling.Readonly) { value = 204 }, new StructWrapper(Pooling.Readonly) { value = 205 }, new StructWrapper(Pooling.Readonly) { value = 206 }, new StructWrapper(Pooling.Readonly) { value = 207 }, new StructWrapper(Pooling.Readonly) { value = 208 }, new StructWrapper(Pooling.Readonly) { value = 209 }, new StructWrapper(Pooling.Readonly) { value = 210 }, new StructWrapper(Pooling.Readonly) { value = 211 }, new StructWrapper(Pooling.Readonly) { value = 212 }, new StructWrapper(Pooling.Readonly) { value = 213 }, new StructWrapper(Pooling.Readonly) { value = 214 }, new StructWrapper(Pooling.Readonly) { value = 215 }, new StructWrapper(Pooling.Readonly) { value = 216 }, new StructWrapper(Pooling.Readonly) { value = 217 }, new StructWrapper(Pooling.Readonly) { value = 218 }, new StructWrapper(Pooling.Readonly) { value = 219 }, new StructWrapper(Pooling.Readonly) { value = 220 }, new StructWrapper(Pooling.Readonly) { value = 221 }, new StructWrapper(Pooling.Readonly) { value = 222 }, new StructWrapper(Pooling.Readonly) { value = 223 }, new StructWrapper(Pooling.Readonly) { value = 224 }, new StructWrapper(Pooling.Readonly) { value = 225 }, new StructWrapper(Pooling.Readonly) { value = 226 }, new StructWrapper(Pooling.Readonly) { value = 227 }, new StructWrapper(Pooling.Readonly) { value = 228 }, new StructWrapper(Pooling.Readonly) { value = 229 }, new StructWrapper(Pooling.Readonly) { value = 230 }, new StructWrapper(Pooling.Readonly) { value = 231 }, new StructWrapper(Pooling.Readonly) { value = 232 }, new StructWrapper(Pooling.Readonly) { value = 233 }, new StructWrapper(Pooling.Readonly) { value = 234 }, new StructWrapper(Pooling.Readonly) { value = 235 }, new StructWrapper(Pooling.Readonly) { value = 236 }, new StructWrapper(Pooling.Readonly) { value = 237 }, new StructWrapper(Pooling.Readonly) { value = 238 }, new StructWrapper(Pooling.Readonly) { value = 239 }, new StructWrapper(Pooling.Readonly) { value = 240 }, new StructWrapper(Pooling.Readonly) { value = 241 }, new StructWrapper(Pooling.Readonly) { value = 242 }, new StructWrapper(Pooling.Readonly) { value = 243 }, new StructWrapper(Pooling.Readonly) { value = 244 }, new StructWrapper(Pooling.Readonly) { value = 245 }, new StructWrapper(Pooling.Readonly) { value = 246 }, new StructWrapper(Pooling.Readonly) { value = 247 }, new StructWrapper(Pooling.Readonly) { value = 248 }, new StructWrapper(Pooling.Readonly) { value = 249 }, new StructWrapper(Pooling.Readonly) { value = 250 }, new StructWrapper(Pooling.Readonly) { value = 251 }, new StructWrapper(Pooling.Readonly) { value = 252 }, new StructWrapper(Pooling.Readonly) { value = 253 }, new StructWrapper(Pooling.Readonly) { value = 254 }, new StructWrapper(Pooling.Readonly) { value = byte.MaxValue } }; public static readonly StructWrapper[] mappedBoolWrappers = new StructWrapper[2] { new StructWrapper(Pooling.Readonly) { value = false }, new StructWrapper(Pooling.Readonly) { value = true } }; private readonly Dictionary pools = new Dictionary(); private readonly List used = new List(); private StructWrapperPool GetPoolForType() { if (pools.TryGetValue(typeof(T), out var value)) { return value as StructWrapperPool; } StructWrapperPool structWrapperPool = new StructWrapperPool(isStaticPool: false); pools.Add(typeof(T), structWrapperPool); return structWrapperPool; } public StructWrapper Acquire(byte value) { return mappedByteWrappers[value]; } public StructWrapper Acquire(bool value) { return mappedBoolWrappers[value ? 1u : 0u]; } public StructWrapper Acquire(T value) { StructWrapper structWrapper = GetPoolForType().Acquire(value); used.Add(structWrapper); return structWrapper; } public void Clear() { foreach (IDisposable item in used) { item.Dispose(); } used.Clear(); } } public static class StructWrapperUtility { public static Type GetWrappedType(this object obj) { if (!(obj is StructWrapper structWrapper)) { return obj.GetType(); } return structWrapper.ttype; } public static StructWrapper Wrap(this T value, bool persistant) { StructWrapper structWrapper = StructWrapper.staticPool.Acquire(value); if (persistant) { structWrapper.DisconnectFromPool(); } return structWrapper; } public static StructWrapper Wrap(this T value) { return StructWrapper.staticPool.Acquire(value); } public static StructWrapper Wrap(this byte value) { return StructWrapperPools.mappedByteWrappers[value]; } public static StructWrapper Wrap(this bool value) { return StructWrapperPools.mappedBoolWrappers[value ? 1u : 0u]; } public static bool IsType(this object obj) { if (obj is T) { return true; } if (obj is StructWrapper) { return true; } return false; } public static T DisconnectPooling(this T table) where T : IEnumerable { foreach (object item in table) { if (item is StructWrapper structWrapper) { structWrapper.DisconnectFromPool(); } } return table; } public static List ReleaseAllWrappers(this List collection) { foreach (object item in collection) { if (item is StructWrapper structWrapper) { structWrapper.Dispose(); } } return collection; } public static object[] ReleaseAllWrappers(this object[] collection) { for (int i = 0; i < collection.Length; i++) { if (collection[i] is StructWrapper structWrapper) { structWrapper.Dispose(); } } return collection; } public static PhotonHashtable ReleaseAllWrappers(this PhotonHashtable table) { foreach (object value in table.Values) { if (value is StructWrapper structWrapper) { structWrapper.Dispose(); } } return table; } public static void BoxAll(this PhotonHashtable table, bool recursive = false) { foreach (object value in table.Values) { if (recursive && value is PhotonHashtable table2) { table2.BoxAll(); } if (value is StructWrapper structWrapper) { structWrapper.Box(); } } } public static T Unwrap(this object obj) { if (!(obj is StructWrapper structWrapper)) { return (T)obj; } _ = structWrapper.value; if ((structWrapper.pooling & Pooling.ReleaseOnUnwrap) == Pooling.ReleaseOnUnwrap) { structWrapper.Dispose(); } return structWrapper.value; } public static T Get(this object obj) { if (!(obj is StructWrapper structWrapper)) { return (T)obj; } return structWrapper.value; } public static T Unwrap(this PhotonHashtable table, object key) { return table[key].Unwrap(); } public static bool TryUnwrapValue(this PhotonHashtable table, byte key, out T value) where T : new() { if (!table.TryGetValue(key, out var value2)) { value = default(T); return false; } value = value2.Unwrap(); return true; } public static bool TryGetValue(this PhotonHashtable table, byte key, out T value) where T : new() { if (!table.TryGetValue(key, out var value2)) { value = default(T); return false; } value = value2.Get(); return true; } public static bool TryGetValue(this PhotonHashtable table, object key, out T value) where T : new() { if (!table.TryGetValue(key, out var value2)) { value = default(T); return false; } value = value2.Get(); return true; } public static bool TryUnwrapValue(this PhotonHashtable table, object key, out T value) where T : new() { if (!table.TryGetValue(key, out var value2)) { value = default(T); return false; } value = value2.Unwrap(); return true; } public static T Unwrap(this PhotonHashtable table, byte key) { return table[key].Unwrap(); } public static T Get(this PhotonHashtable table, byte key) { return table[key].Get(); } } } namespace Photon.Client.Encryption { internal class DiffieHellmanCryptoProvider : ICryptoProvider, IDisposable { private static readonly BigInteger primeRoot = new BigInteger(OakleyGroups.Generator); private readonly BigInteger prime; private readonly BigInteger secret; private readonly BigInteger publicKey; private Rijndael crypto; private byte[] sharedKey; public bool IsInitialized => crypto != null; public byte[] PublicKey { get { BigInteger bigInteger = publicKey; return MsBigIntArrayToPhotonBigIntArray(bigInteger.ToByteArray()); } } public DiffieHellmanCryptoProvider() { prime = new BigInteger(OakleyGroups.OakleyPrime768); secret = GenerateRandomSecret(160); publicKey = CalculatePublicKey(); } public DiffieHellmanCryptoProvider(byte[] cryptoKey) { crypto = new RijndaelManaged(); crypto.Key = cryptoKey; crypto.IV = new byte[16]; crypto.Padding = PaddingMode.PKCS7; } public void DeriveSharedKey(byte[] otherPartyPublicKey) { otherPartyPublicKey = PhotonBigIntArrayToMsBigIntArray(otherPartyPublicKey); BigInteger otherPartyPublicKey2 = new BigInteger(otherPartyPublicKey); sharedKey = MsBigIntArrayToPhotonBigIntArray(CalculateSharedKey(otherPartyPublicKey2).ToByteArray()); byte[] key; using (SHA256 sHA = new SHA256Managed()) { key = sHA.ComputeHash(sharedKey); } crypto = new RijndaelManaged(); crypto.Key = key; crypto.IV = new byte[16]; crypto.Padding = PaddingMode.PKCS7; } private byte[] PhotonBigIntArrayToMsBigIntArray(byte[] array) { Array.Reverse((Array)array); if ((array[^1] & 0x80) == 128) { byte[] array2 = new byte[array.Length + 1]; Buffer.BlockCopy(array, 0, array2, 0, array.Length); return array2; } return array; } private byte[] MsBigIntArrayToPhotonBigIntArray(byte[] array) { Array.Reverse((Array)array); if (array[0] == 0) { byte[] array2 = new byte[array.Length - 1]; Buffer.BlockCopy(array, 1, array2, 0, array.Length - 1); return array2; } return array; } public byte[] Encrypt(byte[] data) { return Encrypt(data, 0, data.Length); } public byte[] Encrypt(byte[] data, int offset, int count) { using ICryptoTransform cryptoTransform = crypto.CreateEncryptor(); return cryptoTransform.TransformFinalBlock(data, offset, count); } public byte[] Decrypt(byte[] data) { return Decrypt(data, 0, data.Length); } public byte[] Decrypt(byte[] data, int offset, int count) { using ICryptoTransform cryptoTransform = crypto.CreateDecryptor(); return cryptoTransform.TransformFinalBlock(data, offset, count); } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } protected void Dispose(bool disposing) { } private BigInteger CalculatePublicKey() { return BigInteger.ModPow(primeRoot, secret, prime); } private BigInteger CalculateSharedKey(BigInteger otherPartyPublicKey) { return BigInteger.ModPow(otherPartyPublicKey, secret, prime); } private BigInteger GenerateRandomSecret(int secretLength) { RNGCryptoServiceProvider rNGCryptoServiceProvider = new RNGCryptoServiceProvider(); byte[] array = new byte[secretLength / 8]; BigInteger bigInteger; do { rNGCryptoServiceProvider.GetBytes(array); bigInteger = new BigInteger(array); } while (bigInteger >= prime - 1 || bigInteger < 2L); return bigInteger; } } public interface IPhotonEncryptor { int LogLevel { get; set; } void Init(byte[] encryptionSecret, byte[] hmacSecret, byte[] ivBytes = null, bool chainingModeGCM = false, int mtu = 1200); void Encrypt2(byte[] data, int len, byte[] header, byte[] output, int outOffset, ref int outSize); byte[] Decrypt2(byte[] data, int offset, int len, byte[] header, out int outLen); int CalculateEncryptedSize(int unencryptedSize); int CalculateFragmentLength(); } internal interface ICryptoProvider : IDisposable { bool IsInitialized { get; } byte[] PublicKey { get; } void DeriveSharedKey(byte[] otherPartyPublicKey); byte[] Encrypt(byte[] data); byte[] Encrypt(byte[] data, int offset, int count); byte[] Decrypt(byte[] data); byte[] Decrypt(byte[] data, int offset, int count); } internal static class OakleyGroups { public static readonly int Generator = 22; public static readonly byte[] OakleyPrime768 = new byte[97] { 255, 255, 255, 255, 255, 255, 255, 255, 32, 54, 58, 166, 233, 66, 76, 244, 198, 126, 94, 98, 118, 181, 133, 228, 69, 194, 81, 109, 109, 53, 225, 79, 55, 20, 95, 242, 109, 10, 43, 48, 27, 67, 58, 205, 179, 25, 149, 239, 221, 4, 52, 142, 121, 8, 74, 81, 34, 155, 19, 59, 166, 190, 11, 2, 116, 204, 103, 138, 8, 78, 2, 41, 209, 28, 220, 128, 139, 98, 198, 196, 52, 194, 104, 33, 162, 218, 15, 201, 255, 255, 255, 255, 255, 255, 255, 255, 0 }; public static readonly byte[] OakleyPrime1024 = new byte[128] { 255, 255, 255, 255, 255, 255, 255, 255, 201, 15, 218, 162, 33, 104, 194, 52, 196, 198, 98, 139, 128, 220, 28, 209, 41, 2, 78, 8, 138, 103, 204, 116, 2, 11, 190, 166, 59, 19, 155, 34, 81, 74, 8, 121, 142, 52, 4, 221, 239, 149, 25, 179, 205, 58, 67, 27, 48, 43, 10, 109, 242, 95, 20, 55, 79, 225, 53, 109, 109, 81, 194, 69, 228, 133, 181, 118, 98, 94, 126, 198, 244, 76, 66, 233, 166, 55, 237, 107, 11, 255, 92, 182, 244, 6, 183, 237, 238, 56, 107, 251, 90, 137, 159, 165, 174, 159, 36, 17, 124, 75, 31, 230, 73, 40, 102, 81, 236, 230, 83, 129, 255, 255, 255, 255, 255, 255, 255, 255 }; public static readonly byte[] OakleyPrime1536 = new byte[192] { 255, 255, 255, 255, 255, 255, 255, 255, 201, 15, 218, 162, 33, 104, 194, 52, 196, 198, 98, 139, 128, 220, 28, 209, 41, 2, 78, 8, 138, 103, 204, 116, 2, 11, 190, 166, 59, 19, 155, 34, 81, 74, 8, 121, 142, 52, 4, 221, 239, 149, 25, 179, 205, 58, 67, 27, 48, 43, 10, 109, 242, 95, 20, 55, 79, 225, 53, 109, 109, 81, 194, 69, 228, 133, 181, 118, 98, 94, 126, 198, 244, 76, 66, 233, 166, 55, 237, 107, 11, 255, 92, 182, 244, 6, 183, 237, 238, 56, 107, 251, 90, 137, 159, 165, 174, 159, 36, 17, 124, 75, 31, 230, 73, 40, 102, 81, 236, 228, 91, 61, 194, 0, 124, 184, 161, 99, 191, 5, 152, 218, 72, 54, 28, 85, 211, 154, 105, 22, 63, 168, 253, 36, 207, 95, 131, 101, 93, 35, 220, 163, 173, 150, 28, 98, 243, 86, 32, 133, 82, 187, 158, 213, 41, 7, 112, 150, 150, 109, 103, 12, 53, 78, 74, 188, 152, 4, 241, 116, 108, 8, 202, 35, 115, 39, 255, 255, 255, 255, 255, 255, 255, 255 }; } }