Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaServer/Source/Networking/FileTransfer/FileSender.cs
Joonas Rikkonen c2e8263927 f9e8100...ccacceb
commit ccacceb16a184f00ecd384eede64ca9c4fab08a0
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Mon Mar 25 14:05:59 2019 +0200

    NetEntityEventManager checks the length of the event data (and logs an error if it's too long) before checking if there's still room to keep writing events in the packet. Checking the available room first could lead to situations where an excessively large event can't fit to any packet, "soft-locking" the EventManager without any error messages.

commit 5ac8259372aa900adc724aa4da1fd81af41ca195
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Mon Mar 25 13:41:52 2019 +0200

    Don't display disabled limbs on sonar (i.e. severed limbs that have "faded out")

commit 5f84df73ad86be96f3678c450351b3905e7317a4
Merge: b981f1635 dc429d6c4
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Mon Mar 25 13:41:16 2019 +0200

    Merge branch 'dev' of https://github.com/Regalis11/Barotrauma-development into dev

commit b981f163575b2bfc9a83b9925c94eca19b9d4554
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Mon Mar 25 13:36:19 2019 +0200

    Multiplayer campaign fixes:
    - Server uses a different temp folder to decompress save/sub files into than the clients. Should fix files occasionally getting corrupted and exceptions when trying to read the files when hosting a server from the main executable.
    - Some additional debug logging.
    - Use the base names of the adjacent locations as level seeds (i.e. "Vorta" instead of "Vorta Outpost"). The levels should not change when the type (and full name) of the location changes.

commit 42c5d18df77fc7acd5873d8e25f20bdd31b1ed76
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Mon Mar 25 13:31:06 2019 +0200

    Don't transfer files through the network when sending them to the owner of the server (i.e. a client hosting directly from the main executable), but simply tell the client where the file is located.

commit dc429d6c450f4893fe29c51d3c830527e587a871
Author: Daniel Asteljoki <daniel.asteljoki@gmail.com>
Date:   Mon Mar 25 13:30:26 2019 +0200

    Added labels next to periscopes in Humpback and Dugong

commit 789f02a87a2917dd2ae378f136cbe8dd3236c60d
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Mon Mar 25 13:29:29 2019 +0200

    If loading a submarine fails, wait a bit and retry up to 4 times. Fixes loading occasionally failing when running multiple instances of the game from the same directory.

commit be9ea3a58832992b6226917117247e1bf1efeff9
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Mon Mar 25 11:03:36 2019 +0200

    Fixed a bunch of disconnection messages being in an incorrect format & DisconnectUnauthClient not getting the messages from the xml

commit c6f744b4d6b3520720010f5cd4f22a25b42bfc8b
Author: Joonas Rikkonen <poe.regalis@gmail.com>
Date:   Mon Mar 25 10:43:10 2019 +0200

    Log entity event errors into server logs when verbose logging is enabled
2019-03-25 14:30:00 +02:00

302 lines
11 KiB
C#

using Lidgren.Network;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Barotrauma.Networking
{
class FileSender
{
public class FileTransferOut
{
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 SentOffset / (float)Data.Length; }
}
public float WaitTimer
{
get;
set;
}
public byte[] Data
{
get { return data; }
}
public int SentOffset
{
get;
set;
}
public NetConnection Connection
{
get { return connection; }
}
public int SequenceChannel;
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);
}
}
const int MaxTransferCount = 16;
const int MaxTransferCountPerRecipient = 5;
public static TimeSpan MaxTransferDuration = new TimeSpan(0, 2, 0);
public delegate void FileTransferDelegate(FileTransferOut fileStreamReceiver);
public FileTransferDelegate OnStarted;
public FileTransferDelegate OnEnded;
private List<FileTransferOut> activeTransfers;
private int chunkLen;
private NetPeer peer;
public List<FileTransferOut> ActiveTransfers
{
get { return activeTransfers; }
}
public FileSender(NetworkMember networkMember)
{
peer = networkMember.NetPeer;
chunkLen = peer.Configuration.MaximumTransmissionUnit - 100;
activeTransfers = new List<FileTransferOut>();
}
public FileTransferOut StartTransfer(NetConnection recipient, FileTransferType fileType, string filePath)
{
if (activeTransfers.Count >= MaxTransferCount)
{
return null;
}
if (activeTransfers.Count(t => t.Connection == recipient) > MaxTransferCountPerRecipient)
{
return null;
}
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)
{
SequenceChannel = 1
};
while (activeTransfers.Any(t => t.Connection == recipient && t.SequenceChannel == transfer.SequenceChannel))
{
transfer.SequenceChannel++;
}
activeTransfers.Add(transfer);
}
catch (Exception e)
{
DebugConsole.ThrowError("Failed to initiate file transfer", e);
return null;
}
OnStarted(transfer);
return transfer;
}
public void Update(float deltaTime)
{
activeTransfers.RemoveAll(t => t.Connection.Status != NetConnectionStatus.Connected);
var endedTransfers = activeTransfers.FindAll(t =>
t.Connection.Status != NetConnectionStatus.Connected ||
t.Status == FileTransferStatus.Finished ||
t.Status == FileTransferStatus.Canceled ||
t.Status == FileTransferStatus.Error);
foreach (FileTransferOut transfer in endedTransfers)
{
activeTransfers.Remove(transfer);
OnEnded(transfer);
}
foreach (FileTransferOut transfer in activeTransfers)
{
transfer.WaitTimer -= deltaTime;
if (transfer.WaitTimer > 0.0f) continue;
if (!transfer.Connection.CanSendImmediately(NetDeliveryMethod.ReliableOrdered, transfer.SequenceChannel)) continue;
transfer.WaitTimer = 0.05f;// 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();
message.Write((byte)ServerPacketHeader.FILE_TRANSFER);
//if the recipient is the owner of the server (= a client running the server from the main exe)
//we don't need to send anything, the client can just read the file directly
if (transfer.Connection == GameMain.Server.OwnerConnection)
{
message.Write((byte)FileTransferMessageType.TransferOnSameMachine);
message.Write((byte)transfer.FileType);
message.Write(transfer.FilePath);
GameMain.Server.CompressOutgoingMessage(message);
transfer.Connection.SendMessage(message, NetDeliveryMethod.ReliableOrdered, transfer.SequenceChannel);
transfer.Status = FileTransferStatus.Finished;
return;
}
else
{
message.Write((byte)FileTransferMessageType.Initiate);
message.Write((byte)transfer.FileType);
message.Write((ushort)chunkLen);
message.Write((ulong)transfer.Data.Length);
message.Write(transfer.FileName);
GameMain.Server.CompressOutgoingMessage(message);
transfer.Connection.SendMessage(message, NetDeliveryMethod.ReliableOrdered, transfer.SequenceChannel);
transfer.Status = FileTransferStatus.Sending;
if (GameSettings.VerboseLogging)
{
DebugConsole.Log("Sending file transfer initiation message: ");
DebugConsole.Log(" File: " + transfer.FileName);
DebugConsole.Log(" Size: " + transfer.Data.Length);
DebugConsole.Log(" Sequence channel: " + transfer.SequenceChannel);
}
}
}
message = peer.CreateMessage(1 + 1 + sendByteCount);
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);
GameMain.Server.CompressOutgoingMessage(message);
transfer.Connection.SendMessage(message, NetDeliveryMethod.ReliableOrdered, transfer.SequenceChannel);
transfer.SentOffset += sendByteCount;
if (GameSettings.VerboseLogging)
{
DebugConsole.Log("Sending " + sendByteCount + " bytes of the file " + transfer.FileName + " (" + transfer.SentOffset + "/" + transfer.Data.Length + " sent)");
}
if (remaining - sendByteCount <= 0)
{
transfer.Status = FileTransferStatus.Finished;
}
}
}
public void CancelTransfer(FileTransferOut transfer)
{
transfer.Status = FileTransferStatus.Canceled;
activeTransfers.Remove(transfer);
OnEnded(transfer);
GameMain.Server.SendCancelTransferMsg(transfer);
}
public void ReadFileRequest(NetIncomingMessage inc, Client client)
{
byte messageType = inc.ReadByte();
if (messageType == (byte)FileTransferMessageType.Cancel)
{
byte sequenceChannel = inc.ReadByte();
var matchingTransfer = activeTransfers.Find(t => t.Connection == inc.SenderConnection && t.SequenceChannel == sequenceChannel);
if (matchingTransfer != null) CancelTransfer(matchingTransfer);
return;
}
byte fileType = inc.ReadByte();
switch (fileType)
{
case (byte)FileTransferType.Submarine:
string fileName = inc.ReadString();
string fileHash = inc.ReadString();
var requestedSubmarine = Submarine.SavedSubmarines.FirstOrDefault(s => s.Name == fileName && s.MD5Hash.Hash == fileHash);
if (requestedSubmarine != null)
{
StartTransfer(inc.SenderConnection, FileTransferType.Submarine, requestedSubmarine.FilePath);
}
break;
case (byte)FileTransferType.CampaignSave:
if (GameMain.GameSession != null &&
!ActiveTransfers.Any(t => t.Connection == inc.SenderConnection && t.FileType == FileTransferType.CampaignSave))
{
StartTransfer(inc.SenderConnection, FileTransferType.CampaignSave, GameMain.GameSession.SavePath);
if (GameMain.GameSession?.GameMode is MultiPlayerCampaign campaign)
{
client.LastCampaignSaveSendTime = new Pair<ushort, float>(campaign.LastSaveID, (float)NetTime.Now);
}
}
break;
}
}
}
}