Preparing for PluginLoader

This commit is contained in:
Sardelka 2022-06-30 09:28:13 +08:00
parent f9b9d78b79
commit 58362c2613
7 changed files with 101 additions and 74 deletions

View File

@ -1,6 +1,7 @@
using System.IO;
using RageCoop.Core.Scripting;
using ICSharpCode.SharpZipLib.Zip;
using System;
namespace RageCoop.Client.Scripting
{
@ -15,7 +16,15 @@ namespace RageCoop.Client.Scripting
{
foreach (var s in d.Scripts)
{
Main.QueueAction(() => s.OnStart());
try
{
s.OnStart();
}
catch(Exception ex)
{
Logger.Error("Error occurred when starting script:"+s.GetType().FullName);
Logger?.Error(ex);
}
}
}
}
@ -28,7 +37,15 @@ namespace RageCoop.Client.Scripting
{
foreach (var s in d.Scripts)
{
Main.QueueAction(() => s.OnStop());
try
{
s.OnStop();
}
catch (Exception ex)
{
Logger.Error("Error occurred when stopping script:"+s.GetType().FullName);
Logger?.Error(ex);
}
}
}
}

View File

@ -13,10 +13,10 @@ namespace RageCoop.Core
private bool _stopping=false;
public string Name { get; set; }
public bool IsBusy { get;private set; }
internal Worker(int maxJobs = Int32.MaxValue,string name="Worker")
internal Worker(string name,Logger logger,int maxJobs = Int32.MaxValue)
{
Name = name;
_semaphoreSlim = new SemaphoreSlim(maxJobs);
_semaphoreSlim = new SemaphoreSlim(0,maxJobs);
_workerThread=new Thread(() =>
{
while (!_stopping)
@ -26,7 +26,15 @@ namespace RageCoop.Core
if(Jobs.TryDequeue(out var job))
{
IsBusy=true;
job.Invoke();
try
{
job.Invoke();
}
catch (Exception ex)
{
logger.Error("Error occurred when executing queued job:");
logger.Error(ex);
}
}
else
{
@ -37,7 +45,7 @@ namespace RageCoop.Core
});
_workerThread.Start();
}
public void QueueWork(Action work)
public void QueueJob(Action work)
{
Jobs.Enqueue(work);
_semaphoreSlim.Release();
@ -45,6 +53,7 @@ namespace RageCoop.Core
public void Stop()
{
_stopping=true;
QueueJob(() => { });
if (_workerThread.IsAlive)
{
_workerThread.Join();

View File

@ -46,6 +46,9 @@
</ItemGroup>
<ItemGroup>
<Reference Include="McMaster.NETCore.Plugins">
<HintPath>..\libs\McMaster.NETCore.Plugins.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\libs\Newtonsoft.Json.dll</HintPath>
</Reference>

View File

@ -17,6 +17,13 @@ namespace RageCoop.Server.Scripting
internal Dictionary<int, List<Action<CustomEventReceivedArgs>>> CustomEventHandlers = new();
#endregion
public event EventHandler<ChatEventArgs> OnChatMessage;
/// <summary>
/// Will be invoked from main thread before registered handlers
/// </summary>
public event EventHandler<OnCommandEventArgs> OnCommandReceived;
/// <summary>
/// Will be invoked from main thread when a client is attempting to connect, use <see cref="HandshakeEventArgs.Deny(string)"/> to deny the connection request.
/// </summary>
public event EventHandler<HandshakeEventArgs> OnPlayerHandshake;
/// <summary>
/// Will be invoked when a player is connected, but this player might not be ready yet(client resources not loaded), using <see cref="OnPlayerReady"/> is recommended.
@ -28,10 +35,6 @@ namespace RageCoop.Server.Scripting
public event EventHandler<Client> OnPlayerReady;
public event EventHandler<Client> OnPlayerDisconnected;
/// <summary>
/// Will be invoked before registered handlers
/// </summary>
public event EventHandler<OnCommandEventArgs> OnCommandReceived;
/// <summary>
/// Invoked everytime a player's main ped has been updated
/// </summary>
public event EventHandler<Client> OnPlayerUpdate;
@ -47,6 +50,41 @@ namespace RageCoop.Server.Scripting
OnPlayerUpdate=null;
}
#region INVOKE
internal void InvokePlayerHandshake(HandshakeEventArgs args)
{ OnPlayerHandshake?.Invoke(this, args); }
internal void InvokeOnCommandReceived(string cname, string[] cargs, Client sender)
{
var args = new OnCommandEventArgs()
{
Name=cname,
Args=cargs,
Sender=sender
};
OnCommandReceived?.Invoke(this, args);
if (args.Cancel)
{
return;
}
if (Commands.Any(x => x.Key.Name == cmdName))
{
string[] argsWithoutCmd = cmdArgs.Skip(1).ToArray();
CommandContext ctx = new()
{
Client = sender,
Args = argsWithoutCmd
};
KeyValuePair<Command, Action<CommandContext>> command = Commands.First(x => x.Key.Name == cmdName);
command.Value.Invoke(ctx);
}
else
{
SendChatMessage("Server", "Command not found!", sender.Connection);
}
}
internal void InvokeOnChatMessage(Packets.ChatMessage p, Client sender)
{
OnChatMessage?.Invoke(this, new ChatEventArgs()
@ -61,8 +99,6 @@ namespace RageCoop.Server.Scripting
{ OnPlayerReady?.Invoke(this, client); }
internal void InvokePlayerDisconnected(Client client)
{ OnPlayerDisconnected?.Invoke(this,client); }
internal void InvokePlayerHandshake(HandshakeEventArgs args)
{ OnPlayerHandshake?.Invoke(this, args); }
internal void InvokeCustomEventReceived(Packets.CustomEvent p, Client sender)
{
@ -73,17 +109,6 @@ namespace RageCoop.Server.Scripting
handlers.ForEach((x) => { x.Invoke(args); });
}
}
internal bool InvokeOnCommandReceived(string cname, string[] cargs, Client sender)
{
var args = new OnCommandEventArgs()
{
Name=cname,
Args=cargs,
Sender=sender
};
OnCommandReceived?.Invoke(this, args);
return args.Cancel;
}
internal void InvokePlayerUpdate(Client client)
{
OnPlayerUpdate?.Invoke(this, client);

View File

@ -116,7 +116,6 @@ namespace RageCoop.Server.Scripting
{
try
{
s.OnStop();
}
catch(Exception ex)

View File

@ -26,23 +26,24 @@ namespace RageCoop.Server
public class Server
{
private readonly string _compatibleVersion = "V0_5";
public API API { get; private set; }
internal BaseScript BaseScript { get; set; }=new BaseScript();
internal readonly ServerSettings Settings;
internal NetServer MainNetServer;
private bool _stopping=false;
internal readonly Dictionary<Command, Action<CommandContext>> Commands = new();
internal readonly Dictionary<long,Client> Clients = new();
private System.Timers.Timer SendPlayerTimer = new System.Timers.Timer(5000);
private Dictionary<int,FileTransfer> InProgressFileTransfers=new();
private Resources Resources;
public API API { get; private set; }
internal Logger Logger;
private Security Security;
private System.Timers.Timer _sendInfoTimer = new System.Timers.Timer(5000);
private bool _stopping = false;
private Thread _listenerThread;
private Thread _announceThread;
private Worker _worker;
private readonly string _compatibleVersion = "V0_5";
public Server(ServerSettings settings,Logger logger=null)
{
Settings = settings;
@ -77,9 +78,10 @@ namespace RageCoop.Server
MainNetServer = new NetServer(config);
MainNetServer.Start();
SendPlayerTimer.Elapsed+=(s, e) => { SendPlayerInfos(); };
SendPlayerTimer.AutoReset=true;
SendPlayerTimer.Enabled=true;
_worker=new Worker("ServerWorker");
_sendInfoTimer.Elapsed+=(s, e) => { SendPlayerInfos(); };
_sendInfoTimer.AutoReset=true;
_sendInfoTimer.Enabled=true;
Logger?.Info(string.Format("Server listening on {0}:{1}", config.LocalAddress.ToString(), config.Port));
if (Settings.AnnounceSelf)
{
@ -193,12 +195,13 @@ namespace RageCoop.Server
public void Stop()
{
_stopping = true;
SendPlayerTimer.Stop();
SendPlayerTimer.Enabled=false;
SendPlayerTimer.Dispose();
_sendInfoTimer.Stop();
_sendInfoTimer.Enabled=false;
_sendInfoTimer.Dispose();
Logger?.Flush();
_listenerThread?.Join();
_announceThread?.Join();
_worker.Dispose();
}
private void Listen()
{
@ -273,10 +276,10 @@ namespace RageCoop.Server
{
SendPlayerConnectPacket(sender);
Resources.SendTo(sender);
API.Events.InvokePlayerConnected(sender);
_worker.QueueJob(()=> API.Events.InvokePlayerConnected(sender));
if (sender.IsReady)
{
API.Events.InvokePlayerReady(sender);
_worker.QueueJob(()=>API.Events.InvokePlayerReady(sender));
}
}
break;
@ -356,7 +359,7 @@ namespace RageCoop.Server
Packets.ChatMessage packet = new();
packet.Unpack(data);
API.Events.InvokeOnChatMessage(packet, sender);
_worker.QueueJob(()=>API.Events.InvokeOnChatMessage(packet, sender));
SendChatMessage(packet,sender);
}
break;
@ -364,7 +367,7 @@ namespace RageCoop.Server
{
Packets.CustomEvent packet = new Packets.CustomEvent();
packet.Unpack(data);
API.Events.InvokeCustomEventReceived(packet, sender);
_worker.QueueJob(() => API.Events.InvokeCustomEventReceived(packet, sender));
}
break;
@ -381,7 +384,7 @@ namespace RageCoop.Server
if (toRemove.Name=="Resources.zip")
{
sender.IsReady=true;
API.Events.InvokePlayerReady(sender);
_worker.QueueJob(() => API.Events.InvokePlayerReady(sender));
}
}
}
@ -632,7 +635,7 @@ namespace RageCoop.Server
}.Pack(outgoingMessage);
MainNetServer.SendMessage(outgoingMessage,cons , NetDeliveryMethod.ReliableOrdered, 0);
}
API.Events.InvokePlayerDisconnected(localClient);
_worker.QueueJob(() => API.Events.InvokePlayerDisconnected(localClient));
Logger?.Info($"Player {localClient.Username} disconnected! ID:{localClient.ID}");
Clients.Remove(localClient.NetID);
Security.RemoveConnection(localClient.Connection.RemoteEndPoint);
@ -675,8 +678,8 @@ namespace RageCoop.Server
if (isPlayer)
{
client.Player.Position=packet.Position;
client.Player.Health=packet.Health ;
API.Events.InvokePlayerUpdate(client);
client.Player.Health=packet.Health ;
_worker.QueueJob(() => API.Events.InvokePlayerUpdate(client));
}
foreach (var c in Clients.Values)
@ -746,38 +749,9 @@ namespace RageCoop.Server
if (packet.Message.StartsWith('/'))
{
string[] cmdArgs = packet.Message.Split(" ");
string cmdName = cmdArgs[0].Remove(0, 1);
if (API.Events.InvokeOnCommandReceived(cmdName, cmdArgs, sender))
{
return;
}
if (Commands.Any(x => x.Key.Name == cmdName))
{
string[] argsWithoutCmd = cmdArgs.Skip(1).ToArray();
CommandContext ctx = new()
{
Client = Clients.Values.Where(x => x.Username == packet.Username).FirstOrDefault(),
Args = argsWithoutCmd
};
KeyValuePair<Command, Action<CommandContext>> command = Commands.First(x => x.Key.Name == cmdName);
if (command.Key.Usage != null && command.Key.ArgsLength != argsWithoutCmd.Length)
{
SendChatMessage("Server", command.Key.Usage,sender.Connection);
return;
}
command.Value.Invoke(ctx);
}
else
{
SendChatMessage("Server", "Command not found!", sender.Connection);
}
string cmdName = cmdArgs[0].Remove(0, 1);
_worker.QueueJob(()=>API.Events.InvokeOnCommandReceived(cmdName, cmdArgs, sender));
return;
}

Binary file not shown.