Files
LuaCsForBarotraumaEP/Subsurface/Source/Networking/disabled/ReliableSender.cs
juanjp600 6e456bcee2 Disable reliable sender
I'll be replacing reliability with redundancy; basically, instead of requiring acks for reliable packets, both the client and server will stack and send several previous states redundantly. This has several benefits, such as allowing the frequency at which packets are sent to be greatly reduced (at the cost of larger packets, but that's a non-issue really, the overhead of many tiny packets is probably worse), and it makes implementing the authoritative server a lot easier.

The reliable sender will probably be needed later to make clients able to download subs again, but for now it's not important.
2016-08-25 18:40:59 -03:00

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);
}
}
}