Boss Room 是一款使用 Unity MLAPI 制作的全功能合作多人 RPG。 它旨在作为学习样本,展示类似游戏中经常出现的某些典型游戏模式。
您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

323 行
12 KiB

using System;
namespace LiteNetLib
{
internal sealed class ReliableChannel : BaseChannel
{
private struct PendingPacket
{
private NetPacket _packet;
private long _timeStamp;
private bool _isSent;
public override string ToString()
{
return _packet == null ? "Empty" : _packet.Sequence.ToString();
}
public void Init(NetPacket packet)
{
_packet = packet;
_isSent = false;
}
public void TrySend(long currentTime, NetPeer peer)
{
if (_packet == null)
return;
if (_isSent) //check send time
{
double resendDelay = peer.ResendDelay * TimeSpan.TicksPerMillisecond;
double packetHoldTime = currentTime - _timeStamp;
if (packetHoldTime < resendDelay)
return;
NetDebug.Write("[RC]Resend: {0} > {1}", (int)packetHoldTime, resendDelay);
}
_timeStamp = currentTime;
_isSent = true;
peer.SendUserData(_packet);
}
public bool Clear(NetPeer peer)
{
if (_packet != null)
{
peer.RecycleAndDeliver(_packet);
_packet = null;
return true;
}
return false;
}
}
private readonly NetPacket _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 bool _mustSendAcks;
private readonly DeliveryMethod _deliveryMethod;
private readonly bool _ordered;
private readonly int _windowSize;
private const int BitsInByte = 8;
private readonly byte _id;
public ReliableChannel(NetPeer peer, bool ordered, byte id) : base(peer)
{
_id = id;
_windowSize = NetConstants.DefaultWindowSize;
_ordered = ordered;
_pendingPackets = new PendingPacket[_windowSize];
for (int i = 0; i < _pendingPackets.Length; i++)
_pendingPackets[i] = new PendingPacket();
if (_ordered)
{
_deliveryMethod = DeliveryMethod.ReliableOrdered;
_receivedPackets = new NetPacket[_windowSize];
}
else
{
_deliveryMethod = DeliveryMethod.ReliableUnordered;
_earlyReceived = new bool[_windowSize];
}
_localWindowStart = 0;
_localSeqence = 0;
_remoteSequence = 0;
_remoteWindowStart = 0;
_outgoingAcks = new NetPacket(PacketProperty.Ack, (_windowSize - 1) / BitsInByte + 2) {ChannelId = id};
}
//ProcessAck in packet
private void ProcessAck(NetPacket packet)
{
if (packet.Size != _outgoingAcks.Size)
{
NetDebug.Write("[PA]Invalid acks packet size");
return;
}
ushort ackWindowStart = packet.Sequence;
int windowRel = NetUtils.RelativeSequenceNumber(_localWindowStart, ackWindowStart);
if (ackWindowStart >= NetConstants.MaxSequence || windowRel < 0)
{
NetDebug.Write("[PA]Bad window start");
return;
}
//check relevance
if (windowRel >= _windowSize)
{
NetDebug.Write("[PA]Old acks");
return;
}
byte[] acksData = packet.RawData;
lock (_pendingPackets)
{
for (int pendingSeq = _localWindowStart;
pendingSeq != _localSeqence;
pendingSeq = (pendingSeq + 1) % NetConstants.MaxSequence)
{
int rel = NetUtils.RelativeSequenceNumber(pendingSeq, ackWindowStart);
if (rel >= _windowSize)
{
NetDebug.Write("[PA]REL: " + rel);
break;
}
int pendingIdx = pendingSeq % _windowSize;
int currentByte = NetConstants.ChanneledHeaderSize + pendingIdx / BitsInByte;
int currentBit = pendingIdx % BitsInByte;
if ((acksData[currentByte] & (1 << currentBit)) == 0)
{
#if DEBUG
Peer.Statistics.PacketLoss++;
#else
if (Peer.NetManager.EnableStatistics)
{
Peer.Statistics.PacketLoss++;
}
#endif
//Skip false ack
NetDebug.Write("[PA]False ack: {0}", pendingSeq);
continue;
}
if (pendingSeq == _localWindowStart)
{
//Move window
_localWindowStart = (_localWindowStart + 1) % NetConstants.MaxSequence;
}
//clear packet
if (_pendingPackets[pendingIdx].Clear(Peer))
NetDebug.Write("[PA]Removing reliableInOrder ack: {0} - true", pendingSeq);
}
}
}
public override void SendNextPackets()
{
if (_mustSendAcks)
{
_mustSendAcks = false;
NetDebug.Write("[RR]SendAcks");
lock(_outgoingAcks)
Peer.SendUserData(_outgoingAcks);
}
long currentTime = DateTime.UtcNow.Ticks;
lock (_pendingPackets)
{
//get packets from queue
lock (OutgoingQueue)
{
while (OutgoingQueue.Count > 0)
{
int relate = NetUtils.RelativeSequenceNumber(_localSeqence, _localWindowStart);
if (relate >= _windowSize)
break;
var netPacket = OutgoingQueue.Dequeue();
netPacket.Sequence = (ushort) _localSeqence;
netPacket.ChannelId = _id;
_pendingPackets[_localSeqence % _windowSize].Init(netPacket);
_localSeqence = (_localSeqence + 1) % NetConstants.MaxSequence;
}
}
//send
for (int pendingSeq = _localWindowStart; pendingSeq != _localSeqence; pendingSeq = (pendingSeq + 1) % NetConstants.MaxSequence)
_pendingPackets[pendingSeq % _windowSize].TrySend(currentTime, Peer);
}
}
//Process incoming packet
public override bool ProcessPacket(NetPacket packet)
{
if (packet.Property == PacketProperty.Ack)
{
ProcessAck(packet);
return false;
}
int seq = packet.Sequence;
if (seq >= NetConstants.MaxSequence)
{
NetDebug.Write("[RR]Bad sequence");
return false;
}
int relate = NetUtils.RelativeSequenceNumber(seq, _remoteWindowStart);
int relateSeq = NetUtils.RelativeSequenceNumber(seq, _remoteSequence);
if (relateSeq > _windowSize)
{
NetDebug.Write("[RR]Bad sequence");
return false;
}
//Drop bad packets
if (relate < 0)
{
//Too old packet doesn't ack
NetDebug.Write("[RR]ReliableInOrder too old");
return false;
}
if (relate >= _windowSize * 2)
{
//Some very new packet
NetDebug.Write("[RR]ReliableInOrder too new");
return false;
}
//If very new - move window
int ackIdx;
int ackByte;
int ackBit;
lock (_outgoingAcks)
{
if (relate >= _windowSize)
{
//New window position
int newWindowStart = (_remoteWindowStart + relate - _windowSize + 1) % NetConstants.MaxSequence;
_outgoingAcks.Sequence = (ushort) newWindowStart;
//Clean old data
while (_remoteWindowStart != newWindowStart)
{
ackIdx = _remoteWindowStart % _windowSize;
ackByte = NetConstants.ChanneledHeaderSize + ackIdx / BitsInByte;
ackBit = ackIdx % BitsInByte;
_outgoingAcks.RawData[ackByte] &= (byte) ~(1 << ackBit);
_remoteWindowStart = (_remoteWindowStart + 1) % NetConstants.MaxSequence;
}
}
//Final stage - process valid packet
//trigger acks send
_mustSendAcks = true;
ackIdx = seq % _windowSize;
ackByte = NetConstants.ChanneledHeaderSize + ackIdx / BitsInByte;
ackBit = ackIdx % BitsInByte;
if ((_outgoingAcks.RawData[ackByte] & (1 << ackBit)) != 0)
{
NetDebug.Write("[RR]ReliableInOrder duplicate");
return false;
}
//save ack
_outgoingAcks.RawData[ackByte] |= (byte) (1 << ackBit);
}
//detailed check
if (seq == _remoteSequence)
{
NetDebug.Write("[RR]ReliableInOrder packet succes");
Peer.AddReliablePacket(_deliveryMethod, packet);
_remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence;
if (_ordered)
{
NetPacket p;
while ((p = _receivedPackets[_remoteSequence % _windowSize]) != null)
{
//process holden packet
_receivedPackets[_remoteSequence % _windowSize] = null;
Peer.AddReliablePacket(_deliveryMethod, p);
_remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence;
}
}
else
{
while (_earlyReceived[_remoteSequence % _windowSize])
{
//process early packet
_earlyReceived[_remoteSequence % _windowSize] = false;
_remoteSequence = (_remoteSequence + 1) % NetConstants.MaxSequence;
}
}
return true;
}
//holden packet
if (_ordered)
{
_receivedPackets[ackIdx] = packet;
}
else
{
_earlyReceived[ackIdx] = true;
Peer.AddReliablePacket(_deliveryMethod, packet);
}
return true;
}
}
}