diff --git a/Subsurface/Barotrauma.csproj b/Subsurface/Barotrauma.csproj
index f3ce921e0..e50021540 100644
--- a/Subsurface/Barotrauma.csproj
+++ b/Subsurface/Barotrauma.csproj
@@ -155,6 +155,7 @@
+
diff --git a/Subsurface/Source/Networking/FileTransfer/FileSender.cs b/Subsurface/Source/Networking/FileTransfer/FileSender.cs
new file mode 100644
index 000000000..6c4173532
--- /dev/null
+++ b/Subsurface/Source/Networking/FileTransfer/FileSender.cs
@@ -0,0 +1,197 @@
+using Lidgren.Network;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Barotrauma.Networking
+{
+ enum FileTransferStatus
+ {
+ NotStarted, Sending, Receiving, Finished, Canceled
+ }
+
+ enum FileTransferMessageType
+ {
+ Unknown, Initiate, Data, Cancel
+ }
+
+ enum FileTransferType
+ {
+ Submarine
+ }
+
+ class FileSender
+ {
+ public class FileTransferOut
+ {
+ private byte[] tempBuffer;
+ private byte[] data;
+
+ private DateTime startingTime;
+
+ private NetConnection connection;
+
+ public FileTransferStatus Status;
+
+ public string FileName
+ {
+ get;
+ private set;
+ }
+
+ public string FilePath
+ {
+ get;
+ private set;
+ }
+
+ public FileTransferType FileType
+ {
+ get;
+ private set;
+ }
+
+ public float Progress
+ {
+ get { return 0.0f; }//inputStream == null ? 0.0f : (float)sentOffset / (float)inputStream.Length; }
+ }
+
+ public float WaitTimer
+ {
+ get;
+ set;
+ }
+
+ public byte[] Data
+ {
+ get { return data; }
+ }
+
+ public int SentOffset
+ {
+ get;
+ set;
+ }
+
+ public NetConnection Connection
+ {
+ get { return connection; }
+ }
+
+ public FileTransferOut(NetConnection recipient, FileTransferType fileType, string filePath)
+ {
+ connection = recipient;
+
+ FileType = fileType;
+ FilePath = filePath;
+ FileName = Path.GetFileName(filePath);
+
+ Status = FileTransferStatus.NotStarted;
+
+ startingTime = DateTime.Now;
+
+ data = File.ReadAllBytes(filePath);
+ }
+ }
+
+ public static TimeSpan MaxTransferDuration = new TimeSpan(0, 2, 0);
+
+ private List activeTransfers;
+
+ private int chunkLen;
+
+ private NetPeer peer;
+
+ public FileSender(NetworkMember networkMember)
+ {
+ peer = networkMember.netPeer;
+ chunkLen = peer.Configuration.MaximumTransmissionUnit - 100;
+
+ activeTransfers = new List();
+ }
+
+ public FileTransferOut StartTransfer(NetConnection recipient, FileTransferType fileType, string filePath)
+ {
+ if (!File.Exists(filePath))
+ {
+ DebugConsole.ThrowError("Failed to initiate file transfer (file \""+filePath+"\" not found.");
+ return null;
+ }
+
+ FileTransferOut transfer = null;
+ try
+ {
+ transfer = new FileTransferOut(recipient, fileType, filePath);
+ }
+ catch (Exception e)
+ {
+ DebugConsole.ThrowError("Failed to initiate file transfer", e);
+ }
+
+ return transfer;
+ }
+
+ public void Update(float deltaTime)
+ {
+ foreach (FileTransferOut transfer in activeTransfers)
+ {
+ transfer.WaitTimer -= deltaTime;
+ if (transfer.WaitTimer > 0.0f) return;
+
+ if (!transfer.Connection.CanSendImmediately(NetDeliveryMethod.ReliableOrdered, 1)) continue;
+
+ transfer.WaitTimer = transfer.Connection.AverageRoundtripTime;
+
+ // send another part of the file
+ long remaining = transfer.Data.Length - transfer.SentOffset;
+ int sendByteCount = (remaining > chunkLen ? chunkLen : (int)remaining);
+
+ NetOutgoingMessage message;
+
+ //first message; send length, chunk length, file name etc
+ if (transfer.SentOffset == 0)
+ {
+ message = peer.CreateMessage(sendByteCount + 8 + 1);
+ message.Write((byte)ServerPacketHeader.FILE_TRANSFER);
+ message.Write((byte)FileTransferMessageType.Initiate);
+ message.Write((byte)transfer.FileType);
+ message.Write((ushort)chunkLen);
+ message.Write((ulong)transfer.Data.Length);
+ message.Write(transfer.FileName);
+ transfer.Connection.SendMessage(message, NetDeliveryMethod.ReliableOrdered, 1);
+
+ transfer.Status = FileTransferStatus.Sending;
+ }
+
+ message = peer.CreateMessage(sendByteCount + 8 + 1);
+ message.Write((byte)ServerPacketHeader.FILE_TRANSFER);
+ message.Write((byte)FileTransferMessageType.Data);
+
+ byte[] sendBytes = new byte[sendByteCount];
+ Array.Copy(transfer.Data, transfer.SentOffset, sendBytes, 0, sendByteCount);
+
+ message.Write(sendBytes);
+
+ transfer.Connection.SendMessage(message, NetDeliveryMethod.ReliableOrdered, 1);
+ transfer.SentOffset += sendByteCount;
+
+ if (remaining - sendByteCount <= 0)
+ {
+ transfer.Status = FileTransferStatus.Finished;
+ }
+ }
+
+ activeTransfers.RemoveAll(t => t.Status == FileTransferStatus.Finished);
+ }
+
+ public void CancelTransfer(FileTransferOut transfer)
+ {
+ transfer.Status = FileTransferStatus.Canceled;
+ activeTransfers.Remove(transfer);
+ }
+
+ }
+}
diff --git a/Subsurface/Source/Networking/NetworkMember.cs b/Subsurface/Source/Networking/NetworkMember.cs
index 9df69e4fe..6002da628 100644
--- a/Subsurface/Source/Networking/NetworkMember.cs
+++ b/Subsurface/Source/Networking/NetworkMember.cs
@@ -15,7 +15,7 @@ namespace Barotrauma.Networking
REQUEST_INIT, //ask the server to give you initialization
UPDATE_LOBBY, //update state in lobby
UPDATE_INGAME, //update state ingame
-
+
RESPONSE_STARTGAME, //tell the server whether you're ready to start
SERVER_COMMAND //tell the server to end a round or kick/ban someone (special permissions required)
}
@@ -38,6 +38,8 @@ namespace Barotrauma.Networking
PERMISSIONS, //tell the client which special permissions they have (if any)
+ FILE_TRANSFER,
+
QUERY_STARTGAME, //ask the clients whether they're ready to start
STARTGAME, //start a new round
ENDGAME
@@ -70,7 +72,11 @@ namespace Barotrauma.Networking
public Dictionary messageCount = new Dictionary();
#endif
- protected NetPeer netPeer;
+ public NetPeer netPeer
+ {
+ get;
+ protected set;
+ }
protected string name;