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 ;
2022-06-24 16:49:59 +08:00
using System.Reflection ;
2022-07-19 17:15:53 +08:00
using System.IO ;
2022-06-04 18:09:42 +08:00
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
2022-06-23 14:10:16 +08:00
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>
2022-06-23 14:10:16 +08:00
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>
2022-06-23 14:10:16 +08:00
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>
2022-06-23 14:10:16 +08:00
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
{
2022-06-23 14:10:16 +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-22 19:43:48 +08:00
Server . SendChatMessage ( "Server" , "Command not found!" , sender ) ;
2022-06-30 09:28:13 +08:00
}
}
2022-07-29 01:31:34 +08:00
internal void InvokeOnChatMessage ( string msg , Client sender )
2022-06-23 14:10:16 +08:00
{
OnChatMessage ? . Invoke ( this , new ChatEventArgs ( )
2022-06-11 18:41:10 +08:00
{
2022-06-21 18:13:30 +08:00
Sender = sender ,
2022-07-29 01:31:34 +08:00
Message = msg
2022-06-23 14:10:16 +08:00
} ) ;
}
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
2022-06-23 14:10:16 +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
{
2022-06-23 14:10:16 +08:00
handlers . ForEach ( ( x ) = > { x . Invoke ( args ) ; } ) ;
2022-06-21 18:13:30 +08:00
}
2022-06-23 14:10:16 +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>
2022-06-23 14:10:16 +08:00
public class API
{
2022-07-04 09:41:00 +08:00
internal readonly Server Server ;
2022-07-19 17:15:53 +08:00
internal readonly Dictionary < string , Func < Stream > > RegisteredFiles = new Dictionary < string , Func < System . IO . Stream > > ( ) ;
2022-06-23 14:10:16 +08:00
internal API ( Server server )
{
Server = server ;
2022-07-01 12:22:31 +08:00
Events = new ( server ) ;
2022-07-19 17:15:53 +08:00
Server . RequestHandlers . Add ( PacketType . FileTransferRequest , ( data , client ) = >
{
var p = new Packets . FileTransferRequest ( ) ;
p . Unpack ( data ) ;
var id = Server . NewFileID ( ) ;
if ( RegisteredFiles . TryGetValue ( p . Name , out var s ) )
{
Task . Run ( ( ) = >
{
Server . SendFile ( s ( ) , p . Name , client , id ) ;
} ) ;
return new Packets . FileTransferResponse ( )
{
ID = id ,
Response = FileResponse . Loaded
} ;
}
return new Packets . FileTransferResponse ( )
{
ID = id ,
Response = FileResponse . LoadFailed
} ;
} ) ;
2022-06-23 14:10:16 +08:00
}
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-07-01 19:02:38 +08:00
2022-06-04 18:09:42 +08:00
/// <summary>
2022-07-01 19:02:38 +08:00
/// All synchronized entities on this server.
2022-06-04 18:09:42 +08:00
/// </summary>
2022-07-01 19:02:38 +08:00
public ServerEntities Entities { get { return Server . Entities ; } }
2022-07-02 11:23:12 +08:00
2022-07-01 19:02:38 +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>
2022-06-23 14:10:16 +08:00
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>
2022-06-23 14:10:16 +08:00
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 )
{
2022-07-22 19:43:48 +08:00
Server . SendChatMessage ( username , message , client ) ;
2022-06-21 18:13:30 +08:00
}
}
catch ( Exception e )
{
2022-06-23 14:10:16 +08:00
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>
2022-07-19 17:15:53 +08:00
/// Register a file to be shared with clients
2022-06-04 18:09:42 +08:00
/// </summary>
2022-07-19 17:15:53 +08:00
/// <param name="name">name of this file</param>
/// <param name="path">path to this file</param>
public void RegisterSharedFile ( string name , string path )
{
RegisteredFiles . Add ( name , ( ) = > { return File . OpenRead ( path ) ; } ) ;
}
/// <summary>
/// Register a file to be shared with clients
/// </summary>
/// <param name="name">name of this file</param>
/// <param name="file"></param>
public void RegisterSharedFile ( string name , ResourceFile file )
{
RegisteredFiles . Add ( name , file . GetStream ) ;
}
2022-06-04 18:09:42 +08: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>
2022-07-02 18:30:16 +08:00
/// <param name="callback">A callback to invoke when the command received.</param>
2022-06-23 14:10:16 +08:00
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>
2022-06-23 14:10:16 +08:00
public void RegisterCommand ( string name , Action < CommandContext > callback )
2022-06-04 18:09:42 +08:00
{
Server . RegisterCommand ( name , callback ) ;
}
/// <summary>
2022-06-24 16:49:59 +08:00
/// Register all commands in a static class
2022-06-04 18:09:42 +08:00
/// </summary>
2022-06-24 16:49:59 +08:00
/// <typeparam name="T">Your static class with commands</typeparam>
2022-06-23 14:10:16 +08:00
public void RegisterCommands < T > ( )
2022-06-04 18:09:42 +08:00
{
Server . RegisterCommands < T > ( ) ;
}
2022-06-24 16:49:59 +08:00
/// <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 } ) ; } ) ;
}
}
2022-07-04 09:41:00 +08:00
/// <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 )
2022-07-04 09:41:00 +08:00
{
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-07-04 09:41:00 +08:00
}
2022-07-05 10:52:22 +08:00
2022-06-21 18:13:30 +08:00
/// <summary>
2022-07-06 01:03:18 +08:00
/// 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>
2022-06-23 14:10:16 +08:00
/// <param name="targets">The target clients to send. Leave it null to send to all clients</param>
2022-07-05 10:52:22 +08:00
public void SendCustomEvent ( List < Client > targets , int eventHash , params object [ ] args )
2022-06-12 15:39:32 +08:00
{
2022-07-05 10:52:22 +08:00
2022-06-19 11:12:20 +08:00
targets ? ? = new ( Server . Clients . Values ) ;
var p = new Packets . CustomEvent ( )
2022-06-12 15:39:32 +08:00
{
2022-06-19 11:12:20 +08:00
Args = args ,
Hash = eventHash
} ;
2022-07-05 10:52:22 +08:00
foreach ( var c in targets )
2022-06-12 15:39:32 +08:00
{
2022-07-05 10:52:22 +08:00
Server . Send ( p , c , ConnectionChannel . Event , NetDeliveryMethod . ReliableOrdered ) ;
2022-06-12 15:39:32 +08:00
}
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>
2022-07-11 10:08:21 +08:00
/// <param name="hash">An unique identifier of the event, you can hash your event name with <see cref="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>
2022-06-23 14:10:16 +08:00
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 ;
2022-06-23 14:10:16 +08:00
lock ( Events . CustomEventHandlers )
2022-06-21 18:13:30 +08:00
{
2022-06-23 14:10:16 +08:00
if ( ! Events . CustomEventHandlers . TryGetValue ( hash , out handlers ) )
2022-06-21 18:13:30 +08:00
{
2022-06-23 14:10:16 +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>
2022-06-23 14:10:16 +08:00
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-07-10 16:13:08 +08:00
/// <summary>
/// Gets or sets the client that is resposible for synchronizing time and weather
/// </summary>
public Client Host
{
get { return Server . _hostClient ; }
set {
if ( Server . _hostClient ! = value )
{
Server . _hostClient ? . SendCustomEvent ( CustomEvents . IsHost , false ) ;
value . SendCustomEvent ( CustomEvents . IsHost , true ) ;
Server . _hostClient = value ;
}
}
}
2022-06-04 18:09:42 +08:00
#endregion
}
}