RAGECOOP-V/Server/ServerScript.cs

505 lines
17 KiB
C#
Raw Normal View History

2021-08-15 07:54:25 +02:00
using System;
using System.Collections;
2021-08-15 07:54:25 +02:00
using System.Collections.Generic;
2021-08-18 12:47:36 +02:00
using System.ComponentModel;
2021-08-22 13:59:15 +02:00
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
2021-08-15 07:54:25 +02:00
using Lidgren.Network;
2021-08-14 21:49:23 +02:00
namespace CoopServer
{
public abstract class ServerScript
{
public API API { get; } = new();
}
internal class Resource
{
public bool ReadyToStop = false;
private static Thread _mainThread;
private static Queue _actionQueue;
private static ServerScript _script;
public Resource(ServerScript script)
{
_actionQueue = Queue.Synchronized(new Queue());
_mainThread = new(ThreadLoop) { IsBackground = true };
_mainThread.Start();
lock (_actionQueue.SyncRoot)
{
2021-11-23 23:02:41 +01:00
_actionQueue.Enqueue(() =>
{
_script = script;
_script.API.InvokeStart();
});
}
}
private void ThreadLoop()
{
while (!Program.ReadyToStop)
{
Queue localQueue;
lock (_actionQueue.SyncRoot)
{
localQueue = new(_actionQueue);
_actionQueue.Clear();
}
while (localQueue.Count > 0)
{
(localQueue.Dequeue() as Action)?.Invoke();
}
2021-11-23 23:02:41 +01:00
// 16 milliseconds to sleep to reduce CPU usage
Thread.Sleep(1000 / 60);
}
_script.API.InvokeStop();
ReadyToStop = true;
}
2022-01-01 03:07:18 +01:00
public bool InvokeModPacketReceived(long from, long target, string modName, byte customID, byte[] bytes)
{
2022-01-01 03:07:18 +01:00
Task<bool> task = new(() => _script.API.InvokeModPacketReceived(from, target, modName, customID, bytes));
task.Start();
task.Wait(5000);
return task.Result;
}
public void InvokePlayerHandshake(Client client)
{
lock (_actionQueue.SyncRoot)
{
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerHandshake(client)));
}
}
public void InvokePlayerConnected(Client client)
{
lock (_actionQueue.SyncRoot)
{
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerConnected(client)));
}
}
public void InvokePlayerDisconnected(Client client)
{
lock (_actionQueue.SyncRoot)
{
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerDisconnected(client)));
}
}
public bool InvokeChatMessage(string username, string message)
{
Task<bool> task = new(() => _script.API.InvokeChatMessage(username, message));
task.Start();
task.Wait(5000);
return task.Result;
}
public void InvokePlayerPositionUpdate(string username)
{
lock (_actionQueue.SyncRoot)
{
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerPositionUpdate(username)));
}
}
public void InvokePlayerUpdate(Client client)
{
lock (_actionQueue.SyncRoot)
{
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerUpdate(client)));
}
}
public void InvokePlayerHealthUpdate(string username)
{
lock (_actionQueue.SyncRoot)
{
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerHealthUpdate(username)));
}
}
public void InvokePlayerPedHandleUpdate(string username)
{
lock (_actionQueue.SyncRoot)
{
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerPedHandleUpdate(username)));
}
}
public void InvokePlayerVehicleHandleUpdate(string username)
{
lock (_actionQueue.SyncRoot)
{
_actionQueue.Enqueue(new Action(() => _script.API.InvokePlayerVehicleHandleUpdate(username)));
}
}
public void InvokeTick(long tick)
{
lock (_actionQueue.SyncRoot)
{
_actionQueue.Enqueue(new Action(() => _script.API.InvokeTick(tick)));
}
}
}
2021-08-18 12:47:36 +02:00
public class API
{
#region DELEGATES
public delegate void EmptyEvent();
public delegate void OnTickEvent(long tick);
2021-08-18 12:47:36 +02:00
public delegate void ChatEvent(string username, string message, CancelEventArgs cancel);
2021-08-26 17:01:32 +02:00
public delegate void PlayerEvent(Client client);
2022-01-01 03:07:18 +01:00
public delegate void ModEvent(long from, long target, string modName, byte customID, byte[] bytes, CancelEventArgs args);
2021-08-18 12:47:36 +02:00
#endregion
#region EVENTS
2021-12-16 14:52:48 +01:00
/// <summary>
/// Called every tick
/// </summary>
public event OnTickEvent OnTick;
/// <summary>
2021-12-16 14:52:48 +01:00
/// Called when the server has started
/// </summary>
public event EmptyEvent OnStart;
2021-12-16 14:52:48 +01:00
/// <summary>
/// Called when the server has stopped
/// </summary>
public event EmptyEvent OnStop;
2021-12-16 14:52:48 +01:00
/// <summary>
/// Called when the server receives a new chat message for players
/// </summary>
2021-08-18 12:47:36 +02:00
public event ChatEvent OnChatMessage;
2021-12-16 14:52:48 +01:00
/// <summary>
/// Called when the server receives a new incoming connection
/// </summary>
public event PlayerEvent OnPlayerHandshake;
2021-12-16 14:52:48 +01:00
/// <summary>
/// Called when a new player has successfully connected
/// </summary>
2021-08-18 12:47:36 +02:00
public event PlayerEvent OnPlayerConnected;
2021-12-16 14:52:48 +01:00
/// <summary>
/// Called when a new player has successfully disconnected
/// </summary>
2021-08-18 12:47:36 +02:00
public event PlayerEvent OnPlayerDisconnected;
2021-12-16 14:52:48 +01:00
/// <summary>
/// Called when a new player sends data like health
/// </summary>
public event PlayerEvent OnPlayerUpdate;
2021-12-16 14:52:48 +01:00
/// <summary>
/// Called when a player has a new health value
/// </summary>
public event PlayerEvent OnPlayerHealthUpdate;
2021-12-16 14:52:48 +01:00
/// <summary>
/// Called when a player has a new position
/// </summary>
2021-08-20 17:28:13 +02:00
public event PlayerEvent OnPlayerPositionUpdate;
2021-12-16 14:52:48 +01:00
/// <summary>
/// Called when a player has a new position
/// </summary>
public event PlayerEvent OnPlayerPedHandleUpdate;
/// <summary>
/// Called when a player has a new position
/// </summary>
public event PlayerEvent OnPlayerVehicleHandleUpdate;
/// <summary>
2021-12-16 14:52:48 +01:00
/// Called when a player sends a packet from another modification
/// </summary>
2021-09-29 14:34:22 +02:00
public event ModEvent OnModPacketReceived;
2021-08-18 12:47:36 +02:00
internal void InvokeTick(long tick)
{
OnTick?.Invoke(tick);
}
2021-08-18 12:47:36 +02:00
internal void InvokeStart()
{
OnStart?.Invoke();
}
internal void InvokeStop()
{
OnStop?.Invoke();
}
internal void InvokePlayerHandshake(Client client)
{
OnPlayerHandshake?.Invoke(client);
2021-08-18 12:47:36 +02:00
}
2021-08-15 07:54:25 +02:00
2021-08-26 17:01:32 +02:00
internal void InvokePlayerConnected(Client client)
2021-08-15 07:54:25 +02:00
{
2021-08-26 17:01:32 +02:00
OnPlayerConnected?.Invoke(client);
2021-08-15 07:54:25 +02:00
}
2021-08-26 17:01:32 +02:00
internal void InvokePlayerDisconnected(Client client)
2021-08-15 07:54:25 +02:00
{
2021-08-26 17:01:32 +02:00
OnPlayerDisconnected?.Invoke(client);
2021-08-18 11:47:59 +02:00
}
2021-08-15 07:54:25 +02:00
internal void InvokePlayerUpdate(Client client)
{
OnPlayerUpdate?.Invoke(client);
}
internal void InvokePlayerHealthUpdate(string username)
{
OnPlayerHealthUpdate?.Invoke(Server.Clients.FirstOrDefault(x => x.Player.Username == username));
}
2021-08-18 12:47:36 +02:00
internal bool InvokeChatMessage(string username, string message)
2021-08-18 11:47:59 +02:00
{
CancelEventArgs args = new(false);
2021-08-18 12:47:36 +02:00
OnChatMessage?.Invoke(username, message, args);
return args.Cancel;
2021-08-15 07:54:25 +02:00
}
2021-08-20 17:28:13 +02:00
internal void InvokePlayerPositionUpdate(string username)
{
OnPlayerPositionUpdate?.Invoke(Server.Clients.FirstOrDefault(x => x.Player.Username == username));
}
internal void InvokePlayerPedHandleUpdate(string username)
{
OnPlayerPedHandleUpdate?.Invoke(Server.Clients.FirstOrDefault(x => x.Player.Username == username));
}
internal void InvokePlayerVehicleHandleUpdate(string username)
2021-08-20 17:28:13 +02:00
{
OnPlayerVehicleHandleUpdate?.Invoke(Server.Clients.FirstOrDefault(x => x.Player.Username == username));
2021-08-20 17:28:13 +02:00
}
2021-09-29 14:34:22 +02:00
2022-01-01 03:07:18 +01:00
internal bool InvokeModPacketReceived(long from, long target, string modName, byte customID, byte[] bytes)
2021-09-29 14:34:22 +02:00
{
CancelEventArgs args = new(false);
2022-01-01 03:07:18 +01:00
OnModPacketReceived?.Invoke(from, target, modName, customID, bytes, args);
2021-09-29 14:34:22 +02:00
return args.Cancel;
}
2021-08-18 12:47:36 +02:00
#endregion
2021-08-15 07:54:25 +02:00
2021-08-18 12:47:36 +02:00
#region FUNCTIONS
2021-12-16 14:52:48 +01:00
/// <summary>
/// Send a mod packet to all players
/// </summary>
2022-01-01 03:07:18 +01:00
/// <param name="modName">The name of the modification that will receive the data</param>
2021-12-16 14:52:48 +01:00
/// <param name="customID">The ID to check what this data is</param>
/// <param name="bytes">The serialized data</param>
/// <param name="netHandleList">The list of connections (players) that will receive the data</param>
2022-01-01 03:07:18 +01:00
public static void SendModPacketToAll(string modName, byte customID, byte[] bytes, List<long> netHandleList = null)
2021-11-23 23:17:47 +01:00
{
try
2021-11-23 23:17:47 +01:00
{
2021-12-16 14:52:48 +01:00
List<NetConnection> connections = netHandleList == null
? Server.MainNetServer.Connections
: Server.MainNetServer.Connections.FindAll(c => netHandleList.Contains(c.RemoteUniqueIdentifier));
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
2022-01-01 03:07:18 +01:00
new Packets.Mod()
{
NetHandle = 0,
Target = 0,
2022-01-01 03:07:18 +01:00
Name = modName,
CustomPacketID = customID,
Bytes = bytes
}.PacketToNetOutGoingMessage(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, connections, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Mod);
Server.MainNetServer.FlushSendQueue();
}
catch (Exception e)
{
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
}
2021-11-23 23:17:47 +01:00
}
2021-12-16 14:52:48 +01:00
/// <summary>
/// Send a native call (Function.Call) to all players.
/// Keys = int, float, bool, string and lvector3
2021-12-16 14:52:48 +01:00
/// </summary>
/// <param name="hash">The hash (Example: 0x25223CA6B4D20B7F = GET_CLOCK_HOURS)</param>
/// <param name="args">The arguments (Example: string = int, object = 5)</param>
public static void SendNativeCallToAll(ulong hash, params object[] args)
2021-08-15 07:54:25 +02:00
{
try
2021-08-20 17:28:13 +02:00
{
if (Server.MainNetServer.ConnectionsCount == 0)
{
return;
}
2021-08-18 11:47:59 +02:00
if (args != null && args.Length == 0)
{
Logging.Error($"[ServerScript->SendNativeCallToAll(ulong hash, params object[] args)]: args is not null!");
return;
}
2021-08-15 07:54:25 +02:00
2022-01-01 03:07:18 +01:00
Packets.NativeCall packet = new()
{
Hash = hash,
Args = new List<object>(args) ?? new List<object>()
};
2021-08-20 17:28:13 +02:00
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
packet.PacketToNetOutGoingMessage(outgoingMessage);
Server.MainNetServer.SendMessage(outgoingMessage, Server.MainNetServer.Connections, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Native);
}
catch (Exception e)
{
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
}
2021-08-18 11:47:59 +02:00
}
2021-08-15 07:54:25 +02:00
2021-12-16 14:52:48 +01:00
/// <summary>
/// Get all connections as a list of NetHandle(long)
/// </summary>
/// <returns></returns>
2021-08-20 17:28:13 +02:00
public static List<long> GetAllConnections()
{
List<long> result = new();
Server.MainNetServer.Connections.ForEach(x => result.Add(x.RemoteUniqueIdentifier));
return result;
}
2021-08-26 17:01:32 +02:00
public static int GetAllClientsCount()
2021-08-18 11:47:59 +02:00
{
2021-08-26 17:01:32 +02:00
return Server.Clients.Count;
2021-08-18 11:47:59 +02:00
}
2021-08-15 07:54:25 +02:00
2021-08-26 17:01:32 +02:00
public static List<Client> GetAllClients()
2021-08-22 13:59:15 +02:00
{
2021-08-26 17:01:32 +02:00
return Server.Clients;
2021-08-22 13:59:15 +02:00
}
2021-08-26 17:01:32 +02:00
public static Client GetClientByUsername(string username)
2021-08-18 11:47:59 +02:00
{
return Server.Clients.FirstOrDefault(x => x.Player.Username.ToLower() == username.ToLower());
2021-08-15 07:54:25 +02:00
}
2021-12-16 14:52:48 +01:00
/// <summary>
/// Send a chat message to all players
/// </summary>
/// <param name="message">The chat message</param>
/// <param name="username">The username which send this message (default = "Server")</param>
/// <param name="netHandleList">The list of connections (players) who received this chat message</param>
public static void SendChatMessageToAll(string message, string username = "Server", List<long> netHandleList = null)
2021-08-14 21:49:23 +02:00
{
try
2021-08-15 07:54:25 +02:00
{
if (Server.MainNetServer.ConnectionsCount == 0)
{
return;
}
2021-08-15 07:54:25 +02:00
2021-12-16 14:52:48 +01:00
List<NetConnection> connections = netHandleList == null
? Server.MainNetServer.Connections
: Server.MainNetServer.Connections.FindAll(c => netHandleList.Contains(c.RemoteUniqueIdentifier));
Server.SendChatMessage(username, message, connections);
}
catch (Exception e)
2021-08-20 17:28:13 +02:00
{
Logging.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
}
2021-08-14 21:49:23 +02:00
}
/// <summary>
///
/// </summary>
public static void SendCleanUpWorldToAll(List<long> netHandleList = null)
{
if (Server.MainNetServer.ConnectionsCount == 0)
{
return;
}
List<NetConnection> connections = netHandleList == null
? Server.MainNetServer.Connections
: Server.MainNetServer.Connections.FindAll(c => netHandleList.Contains(c.RemoteUniqueIdentifier));
NetOutgoingMessage outgoingMessage = Server.MainNetServer.CreateMessage();
2022-01-01 03:07:18 +01:00
outgoingMessage.Write((byte)PacketTypes.CleanUpWorld);
Server.MainNetServer.SendMessage(outgoingMessage, connections, NetDeliveryMethod.ReliableOrdered, (byte)ConnectionChannel.Default);
}
2021-12-16 14:52:48 +01:00
/// <summary>
/// Register a new command chat command (Example: "/test")
/// </summary>
/// <param name="name">The name of the command (Example: "test" for "/test")</param>
/// <param name="usage">How to use this message (argsLength required!)</param>
/// <param name="argsLength">The length of args (Example: "/message USERNAME MESSAGE" = 2) (usage required!)</param>
/// <param name="callback">Create a new function!</param>
public static void RegisterCommand(string name, string usage, short argsLength, Action<CommandContext> callback)
{
Server.RegisterCommand(name, usage, argsLength, callback);
}
2021-12-16 14:52:48 +01:00
/// <summary>
/// Register a new command chat command (Example: "/test")
/// </summary>
/// <param name="name">The name of the command (Example: "test" for "/test")</param>
/// <param name="callback">Create a new function!</param>
2021-08-18 11:47:59 +02:00
public static void RegisterCommand(string name, Action<CommandContext> callback)
2021-08-14 21:49:23 +02:00
{
2021-08-18 11:47:59 +02:00
Server.RegisterCommand(name, callback);
}
2021-08-14 21:49:23 +02:00
2021-12-16 14:52:48 +01:00
/// <summary>
/// Register a class of commands
/// </summary>
/// <typeparam name="T">The name of your class with functions</typeparam>
2021-08-18 11:47:59 +02:00
public static void RegisterCommands<T>()
{
Server.RegisterCommands<T>();
2021-08-14 21:49:23 +02:00
}
2021-08-18 12:47:36 +02:00
#endregion
2021-08-18 11:47:59 +02:00
}
[AttributeUsage(AttributeTargets.Method)]
public class Command : Attribute
2021-08-18 11:47:59 +02:00
{
/// <summary>
/// Sets name of the command
/// </summary>
public string Name { get; set; }
2021-12-16 14:52:48 +01:00
/// <summary>
/// Set the Usage (Example: "Please use "/help"". ArgsLength required!)
/// </summary>
public string Usage { get; set; }
2021-12-16 14:52:48 +01:00
/// <summary>
/// Set the length of arguments (Example: 2 for "/message USERNAME MESSAGE". Usage required!)
/// </summary>
public short ArgsLength { get; set; }
public Command(string name)
2021-08-15 07:54:25 +02:00
{
2021-08-18 11:47:59 +02:00
Name = name;
2021-08-15 07:54:25 +02:00
}
2021-08-14 21:49:23 +02:00
}
2021-08-18 11:47:59 +02:00
public class CommandContext
{
/// <summary>
/// Gets the client which executed the command
/// </summary>
2021-08-26 17:01:32 +02:00
public Client Client { get; internal set; }
2021-08-18 11:47:59 +02:00
/// <summary>
2021-12-16 14:52:48 +01:00
/// Gets the arguments (Example: "/message USERNAME MESSAGE", Args[0] for USERNAME)
2021-08-18 11:47:59 +02:00
/// </summary>
public string[] Args { get; internal set; }
}
2021-08-14 21:49:23 +02:00
}