1 using Microsoft.Xna.Framework;
3 using System.Collections.Generic;
6 using System.Threading;
39 private float waitTimer;
58 public byte[]
Data {
get; }
82 FileName = Path.GetFileName(filePath);
93 for (
int i = 0; i <= maxRetries; i++)
97 Data = File.ReadAllBytes(filePath, catchUnauthorizedAccessExceptions:
false);
99 catch (System.IO.IOException e)
101 if (i >= maxRetries) {
throw; }
102 DebugConsole.NewMessage(
"Failed to initiate a file transfer {" + e.Message +
"}, retrying in 250 ms...", Color.Red);
109 const int MaxTransferCount = 16;
110 const int MaxTransferCountPerRecipient = 5;
116 private readonly List<FileTransferOut> activeTransfers;
118 private readonly
int chunkLen;
120 private readonly ServerPeer peer;
124 public float ForceMinimumFileTransferDuration {
get;
set; }
132 chunkLen = mtu - 200;
134 activeTransfers =
new List<FileTransferOut>();
139 if (activeTransfers.Count >= MaxTransferCount)
144 if (activeTransfers.Count(t => t.Connection == recipient) > MaxTransferCountPerRecipient)
149 if (!File.Exists(filePath))
151 DebugConsole.ThrowError(
"Failed to initiate file transfer (file \"" + filePath +
"\" not found).\n" + Environment.StackTrace);
162 while (activeTransfers.Any(t => t.Connection == recipient && t.ID == transfer.
ID))
166 activeTransfers.Add(transfer);
170 DebugConsole.ThrowError(
"Failed to initiate file transfer", e);
186 var endedTransfers = activeTransfers.FindAll(t =>
194 activeTransfers.Remove(transfer);
201 if (transfer.
WaitTimer > 0.0f) {
continue; }
205 if (numRemoved > 0 || endedTransfers.Count > 0)
211 private void Send(FileTransferOut transfer)
220 if (!transfer.Acknowledged)
222 message =
new WriteOnlyMessage();
231 message.
WriteByte((
byte)transfer.FileType);
233 peer.Send(message, transfer.Connection, DeliveryMethod.Unreliable);
240 message.
WriteByte((
byte)transfer.FileType);
244 peer.Send(message, transfer.Connection, DeliveryMethod.Unreliable);
248 if (GameSettings.CurrentConfig.VerboseLogging)
250 DebugConsole.Log(
"Sending file transfer initiation message: ");
251 DebugConsole.Log(
" File: " + transfer.FileName);
252 DebugConsole.Log(
" Size: " + transfer.Data.Length);
253 DebugConsole.Log(
" ID: " + transfer.ID);
256 transfer.WaitTimer = 0.1f;
260 for (
int i = 0; i < Math.Floor(transfer.PacketsPerUpdate); i++)
262 long remaining = transfer.Data.Length - transfer.SentOffset;
264 bool stalling =
false;
265 float elapsedTime = (float)(DateTime.Now -
StartTime).TotalSeconds;
266 if (elapsedTime < ForceMinimumFileTransferDuration)
268 int remainingChunks = (int)Math.Max(remaining / chunkLen, 1);
270 Math.Max(transfer.WaitTimer, (ForceMinimumFileTransferDuration - elapsedTime) / remainingChunks);
271 if (remainingChunks <= 1) {
break; }
274 int sendByteCount = remaining > chunkLen ? chunkLen : (int)remaining;
276 message =
new WriteOnlyMessage();
287 Array.Copy(transfer.Data, transfer.SentOffset, message.
Buffer, chunkDestPos, sendByteCount);
289 transfer.SentOffset += sendByteCount;
291 peer.Send(message, transfer.Connection, DeliveryMethod.Unreliable, compressPastThreshold:
false);
293 if (GameSettings.CurrentConfig.VerboseLogging)
295 DebugConsole.Log($
"Sending {sendByteCount} bytes of the file {transfer.FileName} ({transfer.SentOffset / 1000}/{transfer.Data.Length / 1000} kB sent)");
298 if (transfer.SentOffset >= transfer.Data.Length)
300 transfer.SentOffset = transfer.KnownReceivedOffset;
301 transfer.WaitTimer = 1.0f;
306 transfer.PacketsPerUpdate = Math.Min(FileTransferOut.MaxPacketsPerUpdate,
307 transfer.PacketsPerUpdate + 0.05f);
309 if (stalling) {
break; }
316 DebugConsole.ThrowError(
"FileSender threw an exception when trying to send data", e);
317 GameAnalyticsManager.AddErrorEventOnce(
318 "FileSender.Update:Exception",
319 GameAnalyticsManager.ErrorSeverity.Error,
320 "FileSender threw an exception when trying to send data:\n" + e.Message +
"\n" + e.StackTrace.CleanupStackTrace());
329 activeTransfers.Remove(transfer);
343 var matchingTransfer = activeTransfers.Find(t => t.Connection == inc.
Sender && t.ID == transferId);
344 if (matchingTransfer !=
null) {
CancelTransfer(matchingTransfer); }
350 var matchingTransfer = activeTransfers.Find(t => t.Connection == inc.
Sender && t.ID == transferId);
351 if (matchingTransfer !=
null)
353 matchingTransfer.Acknowledged =
true;
355 int lastSeen = Math.Min(matchingTransfer.SentOffset, inc.
ReadInt32());
356 matchingTransfer.KnownReceivedOffset = Math.Max(expecting, matchingTransfer.KnownReceivedOffset);
357 if (matchingTransfer.SentOffset < matchingTransfer.KnownReceivedOffset)
359 matchingTransfer.WaitTimer = 0.0f;
360 matchingTransfer.SentOffset = matchingTransfer.KnownReceivedOffset;
363 if (lastSeen - matchingTransfer.KnownReceivedOffset >= chunkLen * 10 ||
364 matchingTransfer.SentOffset >= matchingTransfer.Data.Length)
366 matchingTransfer.SentOffset = matchingTransfer.KnownReceivedOffset;
367 matchingTransfer.WaitTimer = 1.0f;
370 if (matchingTransfer.KnownReceivedOffset >= matchingTransfer.Data.Length)
373 DebugConsole.Log($
"Finished sending file \"{matchingTransfer.FilePath}\" to \"{client.Name}\". Took {DateTime.Now - StartTime}");
385 var requestedSubmarine =
SubmarineInfo.
SavedSubmarines.FirstOrDefault(s => s.Name == fileName && s.MD5Hash.StringRepresentation == fileHash);
387 DebugConsole.Log($
"Received a submarine file request from \"{client.Name}\" ({fileName}).");
389 if (requestedSubmarine !=
null)
391 if (activeTransfers.Any(t => t.Connection == inc.
Sender && t.FilePath == requestedSubmarine.FilePath))
393 DebugConsole.Log($
"Ignoring a submarine file request from \"{client.Name}\" ({fileName}) - already transferring.");
406 client.LastCampaignSaveSendTime = (campaign.LastSaveID, (float)Lidgren.Network.NetTime.Now);
414 DebugConsole.Log($
"Received a mod file request from \"{client.Name}\" ({modName}).");
416 if (!
GameMain.
Server.ServerSettings.AllowModDownloads) {
return; }
419 ContentPackage mod = ContentPackageManager.AllPackages.FirstOrDefault(p => p.Hash.Equals(modHash));
421 if (mod is
null) {
return; }
424 if (!File.Exists(modCompressedPath)) {
return; }
426 if (activeTransfers.Any(t => t.Connection == inc.
Sender && t.FilePath == modCompressedPath))
428 DebugConsole.Log($
"Ignoring a mod file request from \"{client.Name}\" ({modName}) - already transferring.");
static GameSession GameSession
CampaignDataPath DataPath
static Md5Hash StringAsHash(string hash)
NetworkConnection Connection
FileTransferType FileType
FileTransferOut(NetworkConnection recipient, FileTransferType fileType, string filePath)
static int MaxPacketsPerUpdate
FileTransferStatus Status
IReadOnlyList< FileTransferOut > ActiveTransfers
static DateTime StartTime
FileSender(ServerPeer serverPeer, int mtu)
delegate void FileTransferDelegate(FileTransferOut fileStreamReceiver)
FileTransferOut StartTransfer(NetworkConnection recipient, FileTransferType fileType, string filePath)
void ReadFileRequest(IReadMessage inc, Client client)
FileTransferDelegate OnStarted
void Update(float deltaTime)
FileTransferDelegate OnEnded
void CancelTransfer(FileTransferOut transfer)
NetworkConnection OwnerConnection
void SendCancelTransferMsg(FileSender.FileTransferOut transfer)
static string GetCompressedModPath(ContentPackage mod)
static IEnumerable< SubmarineInfo > SavedSubmarines
void WriteString(string val)
void WriteUInt16(UInt16 val)
void WriteInt32(Int32 val)