/** * 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(); let clientCallCounter = 0; const clientPending = new Map 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(player: import('../player/player-registry').PlayerWrapper, name: string, ...args: any[]): Promise { const id = `${Date.now()}_${clientCallCounter++}`; return new Promise((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)); } });