Renamed project folders from Subsurface to Barotrauma
This commit is contained in:
@@ -0,0 +1,321 @@
|
||||
using Lidgren.Network;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
class FileStreamReceiver : IDisposable
|
||||
{
|
||||
const int MaxFileSize = 1000000;
|
||||
|
||||
public delegate void OnFinished(FileStreamReceiver fileStreamReceiver);
|
||||
private OnFinished onFinished;
|
||||
|
||||
private NetClient client;
|
||||
private ulong length;
|
||||
private ulong received;
|
||||
private FileStream writeStream;
|
||||
private int timeStarted;
|
||||
|
||||
private string downloadFolder;
|
||||
|
||||
private FileTransferMessageType fileType;
|
||||
|
||||
public string FileName
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string FilePath
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public ulong FileSize
|
||||
{
|
||||
get { return length; }
|
||||
}
|
||||
|
||||
public ulong Received
|
||||
{
|
||||
get { return received; }
|
||||
}
|
||||
|
||||
public FileTransferMessageType FileType
|
||||
{
|
||||
get { return fileType; }
|
||||
}
|
||||
|
||||
public FileTransferStatus Status
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string ErrorMessage
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public float BytesPerSecond
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public float Progress
|
||||
{
|
||||
get { return (float)received / (float)length; }
|
||||
}
|
||||
|
||||
public FileStreamReceiver(NetClient client, string filePath, FileTransferMessageType fileType, OnFinished onFinished)
|
||||
{
|
||||
this.client = client;
|
||||
|
||||
this.downloadFolder = filePath;
|
||||
this.fileType = fileType;
|
||||
|
||||
this.onFinished = onFinished;
|
||||
|
||||
Status = FileTransferStatus.NotStarted;
|
||||
}
|
||||
|
||||
public void ReadMessage(NetIncomingMessage inc)
|
||||
{
|
||||
try
|
||||
{
|
||||
TryReadMessage(inc);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ErrorMessage = "Error while receiving file ''"+FileName+"'' {"+e.Message+"}";
|
||||
DeleteFile();
|
||||
|
||||
if (onFinished != null) onFinished(this);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ValidateInitialData(byte type, string fileName, ulong fileSize)
|
||||
{
|
||||
if (fileSize > MaxFileSize)
|
||||
{
|
||||
ErrorMessage = "File too large (" + MathUtils.GetBytesReadable((long)fileSize) + ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type != (byte)fileType)
|
||||
{
|
||||
ErrorMessage = "Unexpected file type ''" + type + "'' (expected " + fileType + ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Regex.Match(fileName, @"^[\w\- ]+[\w\-. ]*$").Success)
|
||||
{
|
||||
ErrorMessage = "Illegal characters in file name ''" + fileName + "''";
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case (byte)FileTransferMessageType.Submarine:
|
||||
if (Path.GetExtension(fileName) != ".sub")
|
||||
{
|
||||
ErrorMessage = "Wrong file extension ''" + Path.GetExtension(fileName) + "''! (Expected .sub)";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void DeleteFile()
|
||||
{
|
||||
if (FileName == null) return;
|
||||
|
||||
string file = Path.Combine(downloadFolder, FileName);
|
||||
|
||||
if (writeStream!=null)
|
||||
{
|
||||
writeStream.Flush();
|
||||
writeStream.Close();
|
||||
writeStream.Dispose();
|
||||
writeStream = null;
|
||||
}
|
||||
|
||||
Status = FileTransferStatus.Canceled;
|
||||
|
||||
if (File.Exists(file))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(file);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't delete file ''" + file + "''!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TryReadMessage(NetIncomingMessage inc)
|
||||
{
|
||||
if (Status == FileTransferStatus.Error ||
|
||||
Status == FileTransferStatus.Finished ||
|
||||
Status == FileTransferStatus.Canceled) return;
|
||||
|
||||
byte transferMessageType = inc.ReadByte();
|
||||
|
||||
//int chunkLen = inc.LengthBytes;
|
||||
if (length == 0)
|
||||
{
|
||||
if (transferMessageType != (byte)FileTransferMessageType.Initiate) return;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(downloadFolder) && !Directory.Exists(downloadFolder))
|
||||
{
|
||||
Directory.CreateDirectory(downloadFolder);
|
||||
}
|
||||
|
||||
byte fileTypeByte = inc.ReadByte();
|
||||
|
||||
|
||||
length = inc.ReadUInt64();
|
||||
FileName = inc.ReadString();
|
||||
|
||||
if (!ValidateInitialData(fileTypeByte, FileName, length))
|
||||
{
|
||||
Status = FileTransferStatus.Error;
|
||||
DeleteFile();
|
||||
if (onFinished != null) onFinished(this);
|
||||
return;
|
||||
}
|
||||
|
||||
FilePath = Path.Combine(downloadFolder, FileName);
|
||||
|
||||
writeStream = new FileStream(FilePath, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||
timeStarted = Environment.TickCount;
|
||||
|
||||
Status = FileTransferStatus.NotStarted;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (received + (ulong)inc.LengthBytes > length*1.1f)
|
||||
{
|
||||
ErrorMessage = "Receiving more data than expected (> " + MathUtils.GetBytesReadable((long)(received + (ulong)inc.LengthBytes)) + ")";
|
||||
Status = FileTransferStatus.Error;
|
||||
if (onFinished != null) onFinished(this);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] all = inc.ReadBytes(inc.LengthBytes - inc.PositionInBytes);
|
||||
received += (ulong)all.Length;
|
||||
writeStream.Write(all, 0, all.Length);
|
||||
|
||||
int passed = Environment.TickCount - timeStarted;
|
||||
float psec = passed / 1000.0f;
|
||||
|
||||
BytesPerSecond = received / psec;
|
||||
|
||||
Status = FileTransferStatus.Receiving;
|
||||
|
||||
|
||||
if (received >= length)
|
||||
{
|
||||
writeStream.Flush();
|
||||
writeStream.Close();
|
||||
writeStream.Dispose();
|
||||
writeStream = null;
|
||||
|
||||
Status = IsReceivedFileValid() ? FileTransferStatus.Finished : FileTransferStatus.Error;
|
||||
if (onFinished!=null) onFinished(this);
|
||||
|
||||
if (Status == FileTransferStatus.Error) DeleteFile();
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsReceivedFileValid()
|
||||
{
|
||||
switch (fileType)
|
||||
{
|
||||
case FileTransferMessageType.Submarine:
|
||||
string file = Path.Combine(downloadFolder, FileName);
|
||||
Stream stream = null;
|
||||
|
||||
try
|
||||
{
|
||||
stream = SaveUtil.DecompressFiletoStream(file);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
ErrorMessage = "Loading submarine ''" + file + "'' failed! {"+ e.Message + "}";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stream == null)
|
||||
{
|
||||
ErrorMessage = "Decompressing submarine file''" + file + "'' failed!";
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
stream.Position = 0;
|
||||
|
||||
XmlReaderSettings settings = new XmlReaderSettings();
|
||||
settings.DtdProcessing = DtdProcessing.Prohibit;
|
||||
settings.IgnoreProcessingInstructions = true;
|
||||
|
||||
using (var reader = XmlReader.Create(stream, settings))
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
stream.Close();
|
||||
stream.Dispose();
|
||||
|
||||
ErrorMessage = "Parsing file ''"+file+"'' failed! The file may not be a valid submarine file.";
|
||||
return false;
|
||||
}
|
||||
|
||||
stream.Close();
|
||||
stream.Dispose();
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (writeStream != null)
|
||||
{
|
||||
writeStream.Flush();
|
||||
writeStream.Close();
|
||||
writeStream.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
using Lidgren.Network;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
enum FileTransferStatus
|
||||
{
|
||||
NotStarted, Sending, Receiving, Finished, Error, Canceled
|
||||
}
|
||||
|
||||
enum FileTransferMessageType
|
||||
{
|
||||
Unknown, Initiate, Submarine, Cancel
|
||||
}
|
||||
|
||||
class FileStreamSender : IDisposable
|
||||
{
|
||||
public static TimeSpan MaxTransferDuration = new TimeSpan(0, 2, 0);
|
||||
|
||||
private FileStream inputStream;
|
||||
private int sentOffset;
|
||||
private int chunkLen;
|
||||
private byte[] tempBuffer;
|
||||
private NetConnection connection;
|
||||
|
||||
float waitTimer;
|
||||
|
||||
DateTime startingTime;
|
||||
|
||||
private FileTransferMessageType fileType;
|
||||
|
||||
public FileTransferStatus Status
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string FileName
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public string FilePath
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public float Progress
|
||||
{
|
||||
get { return inputStream == null ? 0.0f : (float)sentOffset / (float)inputStream.Length; }
|
||||
}
|
||||
|
||||
public int Sent
|
||||
{
|
||||
get { return sentOffset; }
|
||||
}
|
||||
|
||||
public long FileSize
|
||||
{
|
||||
get { return inputStream == null ? 0 : inputStream.Length; }
|
||||
}
|
||||
|
||||
public static FileStreamSender Create(NetConnection conn, string filePath, FileTransferMessageType fileType)
|
||||
{
|
||||
if (!File.Exists(filePath))
|
||||
{
|
||||
DebugConsole.ThrowError("Sending a file failed. File ''"+filePath+"'' not found.");
|
||||
return null;
|
||||
}
|
||||
|
||||
FileStreamSender sender = null;
|
||||
|
||||
try
|
||||
{
|
||||
sender = new FileStreamSender(conn, filePath, fileType);
|
||||
}
|
||||
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't open file ''"+filePath+"''",e);
|
||||
}
|
||||
|
||||
return sender;
|
||||
}
|
||||
|
||||
private FileStreamSender(NetConnection conn, string filePath, FileTransferMessageType fileType)
|
||||
{
|
||||
connection = conn;
|
||||
inputStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
|
||||
chunkLen = connection.Peer.Configuration.MaximumTransmissionUnit - 100;
|
||||
tempBuffer = new byte[chunkLen];
|
||||
sentOffset = 0;
|
||||
|
||||
FilePath = filePath;
|
||||
FileName = Path.GetFileName(filePath);
|
||||
|
||||
this.fileType = fileType;
|
||||
|
||||
Status = FileTransferStatus.NotStarted;
|
||||
|
||||
startingTime = DateTime.Now;
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
if (inputStream == null ||
|
||||
Status == FileTransferStatus.Canceled ||
|
||||
Status == FileTransferStatus.Error ||
|
||||
Status == FileTransferStatus.Finished) return;
|
||||
|
||||
if (DateTime.Now > startingTime + MaxTransferDuration)
|
||||
{
|
||||
CancelTransfer();
|
||||
return;
|
||||
}
|
||||
|
||||
waitTimer -= deltaTime;
|
||||
if (waitTimer > 0.0f) return;
|
||||
|
||||
if (!connection.CanSendImmediately(NetDeliveryMethod.ReliableOrdered, 1)) return;
|
||||
|
||||
// send another part of the file!
|
||||
long remaining = inputStream.Length - sentOffset;
|
||||
int sendBytes = (remaining > chunkLen ? chunkLen : (int)remaining);
|
||||
|
||||
// just assume we can read the whole thing in one Read()
|
||||
inputStream.Read(tempBuffer, 0, sendBytes);
|
||||
|
||||
NetOutgoingMessage message;
|
||||
if (sentOffset == 0)
|
||||
{
|
||||
// first message; send length, chunk length and file name
|
||||
message = connection.Peer.CreateMessage(sendBytes + 8 + 1);
|
||||
message.Write((byte)PacketTypes.FileStream);
|
||||
message.Write((byte)FileTransferMessageType.Initiate);
|
||||
message.Write((byte)fileType);
|
||||
message.Write((ulong)inputStream.Length);
|
||||
message.Write(Path.GetFileName(inputStream.Name));
|
||||
connection.SendMessage(message, NetDeliveryMethod.ReliableOrdered, 1);
|
||||
|
||||
Status = FileTransferStatus.Sending;
|
||||
}
|
||||
|
||||
message = connection.Peer.CreateMessage(sendBytes + 8 + 1);
|
||||
message.Write((byte)PacketTypes.FileStream);
|
||||
message.Write((byte)fileType);
|
||||
message.Write(tempBuffer, 0, sendBytes);
|
||||
|
||||
connection.SendMessage(message, NetDeliveryMethod.ReliableOrdered, 1);
|
||||
sentOffset += sendBytes;
|
||||
|
||||
waitTimer = connection.AverageRoundtripTime;
|
||||
|
||||
//Program.Output("Sent " + m_sentOffset + "/" + m_inputStream.Length + " bytes to " + m_connection);
|
||||
|
||||
if (remaining - sendBytes <= 0)
|
||||
{
|
||||
//Dispose();
|
||||
|
||||
Status = FileTransferStatus.Finished;
|
||||
}
|
||||
}
|
||||
|
||||
public void CancelTransfer()
|
||||
{
|
||||
Status = FileTransferStatus.Canceled;
|
||||
}
|
||||
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
inputStream.Close();
|
||||
inputStream.Dispose();
|
||||
inputStream = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
using System.Collections.Generic;
|
||||
using Lidgren.Network;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma.Networking
|
||||
{
|
||||
enum NetworkEventDeliveryMethod
|
||||
{
|
||||
Unreliable = 0,
|
||||
ReliableChannel = 1,
|
||||
ReliableLidgren = 2
|
||||
}
|
||||
|
||||
enum NetworkEventType
|
||||
{
|
||||
EntityUpdate = 0,
|
||||
ImportantEntityUpdate = 1,
|
||||
|
||||
KillCharacter = 2,
|
||||
SelectCharacter = 3,
|
||||
|
||||
ComponentUpdate = 4,
|
||||
ImportantComponentUpdate = 5,
|
||||
|
||||
PickItem = 6,
|
||||
DropItem = 7,
|
||||
InventoryUpdate = 8,
|
||||
ItemFixed = 9,
|
||||
|
||||
UpdateProperty = 10,
|
||||
WallDamage = 11,
|
||||
|
||||
PhysicsBodyPosition = 12,
|
||||
|
||||
ApplyStatusEffect = 13
|
||||
}
|
||||
|
||||
class NetworkEvent
|
||||
{
|
||||
public static List<NetworkEvent> Events = new List<NetworkEvent>();
|
||||
|
||||
private static NetworkEventDeliveryMethod[] deliveryMethod;
|
||||
private static bool[] overridePrevious;
|
||||
|
||||
static NetworkEvent()
|
||||
{
|
||||
deliveryMethod = new NetworkEventDeliveryMethod[Enum.GetNames(typeof(NetworkEventType)).Length];
|
||||
deliveryMethod[(int)NetworkEventType.ImportantEntityUpdate] = NetworkEventDeliveryMethod.ReliableChannel;
|
||||
deliveryMethod[(int)NetworkEventType.ImportantComponentUpdate] = NetworkEventDeliveryMethod.ReliableChannel;
|
||||
deliveryMethod[(int)NetworkEventType.KillCharacter] = NetworkEventDeliveryMethod.ReliableLidgren;
|
||||
deliveryMethod[(int)NetworkEventType.SelectCharacter] = NetworkEventDeliveryMethod.ReliableChannel;
|
||||
|
||||
deliveryMethod[(int)NetworkEventType.ImportantComponentUpdate] = NetworkEventDeliveryMethod.ReliableChannel;
|
||||
deliveryMethod[(int)NetworkEventType.PickItem] = NetworkEventDeliveryMethod.ReliableChannel;
|
||||
deliveryMethod[(int)NetworkEventType.DropItem] = NetworkEventDeliveryMethod.ReliableChannel;
|
||||
deliveryMethod[(int)NetworkEventType.InventoryUpdate] = NetworkEventDeliveryMethod.ReliableChannel;
|
||||
deliveryMethod[(int)NetworkEventType.ItemFixed] = NetworkEventDeliveryMethod.ReliableLidgren;
|
||||
|
||||
deliveryMethod[(int)NetworkEventType.UpdateProperty] = NetworkEventDeliveryMethod.ReliableChannel;
|
||||
deliveryMethod[(int)NetworkEventType.WallDamage] = NetworkEventDeliveryMethod.ReliableChannel;
|
||||
|
||||
deliveryMethod[(int)NetworkEventType.ApplyStatusEffect] = NetworkEventDeliveryMethod.ReliableLidgren;
|
||||
|
||||
overridePrevious = new bool[deliveryMethod.Length];
|
||||
for (int i = 0; i < overridePrevious.Length; i++ )
|
||||
{
|
||||
overridePrevious[i] = true;
|
||||
}
|
||||
overridePrevious[(int)NetworkEventType.KillCharacter] = false;
|
||||
|
||||
overridePrevious[(int)NetworkEventType.PickItem] = false;
|
||||
overridePrevious[(int)NetworkEventType.DropItem] = false;
|
||||
overridePrevious[(int)NetworkEventType.ItemFixed] = false;
|
||||
overridePrevious[(int)NetworkEventType.ApplyStatusEffect] = false;
|
||||
}
|
||||
|
||||
private readonly ushort id;
|
||||
|
||||
private readonly NetworkEventType eventType;
|
||||
|
||||
private readonly bool isClientEvent;
|
||||
|
||||
private readonly object data;
|
||||
|
||||
public NetConnection SenderConnection;
|
||||
|
||||
//private NetOutgoingMessage message;
|
||||
|
||||
public ushort ID
|
||||
{
|
||||
get { return id; }
|
||||
}
|
||||
|
||||
public bool IsClient
|
||||
{
|
||||
get { return isClientEvent; }
|
||||
}
|
||||
|
||||
public NetworkEventDeliveryMethod DeliveryMethod
|
||||
{
|
||||
get { return deliveryMethod[(int)eventType]; }
|
||||
}
|
||||
|
||||
public NetworkEventType Type
|
||||
{
|
||||
get { return eventType; }
|
||||
}
|
||||
|
||||
public NetworkEvent(ushort id, bool allowClientSend)
|
||||
: this(NetworkEventType.EntityUpdate, id, allowClientSend)
|
||||
{
|
||||
}
|
||||
|
||||
public NetworkEvent(NetworkEventType type, ushort id, bool allowClientSend, object data = null)
|
||||
{
|
||||
if (!allowClientSend && GameMain.Server == null) return;
|
||||
|
||||
eventType = type;
|
||||
|
||||
if (overridePrevious[(int)type])
|
||||
{
|
||||
if (type == NetworkEventType.ComponentUpdate || type == NetworkEventType.ImportantComponentUpdate)
|
||||
{
|
||||
if (Events.Any(e => e.id == id && e.eventType == type && data == e.data)) return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Events.Any(e => e.id == id && e.eventType == type)) return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.id = id;
|
||||
isClientEvent = allowClientSend;
|
||||
|
||||
this.data = data;
|
||||
|
||||
Events.Add(this);
|
||||
}
|
||||
|
||||
public bool FillData(NetBuffer message)
|
||||
{
|
||||
message.Write((byte)eventType);
|
||||
|
||||
Entity e = Entity.FindEntityByID(id);
|
||||
if (e == null) return false;
|
||||
|
||||
message.Write(id);
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
catch (Exception exception)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Failed to write network message for entity "+e.ToString(), exception);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void ReadMessage(NetIncomingMessage message, bool resend=false)
|
||||
{
|
||||
float sendingTime = message.ReadFloat();
|
||||
|
||||
sendingTime = (float)message.SenderConnection.GetLocalTime(sendingTime);
|
||||
|
||||
byte msgCount = message.ReadByte();
|
||||
long currPos = message.PositionInBytes;
|
||||
|
||||
for (int i = 0; i < msgCount; i++)
|
||||
{
|
||||
|
||||
byte msgLength = message.ReadByte();
|
||||
|
||||
try
|
||||
{
|
||||
ReadData(message, sendingTime, resend);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
//+1 because msgLength is one additional byte
|
||||
currPos += msgLength + 1;
|
||||
message.Position = currPos * 8;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ReadData(NetIncomingMessage message, float sendingTime, bool resend=false)
|
||||
{
|
||||
NetworkEventType eventType;
|
||||
ushort id;
|
||||
|
||||
try
|
||||
{
|
||||
eventType = (NetworkEventType)message.ReadByte();
|
||||
id = message.ReadUInt16();
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Received invalid network message", exception);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
Entity e = Entity.FindEntityByID(id);
|
||||
if (e == null)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Couldn't find an entity matching the ID ''" + id + "''");
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//System.Diagnostics.Debug.WriteLine(e.ToString());
|
||||
|
||||
object data;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Received invalid network message", exception);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
/*if (resend)
|
||||
{
|
||||
var resendEvent = new NetworkEvent(eventType, id, false, data);
|
||||
resendEvent.SenderConnection = message.SenderConnection;
|
||||
}*/
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,488 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user