Files
LuaCsForBarotraumaEP/Subsurface/Source/Networking/ReliableSender.cs

489 lines
15 KiB
C#

using Lidgren.Network;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Barotrauma.Networking.ReliableMessages
{
class ReliableChannel
{
ReliableSender sender;
ReliableReceiver receiver;
public ReliableChannel(NetPeer host)
{
sender = new ReliableSender(host);
receiver = new ReliableReceiver(host);
}
public ReliableMessage CreateMessage()
{
return sender.CreateMessage();
}
public void SendMessage(ReliableMessage message, NetConnection receiver)
{
try
{
sender.SendMessage(message, receiver);
}
catch (Exception e)
{
#if DEBUG
DebugConsole.ThrowError("Sending a reliable message failed", e);
#endif
}
}
public void HandleResendRequest(NetIncomingMessage inc)
{
sender.HandleResendRequest(inc);
}
public void HandleLatestMessageID(NetIncomingMessage inc)
{
//make sure we've received what's been sent to us, if not, rerequest
receiver.HandleLatestMessageID(inc);
}
public bool CheckMessage(NetIncomingMessage inc)
{
return receiver.CheckMessage(inc);
}
public void Update(float deltaTime)
{
sender.Update(deltaTime);
//update receiver to rerequest missed messages
receiver.Update(deltaTime);
}
public static int IdDiff(ushort id1, ushort id2)
{
if (Math.Abs((int)id1 - (int)id2) > ushort.MaxValue / 2)
{
return (ushort.MaxValue - Math.Max(id1, id2)) + Math.Min(id1, id2);
}
return Math.Abs(id1 - id2);
}
}
internal class ReliableSender
{
private Dictionary<ushort, ReliableMessage> messageBuffer;
private ushort messageCount;
private NetPeer sender;
private NetConnection recipient;
private float idSendTimer;
private float idSendInterval;
public ReliableSender(NetPeer sender)
{
this.sender = sender;
messageCount = 1;
messageBuffer = new Dictionary<ushort, ReliableMessage>();
}
public ReliableMessage CreateMessage()
{
ushort messageID = (messageCount == ushort.MaxValue) ? (ushort)1 : (ushort)(messageCount + 1);
NetOutgoingMessage message = sender.CreateMessage();
var reliableMessage = new ReliableMessage(message, messageID);
message.Write((byte)PacketTypes.ReliableMessage);
message.Write(messageID);
if (messageBuffer.Count > NetConfig.ReliableMessageBufferSize)
{
int end = messageCount - NetConfig.ReliableMessageBufferSize;
int start = end - (messageBuffer.Count - NetConfig.ReliableMessageBufferSize);
if (start < 0)
{
int wrappedStart = start + ushort.MaxValue;
if (wrappedStart == 0) wrappedStart = ushort.MaxValue;
int wrappedEnd = end + ushort.MaxValue;
if (wrappedEnd == 0) wrappedEnd = ushort.MaxValue;
for (ushort i = (ushort)wrappedStart; i <= (ushort)wrappedEnd; i++)
{
messageBuffer.Remove(i);
if (i == ushort.MaxValue) break;
Debug.WriteLine("removing message " + i);
}
}
for (ushort i = (ushort)Math.Max(start,0); i <= (ushort)Math.Max(end,0); i++)
{
messageBuffer.Remove(i);
if (i == ushort.MaxValue) break;
Debug.WriteLine("removing message " + i);
}
}
return reliableMessage;
}
public void SendMessage(ReliableMessage message, NetConnection connection)
{
idSendInterval = 0.0f;
idSendTimer = connection.AverageRoundtripTime;
messageBuffer.Add(message.ID, message);
Debug.WriteLine("sending reliable massage (id " + message.ID + ")");
if (messageCount == ushort.MaxValue) messageCount = 0;
messageCount++;
message.SaveInnerMessage();
sender.SendMessage(message.InnerMessage, connection, NetDeliveryMethod.Unreliable, 0);
recipient = connection;
}
public void HandleResendRequest(NetIncomingMessage inc)
{
ushort messageId = inc.ReadUInt16();
Debug.WriteLine("received resend request for msg id "+messageId);
ResendMessage(messageId, inc.SenderConnection);
}
private void ResendMessage(ushort messageId, NetConnection connection)
{
ReliableMessage message;
if (!messageBuffer.TryGetValue(messageId, out message)) return;
Debug.WriteLine("resending " + messageId);
NetOutgoingMessage resendMessage = sender.CreateMessage();
message.RestoreInnerMessage(resendMessage);
idSendTimer = connection.AverageRoundtripTime;
sender.SendMessage(resendMessage, connection, NetDeliveryMethod.Unreliable);
}
public void Update(float deltaTime)
{
if (recipient == null) return;
idSendTimer -= deltaTime;
if (idSendTimer > 0.0f) return;
//Debug.WriteLine("Sending ack message: "+messageCount);
NetOutgoingMessage message = sender.CreateMessage();
message.Write((byte)PacketTypes.LatestMessageID);
message.Write(messageCount);
sender.SendMessage(message, recipient, NetDeliveryMethod.Unreliable);
float roundTripTime = Math.Min(recipient.AverageRoundtripTime, 1.0f);
idSendTimer = Math.Max(roundTripTime, NetConfig.IdSendInterval + idSendInterval);
idSendInterval += 0.1f;
}
}
internal class ReliableReceiver
{
ushort lastMessageID;
Queue<ushort> missingMessageIds;
Dictionary<ushort, MissingMessage> missingMessages;
private NetPeer receiver;
private NetConnection recipient;
public ReliableReceiver(NetPeer receiver)
{
this.receiver = receiver;
lastMessageID = 1;
missingMessages = new Dictionary<ushort,MissingMessage>();
missingMessageIds = new Queue<ushort>();
}
public void Update(float deltaTime)
{
foreach (var message in missingMessages.Where(m => m.Value.ResendRequestsSent > NetConfig.ResendAttempts).ToList())
{
Debug.WriteLine("Max rerequest attempts reached on message "+message.Value.ID);
missingMessages.Remove(message.Key);
}
while (missingMessageIds.Count>NetConfig.ReliableMessageBufferSize)
{
ushort id = missingMessageIds.Dequeue();
missingMessages.Remove(id);
}
foreach (KeyValuePair<ushort, MissingMessage> valuePair in missingMessages)
{
MissingMessage missingMessage = valuePair.Value;
missingMessage.ResendTimer -= deltaTime;
if (missingMessage.ResendTimer > 0.0f) continue;
Debug.WriteLine("rerequest "+missingMessage.ID+" (try #"+missingMessage.ResendRequestsSent+")");
NetOutgoingMessage resendRequest = receiver.CreateMessage();
resendRequest.Write((byte)PacketTypes.ResendRequest);
resendRequest.Write(missingMessage.ID);
receiver.SendMessage(resendRequest, recipient,
missingMessage.ResendRequestsSent==0 ? NetDeliveryMethod.ReliableUnordered : NetDeliveryMethod.Unreliable);
float roundTripTime = Math.Min(recipient.AverageRoundtripTime, 1.0f);
missingMessage.ResendTimer = Math.Max(roundTripTime, NetConfig.RerequestInterval);
missingMessage.ResendRequestsSent++;
}
}
public bool CheckMessage(NetIncomingMessage message)
{
recipient = message.SenderConnection;
ushort id = message.ReadUInt16();
if (ReliableChannel.IdDiff(lastMessageID, id) > NetConfig.ReliableMessageBufferSize)
{
Debug.WriteLine("id diff > NetConfig.ReliableMessageBufferSize, resetting reliable channel");
lastMessageID = id;
return false;
}
Debug.WriteLine("received message ID " + id + " - last id: " + lastMessageID);
//wrapped around
if (Math.Abs((int)lastMessageID - (int)id) > ushort.MaxValue / 2)
{
//id wrapped around and we missed some messages in between, rerequest them
if (lastMessageID>ushort.MaxValue/2 && id>=1)
{
for (ushort i = (ushort)(Math.Min(lastMessageID, (ushort)(ushort.MaxValue-1)) + 1); i < ushort.MaxValue; i++)
{
QueueMissingMessage(i);
}
for (ushort i = 1; i < id; i++)
{
QueueMissingMessage(i);
}
lastMessageID = id;
}
//we already wrapped around but the message hasn't, check if it's a duplicate
else if (lastMessageID < ushort.MaxValue / 2 && id > ushort.MaxValue / 2 && !missingMessages.ContainsKey(id))
{
Debug.WriteLine("old already received message, ignore");
return false;
}
else
{
RemoveMissingMessage(id);
}
}
else
{
if (id>lastMessageID+1)
{
for (ushort i = (ushort)(lastMessageID+1); i < id; i++ )
{
QueueMissingMessage(i);
}
}
//received an old message and it wasn't marked as missed, lets ignore it
else if (id<=lastMessageID && !missingMessages.ContainsKey(id))
{
Debug.WriteLine("old already received message, ignore");
return false;
}
else
{
RemoveMissingMessage(id);
}
lastMessageID = Math.Max(lastMessageID, id);
}
return true;
}
private void QueueMissingMessage(ushort id)
{
//message already marked as missed, continue
if (missingMessages.ContainsKey(id)) return;
Debug.WriteLine("added " + id + " to missed");
float waitTime = Math.Abs(lastMessageID - id)>1 ? 0.0f : recipient.AverageRoundtripTime*0.5f;
missingMessages.Add(id, new MissingMessage(id, waitTime));
missingMessageIds.Enqueue(id);
}
private void RemoveMissingMessage(ushort id)
{
if (!missingMessages.ContainsKey(id)) return;
Debug.WriteLine("remove " + id + " from missed");
missingMessages.Remove(id);
}
public void HandleLatestMessageID(NetIncomingMessage inc)
{
ushort messageId = inc.ReadUInt16();
recipient = inc.SenderConnection;
//id matches, all good
if (messageId == lastMessageID)
{
//Debug.WriteLine("Received ack message: " + messageId + ", all good");
return;
}
if (ReliableChannel.IdDiff(lastMessageID, messageId) > NetConfig.ReliableMessageBufferSize)
{
Debug.WriteLine("id diff > NetConfig.ReliableMessageBufferSize, resetting reliable channel");
lastMessageID = messageId;
return;
}
if (messageId < lastMessageID && Math.Abs((int)lastMessageID - (int)messageId) < ushort.MaxValue / 2)
{
Debug.WriteLine("Received id update message: " + messageId + ": ignoring, already received (" + lastMessageID + ")");
return;
}
Debug.WriteLine("Received id update message: " + messageId + ", need to rerequest messages (last id: "+lastMessageID+")");
if (lastMessageID > ushort.MaxValue / 2 && messageId < short.MaxValue / 2)
{
for (ushort i = (ushort)Math.Min((int)lastMessageID + 1, ushort.MaxValue); i <= ushort.MaxValue; i++)
{
if (i == ushort.MaxValue && lastMessageID == ushort.MaxValue) break;
QueueMissingMessage(i);
if (i == ushort.MaxValue) break;
}
for (ushort i = 1; i <= messageId; i++)
{
QueueMissingMessage(i);
}
}
else
{
//we already wrapped around but message hasn't, so it's an old message
if (lastMessageID < ushort.MaxValue / 2 && messageId > ushort.MaxValue / 2)
{
Debug.WriteLine("old already received message, ignore");
return;
}
for (ushort i = (ushort)Math.Min((int)lastMessageID+1, ushort.MaxValue); i <= messageId; i++)
{
QueueMissingMessage(i);
if (i == ushort.MaxValue) break;
}
}
lastMessageID = messageId;
}
}
internal class MissingMessage
{
private ushort id;
public byte ResendRequestsSent;
public float ResendTimer;
public ushort ID
{
get { return id; }
}
public MissingMessage(ushort id)
{
this.id = id;
}
public MissingMessage(ushort id, float resendTimer)
{
this.id = id;
this.ResendTimer = resendTimer;
}
}
class ReliableMessage
{
private NetOutgoingMessage innerMessage;
private ushort id;
private byte[] innerMessageBytes;
public NetOutgoingMessage InnerMessage
{
get { return innerMessage; }
}
public ushort ID
{
get { return id; }
}
public ReliableMessage(NetOutgoingMessage message, ushort id)
{
this.innerMessage = message;
this.id = id;
}
public void SaveInnerMessage()
{
innerMessage.WritePadBits();
innerMessageBytes = innerMessage.PeekBytes(innerMessage.LengthBytes);
//innerMessage = null;
}
public void RestoreInnerMessage(NetOutgoingMessage message)
{
message.Write(innerMessageBytes);
}
}
}