Add ability to share server file

This commit is contained in:
Sardelka
2022-07-19 17:15:53 +08:00
parent 3072db0e21
commit 9527da9785
8 changed files with 143 additions and 29 deletions

View File

@ -8,6 +8,7 @@ namespace RageCoop.Client
{
internal static class DownloadManager
{
public static event EventHandler<string> DownloadCompleted;
static DownloadManager()
{
Networking.RequestHandlers.Add(PacketType.FileTransferRequest, (data) =>
@ -64,23 +65,26 @@ namespace RageCoop.Client
private static readonly List<string> _zips = new List<string>();
public static bool AddFile(int id, string name, long length)
{
Main.Logger.Debug($"Downloading file to {ResourceFolder}\\{name} , id:{id}");
if (!Directory.Exists(ResourceFolder))
var path = $"{ResourceFolder}\\{name}";
Main.Logger.Debug($"Downloading file to {path} , id:{id}");
if (!Directory.Exists(Directory.GetParent(path).FullName))
{
Directory.CreateDirectory(ResourceFolder);
Directory.CreateDirectory(Directory.GetParent(path).FullName);
}
if (FileAlreadyExists(ResourceFolder, name, length))
{
Main.Logger.Debug($"File already exists! canceling download:{name}");
DownloadCompleted?.Invoke(null, Path.Combine(ResourceFolder, name));
return false;
}
/*
if (!name.EndsWith(".zip"))
{
Main.Logger.Error($"File download blocked! [{name}]");
return false;
}
*/
lock (InProgressDownloads)
{
InProgressDownloads.Add(id, new DownloadFile()
@ -88,7 +92,7 @@ namespace RageCoop.Client
FileID = id,
FileName = name,
FileLength = length,
Stream = new FileStream($"{ResourceFolder}\\{name}", FileMode.CreateNew, FileAccess.Write, FileShare.ReadWrite)
Stream = new FileStream(path, FileMode.CreateNew, FileAccess.Write, FileShare.ReadWrite)
});
}
return true;
@ -150,6 +154,7 @@ namespace RageCoop.Client
_zips.Add(f.FileName);
}
Main.Logger.Info($"Download finished:{f.FileName}");
DownloadCompleted?.Invoke(null, Path.Combine(ResourceFolder, f.FileName));
}
else
{

View File

@ -4,6 +4,7 @@ using RageCoop.Core;
using System.Threading.Tasks;
using System.Threading;
using System.IO;
using System.Security.Cryptography;
namespace RageCoop.Client
{
@ -148,6 +149,37 @@ namespace RageCoop.Client
return _publicKeyReceived.WaitOne(timeout);
}
#endregion
internal static void GetResponse<T>(Packet request, Action<T> callback, ConnectionChannel channel = ConnectionChannel.RequestResponse) where T : Packet, new()
{
var received = new AutoResetEvent(false);
var id = NewRequestID();
PendingResponses.Add(id, (type, p) =>
{
var result = new T();
result.Unpack(p);
callback(result);
});
var msg = Client.CreateMessage();
msg.Write((byte)PacketType.Request);
msg.Write(id);
request.Pack(msg);
Client.SendMessage(msg, NetDeliveryMethod.ReliableOrdered, (int)channel);
}
private static int NewRequestID()
{
int ID = 0;
while ((ID==0)
|| PendingResponses.ContainsKey(ID))
{
byte[] rngBytes = new byte[4];
RandomNumberGenerator.Create().GetBytes(rngBytes);
// Convert the bytes into an integer
ID = BitConverter.ToInt32(rngBytes, 0);
}
return ID;
}
}
}

View File

@ -119,7 +119,7 @@ namespace RageCoop.Client
response.Write((byte)PacketType.Response);
response.Write(id);
handler(message.ReadBytes(len)).Pack(response);
Client.SendMessage(response, NetDeliveryMethod.ReliableOrdered);
Client.SendMessage(response, NetDeliveryMethod.ReliableOrdered,(int)ConnectionChannel.RequestResponse);
}
break;
}

View File

