public async Task SendQueueAsync(int millisecondsWait = 0) { if (_sendQueueNotConcurrent.Count == 0) { return; } // Extremely important that this will not allow more than one thread at a time. // This methods handle ordering and potential encryption, hence order matters. if (!(await _syncHack.WaitAsync(millisecondsWait))) { return; } try { var sendList = new List <Packet>(); Queue <Packet> queue = _sendQueueNotConcurrent; int length = queue.Count; for (int i = 0; i < length; i++) { Packet packet; lock (_queueSync) { if (queue.Count == 0) { break; } if (!queue.TryDequeue(out packet)) { break; } } if (packet == null) { continue; } if (State == ConnectionState.Unconnected) { packet.PutPool(); continue; } sendList.Add(packet); } if (sendList.Count == 0) { return; } List <Packet> prepareSend = CustomMessageHandler.PrepareSend(sendList); foreach (Packet packet in prepareSend) { Packet message = packet; if (CustomMessageHandler != null) { message = CustomMessageHandler.HandleOrderedSend(message); } Reliability reliability = message.ReliabilityHeader.Reliability; if (reliability == Reliability.Undefined) { reliability = Reliability.Reliable; // Questionable practice } if (reliability == Reliability.ReliableOrdered) { message.ReliabilityHeader.OrderingIndex = Interlocked.Increment(ref OrderingIndex); } await _packetSender.SendPacketAsync(this, message); } } catch (Exception e) { Log.Error(e); } finally { _syncHack.Release(); } }