mirror of
https://github.com/rilaxik/fivem-rpc.git
synced 2026-09-28 01:29:13 +00:00
427 lines
12 KiB
Markdown
427 lines
12 KiB
Markdown
# FiveM RPC
|
|
|
|
Call FiveM server, client and NUI listeners like async functions: typed, with timeouts, no event ping-pong
|
|
|
|
Installation, quick start and package overview: [main readme](../readme.md). Typed events: [shared-types](../shared-types/readme.md). Upgrading from 0.1: [migration guide](../migration.md).
|
|
|
|
## Exports
|
|
|
|
Besides `createRPC` the package exports:
|
|
|
|
- `RPCError`, `RPCErrors`, `RPCErrorDetails` - see [Errors](#errors)
|
|
- `RPCConfig`, `RPCEnvironment` and the instance types `RPCInstanceServer`, `RPCInstanceClient`, `RPCInstanceWebview`
|
|
- native event types `RPCNativeServerEvents`, `RPCNativeClientEvents`, `RPCNativeClientNetworkEvents`, `RPCNativeClientNetworkEventsNames` and the lists `NATIVE_SERVER_EVENTS`, `NATIVE_CLIENT_EVENTS`, `NATIVE_CLIENT_NETWORK_EVENTS` accepted by the `onNative*` methods
|
|
|
|
## RPCConfig
|
|
|
|
```ts
|
|
type RPCConfig = {
|
|
env: 'server' | 'client' | 'webview'
|
|
debug?: boolean // default false, logs every registration, call and incoming payload
|
|
timeout?: number // default 5000, ms to wait for a response, 0 disables it
|
|
}
|
|
```
|
|
|
|
An unknown `env` makes `createRPC` throw `RPCError` with code `RPCErrors.UNKNOWN_ENVIRONMENT`
|
|
|
|
## Errors
|
|
|
|
Every error from this library is an `RPCError`. `code` is one of `RPCErrors`, `message` says what to fix, and `details` names the call when the error comes from one
|
|
|
|
```ts
|
|
enum RPCErrors {
|
|
EVENT_NOT_REGISTERED = 'Event not registered',
|
|
UNKNOWN_NATIVE = 'Unknown native event',
|
|
UNKNOWN_ENVIRONMENT = 'Unknown environment',
|
|
TIMEOUT = 'Timed out waiting for response',
|
|
HANDLER_ERROR = 'Listener threw an error',
|
|
}
|
|
```
|
|
|
|
### Example error
|
|
|
|
The server has no `onClient('buyItem', ...)` listener, so the call from the client rejects:
|
|
|
|
```ts
|
|
import { RPCError, RPCErrors } from '@entityseven/fivem-rpc'
|
|
|
|
try {
|
|
await rpc.emitServer('buyItem', 'water')
|
|
} catch (e) {
|
|
if (e instanceof RPCError && e.code === RPCErrors.EVENT_NOT_REGISTERED) {
|
|
e.message // 'No listener for "buyItem" on server. Register it with rpc.onClient("buyItem", ...) in server code.'
|
|
e.details // { event: 'buyItem', uuid: '<uuid>', from: 'client', to: 'server' }
|
|
}
|
|
}
|
|
```
|
|
|
|
## How it works
|
|
|
|
### Directions
|
|
|
|
Every call goes from an `emit*` method in one environment to the matching `on*` listener in another. The last column is the [shared-types](../shared-types/readme.md) interface that types it
|
|
|
|
| From | Call | To | Listener | Typed by |
|
|
| ------- | ------------------------------------- | ----------- | ------------------------------------- | ------------------------- |
|
|
| server | `emitClient(player, event, ...args)` | client | `onServer` | `RPCEvents_ServerClient` |
|
|
| server | `emitClientEveryone(event, ...args)` | all clients | `onServer`, no response | `RPCEvents_ServerClient` |
|
|
| server | `emitWebview(player, event, ...args)` | webview | `onServer`, via client | `RPCEvents_ServerWebview` |
|
|
| server | `emitSelf(event, ...args)` | server | `onSelf` | `RPCEvents_Server` |
|
|
| client | `emitServer(event, ...args)` | server | `onClient`, player first | `RPCEvents_ClientServer` |
|
|
| client | `emitWebview(event, ...args)` | webview | `onClient` | `RPCEvents_ClientWebview` |
|
|
| client | `emitSelf(event, ...args)` | client | `onSelf` | `RPCEvents_Client` |
|
|
| webview | `emitServer(event, ...args)` | server | `onWebview`, player first, via client | `RPCEvents_WebviewServer` |
|
|
| webview | `emitClient(event, ...args)` | client | `onWebview` | `RPCEvents_WebviewClient` |
|
|
| webview | `emitSelf(event, ...args)` | webview | `onSelf` | `RPCEvents_Webview` |
|
|
|
|
Commands registered with `onCommand` are typed by `RPCCommands_Server` and `RPCCommands_Client`
|
|
|
|
### Routing
|
|
|
|
Server and client talk over FiveM network events, client and webview over NUI messages and NUI callbacks. Webview and server never talk directly: every call between them is relayed by the client of that player. So every client must run `createRPC({ env: 'client' })`, even with no listeners of its own, or those calls time out
|
|
|
|
### One listener per event
|
|
|
|
Each `on*` method keeps one listener per event name. Registering the same name again replaces the previous listener, `off*` removes it. Directions are separate: `onClient('x')` and `onWebview('x')` on the server do not replace each other
|
|
|
|
### Responses, errors and timeouts
|
|
|
|
- `emit*` resolves with the value the listener returns (promises are awaited)
|
|
- no listener on the target: the call rejects with `RPCErrors.EVENT_NOT_REGISTERED`
|
|
- the listener throws: the target logs the error with `console.error`, the call rejects with `RPCErrors.HANDLER_ERROR` and the original message
|
|
- no response within `RPCConfig.timeout` (default 5000 ms): the call rejects with `RPCErrors.TIMEOUT` and a late response is ignored. `timeout: 0` waits forever
|
|
- `emitSelf` calls the local listener directly, whatever it throws reaches the caller unchanged
|
|
- `emitClientEveryone` does not wait for clients: it resolves once sent, failures stay on each client (`console.error` for a throwing listener, the rest with `debug: true`)
|
|
|
|
### Player identity
|
|
|
|
Server listeners (`onClient`, `onWebview`) get the calling player's server id as the first argument. It comes from FiveM's `source`, never from the payload, so a client cannot pose as another player. Use it instead of player ids passed as arguments. A response to `emitClient` or `emitWebview` is only accepted from the player it was sent to
|
|
|
|
## Server ([source](src/core/server.ts))
|
|
|
|
### onClient
|
|
|
|
Listens to client event
|
|
|
|
```ts
|
|
rpc.onClient('clientServerEvent', (player, arg1, arg2, ...rest) => {
|
|
// logic
|
|
return someData // this will be forwarded back to caller
|
|
})
|
|
```
|
|
|
|
### offClient
|
|
|
|
Stops listening to client event
|
|
|
|
```ts
|
|
rpc.offClient('clientServerEvent')
|
|
```
|
|
|
|
### emitClient
|
|
|
|
Sends event to specified client
|
|
|
|
```ts
|
|
const response = await rpc.emitClient(playerServerId, 'serverClientEvent', someData)
|
|
// response will come from client listener with returned data
|
|
```
|
|
|
|
### emitClientEveryone
|
|
|
|
Sends event to all clients. One-way: clients run their listener but do not answer
|
|
|
|
```ts
|
|
await rpc.emitClientEveryone('serverClientEvent', someData)
|
|
```
|
|
|
|
### onWebview
|
|
|
|
Listens to webview event
|
|
|
|
```ts
|
|
rpc.onWebview('webviewServerEvent', (player, arg1, arg2, ...rest) => {
|
|
// logic
|
|
return someData // this will be forwarded back to caller
|
|
})
|
|
```
|
|
|
|
### offWebview
|
|
|
|
Stops listening to webview event
|
|
|
|
```ts
|
|
rpc.offWebview('webviewServerEvent')
|
|
```
|
|
|
|
### emitWebview
|
|
|
|
Sends event to the webview of specified player
|
|
|
|
```ts
|
|
const response = await rpc.emitWebview(playerServerId, 'serverWebviewEvent', someData)
|
|
// response will come from webview listener with returned data
|
|
```
|
|
|
|
### onSelf
|
|
|
|
Listens to server event
|
|
|
|
```ts
|
|
rpc.onSelf('serverEvent', (arg1, arg2, ...rest) => {
|
|
// logic
|
|
return someData // this will be forwarded back to caller
|
|
})
|
|
```
|
|
|
|
### offSelf
|
|
|
|
Stops listening to server event
|
|
|
|
```ts
|
|
rpc.offSelf('serverEvent')
|
|
```
|
|
|
|
### emitSelf
|
|
|
|
Sends event to server
|
|
|
|
```ts
|
|
const response = await rpc.emitSelf('serverEvent', someData)
|
|
// response will come from server listener with returned data
|
|
```
|
|
|
|
### onCommand
|
|
|
|
Registers chat command. `args` are the raw strings typed after the command, validate them yourself. With `restricted` set to `true` only players with the ACE permission `command.<name>` can use it (defaults to `false`)
|
|
|
|
```ts
|
|
rpc.onCommand('serverCommand', (player, args, rawCommand) => {
|
|
// logic
|
|
}, true /* restricted */)
|
|
```
|
|
|
|
### onNativeEvent
|
|
|
|
Listens to native server event ([reference](https://docs.fivem.net/docs/scripting-reference/events/server-events/))
|
|
|
|
```ts
|
|
rpc.onNativeEvent('playerJoining', (source, oldId) => {
|
|
// logic
|
|
})
|
|
```
|
|
|
|
## Client ([source](src/core/client.ts))
|
|
|
|
### onServer
|
|
|
|
Listens to server event
|
|
|
|
```ts
|
|
rpc.onServer('serverClientEvent', (arg1, arg2, ...rest) => {
|
|
// logic
|
|
return someData // this will be forwarded back to caller
|
|
})
|
|
```
|
|
|
|
### offServer
|
|
|
|
Stops listening to server event
|
|
|
|
```ts
|
|
rpc.offServer('serverClientEvent')
|
|
```
|
|
|
|
### emitServer
|
|
|
|
Sends event to server
|
|
|
|
```ts
|
|
const response = await rpc.emitServer('clientServerEvent', someData)
|
|
// response will come from server listener with returned data
|
|
```
|
|
|
|
### onWebview
|
|
|
|
Listens to webview event
|
|
|
|
```ts
|
|
rpc.onWebview('webviewClientEvent', (arg1, arg2, ...rest) => {
|
|
// logic
|
|
return someData // this will be forwarded back to caller
|
|
})
|
|
```
|
|
|
|
### offWebview
|
|
|
|
Stops listening to webview event
|
|
|
|
```ts
|
|
rpc.offWebview('webviewClientEvent')
|
|
```
|
|
|
|
### emitWebview
|
|
|
|
Sends event to own webview
|
|
|
|
```ts
|
|
const response = await rpc.emitWebview('clientWebviewEvent', someData)
|
|
// response will come from webview listener with returned data
|
|
```
|
|
|
|
### onSelf
|
|
|
|
Listens to client event
|
|
|
|
```ts
|
|
rpc.onSelf('clientEvent', (arg1, arg2, ...rest) => {
|
|
// logic
|
|
return someData // this will be forwarded back to caller
|
|
})
|
|
```
|
|
|
|
### offSelf
|
|
|
|
Stops listening to client event
|
|
|
|
```ts
|
|
rpc.offSelf('clientEvent')
|
|
```
|
|
|
|
### emitSelf
|
|
|
|
Sends event to client
|
|
|
|
```ts
|
|
const response = await rpc.emitSelf('clientEvent', someData)
|
|
// response will come from client listener with returned data
|
|
```
|
|
|
|
### onCommand
|
|
|
|
Registers chat command. `args` are the raw strings typed after the command, validate them yourself
|
|
|
|
```ts
|
|
rpc.onCommand('clientCommand', (player, args, rawCommand) => {
|
|
// logic
|
|
})
|
|
```
|
|
|
|
### onNativeEvent
|
|
|
|
Listens to native client event ([reference](https://docs.fivem.net/docs/scripting-reference/events/client-events/))
|
|
|
|
```ts
|
|
rpc.onNativeEvent('entityDamaged', (victim, culprit, weapon, baseDamage) => {
|
|
// logic
|
|
})
|
|
```
|
|
|
|
### onNativeNetworkEvent
|
|
|
|
Listens to native client network event ([reference](https://docs.fivem.net/docs/game-references/game-events/))
|
|
|
|
```ts
|
|
rpc.onNativeNetworkEvent('CEventShockingCarCrash', (entities, eventEntity, data) => {
|
|
// logic
|
|
})
|
|
```
|
|
|
|
### setWebviewFocus
|
|
|
|
Sets or removes focus and cursor from own webview
|
|
|
|
```ts
|
|
rpc.setWebviewFocus(true /* focus */, true /* show cursor */)
|
|
```
|
|
|
|
## Webview ([source](src/core/webview.ts))
|
|
|
|
### onClient
|
|
|
|
Listens to client event
|
|
|
|
```ts
|
|
rpc.onClient('clientWebviewEvent', (arg1, arg2, ...rest) => {
|
|
// logic
|
|
return someData // this will be forwarded back to caller
|
|
})
|
|
```
|
|
|
|
### offClient
|
|
|
|
Stops listening to client event
|
|
|
|
```ts
|
|
rpc.offClient('clientWebviewEvent')
|
|
```
|
|
|
|
### emitClient
|
|
|
|
Sends event to own client
|
|
|
|
```ts
|
|
const response = await rpc.emitClient('webviewClientEvent', someData)
|
|
// response will come from client listener with returned data
|
|
```
|
|
|
|
### onServer
|
|
|
|
Listens to server event
|
|
|
|
```ts
|
|
rpc.onServer('serverWebviewEvent', (arg1, arg2, ...rest) => {
|
|
// logic
|
|
return someData // this will be forwarded back to caller
|
|
})
|
|
```
|
|
|
|
### offServer
|
|
|
|
Stops listening to server event
|
|
|
|
```ts
|
|
rpc.offServer('serverWebviewEvent')
|
|
```
|
|
|
|
### emitServer
|
|
|
|
Sends event to server
|
|
|
|
```ts
|
|
const response = await rpc.emitServer('webviewServerEvent', someData)
|
|
// response will come from server listener with returned data
|
|
```
|
|
|
|
### onSelf
|
|
|
|
Listens to webview event
|
|
|
|
```ts
|
|
rpc.onSelf('webviewEvent', (arg1, arg2, ...rest) => {
|
|
// logic
|
|
return someData // this will be forwarded back to caller
|
|
})
|
|
```
|
|
|
|
### offSelf
|
|
|
|
Stops listening to webview event
|
|
|
|
```ts
|
|
rpc.offSelf('webviewEvent')
|
|
```
|
|
|
|
### emitSelf
|
|
|
|
Sends event to webview
|
|
|
|
```ts
|
|
const response = await rpc.emitSelf('webviewEvent', someData)
|
|
// response will come from webview listener with returned data
|
|
```
|
|
|
|
## License
|
|
|
|
Licensed under the [Custom Attribution-NoDerivs Software License](license.md)
|