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;