public static unsafe void ReadAckPacket(NetworkPipelineContext context, PacketHeader header) { SharedContext *reliable = (SharedContext *)context.internalSharedProcessBuffer.GetUnsafePtr(); // Store receive timestamp for our acked sequence ID with remote processing time StoreReceiveTimestamp(context.internalSharedProcessBuffer, header.AckedSequenceId, context.timestamp, header.ProcessingTime); // Check the distance of the acked seqId in the header, if it's too far away from last acked packet we // can't process it and add it to the ack mask if (!SequenceHelpers.GreaterThan16(header.AckedSequenceId, (ushort)reliable->SentPackets.Acked)) { // No new acks; return; } reliable->SentPackets.Acked = header.AckedSequenceId; reliable->SentPackets.AckMask = header.AckMask; }
/// <summary> /// Acknowledge the reception of packets which have been sent. The reliability /// shared context/state is updated when packets are received from the other end /// of the connection. The other side will update it's ackmask with which packets /// have been received (starting from last received sequence ID) each time it sends /// a packet back. This checks the resend timers on each non-acknowledged packet /// and notifies if it's time to resend yet. /// </summary> /// <param name="context">Pipeline context, contains the buffer slices this pipeline connection owns.</param> /// <returns></returns> public static unsafe bool ReleaseOrResumePackets(NetworkPipelineContext context) { SharedContext *reliable = (SharedContext *)context.internalSharedProcessBuffer.GetUnsafePtr(); Context * ctx = (Context *)context.internalProcessBuffer.GetUnsafePtr(); // Last sequence ID and ackmask we received from the remote peer, these are confirmed delivered packets var lastReceivedAckMask = reliable->SentPackets.AckMask; var lastOwnSequenceIdAckedByRemote = (ushort)reliable->SentPackets.Acked; // To deal with wrapping, chop off the upper half of the sequence ID and multiply by window size, it // will then never wrap but will map to the correct index in the packet storage, wrapping happens when // sending low sequence IDs (since it checks sequence IDs backwards in time). var sequence = (ushort)(reliable->WindowSize * ((1 - lastOwnSequenceIdAckedByRemote) >> 15)); // Check each slot in the window, starting from the sequence ID calculated above (this isn't the // latest sequence ID though as it was adjusted to avoid wrapping) for (int i = 0; i < reliable->WindowSize; i++) { var info = GetPacketInformation(context.internalProcessBuffer, sequence); if (info->SequenceId >= 0) { // Check the bit for this sequence ID against the ackmask. Bit 0 in the ackmask is the latest // ackedSeqId, bit 1 latest ackedSeqId - 1 (one older) and so on. If bit X is 1 then ackedSeqId-X is acknowledged var ackBits = 1 << (lastOwnSequenceIdAckedByRemote - info->SequenceId); // Release if this seqId has been flipped on in the ackmask (so it's acknowledged) // Ignore if sequence ID is out of window range of the last acknowledged id if (SequenceHelpers.AbsDistance((ushort)lastOwnSequenceIdAckedByRemote, (ushort)info->SequenceId) < reliable->WindowSize && (ackBits & lastReceivedAckMask) != 0) { Release(context.internalProcessBuffer, info->SequenceId); info->SendTime = -1; sequence = (ushort)(sequence - 1); continue; } var timeToResend = CurrentResendTime(context.internalSharedProcessBuffer); if (context.timestamp > info->SendTime + timeToResend) { ctx->Resume = info->SequenceId; } } sequence = (ushort)(sequence - 1); } return(ctx->Resume != NullEntry); }
/// <summary> /// Read header data and update reliability tracking information in the shared context. /// - If the packets sequence ID is lower than the last received ID+1, then it's stale /// - If the packets sequence ID is higher, then we'll process it and update tracking info in the shared context /// </summary> /// <param name="context">Pipeline context, the reliability shared state is used here.</param> /// <param name="header">Packet header of a new received packet.</param> /// <returns>Sequence ID of the received packet.</returns> public static unsafe int Read(NetworkPipelineContext context, PacketHeader header) { SharedContext *reliable = (SharedContext *)context.internalSharedProcessBuffer.GetUnsafePtr(); reliable->stats.PacketsReceived++; if (SequenceHelpers.StalePacket( header.SequenceId, (ushort)(reliable->ReceivedPackets.Sequence + 1), (ushort)reliable->WindowSize)) { reliable->stats.PacketsStale++; return((int)ErrorCodes.Stale_Packet); } var window = reliable->WindowSize - 1; if (SequenceHelpers.GreaterThan16((ushort)(header.SequenceId + 1), (ushort)reliable->ReceivedPackets.Sequence)) { int distance = SequenceHelpers.AbsDistance(header.SequenceId, (ushort)reliable->ReceivedPackets.Sequence); for (var i = 0; i < Math.Min(distance, window); ++i) { if ((reliable->ReceivedPackets.AckMask & 1 << (window - i)) == 0) { reliable->stats.PacketsDropped++; } } if (distance > window) { reliable->stats.PacketsDropped += distance - window; reliable->ReceivedPackets.AckMask = 1; } else { reliable->ReceivedPackets.AckMask <<= distance; reliable->ReceivedPackets.AckMask |= 1; } reliable->ReceivedPackets.Sequence = header.SequenceId; } else if (SequenceHelpers.LessThan16(header.SequenceId, (ushort)reliable->ReceivedPackets.Sequence)) { int distance = SequenceHelpers.AbsDistance(header.SequenceId, (ushort)reliable->ReceivedPackets.Sequence); // If this is a resent packet the distance will seem very big and needs to be calculated again with adjustment for wrapping if (distance >= ushort.MaxValue - reliable->WindowSize) { distance = reliable->ReceivedPackets.Sequence - header.SequenceId; } var ackBit = 1 << distance; if ((ackBit & reliable->ReceivedPackets.AckMask) != 0) { reliable->stats.PacketsDuplicated++; return((int)ErrorCodes.Duplicated_Packet); } reliable->stats.PacketsOutOfOrder++; reliable->ReceivedPackets.AckMask |= (uint)ackBit; } // Store receive timestamp for remote sequence ID we just received StoreRemoteReceiveTimestamp(context.internalSharedProcessBuffer, header.SequenceId, context.timestamp); ReadAckPacket(context, header); return(header.SequenceId); }