DownloadManager works!

This commit is contained in:
EntenKoeniq
2022-04-03 02:27:30 +02:00
parent 7c194f38a6
commit 989dcaeb0b
8 changed files with 296 additions and 50 deletions

View File

@ -6,14 +6,16 @@ namespace CoopClient
{ {
public byte FileID { get; set; } public byte FileID { get; set; }
public Packets.DataFileType FileType { get; set; } public Packets.DataFileType FileType { get; set; }
public string FileName { get; set; } private string FileName = string.Empty;
public long FileLength { get; set; } public long FileLength { get; set; }
private readonly FileStream _stream; private readonly FileStream _stream;
public DownloadManager() public DownloadManager(string fileName)
{ {
string downloadFolder = $"scripts\\{Main.MainSettings.LastServerAddress.Replace(":", ".")}"; FileName = fileName;
string downloadFolder = $"scripts\\resources\\{Main.MainSettings.LastServerAddress.Replace(":", ".")}";
if (!Directory.Exists(downloadFolder)) if (!Directory.Exists(downloadFolder))
{ {
Directory.CreateDirectory(downloadFolder); Directory.CreateDirectory(downloadFolder);
@ -25,7 +27,7 @@ namespace CoopClient
} }
} }
_stream = new FileStream(downloadFolder + "\\" + FileName, FileMode.CreateNew); _stream = new FileStream($"{downloadFolder}\\{fileName}", File.Exists($"{downloadFolder}\\{fileName}") ? FileMode.Truncate : FileMode.CreateNew);
} }
/// <summary> /// <summary>

View File

@ -22,7 +22,7 @@ namespace CoopClient
public int BytesReceived = 0; public int BytesReceived = 0;
public int BytesSend = 0; public int BytesSend = 0;
private Dictionary<byte, DownloadManager> _downloads = new Dictionary<byte, DownloadManager>(); private readonly Dictionary<byte, DownloadManager> _downloads = new Dictionary<byte, DownloadManager>();
public void DisConnectFromServer(string address) public void DisConnectFromServer(string address)
{ {
@ -139,6 +139,7 @@ namespace CoopClient
// Reset all values // Reset all values
Latency = 0; Latency = 0;
LastPlayerFullSync = 0; LastPlayerFullSync = 0;
_downloads.Clear();
Main.CleanUpWorld(); Main.CleanUpWorld();
@ -462,11 +463,10 @@ namespace CoopClient
Packets.FileRequest packet = new Packets.FileRequest(); Packets.FileRequest packet = new Packets.FileRequest();
packet.NetIncomingMessageToPacket(data); packet.NetIncomingMessageToPacket(data);
_downloads.Add(packet.ID, new DownloadManager() _downloads.Add(packet.ID, new DownloadManager(packet.FileName)
{ {
FileID = packet.ID, FileID = packet.ID,
FileType = (Packets.DataFileType)packet.FileType, FileType = (Packets.DataFileType)packet.FileType,
FileName = packet.FileName,
FileLength = packet.FileLength FileLength = packet.FileLength
}); });
} }
@ -488,10 +488,12 @@ namespace CoopClient
packet.NetIncomingMessageToPacket(data); packet.NetIncomingMessageToPacket(data);
KeyValuePair<byte, DownloadManager> dm = _downloads.FirstOrDefault(x => x.Key == packet.ID); KeyValuePair<byte, DownloadManager> dm = _downloads.FirstOrDefault(x => x.Key == packet.ID);
if (dm.Value != null) if (dm.Value == null)
{ {
dm.Value.DownloadPart(packet.FileChunk); throw new Exception("File to download not found!");
} }
dm.Value.DownloadPart(packet.FileChunk);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -27,7 +27,7 @@ namespace CoopClient
public override void PacketToNetOutGoingMessage(NetOutgoingMessage message) public override void PacketToNetOutGoingMessage(NetOutgoingMessage message)
{ {
#region PacketToNetOutGoingMessage #region PacketToNetOutGoingMessage
message.Write((byte)PacketTypes.FullSyncNpc); message.Write((byte)PacketTypes.FileTransferRequest);
List<byte> byteArray = new List<byte>(); List<byte> byteArray = new List<byte>();
@ -75,7 +75,7 @@ namespace CoopClient
public override void PacketToNetOutGoingMessage(NetOutgoingMessage message) public override void PacketToNetOutGoingMessage(NetOutgoingMessage message)
{ {
#region PacketToNetOutGoingMessage #region PacketToNetOutGoingMessage
message.Write((byte)PacketTypes.FullSyncNpc); message.Write((byte)PacketTypes.FileTransferTick);
List<byte> byteArray = new List<byte>(); List<byte> byteArray = new List<byte>();
@ -112,7 +112,7 @@ namespace CoopClient
public override void PacketToNetOutGoingMessage(NetOutgoingMessage message) public override void PacketToNetOutGoingMessage(NetOutgoingMessage message)
{ {
#region PacketToNetOutGoingMessage #region PacketToNetOutGoingMessage
message.Write((byte)PacketTypes.FullSyncNpc); message.Write((byte)PacketTypes.FileTransferComplete);
List<byte> byteArray = new List<byte>(); List<byte> byteArray = new List<byte>();

View File

@ -26,7 +26,8 @@ namespace CoopServer
private readonly Dictionary<string, object> CustomData = new(); private readonly Dictionary<string, object> CustomData = new();
private long CallbacksCount = 0; private long CallbacksCount = 0;
internal readonly Dictionary<long, Action<object>> Callbacks = new(); internal readonly Dictionary<long, Action<object>> Callbacks = new();
internal bool FilesSent = false; internal bool FilesReceived = false;
public bool FilesSent = false;
#region CUSTOMDATA FUNCTIONS #region CUSTOMDATA FUNCTIONS
public void SetData<T>(string name, T data) public void SetData<T>(string name, T data)

View File

@ -2,12 +2,13 @@
using System.Linq; using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using Lidgren.Network;
using System;
namespace CoopServer namespace CoopServer
{ {
internal static class DownloadManager internal static class DownloadManager
{ {
const int MAX_BUFFER = 1048576; // 1MB
private static readonly List<DownloadClient> _clients = new(); private static readonly List<DownloadClient> _clients = new();
private static readonly List<DownloadFile> _files = new(); private static readonly List<DownloadFile> _files = new();
public static bool AnyFileExists = false; public static bool AnyFileExists = false;
@ -19,19 +20,28 @@ namespace CoopServer
return; return;
} }
_clients.Add(new DownloadClient() { NetHandle = nethandle, FilesCount = _files.Count }); _clients.Add(new DownloadClient(nethandle, _files));
} }
public static bool CheckForDirectoryAndFiles() public static bool CheckForDirectoryAndFiles()
{ {
string[] filePaths = Directory.GetFiles("clientside"); string[] filePaths;
if (!Directory.Exists("clientside") || filePaths.Length == 0) if (!Directory.Exists("clientside"))
{ {
AnyFileExists = false; AnyFileExists = false;
return false; return false;
} }
filePaths = Directory.GetFiles("clientside");
if (filePaths.Length == 0)
{
AnyFileExists = false;
return false;
}
byte fileCount = 0;
foreach (string file in filePaths) foreach (string file in filePaths)
{ {
FileInfo fileInfo = new(file); FileInfo fileInfo = new(file);
@ -43,18 +53,15 @@ namespace CoopServer
continue; continue;
} }
Logging.Debug($"===== {fileInfo.Name} ====="); int MAX_BUFFER = fileInfo.Length < 5120 ? (int)fileInfo.Length : 5120; // 5KB
byte[] buffer = new byte[MAX_BUFFER]; byte[] buffer = new byte[MAX_BUFFER];
int bytesRead = 0;
bool fileCreated = false; bool fileCreated = false;
DownloadFile newFile = null; DownloadFile newFile = null;
byte fileCount = 0;
using (FileStream fs = File.Open(file, FileMode.Open, FileAccess.Read)) using (FileStream fs = File.Open(file, FileMode.Open, FileAccess.Read))
using (BufferedStream bs = new(fs)) using (BufferedStream bs = new(fs))
{ {
while ((bytesRead = bs.Read(buffer, 0, MAX_BUFFER)) != 0) // Reading 1MB chunks at time while (bs.Read(buffer, 0, MAX_BUFFER) != 0) // Reading 5KB chunks at time
{ {
if (!fileCreated) if (!fileCreated)
{ {
@ -63,12 +70,11 @@ namespace CoopServer
} }
newFile.AddData(buffer); newFile.AddData(buffer);
Logging.Debug($"{bytesRead}");
} }
} }
_files.Add(newFile); _files.Add(newFile);
fileCount++;
} }
Logging.Info($"{_files.Count} files found!"); Logging.Info($"{_files.Count} files found!");
@ -81,12 +87,12 @@ namespace CoopServer
{ {
_clients.ForEach(client => _clients.ForEach(client =>
{ {
if (!client.SendFiles(_files)) if (!client.SendFiles())
{ {
Client x = Server.Clients.FirstOrDefault(x => x.NetHandle == client.NetHandle); Client x = Server.Clients.FirstOrDefault(x => x.NetHandle == client.NetHandle);
if (x != null) if (x != null)
{ {
x.FilesSent = true; x.FilesReceived = true;
} }
} }
}); });
@ -100,60 +106,117 @@ namespace CoopServer
_clients.Remove(client); _clients.Remove(client);
} }
} }
/// <summary>
/// We try to remove the client when all files have been sent
/// </summary>
/// <param name="nethandle"></param>
/// <param name="id"></param>
public static void TryToRemoveClient(long nethandle, int id)
{
DownloadClient client = _clients.FirstOrDefault(x => x.NetHandle == nethandle);
if (client == null)
{
return;
}
client.FilePosition++;
if (client.DownloadComplete())
{
_clients.Remove(client);
}
}
} }
internal class DownloadClient internal class DownloadClient
{ {
public long NetHandle { get; set; } public long NetHandle = 0;
public int FilesCount { get; set; } private List<DownloadFile> Files = null;
public int FilesSent = 0; public int FilePosition = 0;
public DownloadClient(long nethandle, List<DownloadFile> files)
{
NetHandle = nethandle;
Files = files;
NetConnection conn = Server.MainNetServer.Connections.FirstOrDefault(x => x.RemoteUniqueIdentifier == NetHandle);
if (conn != null)
{
Files.ForEach(file =>
{
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
new Packets.FileRequest()
{
ID = file.FileID,
FileType = (byte)Packets.DataFileType.Script,
FileName = file.FileName,
FileLength = file.FileLength
}.PacketToNetOutGoingMessage(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, conn, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.File);
});
}
}
/// <summary> /// <summary>
/// ///
/// </summary> /// </summary>
/// <returns>true if files should be sent otherwise false</returns> /// <returns>true if files should be sent otherwise false</returns>
public bool SendFiles(List<DownloadFile> files) public bool SendFiles()
{ {
if (FilesSent >= FilesCount) if (DownloadComplete())
{ {
return false; return false;
} }
DownloadFile file = files.FirstOrDefault(x => !x.DownloadFinished()); DownloadFile file = Files[FilePosition];
if (file == null)
{
return false;
}
file.Send(NetHandle); file.Send(NetHandle);
// Check it again, maybe this file is finish now
if (file.DownloadFinished()) if (file.DownloadFinished())
{ {
FilesSent++; FilePosition++;
if (FilesSent >= FilesCount) return DownloadComplete();
{
return false;
}
} }
return true; return true;
} }
public bool DownloadComplete()
{
return FilePosition >= Files.Count;
}
} }
internal class DownloadFile internal class DownloadFile
{ {
public int FileID { get; set; } public byte FileID { get; set; }
public string FileName { get; set; } public string FileName { get; set; }
public long FileLength { get; set; } public long FileLength { get; set; }
private readonly List<byte[]> _data = new(); private readonly List<byte[]> _data = new();
private readonly long _sent = 0; private int _dataPosition = 0;
public void Send(long nethandle) public void Send(long nethandle)
{ {
// TODO NetConnection conn = Server.MainNetServer.Connections.FirstOrDefault(x => x.RemoteUniqueIdentifier == nethandle);
if (conn == null)
{
return;
}
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
new Packets.FileTransferTick() { ID = FileID, FileChunk = _data.ElementAt(_dataPosition) }.PacketToNetOutGoingMessage(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, conn, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.File);
Logging.Debug($"Send _data[{_dataPosition}] ~ {_data.ElementAt(_dataPosition).Length}");
_dataPosition++;
} }
public void AddData(byte[] data) public void AddData(byte[] data)
@ -161,9 +224,14 @@ namespace CoopServer
_data.Add(data); _data.Add(data);
} }
public void Cancel()
{
_dataPosition = _data.Count - 1;
}
public bool DownloadFinished() public bool DownloadFinished()
{ {
return _sent >= FileLength; return _dataPosition >= _data.Count;
} }
} }
} }

View File

@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.Text;
using Lidgren.Network;
namespace CoopServer
{
internal partial class Packets
{
public enum DataFileType
{
Script = 0,
Map = 1
}
public class FileRequest : Packet
{
public byte ID { get; set; }
public byte FileType { get; set; }
public string FileName { get; set; }
public long FileLength { get; set; }
public override void PacketToNetOutGoingMessage(NetOutgoingMessage message)
{
#region PacketToNetOutGoingMessage
message.Write((byte)PacketTypes.FileTransferRequest);
List<byte> byteArray = new List<byte>();
// The ID from the download
byteArray.Add(ID);
// The type of the file
byteArray.Add(FileType);
// The name of the file
byte[] nameBytes = Encoding.UTF8.GetBytes(FileName);
byteArray.AddRange(BitConverter.GetBytes(nameBytes.Length));
byteArray.AddRange(nameBytes);
// The length of the file
byteArray.AddRange(BitConverter.GetBytes(FileLength));
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
}
public override void NetIncomingMessageToPacket(byte[] array)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
ID = reader.ReadByte();
FileType = reader.ReadByte();
int nameArrayLength = reader.ReadInt();
FileName = reader.ReadString(nameArrayLength);
FileLength = reader.ReadLong();
#endregion
}
}
public class FileTransferTick : Packet
{
public byte ID { get; set; }
public byte[] FileChunk { get; set; }
public override void PacketToNetOutGoingMessage(NetOutgoingMessage message)
{
#region PacketToNetOutGoingMessage
message.Write((byte)PacketTypes.FileTransferTick);
List<byte> byteArray = new List<byte>();
// The ID from the download
byteArray.Add(ID);
// The chunk of the file
byteArray.AddRange(BitConverter.GetBytes(FileChunk.Length));
byteArray.AddRange(FileChunk);
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
}
public override void NetIncomingMessageToPacket(byte[] array)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
ID = reader.ReadByte();
int chunkLength = reader.ReadInt();
FileChunk = reader.ReadByteArray(chunkLength);
#endregion
}
}
public class FileTransferComplete : Packet
{
public byte ID { get; set; }
public override void PacketToNetOutGoingMessage(NetOutgoingMessage message)
{
#region PacketToNetOutGoingMessage
message.Write((byte)PacketTypes.FileTransferComplete);
List<byte> byteArray = new List<byte>();
// The ID from the download
byteArray.Add(ID);
byte[] result = byteArray.ToArray();
message.Write(result.Length);
message.Write(result);
#endregion
}
public override void NetIncomingMessageToPacket(byte[] array)
{
#region NetIncomingMessageToPacket
BitReader reader = new BitReader(array);
ID = reader.ReadByte();
#endregion
}
}
}
}

View File

@ -99,7 +99,10 @@ namespace CoopServer
NativeCall, NativeCall,
NativeResponse, NativeResponse,
Mod, Mod,
CleanUpWorld CleanUpWorld,
FileTransferTick,
FileTransferRequest,
FileTransferComplete
} }
enum ConnectionChannel enum ConnectionChannel
@ -111,7 +114,8 @@ namespace CoopServer
NPCFull = 4, NPCFull = 4,
Chat = 5, Chat = 5,
Native = 6, Native = 6,
Mod = 7 Mod = 7,
File = 8
} }
[Flags] [Flags]

View File

@ -224,6 +224,7 @@ namespace CoopServer
if (!client.FilesSent) if (!client.FilesSent)
{ {
DownloadManager.InsertClient(client.NetHandle); DownloadManager.InsertClient(client.NetHandle);
client.FilesSent = true;
} }
}); });
} }
@ -268,7 +269,11 @@ namespace CoopServer
if (status == NetConnectionStatus.Disconnected) if (status == NetConnectionStatus.Disconnected)
{ {
SendPlayerDisconnectPacket(message.SenderConnection.RemoteUniqueIdentifier); long nethandle = message.SenderConnection.RemoteUniqueIdentifier;
DownloadManager.RemoveClient(nethandle);
SendPlayerDisconnectPacket(nethandle);
} }
else if (status == NetConnectionStatus.Connected) else if (status == NetConnectionStatus.Connected)
{ {
@ -504,6 +509,31 @@ namespace CoopServer
} }
} }
break; break;
case (byte)PacketTypes.FileTransferComplete:
{
try
{
if (DownloadManager.AnyFileExists)
{
int len = message.ReadInt32();
byte[] data = message.ReadBytes(len);
Packets.FileTransferComplete packet = new();
packet.NetIncomingMessageToPacket(data);
Client client = Clients.Find(x => x.NetHandle == message.SenderConnection.RemoteUniqueIdentifier);
if (client != null && !client.FilesReceived)
{
DownloadManager.TryToRemoveClient(client.NetHandle, packet.ID);
}
}
}
catch (Exception e)
{
DisconnectAndLog(message.SenderConnection, type, e);
}
}
break;
default: default:
Logging.Error("Unhandled Data / Packet type"); Logging.Error("Unhandled Data / Packet type");
break; break;