@ -279,6 +279,34 @@ namespace RageCoop.Client.Scripting
handlers.Add(handler);
}
}
/// <summary>
///
/// </summary>
/// <returns></returns>
public static void RequestSharedFile(string name,Action<string> callback)
{
EventHandler<string> handler = (s, e) =>
{
if (e.EndsWith(name))
{
callback(e);
}
};
DownloadManager.DownloadCompleted+=handler;
Networking.GetResponse<Packets.FileTransferResponse>(new Packets.FileTransferRequest()
{
Name=name,
},
(p) =>
{
if(p.Response != FileResponse.Loaded)
{
DownloadManager.DownloadCompleted-=handler;
throw new ArgumentException("Requested file was not found on the server: "+name);
}
});
}
#endregion
}
}

View File

@ -52,6 +52,7 @@ namespace RageCoop.Client.Scripting
{
try
{
s.CurrentResource=d;
s.OnStart();
}
catch(Exception ex)
@ -118,6 +119,7 @@ namespace RageCoop.Client.Scripting
{
var r = new ClientResource()
{
Logger = Main.Logger,
Scripts = new List<ClientScript>(),
Name=Path.GetFileNameWithoutExtension(file.Name),
DataFolder=Path.Combine(dataFolderRoot, Path.GetFileNameWithoutExtension(file.Name))

View File

@ -15,7 +15,6 @@ namespace RageCoop.Client
{
internal class EntityPool
{
private static bool _trafficSpawning=true;
public static object PedsLock = new object();
private static Dictionary<int, SyncedPed> ID_Peds = new Dictionary<int, SyncedPed>();
public static int CharactersCount { get { return ID_Peds.Count; } }

View File

@ -7,7 +7,7 @@ using Lidgren.Network;
using RageCoop.Core;
using RageCoop.Core.Scripting;
using System.Reflection;
using System.Net;
using System.IO;
namespace RageCoop.Server.Scripting
{
@ -135,10 +135,34 @@ namespace RageCoop.Server.Scripting
public class API
{
internal readonly Server Server;
internal readonly Dictionary<string, Func<Stream>> RegisteredFiles=new Dictionary<string, Func<System.IO.Stream>>();
internal API(Server server)
{
Server=server;
Events=new(server);
Server.RequestHandlers.Add(PacketType.FileTransferRequest, (data,client) =>
{
var p = new Packets.FileTransferRequest();
p.Unpack(data);
var id=Server.NewFileID();
if(RegisteredFiles.TryGetValue(p.Name,out var s))
{
Task.Run(() =>
{
Server.SendFile(s(), p.Name, client,id);
});
return new Packets.FileTransferResponse()
{
ID=id,
Response=FileResponse.Loaded
};
}
return new Packets.FileTransferResponse()
{
ID=id,
Response=FileResponse.LoadFailed
};
});
}
/// <summary>
/// Server side events
@ -197,8 +221,24 @@ namespace RageCoop.Server.Scripting
}
/// <summary>
/// Send CleanUpWorld to all players to delete all objects created by the server
/// Register a file to be shared with clients
/// </summary>
/// <param name="name">name of this file</param>
/// <param name="path">path to this file</param>
public void RegisterSharedFile(string name,string path)
{
RegisteredFiles.Add(name, () => { return File.OpenRead(path); });
}
/// <summary>
/// Register a file to be shared with clients
/// </summary>
/// <param name="name">name of this file</param>
/// <param name="file"></param>
public void RegisterSharedFile(string name, ResourceFile file)
{
RegisteredFiles.Add(name, file.GetStream);
}
/// <summary>
/// Register a new command chat command (Example: "/test")

View File

@ -46,7 +46,6 @@ namespace RageCoop.Server
internal readonly Dictionary<long,Client> Clients = new();
internal readonly Dictionary<string, Client> ClientsByName = new();
internal Client _hostClient;
internal string CurrentWeather;
private Dictionary<int,FileTransfer> InProgressFileTransfers=new();
private Resources Resources;
@ -58,7 +57,7 @@ namespace RageCoop.Server
private Thread _announceThread;
private Worker _worker;
private Dictionary<int,Action<PacketType,byte[]>> PendingResponses=new();
private Dictionary<PacketType, Func<byte[],Packet>> RequestHandlers=new();
internal Dictionary<PacketType, Func<byte[],Client,Packet>> RequestHandlers=new();
private readonly string _compatibleVersion = "V0_5";
/// <summary>
/// Instantiate a server.
@ -239,17 +238,23 @@ namespace RageCoop.Server
}
private void Listen()
{
NetIncomingMessage msg=null;
Logger?.Info("Listening for clients");
while (!_stopping)
{
try
{
ProcessMessage(MainNetServer.WaitMessage(200));
msg=MainNetServer.WaitMessage(200);
ProcessMessage(msg);
}
catch(Exception ex)
{
Logger?.Error("Error processing message");
Logger?.Error(ex);
if (msg!=null)
{
DisconnectAndLog(msg.SenderConnection, PacketType.Unknown, ex);
}
}
}
Logger?.Info("Server is shutting down!");
@ -342,7 +347,7 @@ namespace RageCoop.Server
var response=MainNetServer.CreateMessage();
response.Write((byte)PacketType.Response);
response.Write(id);
handler(message.ReadBytes(message.ReadInt32())).Pack(response);
handler(message.ReadBytes(message.ReadInt32()),sender).Pack(response);
MainNetServer.SendMessage(response,message.SenderConnection,NetDeliveryMethod.ReliableOrdered);
}
break;
@ -866,22 +871,25 @@ namespace RageCoop.Server
}
}
internal void SendFile(string path,string name,Client client,Action<float> updateCallback=null)
{
SendFile(File.OpenRead(path), name,client,NewFileID(),updateCallback);
}
internal void SendFile(Stream stream, string name, Client client,int id=default, Action<float> updateCallback = null)
{
int id = NewFileID();
var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
fs.Seek(0, SeekOrigin.Begin);
var total = fs.Length;
id = id ==default? NewFileID(): id ;
stream.Seek(0, SeekOrigin.Begin);
var total = stream.Length;
if (GetResponse<Packets.FileTransferResponse>(client, new Packets.FileTransferRequest()
{
FileLength= total,
Name=name,
ID=id,
},ConnectionChannel.File)?.Response!=FileResponse.NeedToDownload)
}, ConnectionChannel.File)?.Response!=FileResponse.NeedToDownload)
{
Logger?.Info($"Skipping file transfer \"{name}\" to {client.Username}");
fs.Close();
fs.Dispose();
stream.Close();
stream.Dispose();
return;
}
Logger?.Debug($"Initiating file transfer:{name}, {total}");
@ -897,7 +905,7 @@ namespace RageCoop.Server
{
// 4 KB chunk
byte[] chunk = new byte[4096];
read += thisRead=fs.Read(chunk, 0, 4096);
read += thisRead=stream.Read(chunk, 0, 4096);
if (thisRead!=chunk.Length)
{
if (thisRead==0) { break; }
@ -911,23 +919,23 @@ namespace RageCoop.Server
FileChunk=chunk,
},
client, ConnectionChannel.File, NetDeliveryMethod.ReliableOrdered);
transfer.Progress=read/fs.Length;
if (updateCallback!=null) { updateCallback(transfer.Progress);}
transfer.Progress=read/stream.Length;
if (updateCallback!=null) { updateCallback(transfer.Progress); }
} while (thisRead>0);
if(GetResponse<Packets.FileTransferResponse>(client, new Packets.FileTransferComplete()
if (GetResponse<Packets.FileTransferResponse>(client, new Packets.FileTransferComplete()
{
ID= id,
},ConnectionChannel.File)?.Response!=FileResponse.Completed)
}, ConnectionChannel.File)?.Response!=FileResponse.Completed)
{
Logger.Warning($"File trasfer to {client.Username} failed: "+name);
}
fs.Close();
fs.Dispose();
}
stream.Close();
stream.Dispose();
Logger?.Debug($"All file chunks sent:{name}");
InProgressFileTransfers.Remove(id);
}
private int NewFileID()
internal int NewFileID()
{
int ID = 0;
while ((ID==0)