From f0d1fee2bd80f8b309399cf17b122219890890cd Mon Sep 17 00:00:00 2001 From: EntenKoeniq <81123713+EntenKoeniq@users.noreply.github.com> Date: Sat, 20 Nov 2021 05:38:00 +0100 Subject: [PATCH 1/3] Resources are now multithreaded. GameMode renamed to Resource --- Server/PlayerData.cs | 4 +- Server/Server.cs | 32 ++++++++-------- Server/ServerScript.cs | 85 ++++++++++++++++++++++++++++++++++++++++++ Server/Settings.cs | 2 +- 4 files changed, 104 insertions(+), 19 deletions(-) diff --git a/Server/PlayerData.cs b/Server/PlayerData.cs index f559784..37d3554 100644 --- a/Server/PlayerData.cs +++ b/Server/PlayerData.cs @@ -18,9 +18,9 @@ LastPosition = CurrentPosition; CurrentPosition = value; - if (Server.GameMode != null && !LVector3.Equals(CurrentPosition, LastPosition)) + if (Server.MainResource != null && !LVector3.Equals(CurrentPosition, LastPosition)) { - Server.GameMode.API.InvokePlayerPositionUpdate(this); + Server.MainResource.InvokePlayerPositionUpdate(this); } } } diff --git a/Server/Server.cs b/Server/Server.cs index 06c777a..02b4fac 100644 --- a/Server/Server.cs +++ b/Server/Server.cs @@ -19,7 +19,7 @@ namespace CoopServer public static NetServer MainNetServer; - public static ServerScript GameMode; + public static Resource MainResource; public static Dictionary> Commands; public static readonly List Clients = new(); @@ -62,13 +62,13 @@ namespace CoopServer } } - if (!string.IsNullOrEmpty(MainSettings.GameMode)) + if (!string.IsNullOrEmpty(MainSettings.Resource)) { try { - Logging.Info("Loading gamemode..."); + Logging.Info("Loading resource..."); - Assembly asm = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "gamemodes" + Path.DirectorySeparatorChar + MainSettings.GameMode + ".dll"); + Assembly asm = Assembly.LoadFrom(AppDomain.CurrentDomain.BaseDirectory + "resources" + Path.DirectorySeparatorChar + MainSettings.Resource + ".dll"); Type[] types = asm.GetExportedTypes(); IEnumerable validTypes = types.Where(t => !t.IsInterface && !t.IsAbstract).Where(t => typeof(ServerScript).IsAssignableFrom(t)); Type[] enumerable = validTypes as Type[] ?? validTypes.ToArray(); @@ -81,14 +81,13 @@ namespace CoopServer { Commands = new(); - GameMode = Activator.CreateInstance(enumerable.ToArray()[0]) as ServerScript; - if (GameMode == null) + if (Activator.CreateInstance(enumerable.ToArray()[0]) is ServerScript script) { - Logging.Warning("Could not create gamemode: it is null."); + MainResource = new(script); } else { - GameMode.API.InvokeStart(); + Logging.Warning("Could not create resource: it is null."); } } } @@ -289,9 +288,10 @@ namespace CoopServer packet.NetIncomingMessageToPacket(message); ModPacket modPacket = (ModPacket)packet; - if (GameMode != null) + if (MainResource != null) { - if (GameMode.API.InvokeModPacketReceived(modPacket.ID, modPacket.Target, modPacket.Mod, modPacket.CustomPacketID, modPacket.Bytes)) + // TODO: We need the true/false result + if (MainResource.InvokeModPacketReceived(modPacket.ID, modPacket.Target, modPacket.Mod, modPacket.CustomPacketID, modPacket.Bytes)) { break; } @@ -458,9 +458,9 @@ namespace CoopServer SendChatMessage(new ChatMessagePacket() { Username = "Server", Message = MainSettings.WelcomeMessage }, new List() { local }); } - if (GameMode != null) + if (MainResource != null) { - GameMode.API.InvokePlayerConnected(Clients.Find(x => x.ID == packet.ID)); + MainResource.InvokePlayerConnected(Clients.Find(x => x.ID == packet.ID)); } List clients; @@ -502,9 +502,9 @@ namespace CoopServer // Send all players a message that someone has left the server private static void SendPlayerDisconnectPacket(PlayerDisconnectPacket packet) { - if (GameMode != null) + if (MainResource != null) { - GameMode.API.InvokePlayerDisconnected(Clients.Find(x => x.ID == packet.ID)); + MainResource.InvokePlayerDisconnected(Clients.Find(x => x.ID == packet.ID)); } List clients; @@ -643,7 +643,7 @@ namespace CoopServer { NetOutgoingMessage outgoingMessage; - if (GameMode != null) + if (MainResource != null) { if (packet.Message.StartsWith("/")) { @@ -679,7 +679,7 @@ namespace CoopServer return; } - else if (GameMode.API.InvokeChatMessage(packet.Username, packet.Message)) + else if (MainResource.InvokeChatMessage(packet.Username, packet.Message)) // TODO: We need the true/false result { return; } diff --git a/Server/ServerScript.cs b/Server/ServerScript.cs index f2a875f..fbbc850 100644 --- a/Server/ServerScript.cs +++ b/Server/ServerScript.cs @@ -2,11 +2,96 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Lidgren.Network; namespace CoopServer { + internal class Resource + { + private static Thread _mainThread; + private static bool _hasToStop = false; + private static Queue _actionQueue; + private static TaskFactory _factory; + private static ServerScript _script; + + public Resource(ServerScript script) + { + _factory = new(); + _actionQueue = new(); + _mainThread = new(ThreadLoop) { IsBackground = true }; + _mainThread.Start(); + + lock (_actionQueue) + { + _actionQueue.Enqueue(() => _script = script); + } + } + + private void ThreadLoop() + { + do + { + // 16 milliseconds to sleep to reduce CPU usage + Thread.Sleep(1000 / 60); + + if (_actionQueue.Count == 0) + { + continue; + } + + lock (_actionQueue) + { + _factory.StartNew(() => _actionQueue.Dequeue()?.Invoke()); + } + } while (_hasToStop); + } + + public bool InvokeModPacketReceived(long from, long target, string mod, byte customID, byte[] bytes) + { + Task shutdownTask = new(() => _script.API.InvokeModPacketReceived(from, target, mod, customID, bytes)); + shutdownTask.Start(); + shutdownTask.Wait(5000); + + return shutdownTask.Result; + } + + public void InvokePlayerConnected(Client client) + { + lock (_actionQueue) + { + _actionQueue.Enqueue(() => _script.API.InvokePlayerConnected(client)); + } + } + + public void InvokePlayerDisconnected(Client client) + { + lock (_actionQueue) + { + _actionQueue.Enqueue(() => _script.API.InvokePlayerDisconnected(client)); + } + } + + public bool InvokeChatMessage(string username, string message) + { + Task shutdownTask = new(() => _script.API.InvokeChatMessage(username, message)); + shutdownTask.Start(); + shutdownTask.Wait(5000); + + return shutdownTask.Result; + } + + public void InvokePlayerPositionUpdate(PlayerData playerData) + { + lock (_actionQueue) + { + _actionQueue.Enqueue(() => _script.API.InvokePlayerPositionUpdate(playerData)); + } + } + } + public abstract class ServerScript { public API API { get; } = new(); diff --git a/Server/Settings.cs b/Server/Settings.cs index 9e40b01..1c339a5 100644 --- a/Server/Settings.cs +++ b/Server/Settings.cs @@ -6,7 +6,7 @@ public int MaxPlayers { get; set; } = 16; public string ServerName { get; set; } = "GTACoop:R server"; public string WelcomeMessage { get; set; } = "Welcome on this server :)"; - public string GameMode { get; set; } = ""; + public string Resource { get; set; } = ""; public bool Allowlist { get; set; } = false; public bool NpcsAllowed { get; set; } = true; public bool ModsAllowed { get; set; } = false; From 48cbff2f11250b149c6592b0da64f9e3d42c0720 Mon Sep 17 00:00:00 2001 From: EntenKoeniq <81123713+EntenKoeniq@users.noreply.github.com> Date: Tue, 23 Nov 2021 23:02:41 +0100 Subject: [PATCH 2/3] Small changes --- Server/Server.cs | 10 +++++----- Server/ServerScript.cs | 28 +++++++++++++++------------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Server/Server.cs b/Server/Server.cs index 02b4fac..99aaa00 100644 --- a/Server/Server.cs +++ b/Server/Server.cs @@ -106,9 +106,6 @@ namespace CoopServer while (true) { - // 16 milliseconds to sleep to reduce CPU usage - Thread.Sleep(1000 / 60); - NetIncomingMessage message; while ((message = MainNetServer.ReadMessage()) != null) @@ -290,7 +287,6 @@ namespace CoopServer ModPacket modPacket = (ModPacket)packet; if (MainResource != null) { - // TODO: We need the true/false result if (MainResource.InvokeModPacketReceived(modPacket.ID, modPacket.Target, modPacket.Mod, modPacket.CustomPacketID, modPacket.Bytes)) { break; @@ -357,6 +353,9 @@ namespace CoopServer MainNetServer.Recycle(message); } + + // 16 milliseconds to sleep to reduce CPU usage + Thread.Sleep(1000 / 60); } } @@ -679,7 +678,8 @@ namespace CoopServer return; } - else if (MainResource.InvokeChatMessage(packet.Username, packet.Message)) // TODO: We need the true/false result + + if (MainResource.InvokeChatMessage(packet.Username, packet.Message)) { return; } diff --git a/Server/ServerScript.cs b/Server/ServerScript.cs index fbbc850..929445d 100644 --- a/Server/ServerScript.cs +++ b/Server/ServerScript.cs @@ -26,27 +26,29 @@ namespace CoopServer lock (_actionQueue) { - _actionQueue.Enqueue(() => _script = script); + _actionQueue.Enqueue(() => + { + _script = script; + _script.API.InvokeStart(); + }); } } private void ThreadLoop() { - do + while (_hasToStop) { + if (_actionQueue.Count != 0) + { + lock (_actionQueue) + { + _factory.StartNew(() => _actionQueue.Dequeue()?.Invoke()); + } + } + // 16 milliseconds to sleep to reduce CPU usage Thread.Sleep(1000 / 60); - - if (_actionQueue.Count == 0) - { - continue; - } - - lock (_actionQueue) - { - _factory.StartNew(() => _actionQueue.Dequeue()?.Invoke()); - } - } while (_hasToStop); + } } public bool InvokeModPacketReceived(long from, long target, string mod, byte customID, byte[] bytes) From f6a49fe87fc6c5ddf759faa5c947ae8c2a444d16 Mon Sep 17 00:00:00 2001 From: EntenKoeniq <81123713+EntenKoeniq@users.noreply.github.com> Date: Tue, 23 Nov 2021 23:17:47 +0100 Subject: [PATCH 3/3] SendModPacket (Server) added --- Server/Client.cs | 17 +++++++++++++++++ Server/ServerScript.cs | 15 +++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/Server/Client.cs b/Server/Client.cs index 70cfe38..7fd97d6 100644 --- a/Server/Client.cs +++ b/Server/Client.cs @@ -83,5 +83,22 @@ namespace CoopServer packet.PacketToNetOutGoingMessage(outgoingMessage); Server.MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, 0); } + + public void SendModPacket(string mod, byte customID, byte[] bytes) + { + NetConnection userConnection = Server.MainNetServer.Connections.Find(x => x.RemoteUniqueIdentifier == ID); + + NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage(); + new ModPacket() + { + ID = -1, + Target = 0, + Mod = mod, + CustomPacketID = customID, + Bytes = bytes + }.PacketToNetOutGoingMessage(outgoingMessage); + Server.MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, 0); + Server.MainNetServer.FlushSendQueue(); + } } } diff --git a/Server/ServerScript.cs b/Server/ServerScript.cs index 929445d..d18f7e5 100644 --- a/Server/ServerScript.cs +++ b/Server/ServerScript.cs @@ -151,6 +151,21 @@ namespace CoopServer #endregion #region FUNCTIONS + public static void SendModPacketToAll(string mod, byte customID, byte[] bytes) + { + NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage(); + new ModPacket() + { + ID = -1, + Target = 0, + Mod = mod, + CustomPacketID = customID, + Bytes = bytes + }.PacketToNetOutGoingMessage(outgoingMessage); + Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, 0); + Server.MainNetServer.FlushSendQueue(); + } + public static void SendNativeCallToAll(ulong hash, params object[] args) { if (Server.MainNetServer.ConnectionsCount == 0)