74 lines
2.6 KiB
TypeScript
74 lines
2.6 KiB
TypeScript
/**
|
|
* Server-side RPC — replaces RageMP's mp.events.addProc / mp.events.callRemoteProc.
|
|
*
|
|
* Server registers a proc:
|
|
* rpc.register('getClosestTrunk', async (player, ...args) => { return result; });
|
|
*
|
|
* Client calls it via the client RPC module (see src/client/core/rpc/index.ts):
|
|
* const result = await rpc.call('getClosestTrunk', ...args);
|
|
*
|
|
* The wire protocol uses two net events:
|
|
* _rpc:call player → server { id, name, args }
|
|
* _rpc:result server → player { id, result, error }
|
|
*/
|
|
|
|
import { playerRegistry } from '../player/player-registry';
|
|
|
|
type ProcHandler = (player: import('../player/player-registry').PlayerWrapper, ...args: any[]) => any;
|
|
|
|
const handlers = new Map<string, ProcHandler>();
|
|
|
|
let clientCallCounter = 0;
|
|
const clientPending = new Map<string, { resolve: (v: any) => void; reject: (e: any) => void }>();
|
|
|
|
RegisterNetEvent('_rpc:clientResult');
|
|
AddEventHandler('_rpc:clientResult', (callId: string, result: any, error: string | null) => {
|
|
const cb = clientPending.get(callId);
|
|
if (!cb) return;
|
|
clientPending.delete(callId);
|
|
if (error) cb.reject(new Error(error));
|
|
else cb.resolve(result);
|
|
});
|
|
|
|
export const rpc = {
|
|
register(name: string, handler: ProcHandler): void {
|
|
handlers.set(name, handler);
|
|
},
|
|
|
|
// Server → client proc call (replaces mp.events.callRemoteProc / player.callProc)
|
|
callClient<T = any>(player: import('../player/player-registry').PlayerWrapper, name: string, ...args: any[]): Promise<T> {
|
|
const id = `${Date.now()}_${clientCallCounter++}`;
|
|
return new Promise<T>((resolve, reject) => {
|
|
clientPending.set(id, { resolve, reject });
|
|
TriggerClientEvent('_rpc:clientCall', player.id, id, name, args);
|
|
setTimeout(() => {
|
|
if (clientPending.has(id)) {
|
|
clientPending.delete(id);
|
|
reject(new Error(`Client RPC timeout: ${name}`));
|
|
}
|
|
}, 10000);
|
|
});
|
|
},
|
|
};
|
|
|
|
RegisterNetEvent('_rpc:call');
|
|
AddEventHandler('_rpc:call', async (callId: string, name: string, args: any[]) => {
|
|
const netId = (global as any).source as number;
|
|
const player = playerRegistry.get(netId);
|
|
|
|
if (!player) return;
|
|
|
|
const handler = handlers.get(name);
|
|
if (!handler) {
|
|
TriggerClientEvent('_rpc:result', netId, callId, null, `No proc registered: ${name}`);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const result = await handler(player, ...(args ?? []));
|
|
TriggerClientEvent('_rpc:result', netId, callId, result, null);
|
|
} catch (err: any) {
|
|
TriggerClientEvent('_rpc:result', netId, callId, null, err?.message ?? String(err));
|
|
}
|
|
});
|