337 lines
14 KiB
C#
Raw Normal View History

2022-06-04 18:09:42 +08:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lidgren.Network;
using RageCoop.Core;
2022-06-23 09:46:38 +08:00
using RageCoop.Core.Scripting;
using System.Reflection;
2022-06-04 18:09:42 +08:00
using System.Net;
namespace RageCoop.Server.Scripting
{
2022-07-01 13:54:18 +08:00
/// <summary>
///
/// </summary>
2022-07-02 11:23:12 +08:00
public class ServerEvents
2022-06-04 18:09:42 +08:00
{
2022-07-01 12:22:31 +08:00
private readonly Server Server;
2022-07-02 11:23:12 +08:00
internal ServerEvents(Server server)
2022-07-01 12:22:31 +08:00
{
Server = server;
}
2022-06-19 11:12:20 +08:00
#region INTERNAL
internal Dictionary<int, List<Action<CustomEventReceivedArgs>>> CustomEventHandlers = new();
2022-06-19 11:12:20 +08:00
#endregion
2022-07-01 13:54:18 +08:00
/// <summary>
/// Invoked when a chat message is received.
/// </summary>
public event EventHandler<ChatEventArgs> OnChatMessage;
2022-06-30 09:28:13 +08:00
/// <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.
/// </summary>
public event EventHandler<Client> OnPlayerConnected;
/// <summary>
/// Will be invoked after the client connected and all resources(if any) have been loaded.
/// </summary>
public event EventHandler<Client> OnPlayerReady;
2022-07-01 13:54:18 +08:00
/// <summary>
/// Invoked when a player disconnected, all method won't be effective in this scope.
/// </summary>
public event EventHandler<Client> OnPlayerDisconnected;
/// <summary>
/// Invoked everytime a player's main ped has been updated
/// </summary>
public event EventHandler<Client> OnPlayerUpdate;
internal void ClearHandlers()
2022-06-04 18:09:42 +08:00
{
OnChatMessage=null;
OnPlayerHandshake=null;
OnPlayerConnected=null;
OnPlayerReady=null;
OnPlayerDisconnected=null;
// OnCustomEventReceived=null;
OnCommandReceived=null;
OnPlayerUpdate=null;
}
#region INVOKE
2022-06-30 09:28:13 +08:00
internal void InvokePlayerHandshake(HandshakeEventArgs args)
{ OnPlayerHandshake?.Invoke(this, args); }
2022-07-01 12:22:31 +08:00
internal void InvokeOnCommandReceived(string cmdName, string[] cmdArgs, Client sender)
2022-06-30 09:28:13 +08:00
{
var args = new OnCommandEventArgs()
{
2022-07-01 12:22:31 +08:00
Name=cmdName,
Args=cmdArgs,
2022-06-30 09:28:13 +08:00
Sender=sender
};
OnCommandReceived?.Invoke(this, args);
if (args.Cancel)
{
return;
}
2022-07-01 12:22:31 +08:00
if (Server.Commands.Any(x => x.Key.Name == cmdName))
2022-06-30 09:28:13 +08:00
{
string[] argsWithoutCmd = cmdArgs.Skip(1).ToArray();
CommandContext ctx = new()
{
Client = sender,
Args = argsWithoutCmd
};
2022-07-01 12:22:31 +08:00
KeyValuePair<Command, Action<CommandContext>> command = Server.Commands.First(x => x.Key.Name == cmdName);
2022-06-30 09:28:13 +08:00
command.Value.Invoke(ctx);
}
else
{
2022-07-01 12:22:31 +08:00
Server.SendChatMessage("Server", "Command not found!", sender.Connection);
2022-06-30 09:28:13 +08:00
}
}
internal void InvokeOnChatMessage(Packets.ChatMessage p, Client sender)
{
OnChatMessage?.Invoke(this, new ChatEventArgs()
{
2022-06-21 18:13:30 +08:00
Sender=sender,
2022-06-04 18:09:42 +08:00
Message=p.Message
});
}
internal void InvokePlayerConnected(Client client)
{ OnPlayerConnected?.Invoke(this,client); }
internal void InvokePlayerReady(Client client)
{ OnPlayerReady?.Invoke(this, client); }
internal void InvokePlayerDisconnected(Client client)
{ OnPlayerDisconnected?.Invoke(this,client); }
2022-06-21 18:13:30 +08:00
internal void InvokeCustomEventReceived(Packets.CustomEvent p, Client sender)
{
var args = new CustomEventReceivedArgs() { Hash=p.Hash, Args=p.Args, Sender=sender };
List<Action<CustomEventReceivedArgs>> handlers;
if (CustomEventHandlers.TryGetValue(p.Hash, out handlers))
2022-06-21 18:13:30 +08:00
{
handlers.ForEach((x) => { x.Invoke(args); });
2022-06-21 18:13:30 +08:00
}
}
internal void InvokePlayerUpdate(Client client)
{
OnPlayerUpdate?.Invoke(this, client);
}
#endregion
}
2022-07-01 13:54:18 +08:00
/// <summary>
/// An class that can be used to interact with RageCoop server.
/// </summary>
public class API
{
internal readonly Server Server;
internal API(Server server)
{
Server=server;
2022-07-01 12:22:31 +08:00
Events=new(server);
}
2022-07-01 13:54:18 +08:00
/// <summary>
/// Server side events
/// </summary>
2022-07-02 11:23:12 +08:00
public readonly ServerEvents Events;
2022-06-04 18:09:42 +08:00
/// <summary>
/// All synchronized entities on this server.
2022-06-04 18:09:42 +08:00
/// </summary>
public ServerEntities Entities { get { return Server.Entities; } }
2022-07-02 11:23:12 +08:00
#region FUNCTIONS
2022-06-04 18:09:42 +08:00
/// <summary>
/// Get a list of all Clients
/// </summary>
/// <returns>All clients as a dictionary indexed by NetID</returns>
2022-07-09 19:32:11 +08:00
public Dictionary<string, Client> GetAllClients()
2022-06-04 18:09:42 +08:00
{
2022-07-09 19:32:11 +08:00
return new(Server.ClientsByName);
2022-06-04 18:09:42 +08:00
}
/// <summary>
/// Get the client by its username
/// </summary>
/// <param name="username">The username to search for (non case-sensitive)</param>
/// <returns>The Client from this user or null</returns>
public Client GetClientByUsername(string username)
2022-06-04 18:09:42 +08:00
{
2022-06-21 18:13:30 +08:00
return Server.Clients.Values.FirstOrDefault(x => x.Username.ToLower() == username.ToLower());
2022-06-04 18:09:42 +08:00
}
/// <summary>
2022-07-01 13:54:18 +08:00
/// Send a chat message to all players, use <see cref="Client.SendChatMessage(string, string)"/> to send to an individual client.
2022-06-04 18:09:42 +08:00
/// </summary>
2022-07-01 13:54:18 +08:00
/// <param name="targets">The clients to send message, leave it null to send to all clients</param>
2022-06-04 18:09:42 +08:00
/// <param name="message">The chat message</param>
/// <param name="username">The username which send this message (default = "Server")</param>
public void SendChatMessage(string message, List<Client> targets = null, string username = "Server")
2022-06-04 18:09:42 +08:00
{
try
{
if (Server.MainNetServer.ConnectionsCount == 0)
{
return;
}
2022-06-21 18:13:30 +08:00
targets ??= new(Server.Clients.Values);
foreach(Client client in targets)
{
Server.SendChatMessage(username, message, client.Connection);
}
}
catch (Exception e)
{
Server.Logger?.Error($">> {e.Message} <<>> {e.Source ?? string.Empty} <<>> {e.StackTrace ?? string.Empty} <<");
2022-06-21 18:13:30 +08:00
}
}
2022-06-04 18:09:42 +08:00
/// <summary>
/// Send CleanUpWorld to all players to delete all objects created by the server
/// </summary>
/// <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>
2022-07-02 18:30:16 +08:00
/// <param name="callback">A callback to invoke when the command received.</param>
public void RegisterCommand(string name, string usage, short argsLength, Action<CommandContext> callback)
2022-06-04 18:09:42 +08:00
{
Server.RegisterCommand(name, usage, argsLength, callback);
}
/// <summary>
/// Register a new command chat command (Example: "/test")
/// </summary>
/// <param name="name">The name of the command (Example: "test" for "/test")</param>
2022-07-02 18:30:16 +08:00
/// <param name="callback">A callback to invoke when the command received.</param>
public void RegisterCommand(string name, Action<CommandContext> callback)
2022-06-04 18:09:42 +08:00
{
Server.RegisterCommand(name, callback);
}
/// <summary>
/// Register all commands in a static class
2022-06-04 18:09:42 +08:00
/// </summary>
/// <typeparam name="T">Your static class with commands</typeparam>
public void RegisterCommands<T>()
2022-06-04 18:09:42 +08:00
{
Server.RegisterCommands<T>();
}
/// <summary>
/// Register all commands inside an class instance
/// </summary>
/// <param name="obj">The instance of type containing the commands</param>
public void RegisterCommands(object obj)
{
IEnumerable<MethodInfo> commands = obj.GetType().GetMethods().Where(method => method.GetCustomAttributes(typeof(Command), false).Any());
foreach (MethodInfo method in commands)
{
Command attribute = method.GetCustomAttribute<Command>(true);
RegisterCommand(attribute.Name, attribute.Usage, attribute.ArgsLength,
(ctx) => { method.Invoke(obj, new object[] { ctx }); });
}
}
/// <summary>
/// Send native call specified clients.
/// </summary>
/// <param name="hash"></param>
/// <param name="args"></param>
/// /// <param name="clients">Clients to send, null for all clients</param>
2022-07-04 21:35:01 +08:00
public void SendNativeCall(List<Client> clients , GTA.Native.Hash hash, params object[] args)
{
var argsList = new List<object>(args);
argsList.InsertRange(0, new object[] { (byte)TypeCode.Empty, (ulong)hash });
2022-07-05 11:18:26 +08:00
SendCustomEventQueued(clients, CustomEvents.NativeCall, argsList.ToArray());
}
2022-06-21 18:13:30 +08:00
/// <summary>
/// Send an event and data to the specified clients. Use <see cref="Client.SendCustomEvent(int,object[])"/> if you want to send event to individual client.
2022-06-21 18:13:30 +08:00
/// </summary>
2022-07-01 13:54:18 +08:00
/// <param name="eventHash">An unique identifier of the event, you can use <see cref="CustomEvents.Hash(string)"/> to get it from a string</param>
2022-07-02 18:30:16 +08:00
/// <param name="args">The objects conataing your data, see <see cref="Scripting.CustomEventReceivedArgs.Args"/> for supported types.</param>
/// <param name="targets">The target clients to send. Leave it null to send to all clients</param>
public void SendCustomEvent(List<Client> targets, int eventHash, params object[] args)
{
2022-06-19 11:12:20 +08:00
targets ??= new(Server.Clients.Values);
var p = new Packets.CustomEvent()
{
2022-06-19 11:12:20 +08:00
Args=args,
Hash=eventHash
};
foreach (var c in targets)
{
Server.Send(p, c, ConnectionChannel.Event, NetDeliveryMethod.ReliableOrdered);
}
2022-06-04 18:09:42 +08:00
}
2022-07-05 11:18:26 +08:00
/// <summary>
/// Send a CustomEvent that'll be queued at client side and invoked from script thread
/// </summary>
/// <param name="targets"></param>
/// <param name="eventHash"></param>
/// <param name="args"></param>
public void SendCustomEventQueued(List<Client> targets, int eventHash, params object[] args)
{
targets ??= new(Server.Clients.Values);
var p = new Packets.CustomEvent(null,true)
{
Args=args,
Hash=eventHash
};
foreach (var c in targets)
{
Server.Send(p, c, ConnectionChannel.Event, NetDeliveryMethod.ReliableOrdered);
}
}
2022-06-22 14:18:20 +08:00
/// <summary>
/// Register an handler to the specifed event hash, one event can have multiple handlers.
/// </summary>
/// <param name="hash">An unique identifier of the event, you can hash your event name with <see cref="Core.Scripting.CustomEvents.Hash(string)"/></param>
2022-07-02 18:30:16 +08:00
/// <param name="handler">An handler to be invoked when the event is received from the server.</param>
public void RegisterCustomEventHandler(int hash,Action<CustomEventReceivedArgs> handler)
2022-06-21 18:13:30 +08:00
{
2022-06-22 14:18:20 +08:00
List<Action<CustomEventReceivedArgs>> handlers;
lock (Events.CustomEventHandlers)
2022-06-21 18:13:30 +08:00
{
if (!Events.CustomEventHandlers.TryGetValue(hash,out handlers))
2022-06-21 18:13:30 +08:00
{
Events.CustomEventHandlers.Add(hash, handlers = new List<Action<CustomEventReceivedArgs>>());
2022-06-21 18:13:30 +08:00
}
handlers.Add(handler);
}
}
2022-07-01 13:54:18 +08:00
/// <summary>
/// Register an event handler for specified event name.
/// </summary>
/// <param name="name">This value will be hashed to an int to reduce overhead</param>
/// <param name="handler">The handler to be invoked when the event is received</param>
public void RegisterCustomEventHandler(string name, Action<CustomEventReceivedArgs> handler)
2022-06-23 09:46:38 +08:00
{
RegisterCustomEventHandler(CustomEvents.Hash(name), handler);
}
2022-07-01 13:54:18 +08:00
/// <summary>
2022-07-02 11:23:12 +08:00
/// Get a <see cref="Core.Logger"/> that the server is currently using, you should use <see cref="ServerResource.Logger"/> to display resource-specific information.
2022-07-01 13:54:18 +08:00
/// </summary>
public Logger Logger { get { return Server.Logger; } }
2022-06-04 18:09:42 +08:00
#endregion
}
}