Skip to content

Instantly share code, notes, and snippets.

@TheDutchDev
Last active November 5, 2019 21:39
Show Gist options
  • Save TheDutchDev/de34e410f615adebbd6b6d27e8f60167 to your computer and use it in GitHub Desktop.
Save TheDutchDev/de34e410f615adebbd6b6d27e8f60167 to your computer and use it in GitHub Desktop.
Rage RPC in C#, server-side only for now.
// Small example, fetches the getInvincible from the client to see if the client is invincible or not.
// SERVER-SIDE
var response = await client.RequestDataFromClient( "IsPlayerInvincible" );
if( response == null )
Console.WriteLine( "Couldnt fetch" );
else
Console.WriteLine( $"Player invincible: { response[ 0 ] }" );
// CLIENT-SIDE(JS)
mp.events.add( "IsPlayerInvincible", ( reqId ) => {
let invincible = mp.game.invoke( '0xB721981B2B939E07', localPlayer );
mp.events.callRemote( "OnPlayerCompleteRPCTask", reqId, invincible );
} );
public static class RageRPC
{
private static readonly int TimeoutMs = 2000; // time in milliseconds before a request is timedout
private static uint RequestId = 0;
public static Dictionary<uint, TaskCompletionSource<Response>> RequestedTasks { get; set; } = new Dictionary<uint, TaskCompletionSource<Response>>( );
/// <summary>
/// Calls a client-side event and waits for a return call, then returns the data. This call must be awaited.
/// </summary>
/// <param name="client">The client to submit the event to</param>
/// <param name="eventName">The event name to be called on the client</param>
/// <param name="data">The data to be submitted to the client-side event</param>
/// <returns></returns>
public static async Task<dynamic> RequestDataFromClient( this Client client, string eventName, params dynamic[] data )
{
try
{
if( RequestId > 1000000 ) // reset the ID after 1 million requests
RequestId = 0;
TaskCompletionSource<Response> tcs = new TaskCompletionSource<Response>( );
var reqId = RequestId;
RequestId++;
// store requested task so we can complete it later on when the data is recieved
RequestedTasks.Add( reqId, tcs );
// call the client-side event with request ID and additional optional data
client.TriggerEvent( eventName, reqId, data );
// wait for the task to complete or timeout
var completed = await Task.WhenAny( tcs.Task, DelayResult( default( Response ), TimeSpan.FromMilliseconds( TimeoutMs ) ) );
// remove it from the list after we're done with it.
RequestedTasks.Remove( reqId );
// if the task timedout
if( completed != tcs.Task )
return null;
// return data
var response = await completed;
return response.Data;
}
catch( Exception ex )
{
throw ex;
return null;
}
}
public static async Task<T> DelayResult<T>( T result, TimeSpan delay )
{
try
{
await Task.Delay( delay );
return result;
}
catch( Exception ex )
{
throw ex;
return default( T );
}
}
}
public class RageRPCEventHandler : Script
{
[RemoteEvent( "OnPlayerCompleteRPCTask" )]
public void OnPlayerCompleteRPCTask( Client client, params dynamic[] data )
{
try
{
// if the request couldn't be found in the list with existing requests
if( !RageRPC.RequestedTasks.ContainsKey( Convert.ToUInt32( data[ 0 ] ) ) )
throw new Exception( "The requested task could not be found." );
// get the task and set it's result value to the data that was recieved.
var currentTask = RageRPC.RequestedTasks.FirstOrDefault( rt => rt.Key == Convert.ToUInt32( data[ 0 ] ) );
currentTask.Value.TrySetResult( new Response { Id = data[ 0 ], Data = data.Skip( 1 ).ToArray( ) } );
}
catch( Exception ex )
{
throw ex;
}
}
}
public class Response
{
public int Id { get; set; }
public dynamic[] Data { get; set; }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment