Merge pull request #31 from EntenKoeniq/main

Added multithreading and SendModPacket from the server
This commit is contained in:
Nick-I. A 2021-11-23 23:19:56 +01:00 committed by GitHub
commit 490ec02a3c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 141 additions and 22 deletions

View File

@ -83,5 +83,22 @@ namespace CoopServer
packet.PacketToNetOutGoingMessage(outgoingMessage); packet.PacketToNetOutGoingMessage(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, userConnection, NetDeliveryMethod.ReliableOrdered, 0); 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();
}
} }
} }

View File

@ -18,9 +18,9 @@
LastPosition = CurrentPosition; LastPosition = CurrentPosition;
CurrentPosition = value; 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);
} }
} }
} }

View File

@ -19,7 +19,7 @@ namespace CoopServer
public static NetServer MainNetServer; public static NetServer MainNetServer;
public static ServerScript GameMode; public static Resource MainResource;
public static Dictionary<Command, Action<CommandContext>> Commands; public static Dictionary<Command, Action<CommandContext>> Commands;
public static readonly List<Client> Clients = new(); public static readonly List<Client> Clients = new();
@ -62,13 +62,13 @@ namespace CoopServer
} }
} }
if (!string.IsNullOrEmpty(MainSettings.GameMode)) if (!string.IsNullOrEmpty(MainSettings.Resource))
{ {
try 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(); Type[] types = asm.GetExportedTypes();
IEnumerable<Type> validTypes = types.Where(t => !t.IsInterface && !t.IsAbstract).Where(t => typeof(ServerScript).IsAssignableFrom(t)); IEnumerable<Type> validTypes = types.Where(t => !t.IsInterface && !t.IsAbstract).Where(t => typeof(ServerScript).IsAssignableFrom(t));
Type[] enumerable = validTypes as Type[] ?? validTypes.ToArray(); Type[] enumerable = validTypes as Type[] ?? validTypes.ToArray();
@ -81,14 +81,13 @@ namespace CoopServer
{ {
Commands = new(); Commands = new();
GameMode = Activator.CreateInstance(enumerable.ToArray()[0]) as ServerScript; if (Activator.CreateInstance(enumerable.ToArray()[0]) is ServerScript script)
if (GameMode == null)
{ {
Logging.Warning("Could not create gamemode: it is null."); MainResource = new(script);
} }
else else
{ {
GameMode.API.InvokeStart(); Logging.Warning("Could not create resource: it is null.");
} }
} }
} }
@ -107,9 +106,6 @@ namespace CoopServer
while (true) while (true)
{ {
// 16 milliseconds to sleep to reduce CPU usage
Thread.Sleep(1000 / 60);
NetIncomingMessage message; NetIncomingMessage message;
while ((message = MainNetServer.ReadMessage()) != null) while ((message = MainNetServer.ReadMessage()) != null)
@ -289,9 +285,9 @@ namespace CoopServer
packet.NetIncomingMessageToPacket(message); packet.NetIncomingMessageToPacket(message);
ModPacket modPacket = (ModPacket)packet; ModPacket modPacket = (ModPacket)packet;
if (GameMode != null) if (MainResource != null)
{ {
if (GameMode.API.InvokeModPacketReceived(modPacket.ID, modPacket.Target, modPacket.Mod, modPacket.CustomPacketID, modPacket.Bytes)) if (MainResource.InvokeModPacketReceived(modPacket.ID, modPacket.Target, modPacket.Mod, modPacket.CustomPacketID, modPacket.Bytes))
{ {
break; break;
} }
@ -357,6 +353,9 @@ namespace CoopServer
MainNetServer.Recycle(message); MainNetServer.Recycle(message);
} }
// 16 milliseconds to sleep to reduce CPU usage
Thread.Sleep(1000 / 60);
} }
} }
@ -458,9 +457,9 @@ namespace CoopServer
SendChatMessage(new ChatMessagePacket() { Username = "Server", Message = MainSettings.WelcomeMessage }, new List<NetConnection>() { local }); SendChatMessage(new ChatMessagePacket() { Username = "Server", Message = MainSettings.WelcomeMessage }, new List<NetConnection>() { 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<NetConnection> clients; List<NetConnection> clients;
@ -502,9 +501,9 @@ namespace CoopServer
// Send all players a message that someone has left the server // Send all players a message that someone has left the server
private static void SendPlayerDisconnectPacket(PlayerDisconnectPacket packet) 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<NetConnection> clients; List<NetConnection> clients;
@ -643,7 +642,7 @@ namespace CoopServer
{ {
NetOutgoingMessage outgoingMessage; NetOutgoingMessage outgoingMessage;
if (GameMode != null) if (MainResource != null)
{ {
if (packet.Message.StartsWith("/")) if (packet.Message.StartsWith("/"))
{ {
@ -679,7 +678,8 @@ namespace CoopServer
return; return;
} }
else if (GameMode.API.InvokeChatMessage(packet.Username, packet.Message))
if (MainResource.InvokeChatMessage(packet.Username, packet.Message))
{ {
return; return;
} }

View File

@ -2,11 +2,98 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Lidgren.Network; using Lidgren.Network;
namespace CoopServer namespace CoopServer
{ {
internal class Resource
{
private static Thread _mainThread;
private static bool _hasToStop = false;
private static Queue<Action> _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;
_script.API.InvokeStart();
});
}
}
private void ThreadLoop()
{
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);
}
}
public bool InvokeModPacketReceived(long from, long target, string mod, byte customID, byte[] bytes)
{
Task<bool> 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<bool> 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 abstract class ServerScript
{ {
public API API { get; } = new(); public API API { get; } = new();
@ -64,6 +151,21 @@ namespace CoopServer
#endregion #endregion
#region FUNCTIONS #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) public static void SendNativeCallToAll(ulong hash, params object[] args)
{ {
if (Server.MainNetServer.ConnectionsCount == 0) if (Server.MainNetServer.ConnectionsCount == 0)

View File

@ -6,7 +6,7 @@
public int MaxPlayers { get; set; } = 16; public int MaxPlayers { get; set; } = 16;
public string ServerName { get; set; } = "GTACoop:R server"; public string ServerName { get; set; } = "GTACoop:R server";
public string WelcomeMessage { get; set; } = "Welcome on this 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 Allowlist { get; set; } = false;
public bool NpcsAllowed { get; set; } = true; public bool NpcsAllowed { get; set; } = true;
public bool ModsAllowed { get; set; } = false; public bool ModsAllowed { get; set; } = false;