#if DEBUG && !UNITY_WP_8_1 && !UNITY_WSA using System; using System.Collections.Generic; using System.Threading; namespace FlyingWormConsole3.LiteNetLib { internal sealed class ReliableChannel { private class PendingPacket { public NetPacket Packet; public DateTime? TimeStamp; public NetPacket GetAndClear() { var packet = Packet; Packet = null; TimeStamp = null; return packet; } } private readonly Queue _outgoingPackets; private readonly bool[] _outgoingAcks; //for send acks private readonly PendingPacket[] _pendingPackets; //for unacked packets and duplicates private readonly NetPacket[] _receivedPackets; //for order private readonly bool[] _earlyReceived; //for unordered private int _localSeqence; private int _remoteSequence; private int _localWindowStart; private int _remoteWindowStart; private readonly NetPeer _peer; private bool _mustSendAcks; private readonly bool _ordered; private readonly int _windowSize; private const int BitsInByte = 8; private int _queueIndex; public int PacketsInQueue { get { return _outgoingPackets.Count; } } public ReliableChannel(NetPeer peer, bool ordered, int windowSize) { _windowSize = windowSize; _peer = peer; _ordered = ordered; _outgoingPackets = new Queue(_windowSize); _outgoingAcks = new bool[_windowSize]; _pendingPackets = new PendingPacket[_windowSize]; for (int i = 0; i < _pendingPackets.Length; i++) { _pendingPackets[i] = new PendingPacket(); } if (_ordered) _receivedPackets = new NetPacket[_windowSize]; else _earlyReceived = new bool[_windowSize]; _localWindowStart = 0; _localSeqence = 0; _remoteSequence = 0; _remoteWindowStart = 0; } //ProcessAck in packet public void ProcessAck(NetPacket packet) { int validPacketSize = (_windowSize - 1) / BitsInByte + 1 + NetConstants.SequencedHeaderSize; if (packet.Size != validPacketSize) { NetUtils.DebugWrite("[PA]Invalid acks packet size"); return; } ushort ackWindowStart = packet.Sequence; if (ackWindowStart > NetConstants.MaxSequence) { NetUtils.DebugWrite("[PA]Bad window start"); return; } //check relevance if (NetUtils.RelativeSequenceNumber(ackWindowStart, _localWindowStart) <= -_windowSize) { NetUtils.DebugWrite("[PA]Old acks"); return; } byte[] acksData = packet.RawData; NetUtils.DebugWrite("[PA]AcksStart: {0}", ackWindowStart); int startByte = NetConstants.SequencedHeaderSize; Monitor.Enter(_pendingPackets); for (int i = 0; i < _windowSize; i++) { int ackSequence = (ackWindowStart + i) % NetConstants.MaxSequence; if (NetUtils.RelativeSequenceNumber(ackSequence, _localWindowStart) < 0) { //NetUtils.DebugWrite(ConsoleColor.Cyan, "[PA] SKIP OLD: " + ackSequence); //Skip old ack continue; } int currentByte = startByte + i / BitsInByte; int currentBit = i % BitsInByte; if ((acksData[currentByte] & (1 << currentBit)) == 0) { //NetUtils.DebugWrite(ConsoleColor.Cyan, "[PA] SKIP FALSE: " + ackSequence); //Skip false ack continue; } if (ackSequence == _localWindowStart) { //Move window _localWindowStart = (_localWindowStart + 1) % NetConstants.MaxSequence; } NetPacket removed = _pendingPackets[ackSequence % _windowSize].GetAndClear(); if (removed != null) { _peer.Recycle(removed); NetUtils.DebugWrite("[PA]Removing reliableInOrder ack: {0} - true", ackSequence); } else { NetUtils.DebugWrite("[PA]Removing reliableInOrder ack: {0} - false", ackSequence); } } Monitor.Exit(_pendingPackets); } public void AddToQueue(NetPacket packet) { lock (_outgoingPackets) { _outgoingPackets.Enqueue(packet); } } private void ProcessQueuedPackets() { //get packets from queue while (_outgoingPackets.Count > 0) { int relate = NetUtils.RelativeSequenceNumber(_localSeqence, _localWindowStart); if (relate < _windowSize) { NetPacket packet; lock (_outgoingPackets) { packet = _outgoingPackets.Dequeue(); } packet.Sequence = (ushort)_localSeqence; _pendingPackets[_localSeqence % _windowSize].Packet = packet; _localSeqence = (_localSeqence + 1) % NetConstants.MaxSequence; } else //Queue filled { break; } } } public bool SendNextPacket() { //check sending acks DateTime currentTime = DateTime.UtcNow; Monitor.Enter(_pendingPackets); ProcessQueuedPackets(); //send PendingPacket currentPacket; bool packetFound = false; int startQueueIndex = _queueIndex; do { currentPacket = _pendingPackets[_queueIndex]; if (currentPacket.Packet != null) { //check send time if(currentPacket.TimeStamp.HasValue) { double packetHoldTime = (currentTime - currentPacket.TimeStamp.Value).TotalMilliseconds; if (packetHoldTime > _peer.ResendDelay) { NetUtils.DebugWrite("[RC]Resend: {0} > {1}", (int)packetHoldTime, _peer.ResendDelay); packetFound = true; } } else //Never sended { packetFound = true; } } _queueIndex = (_queueIndex + 1) % _windowSize; } while (!packetFound && _queueIndex != startQueueIndex); if (packetFound) { currentPacket.TimeStamp = DateTime.UtcNow; _peer.SendRawData(currentPacket.Packet); NetUtils.DebugWrite("[RR]Sended"); } Monitor.Exit(_pendingPackets); return packetFound; } public void SendAcks() { if (!_mustSendAcks) return; _mustSendAcks = false; NetUtils.DebugWrite("[RR]SendAcks"); //Init packet int bytesCount = (_windowSize - 1) / BitsInByte + 1; PacketProperty property = _ordered ? PacketProperty.AckReliableOrdered : PacketProperty.AckReliable; var acksPacket = _peer.GetPacketFromPool(property, bytesCount); //For quick access byte[] data = acksPacket.RawData; //window start + acks size //Put window start Monitor.Enter(_outgoingAcks); acksPacket.Sequence = (ushort)_remoteWindowStart; //Put acks int startAckIndex = _remoteWindowStart % _windowSize; int currentAckIndex = startAckIndex; int currentBit = 0; int currentByte = NetConstants.SequencedHeaderSize; do { if (_outgoingAcks[currentAckIndex]) { data[currentByte] |= (byte)(1 << currentBit); } currentBit++; if (currentBit == BitsInByte) { currentByte++; currentBit = 0; } currentAckIndex = (currentAckIndex + 1) % _windowSize; } while (currentAckIndex != startAckIndex); Monitor.Exit(_outgoingAcks); _peer.SendRawData(acksPacket); _peer.Recycle(acksPacket); } //Process incoming packet public void ProcessPacket(NetPacket packet) { if (packet.Sequence >= NetConstants.MaxSequence) { NetUtils.DebugWrite("[RR]Bad sequence"); return; } int relate = NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteWindowStart); int relateSeq = NetUtils.RelativeSequenceNumber(packet.Sequence, _remoteSequence); if (relateSeq > _windowSize) { NetUtils.DebugWrite("[RR]Bad sequence"); return; } //Drop bad packets if(relate < 0) { //Too old packet doesn't ack NetUtils.DebugWrite("[RR]ReliableInOrder too old"); return; } if (relate >= _windowSize * 2) { //Some very new packet NetUtils.DebugWrite("[RR]ReliableInOrder too new"); return; } //If very new - move window Monitor.Enter(_outgoingAcks); if (relate >= _windowSize) { //New window position int newWindowStart = (_remoteWindowStart + relate - _windowSize + 1) % NetConstants.MaxSequence; //Clean old data while (_remoteWindowStart != newWindowStart) { _outgoingAcks[_remoteWindowStart % _windowSize] = false; _remoteWindowStart = (_remoteWindowStart + 1) % NetConstants.MaxSequence; } } //Final stage - process valid packet //trigger acks send _mustSendAcks = true; if (_outgoingAcks[packet.Sequence % _windowSize]) { NetUtils.DebugWrite("[RR]ReliableInOrder duplicate"); Monitor.Exit(_outgoingAcks); return; } //save ack _outgoingAcks[packet.Sequence % _windowSize] = true; Monitor.Exit(_outgoingAcks); //detailed check if (packet.Sequence == _remoteSequence) { NetUtils.DebugWrite("[RR]ReliableInOrder packet succes"); _peer.AddIncomingPacket(packet); _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; if (_ordered) { NetPacket p; while ( (p = _receivedPackets[_remoteSequence % _windowSize]) != null) { //process holded packet _receivedPackets[_remoteSequence % _windowSize] = null; _peer.AddIncomingPacket(p); _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; } } else { while (_earlyReceived[_remoteSequence % _windowSize]) { //process early packet _earlyReceived[_remoteSequence % _windowSize] = false; _remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence; } } return; } //holded packet if (_ordered) { _receivedPackets[packet.Sequence % _windowSize] = packet; } else { _earlyReceived[packet.Sequence % _windowSize] = true; _peer.AddIncomingPacket(packet); } } } } #endif