Archived
init
This commit is contained in:
@@ -0,0 +1,402 @@
|
||||
/// <reference types="@citizenfx/client" />
|
||||
|
||||
import type * as s from '@entityseven/fivem-rpc-shared-types'
|
||||
import { Emitter } from '../utils/emitter'
|
||||
import { generateUUID, parse, stringify, stringifyWeb } from '../utils/funcs'
|
||||
import {
|
||||
NATIVE_CLIENT_EVENTS,
|
||||
NATIVE_CLIENT_NETWORK_EVENTS,
|
||||
} from '../utils/native'
|
||||
import {
|
||||
type RPCConfig,
|
||||
RPCErrors,
|
||||
RPCEvents,
|
||||
type RPCNativeClientEvents,
|
||||
type RPCNativeClientNetworksEvents,
|
||||
type RPCState,
|
||||
type RPCStateRaw,
|
||||
type RPCStateWeb,
|
||||
} from '../utils/types'
|
||||
import { Wrapper } from './wrapper'
|
||||
|
||||
export class RPCInstanceClient extends Wrapper {
|
||||
private readonly _emitterServer: Emitter
|
||||
private readonly _pendingServer: Emitter
|
||||
private readonly _emitterWeb: Emitter
|
||||
private readonly _pendingWeb: Emitter
|
||||
private readonly _pendingWebToServer: Emitter
|
||||
|
||||
constructor(props: RPCConfig<'client'>) {
|
||||
super(props)
|
||||
|
||||
this._emitterServer = new Emitter()
|
||||
this._pendingServer = new Emitter()
|
||||
this._emitterWeb = new Emitter()
|
||||
this._pendingWeb = new Emitter()
|
||||
this._pendingWebToServer = new Emitter()
|
||||
|
||||
this.console.log('[RPC] Initialized Client')
|
||||
|
||||
onNet(RPCEvents.LISTENER_SERVER, this._handleServer.bind(this))
|
||||
RegisterNuiCallbackType(RPCEvents.LISTENER_WEB)
|
||||
on(
|
||||
`__cfx_nui:${RPCEvents.LISTENER_WEB}`,
|
||||
async (data: RPCState, callback: (res: unknown) => void) => {
|
||||
const res = await this._handleWeb(data)
|
||||
callback(res)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// ===== HANDLERS =====
|
||||
|
||||
private async _handleServer(payloadRaw: RPCStateRaw) {
|
||||
try {
|
||||
parse(payloadRaw)
|
||||
} catch (e) {
|
||||
throw new Error(RPCErrors.INVALID_DATA)
|
||||
}
|
||||
const payload = parse(payloadRaw)
|
||||
|
||||
if (this.debug) {
|
||||
this.console.log(
|
||||
`[RPC]:client:accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
|
||||
)
|
||||
}
|
||||
|
||||
if (payload.type === 'event') {
|
||||
if (payload.calledTo === 'client') {
|
||||
this.verifyEvent(this._emitterServer, payload)
|
||||
|
||||
const responseData = await this._emitterServer.emit(
|
||||
payload.event,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
|
||||
const response: RPCState = {
|
||||
event: payload.event,
|
||||
uuid: payload.uuid,
|
||||
calledFrom: 'client',
|
||||
calledTo: 'server',
|
||||
error: null,
|
||||
data: [responseData],
|
||||
player: payload.player,
|
||||
type: 'response',
|
||||
}
|
||||
|
||||
emitNet(RPCEvents.LISTENER_CLIENT, stringify(response))
|
||||
}
|
||||
if (payload.calledTo === 'webview') {
|
||||
this._sendWebMessage({
|
||||
origin: RPCEvents.LISTENER_SERVER,
|
||||
data: payload,
|
||||
})
|
||||
}
|
||||
}
|
||||
if (payload.type === 'response') {
|
||||
if (payload.calledTo === 'client') {
|
||||
await this._pendingServer.emit(
|
||||
payload.uuid,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
}
|
||||
if (payload.calledTo === 'webview') {
|
||||
await this._pendingWebToServer.emit(
|
||||
payload.uuid,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _handleWeb(payload: RPCState): Promise<unknown> {
|
||||
if (this.debug) {
|
||||
this.console.log(
|
||||
`[RPC]:client:accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
|
||||
)
|
||||
}
|
||||
|
||||
if (payload.type === 'event') {
|
||||
if (payload.calledTo === 'client') {
|
||||
return await this._emitterWeb.emit(
|
||||
payload.event,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
}
|
||||
if (payload.calledTo === 'server') {
|
||||
payload.player = GetPlayerServerId(PlayerId())
|
||||
emitNet(RPCEvents.LISTENER_WEB, stringify(payload))
|
||||
|
||||
return new Promise(res => {
|
||||
this._pendingWebToServer.once(payload.uuid, res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (payload.type === 'response') {
|
||||
if (payload.calledTo === 'client') {
|
||||
await this._pendingWeb.emit(
|
||||
payload.uuid,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
|
||||
return { status: 'ok' }
|
||||
}
|
||||
if (payload.calledTo === 'server') {
|
||||
payload.player = GetPlayerServerId(PlayerId())
|
||||
emitNet(RPCEvents.LISTENER_WEB, stringify(payload))
|
||||
|
||||
return { status: 'ok' }
|
||||
}
|
||||
}
|
||||
return { status: 'unknown' }
|
||||
}
|
||||
|
||||
// ===== SERVER =====
|
||||
|
||||
public onServer<
|
||||
EventName extends keyof s.RPCEvents_ServerClient,
|
||||
CallbackArguments extends Parameters<s.RPCEvents_ServerClient[EventName]>,
|
||||
CallbackReturn extends ReturnType<s.RPCEvents_ServerClient[EventName]>,
|
||||
>(
|
||||
eventName: EventName,
|
||||
cb: (
|
||||
...args: CallbackArguments
|
||||
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onServer ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterServer.on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public offServer<EventName extends keyof s.RPCEvents_ServerClient>(
|
||||
eventName: EventName,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:offServer ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterServer.off(eventName)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async emitServer<
|
||||
EventName extends keyof s.RPCEvents_ClientServer,
|
||||
Arguments extends Parameters<s.RPCEvents_ClientServer[EventName]>,
|
||||
Response extends ReturnType<s.RPCEvents_ClientServer[EventName]>,
|
||||
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'client',
|
||||
calledTo: 'server',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: GetPlayerServerId(PlayerId()),
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
emitNet(RPCEvents.LISTENER_CLIENT, stringify(payload))
|
||||
|
||||
return new Promise<Awaited<Response>>(res => {
|
||||
this._pendingServer.once(payload.uuid, res)
|
||||
})
|
||||
}
|
||||
|
||||
// ===== WEBVIEW =====
|
||||
|
||||
public onWebview<
|
||||
EventName extends keyof s.RPCEvents_WebviewClient,
|
||||
CallbackArguments extends Parameters<s.RPCEvents_WebviewClient[EventName]>,
|
||||
CallbackReturn extends ReturnType<s.RPCEvents_WebviewClient[EventName]>,
|
||||
>(
|
||||
eventName: EventName,
|
||||
cb: (
|
||||
...args: CallbackArguments
|
||||
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onWebview ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterWeb.on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public offWebview<EventName extends keyof s.RPCEvents_WebviewClient>(
|
||||
eventName: EventName,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:offWebview ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterWeb.off(eventName)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async emitWebview<
|
||||
EventName extends keyof s.RPCEvents_ClientWebview,
|
||||
Arguments extends Parameters<s.RPCEvents_ClientWebview[EventName]>,
|
||||
Response extends ReturnType<s.RPCEvents_ClientWebview[EventName]>,
|
||||
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'client',
|
||||
calledTo: 'webview',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: PlayerId(),
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
this._sendWebMessage({
|
||||
origin: RPCEvents.LISTENER_CLIENT,
|
||||
data: payload,
|
||||
})
|
||||
|
||||
return new Promise<Awaited<Response>>(res => {
|
||||
this._pendingWeb.once(payload.uuid, res)
|
||||
})
|
||||
}
|
||||
|
||||
// ===== SELF =====
|
||||
|
||||
public onSelf<
|
||||
EventName extends keyof s.RPCEvents_Client,
|
||||
CallbackArguments extends Parameters<s.RPCEvents_Client[EventName]>,
|
||||
CallbackReturn extends ReturnType<s.RPCEvents_Client[EventName]>,
|
||||
>(
|
||||
eventName: EventName,
|
||||
cb: (
|
||||
...args: CallbackArguments
|
||||
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onSelf ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterLocal.on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public offSelf<EventName extends keyof s.RPCEvents_Client>(
|
||||
eventName: EventName,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:offSelf ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterLocal.off(eventName)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async emitSelf<
|
||||
EventName extends keyof s.RPCEvents_Client,
|
||||
Arguments extends Parameters<s.RPCEvents_Client[EventName]>,
|
||||
Response extends ReturnType<s.RPCEvents_Client[EventName]>,
|
||||
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'client',
|
||||
calledTo: 'client',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: null,
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
if (this.debug) {
|
||||
this.console.log(
|
||||
`[RPC]:accepted ${payload.event} from ${payload.calledFrom}`,
|
||||
)
|
||||
}
|
||||
|
||||
this.verifyEvent(this._emitterLocal, payload)
|
||||
|
||||
return await this._emitterLocal.emit<Awaited<Response>>(
|
||||
payload.event,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
}
|
||||
|
||||
// ===== OTHER =====
|
||||
|
||||
public onCommand<
|
||||
CommandName extends s.RPCCommands_Client,
|
||||
CallbackArguments extends unknown[],
|
||||
>(
|
||||
command: CommandName,
|
||||
cb: (player: number, args: CallbackArguments, commandRaw: string) => void,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onCommand ${command}`)
|
||||
}
|
||||
|
||||
RegisterCommand(command, cb, false)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public onNativeEvent<
|
||||
EventName extends keyof RPCNativeClientEvents,
|
||||
CallbackArguments extends Parameters<RPCNativeClientEvents[EventName]>,
|
||||
>(eventName: EventName, cb: (...args: CallbackArguments) => void): this {
|
||||
if (!NATIVE_CLIENT_EVENTS.includes(eventName)) {
|
||||
throw new Error(RPCErrors.UNKNOWN_NATIVE)
|
||||
}
|
||||
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onNativeEvent ${eventName}`)
|
||||
}
|
||||
|
||||
on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public onNativeNetworkEvent<
|
||||
EventName extends keyof RPCNativeClientNetworksEvents,
|
||||
CallbackArguments extends Parameters<
|
||||
RPCNativeClientNetworksEvents[EventName]
|
||||
>,
|
||||
>(eventName: EventName, cb: (...args: CallbackArguments) => void): this {
|
||||
if (!NATIVE_CLIENT_NETWORK_EVENTS.includes(eventName)) {
|
||||
throw new Error(RPCErrors.UNKNOWN_NATIVE)
|
||||
}
|
||||
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onNativeNetworkEvent ${eventName}`)
|
||||
}
|
||||
|
||||
on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public setWebviewFocus(hasFocus: boolean, hasCursor: boolean): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:setWebviewFocus ${hasFocus} ${hasCursor}`)
|
||||
}
|
||||
|
||||
SetNuiFocus(hasFocus, hasCursor)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
// ===== UTILS =====
|
||||
|
||||
private _sendWebMessage(payload: RPCStateWeb): void {
|
||||
SendNuiMessage(stringifyWeb(payload))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
/// <reference types="@citizenfx/server" />
|
||||
|
||||
import type * as s from '@entityseven/fivem-rpc-shared-types'
|
||||
import { Emitter } from '../utils/emitter'
|
||||
import { generateUUID, parse, stringify } from '../utils/funcs'
|
||||
import { NATIVE_SERVER_EVENTS } from '../utils/native'
|
||||
import {
|
||||
type RPCConfig,
|
||||
RPCErrors,
|
||||
RPCEvents,
|
||||
type RPCNativeServerEvents,
|
||||
type RPCState,
|
||||
type RPCStateRaw,
|
||||
} from '../utils/types'
|
||||
import { Wrapper } from './wrapper'
|
||||
|
||||
export class RPCInstanceServer extends Wrapper {
|
||||
private readonly _emitterClient: Emitter
|
||||
private readonly _pendingClient: Emitter
|
||||
private readonly _emitterWeb: Emitter
|
||||
private readonly _pendingWeb: Emitter
|
||||
|
||||
constructor(props: RPCConfig<'server'>) {
|
||||
super(props)
|
||||
|
||||
this._emitterClient = new Emitter()
|
||||
this._pendingClient = new Emitter()
|
||||
this._emitterWeb = new Emitter()
|
||||
this._pendingWeb = new Emitter()
|
||||
|
||||
this.console.log('[RPC] Initialized Server')
|
||||
|
||||
onNet(RPCEvents.LISTENER_CLIENT, this._handleClient.bind(this))
|
||||
onNet(RPCEvents.LISTENER_WEB, this._handleWeb.bind(this))
|
||||
}
|
||||
|
||||
// ===== HANDLERS =====
|
||||
|
||||
private async _handleClient(payloadRaw: RPCStateRaw) {
|
||||
try {
|
||||
parse(payloadRaw)
|
||||
} catch (e) {
|
||||
throw new Error(RPCErrors.INVALID_DATA)
|
||||
}
|
||||
const payload = parse(payloadRaw)
|
||||
|
||||
if (this.debug) {
|
||||
this.console.log(
|
||||
`[RPC]:server:accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
|
||||
)
|
||||
}
|
||||
|
||||
if (payload.calledFrom === 'client') {
|
||||
if (payload.type === 'event') {
|
||||
this.verifyEvent(this._emitterClient, payload)
|
||||
if (payload.player === null || payload.player === -1) {
|
||||
payload.error = RPCErrors.NO_PLAYER
|
||||
this.triggerError(payload)
|
||||
return
|
||||
}
|
||||
|
||||
const responseData = await this._emitterClient.emit(
|
||||
payload.event,
|
||||
payload.player,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
|
||||
const response: RPCState = {
|
||||
event: payload.event,
|
||||
uuid: payload.uuid,
|
||||
calledFrom: 'server',
|
||||
calledTo: 'client',
|
||||
error: null,
|
||||
data: [responseData],
|
||||
player: payload.player,
|
||||
type: 'response',
|
||||
}
|
||||
|
||||
emitNet(RPCEvents.LISTENER_SERVER, response.player, stringify(response))
|
||||
}
|
||||
if (payload.type === 'response') {
|
||||
await this._pendingClient.emit(
|
||||
payload.uuid,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _handleWeb(payloadRaw: RPCStateRaw) {
|
||||
try {
|
||||
parse(payloadRaw)
|
||||
} catch (e) {
|
||||
throw new Error(RPCErrors.INVALID_DATA)
|
||||
}
|
||||
const payload = parse(payloadRaw)
|
||||
|
||||
if (this.debug) {
|
||||
this.console.log(
|
||||
`[RPC]:server:accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
|
||||
)
|
||||
}
|
||||
|
||||
if (payload.calledFrom === 'webview') {
|
||||
if (payload.type === 'event') {
|
||||
this.verifyEvent(this._emitterWeb, payload)
|
||||
if (payload.player === null || payload.player === -1) {
|
||||
payload.error = RPCErrors.NO_PLAYER
|
||||
this.triggerError(payload)
|
||||
return
|
||||
}
|
||||
|
||||
const responseData = await this._emitterWeb.emit(
|
||||
payload.event,
|
||||
payload.player,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
|
||||
const response: RPCState = {
|
||||
event: payload.event,
|
||||
uuid: payload.uuid,
|
||||
calledFrom: 'server',
|
||||
calledTo: 'webview',
|
||||
error: null,
|
||||
data: [responseData],
|
||||
player: payload.player,
|
||||
type: 'response',
|
||||
}
|
||||
|
||||
emitNet(RPCEvents.LISTENER_SERVER, response.player, stringify(response))
|
||||
}
|
||||
if (payload.type === 'response') {
|
||||
await this._pendingWeb.emit(
|
||||
payload.uuid,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== CLIENT =====
|
||||
|
||||
public onClient<
|
||||
EventName extends keyof s.RPCEvents_ClientServer,
|
||||
CallbackArguments extends Parameters<s.RPCEvents_ClientServer[EventName]>,
|
||||
CallbackReturn extends ReturnType<s.RPCEvents_ClientServer[EventName]>,
|
||||
>(
|
||||
eventName: EventName,
|
||||
cb: (
|
||||
player: number,
|
||||
...args: CallbackArguments
|
||||
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onClient ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterClient.on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public offClient<EventName extends keyof s.RPCEvents_ClientServer>(
|
||||
eventName: EventName,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:offClient ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterClient.off(eventName)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async emitClient<
|
||||
EventName extends keyof s.RPCEvents_ServerClient,
|
||||
Arguments extends Parameters<s.RPCEvents_ServerClient[EventName]>,
|
||||
Response extends ReturnType<s.RPCEvents_ServerClient[EventName]>,
|
||||
>(
|
||||
player: number,
|
||||
eventName: EventName,
|
||||
...args: Arguments
|
||||
): Promise<Awaited<Response>> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'server',
|
||||
calledTo: 'client',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: player,
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
emitNet(RPCEvents.LISTENER_SERVER, player, stringify(payload))
|
||||
|
||||
return new Promise<Awaited<Response>>(res => {
|
||||
this._pendingClient.once(payload.uuid, res)
|
||||
})
|
||||
}
|
||||
|
||||
public async emitClientEveryone<
|
||||
EventName extends keyof s.RPCEvents_ServerClient,
|
||||
Arguments extends Parameters<s.RPCEvents_ServerClient[EventName]>,
|
||||
>(eventName: EventName, ...args: Arguments): Promise<void> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'server',
|
||||
calledTo: 'client',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: -1,
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
emitNet(RPCEvents.LISTENER_SERVER, -1, stringify(payload))
|
||||
}
|
||||
|
||||
// ===== WEBVIEW =====
|
||||
|
||||
public onWebview<
|
||||
EventName extends keyof s.RPCEvents_WebviewServer,
|
||||
CallbackArguments extends Parameters<s.RPCEvents_WebviewServer[EventName]>,
|
||||
CallbackReturn extends ReturnType<s.RPCEvents_WebviewServer[EventName]>,
|
||||
>(
|
||||
eventName: EventName,
|
||||
cb: (
|
||||
player: number,
|
||||
...args: CallbackArguments
|
||||
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onWebview ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterWeb.on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public offWebview<EventName extends keyof s.RPCEvents_WebviewServer>(
|
||||
eventName: EventName,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:offWebview ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterWeb.off(eventName)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async emitWebview<
|
||||
EventName extends keyof s.RPCEvents_ServerWebview,
|
||||
Arguments extends Parameters<s.RPCEvents_ServerWebview[EventName]>,
|
||||
Response extends ReturnType<s.RPCEvents_ServerWebview[EventName]>,
|
||||
>(
|
||||
player: number,
|
||||
eventName: EventName,
|
||||
...args: Arguments
|
||||
): Promise<Awaited<Response>> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'server',
|
||||
calledTo: 'webview',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: player,
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
emitNet(RPCEvents.LISTENER_SERVER, player, stringify(payload))
|
||||
|
||||
return new Promise<Awaited<Response>>(res => {
|
||||
this._pendingWeb.once(payload.uuid, res)
|
||||
})
|
||||
}
|
||||
|
||||
// ===== SELF =====
|
||||
|
||||
public onSelf<
|
||||
EventName extends keyof s.RPCEvents_Server,
|
||||
CallbackArguments extends Parameters<s.RPCEvents_Server[EventName]>,
|
||||
CallbackReturn extends ReturnType<s.RPCEvents_Server[EventName]>,
|
||||
>(
|
||||
eventName: EventName,
|
||||
cb: (
|
||||
...args: CallbackArguments
|
||||
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onSelf ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterLocal.on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public offSelf<EventName extends keyof s.RPCEvents_Server>(
|
||||
eventName: EventName,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:offSelf ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterLocal.off(eventName)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async emitSelf<
|
||||
EventName extends keyof s.RPCEvents_Server,
|
||||
Arguments extends Parameters<s.RPCEvents_Server[EventName]>,
|
||||
Response extends ReturnType<s.RPCEvents_Server[EventName]>,
|
||||
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'server',
|
||||
calledTo: 'server',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: null,
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
if (this.debug) {
|
||||
this.console.log(
|
||||
`[RPC]:accepted ${payload.event} from ${payload.calledFrom}`,
|
||||
)
|
||||
}
|
||||
|
||||
this.verifyEvent(this._emitterLocal, payload)
|
||||
|
||||
return await this._emitterLocal.emit<Awaited<Response>>(
|
||||
payload.event,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
}
|
||||
|
||||
// ===== OTHER =====
|
||||
|
||||
public onCommand<
|
||||
CommandName extends s.RPCCommands_Server,
|
||||
CallbackArguments extends unknown[],
|
||||
>(
|
||||
command: CommandName,
|
||||
cb: (player: number, args: CallbackArguments, commandRaw: string) => void,
|
||||
restricted = false,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onCommand ${command}`)
|
||||
}
|
||||
|
||||
RegisterCommand(command, cb, restricted)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public onNativeEvent<
|
||||
EventName extends keyof RPCNativeServerEvents,
|
||||
CallbackArguments extends Parameters<RPCNativeServerEvents[EventName]>,
|
||||
>(eventName: EventName, cb: (...args: CallbackArguments) => void): this {
|
||||
if (!NATIVE_SERVER_EVENTS.includes(eventName)) {
|
||||
throw new Error(RPCErrors.UNKNOWN_NATIVE)
|
||||
}
|
||||
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onNativeEvent ${eventName}`)
|
||||
}
|
||||
|
||||
on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
import type * as s from '@entityseven/fivem-rpc-shared-types'
|
||||
import { Emitter } from '../utils/emitter'
|
||||
import { generateUUID, stringify } from '../utils/funcs'
|
||||
import {
|
||||
RPCEvents,
|
||||
type RPCConfig,
|
||||
type RPCState,
|
||||
type RPCStateRaw,
|
||||
type RPCStateWeb,
|
||||
} from '../utils/types'
|
||||
import { Wrapper } from './wrapper'
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
GetParentResourceName?: () => string
|
||||
}
|
||||
}
|
||||
|
||||
export class RPCInstanceWebview extends Wrapper {
|
||||
private readonly _emitterClient: Emitter
|
||||
private readonly _emitterServer: Emitter
|
||||
|
||||
constructor(props: RPCConfig<'webview'>) {
|
||||
super(props)
|
||||
|
||||
this._emitterClient = new Emitter()
|
||||
this._emitterServer = new Emitter()
|
||||
|
||||
this.console.log('[RPC] Initialized Webview')
|
||||
|
||||
window.addEventListener('message', (e: MessageEvent<RPCStateWeb>) => {
|
||||
if (e.data.origin === RPCEvents.LISTENER_CLIENT) {
|
||||
this._handleClient(e.data.data)
|
||||
}
|
||||
if (e.data.origin === RPCEvents.LISTENER_SERVER) {
|
||||
this._handleServer(e.data.data)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// ===== HANDLERS =====
|
||||
|
||||
private async _handleClient(payload: RPCState) {
|
||||
if (this.debug) {
|
||||
this.console.log(
|
||||
`[RPC]:webview:accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
|
||||
)
|
||||
}
|
||||
|
||||
if (payload.calledFrom === 'client' && payload.type === 'event') {
|
||||
this.verifyEvent(this._emitterClient, payload)
|
||||
|
||||
const responseData = await this._emitterClient.emit(
|
||||
payload.event,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
|
||||
const response: RPCState = {
|
||||
event: payload.event,
|
||||
uuid: payload.uuid,
|
||||
calledFrom: 'webview',
|
||||
calledTo: 'client',
|
||||
error: null,
|
||||
data: [responseData],
|
||||
player: payload.player,
|
||||
type: 'response',
|
||||
}
|
||||
|
||||
await this._createHttpClientRequest(response).then()
|
||||
}
|
||||
}
|
||||
|
||||
private async _handleServer(payload: RPCState) {
|
||||
if (this.debug) {
|
||||
this.console.log(
|
||||
`[RPC]:webview:accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
|
||||
)
|
||||
}
|
||||
|
||||
if (payload.calledFrom === 'server' && payload.type === 'event') {
|
||||
this.verifyEvent(this._emitterServer, payload)
|
||||
|
||||
const responseData = await this._emitterServer.emit(
|
||||
payload.event,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
|
||||
const response: RPCState = {
|
||||
event: payload.event,
|
||||
uuid: payload.uuid,
|
||||
calledFrom: 'webview',
|
||||
calledTo: 'server',
|
||||
error: null,
|
||||
data: [responseData],
|
||||
player: payload.player,
|
||||
type: 'response',
|
||||
}
|
||||
|
||||
await this._createHttpClientRequest(response)
|
||||
}
|
||||
}
|
||||
|
||||
// ===== CLIENT =====
|
||||
|
||||
public onClient<
|
||||
EventName extends keyof s.RPCEvents_ClientWebview,
|
||||
CallbackArguments extends Parameters<s.RPCEvents_ClientWebview[EventName]>,
|
||||
CallbackReturn extends ReturnType<s.RPCEvents_ClientWebview[EventName]>,
|
||||
>(
|
||||
eventName: EventName,
|
||||
cb: (
|
||||
...args: CallbackArguments
|
||||
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onClient ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterClient.on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public offClient<EventName extends keyof s.RPCEvents_ClientWebview>(
|
||||
eventName: EventName,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:offClient ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterClient.off(eventName)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async emitClient<
|
||||
EventName extends keyof s.RPCEvents_WebviewClient,
|
||||
Arguments extends Parameters<s.RPCEvents_WebviewClient[EventName]>,
|
||||
Response extends ReturnType<s.RPCEvents_WebviewClient[EventName]>,
|
||||
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'webview',
|
||||
calledTo: 'client',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: null,
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
return await this._createHttpClientRequest<Awaited<Response>>(payload)
|
||||
}
|
||||
|
||||
// ===== SERVER =====
|
||||
|
||||
public onServer<
|
||||
EventName extends keyof s.RPCEvents_ServerWebview,
|
||||
CallbackArguments extends Parameters<s.RPCEvents_ServerWebview[EventName]>,
|
||||
CallbackReturn extends ReturnType<s.RPCEvents_ServerWebview[EventName]>,
|
||||
>(
|
||||
eventName: EventName,
|
||||
cb: (
|
||||
...args: CallbackArguments
|
||||
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onServer ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterServer.on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public offServer<EventName extends keyof s.RPCEvents_ServerWebview>(
|
||||
eventName: EventName,
|
||||
): RPCInstanceWebview {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:offServer ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterServer.off(eventName)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async emitServer<
|
||||
EventName extends keyof s.RPCEvents_WebviewServer,
|
||||
Arguments extends Parameters<s.RPCEvents_WebviewServer[EventName]>,
|
||||
Response extends ReturnType<s.RPCEvents_WebviewServer[EventName]>,
|
||||
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'webview',
|
||||
calledTo: 'server',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: null,
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
return await this._createHttpClientRequest<Awaited<Response>>(payload)
|
||||
}
|
||||
|
||||
// ===== SELF =====
|
||||
|
||||
public onSelf<
|
||||
EventName extends keyof s.RPCEvents_Webview,
|
||||
CallbackArguments extends Parameters<s.RPCEvents_Webview[EventName]>,
|
||||
CallbackReturn extends ReturnType<s.RPCEvents_Webview[EventName]>,
|
||||
>(
|
||||
eventName: EventName,
|
||||
cb: (
|
||||
...args: CallbackArguments
|
||||
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:onSelf ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterLocal.on(eventName, cb)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public offSelf<EventName extends keyof s.RPCEvents_Webview>(
|
||||
eventName: EventName,
|
||||
): this {
|
||||
if (this.debug) {
|
||||
this.console.log(`[RPC]:offSelf ${eventName}`)
|
||||
}
|
||||
|
||||
this._emitterLocal.off(eventName)
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
public async emitSelf<
|
||||
EventName extends keyof s.RPCEvents_Webview,
|
||||
Arguments extends Parameters<s.RPCEvents_Webview[EventName]>,
|
||||
Response extends ReturnType<s.RPCEvents_Webview[EventName]>,
|
||||
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
|
||||
const payload: RPCState = {
|
||||
event: eventName,
|
||||
uuid: generateUUID(),
|
||||
calledFrom: 'webview',
|
||||
calledTo: 'webview',
|
||||
error: null,
|
||||
data: args.length ? args : null,
|
||||
player: null,
|
||||
type: 'event',
|
||||
}
|
||||
|
||||
if (this.debug) {
|
||||
this.console.log(
|
||||
`[RPC]:accepted ${payload.event} from ${payload.calledFrom}`,
|
||||
)
|
||||
}
|
||||
|
||||
this.verifyEvent(this._emitterLocal, payload)
|
||||
|
||||
return await this._emitterLocal.emit<Awaited<Response>>(
|
||||
payload.event,
|
||||
...(payload.data && payload.data.length > 0 ? payload.data : []),
|
||||
)
|
||||
}
|
||||
|
||||
// ===== UTILS =====
|
||||
|
||||
private async _createHttpClientRequest<R>(
|
||||
data: RPCStateRaw | RPCState,
|
||||
): Promise<R> {
|
||||
const dataRaw = typeof data === 'string' ? data : stringify(data)
|
||||
const options = {
|
||||
method: 'post',
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=UTF-8',
|
||||
},
|
||||
body: dataRaw,
|
||||
}
|
||||
const resourceName = window?.GetParentResourceName?.() ?? 'nui-frame-app'
|
||||
return fetch(
|
||||
`https://${resourceName}/${RPCEvents.LISTENER_WEB}`,
|
||||
options,
|
||||
).then(res => res.json())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Emitter } from '../utils/emitter'
|
||||
import { parse } from '../utils/funcs'
|
||||
import {
|
||||
type RPCConfig,
|
||||
type RPCEnvironment,
|
||||
RPCErrors,
|
||||
type RPCState,
|
||||
type RPCStateRaw,
|
||||
} from '../utils/types'
|
||||
|
||||
export class Wrapper {
|
||||
protected env: RPCEnvironment
|
||||
protected _emitterLocal: Emitter
|
||||
protected debug: boolean
|
||||
protected console: Console
|
||||
|
||||
constructor(cfg: RPCConfig<RPCEnvironment>) {
|
||||
this.env = cfg.env
|
||||
this._emitterLocal = new Emitter()
|
||||
this.debug = cfg.debug ?? false
|
||||
this.console = console
|
||||
}
|
||||
|
||||
protected verifyEvent(state: Emitter, data: RPCStateRaw | RPCState) {
|
||||
const rpcData = typeof data === 'string' ? parse(data) : data
|
||||
|
||||
if (!state.has(rpcData.event)) {
|
||||
rpcData.error = RPCErrors.EVENT_NOT_REGISTERED
|
||||
this.triggerError(rpcData)
|
||||
}
|
||||
}
|
||||
|
||||
protected triggerError(rpcData: RPCState, error?: string): Error {
|
||||
const errorMessage = [
|
||||
`${rpcData.error}`,
|
||||
`Event: ${rpcData.event}`,
|
||||
`Uuid: ${rpcData.uuid}`,
|
||||
`From: ${rpcData.calledFrom}`,
|
||||
`To: ${rpcData.calledTo}`,
|
||||
`Player: ${rpcData.player}`,
|
||||
`Type: ${rpcData.type}`,
|
||||
`Data: ${rpcData.data}`,
|
||||
]
|
||||
|
||||
if (error) {
|
||||
errorMessage.push(`Info: ${error}`)
|
||||
}
|
||||
|
||||
throw new Error(errorMessage.join('\n | '))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { RPCInstanceClient } from './core/client'
|
||||
import { RPCInstanceServer } from './core/server'
|
||||
import { RPCInstanceWebview } from './core/webview'
|
||||
import { Wrapper } from './core/wrapper'
|
||||
import {
|
||||
type RPCConfig,
|
||||
type RPCEnvironment,
|
||||
type RPCEnvironmentResolved,
|
||||
RPCErrors,
|
||||
} from './utils/types'
|
||||
|
||||
/**
|
||||
* RPC Factory
|
||||
*
|
||||
* @example
|
||||
* // returns RPCInstanceServer
|
||||
* const rpc = new RPCFactory({ env: "server" }).get()
|
||||
*
|
||||
* @example
|
||||
* // returns RPCInstanceClient
|
||||
* const rpc = new RPCFactory({ env: "client" }).get()
|
||||
*
|
||||
* @example
|
||||
* // returns RPCInstanceWebview
|
||||
* const rpc = new RPCFactory({ env: "webview" }).get()
|
||||
*
|
||||
* @class
|
||||
*/
|
||||
class RPCFactory<T extends RPCEnvironment> extends Wrapper {
|
||||
private readonly operator:
|
||||
| RPCInstanceServer
|
||||
| RPCInstanceClient
|
||||
| RPCInstanceWebview
|
||||
|
||||
/**
|
||||
* Instance options
|
||||
* @param {object} opts - Options
|
||||
* @param {string} opts.env - Instance environment
|
||||
* @param {boolean} opts.debug - Show additional logs
|
||||
*/
|
||||
constructor(opts: RPCConfig<T>) {
|
||||
super(opts)
|
||||
|
||||
this.console.log('[RPC] Initializing...')
|
||||
|
||||
switch (opts.env) {
|
||||
case 'server':
|
||||
this.operator = new RPCInstanceServer(opts as RPCConfig<'server'>)
|
||||
break
|
||||
case 'client':
|
||||
this.operator = new RPCInstanceClient(opts as RPCConfig<'client'>)
|
||||
break
|
||||
case 'webview':
|
||||
this.operator = new RPCInstanceWebview(opts as RPCConfig<'webview'>)
|
||||
break
|
||||
default:
|
||||
throw new Error(RPCErrors.UNKNOWN_ENVIRONMENT)
|
||||
}
|
||||
}
|
||||
|
||||
public get(): RPCEnvironmentResolved<T> {
|
||||
return this.operator as RPCEnvironmentResolved<T>
|
||||
}
|
||||
}
|
||||
|
||||
export { RPCFactory }
|
||||
// export const rpcClient = new RPCFactory({ env: "client" }).get();
|
||||
// export const rpcServer = new RPCFactory({ env: "server" }).get();
|
||||
// export const rpcWebview = new RPCFactory({ env: "webview" }).get();
|
||||
export * from './utils/types'
|
||||
export * from './utils/native'
|
||||
export type * from './core/server'
|
||||
export type * from './core/client'
|
||||
export type * from './core/webview'
|
||||
@@ -0,0 +1,54 @@
|
||||
import { RPCErrors } from './types'
|
||||
|
||||
export class Emitter {
|
||||
/** Map<event, [callback function, once]> */
|
||||
private _storage: Map<string, [(...args: any[]) => any, boolean]>
|
||||
|
||||
constructor() {
|
||||
this._storage = new Map()
|
||||
}
|
||||
|
||||
get _raw_storage() {
|
||||
return this._storage
|
||||
}
|
||||
|
||||
public on(event: string, cb: (...args: any[]) => any): this {
|
||||
this._storage.set(event, [cb, false])
|
||||
return this
|
||||
}
|
||||
|
||||
public once(event: string, cb: (...args: any[]) => any): this {
|
||||
this._storage.set(event, [cb, true])
|
||||
return this
|
||||
}
|
||||
|
||||
public off(event: string): this {
|
||||
this._storage.delete(event)
|
||||
return this
|
||||
}
|
||||
|
||||
public has(event: string): boolean {
|
||||
return this._storage.has(event)
|
||||
}
|
||||
|
||||
public async emit<R>(event: string, ...args: any[]): Promise<R> {
|
||||
return new Promise((res, rej) => {
|
||||
if (!this._storage.has(event)) {
|
||||
rej(RPCErrors.EVENT_NOT_REGISTERED)
|
||||
}
|
||||
|
||||
const [cb, once] = this._storage.get(event) as [
|
||||
(...args: any[]) => any,
|
||||
boolean,
|
||||
]
|
||||
|
||||
if (once) {
|
||||
this._storage.delete(event)
|
||||
}
|
||||
|
||||
Promise.resolve(cb(...args))
|
||||
.then(res)
|
||||
.catch(rej)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import type {
|
||||
RPCState,
|
||||
RPCStateRaw,
|
||||
RPCStateWeb,
|
||||
RPCStateWebRaw,
|
||||
} from './types'
|
||||
|
||||
/**
|
||||
* **Internal**
|
||||
*
|
||||
* Typed data parser
|
||||
*/
|
||||
export function parse(data: RPCStateRaw): RPCState {
|
||||
return JSON.parse(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* **Internal**
|
||||
*
|
||||
* Typed data serializer
|
||||
*/
|
||||
export function stringify(data: RPCState): RPCStateRaw {
|
||||
return JSON.stringify(data) as RPCStateRaw
|
||||
}
|
||||
|
||||
// automatically parsed by FiveM
|
||||
// export function parseWeb(data: RPCStateWebRaw): RPCStateWeb {
|
||||
// return JSON.parse(data)
|
||||
// }
|
||||
|
||||
/**
|
||||
* **Internal**
|
||||
*
|
||||
* Typed data serializer
|
||||
*/
|
||||
export function stringifyWeb(data: RPCStateWeb): RPCStateWebRaw {
|
||||
return JSON.stringify(data) as RPCStateWebRaw
|
||||
}
|
||||
|
||||
/** **Internal** */
|
||||
export function generateUUID(): string {
|
||||
let uuid = ''
|
||||
let random = 0
|
||||
for (let i = 0; i < 32; i++) {
|
||||
random = (Math.random() * 16) | 0
|
||||
if (i === 8 || i === 12 || i === 16 || i === 20) uuid += '-'
|
||||
uuid += (i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(16)
|
||||
}
|
||||
return uuid
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
import type {
|
||||
RPCNativeClientEvents,
|
||||
RPCNativeClientNetworkEventsNames,
|
||||
RPCNativeServerEvents,
|
||||
} from './types'
|
||||
|
||||
/**
|
||||
* https://docs.fivem.net/docs/scripting-reference/events/server-events/
|
||||
* @readonly
|
||||
*/
|
||||
export const NATIVE_SERVER_EVENTS: readonly (keyof RPCNativeServerEvents)[] = [
|
||||
'entityCreated',
|
||||
'entityCreating',
|
||||
'entityRemoved',
|
||||
'onResourceListRefresh',
|
||||
'onResourceStart',
|
||||
'onResourceStarting',
|
||||
'onResourceStop',
|
||||
'onServerResourceStart',
|
||||
'onServerResourceStop',
|
||||
'playerConnecting',
|
||||
'playerEnteredScope',
|
||||
'playerJoining',
|
||||
'playerLeftScope',
|
||||
'ptFxEvent',
|
||||
'removeAllWeaponsEvent',
|
||||
'startProjectileEvent',
|
||||
'weaponDamageEvent',
|
||||
] as const
|
||||
|
||||
/**
|
||||
* https://docs.fivem.net/docs/scripting-reference/events/client-events/
|
||||
* @readonly
|
||||
*/
|
||||
export const NATIVE_CLIENT_EVENTS: readonly (keyof RPCNativeClientEvents)[] = [
|
||||
'entityDamaged',
|
||||
'gameEventTriggered',
|
||||
'mumbleConnected',
|
||||
'mumbleDisconnected',
|
||||
'onClientResourceStart',
|
||||
'onClientResourceStop',
|
||||
'onResourceStart',
|
||||
'onResourceStarting',
|
||||
'onResourceStop',
|
||||
'populationPedCreating',
|
||||
] as const
|
||||
|
||||
/**
|
||||
* https://docs.fivem.net/docs/game-references/game-events/
|
||||
* @readonly
|
||||
*/
|
||||
export const NATIVE_CLIENT_NETWORK_EVENTS: readonly RPCNativeClientNetworkEventsNames[] =
|
||||
[
|
||||
'CEventAcquaintancePed',
|
||||
'CEventAcquaintancePedDead',
|
||||
'CEventAcquaintancePedDislike',
|
||||
'CEventAcquaintancePedHate',
|
||||
'CEventAcquaintancePedLike',
|
||||
'CEventAcquaintancePedWanted',
|
||||
'CEventAgitated',
|
||||
'CEventAgitatedAction',
|
||||
'CEventCallForCover',
|
||||
'CEventCarUndriveable',
|
||||
'CEventClimbLadderOnRoute',
|
||||
'CEventClimbNavMeshOnRoute',
|
||||
'CEventCombatTaunt',
|
||||
'CEventCommunicateEvent',
|
||||
'CEventCopCarBeingStolen',
|
||||
'CEventCrimeCryForHelp',
|
||||
'CEventCrimeReported',
|
||||
'CEventDamage',
|
||||
'CEventDataDecisionMaker',
|
||||
'CEventDataFileMounter',
|
||||
'CEventDataResponseAggressiveRubberneck',
|
||||
'CEventDataResponseDeferToScenarioPointFlags',
|
||||
'CEventDataResponseFriendlyAimedAt',
|
||||
'CEventDataResponseFriendlyNearMiss',
|
||||
'CEventDataResponsePlayerDeath',
|
||||
'CEventDataResponsePoliceTaskWanted',
|
||||
'CEventDataResponseSwatTaskWanted',
|
||||
'CEventDataResponseTask',
|
||||
'CEventDataResponseTaskAgitated',
|
||||
'CEventDataResponseTaskCombat',
|
||||
'CEventDataResponseTaskCower',
|
||||
'CEventDataResponseTaskCrouch',
|
||||
'CEventDataResponseTaskDuckAndCover',
|
||||
'CEventDataResponseTaskEscapeBlast',
|
||||
'CEventDataResponseTaskEvasiveStep',
|
||||
'CEventDataResponseTaskExhaustedFlee',
|
||||
'CEventDataResponseTaskExplosion',
|
||||
'CEventDataResponseTaskFlee',
|
||||
'CEventDataResponseTaskFlyAway',
|
||||
'CEventDataResponseTaskGrowlAndFlee',
|
||||
'CEventDataResponseTaskGunAimedAt',
|
||||
'CEventDataResponseTaskHandsUp',
|
||||
'CEventDataResponseTaskHeadTrack',
|
||||
'CEventDataResponseTaskLeaveCarAndFlee',
|
||||
'CEventDataResponseTaskScenarioFlee',
|
||||
'CEventDataResponseTaskSharkAttack',
|
||||
'CEventDataResponseTaskShockingEventBackAway',
|
||||
'CEventDataResponseTaskShockingEventGoto',
|
||||
'CEventDataResponseTaskShockingEventHurryAway',
|
||||
'CEventDataResponseTaskShockingEventReact',
|
||||
'CEventDataResponseTaskShockingEventReactToAircraft',
|
||||
'CEventDataResponseTaskShockingEventStopAndStare',
|
||||
'CEventDataResponseTaskShockingEventThreatResponse',
|
||||
'CEventDataResponseTaskShockingEventWatch',
|
||||
'CEventDataResponseTaskShockingNiceCar',
|
||||
'CEventDataResponseTaskShockingPoliceInvestigate',
|
||||
'CEventDataResponseTaskThreat',
|
||||
'CEventDataResponseTaskTurnToFace',
|
||||
'CEventDataResponseTaskWalkAway',
|
||||
'CEventDataResponseTaskWalkRoundEntity',
|
||||
'CEventDataResponseTaskWalkRoundFire',
|
||||
'CEventDeadPedFound',
|
||||
'CEventDeath',
|
||||
'CEventDecisionMakerResponse',
|
||||
'CEventDisturbance',
|
||||
'CEventDraggedOutCar',
|
||||
'CEventEditableResponse',
|
||||
'CEventEncroachingPed',
|
||||
'CEventEntityDamaged',
|
||||
'CEventEntityDestroyed',
|
||||
'CEventExplosion',
|
||||
'CEventExplosionHeard',
|
||||
'CEventFireNearby',
|
||||
'CEventFootStepHeard',
|
||||
'CEventFriendlyAimedAt',
|
||||
'CEventFriendlyFireNearMiss',
|
||||
'CEventGetOutOfWater',
|
||||
'CEventGivePedTask',
|
||||
'CEventGroupScriptAI',
|
||||
'CEventGroupScriptNetwork',
|
||||
'CEventGunAimedAt',
|
||||
'CEventGunShot',
|
||||
'CEventGunShotBulletImpact',
|
||||
'CEventGunShotWhizzedBy',
|
||||
'CEventHelpAmbientFriend',
|
||||
'CEventHurtTransition',
|
||||
'CEventInAir',
|
||||
'CEventInfo',
|
||||
'CEventInfoBase',
|
||||
'CEventInjuredCryForHelp',
|
||||
'CEventLeaderEnteredCarAsDriver',
|
||||
'CEventLeaderExitedCarAsDriver',
|
||||
'CEventLeaderHolsteredWeapon',
|
||||
'CEventLeaderLeftCover',
|
||||
'CEventLeaderUnholsteredWeapon',
|
||||
'CEventMeleeAction',
|
||||
'CEventMustLeaveBoat',
|
||||
'CEventNetworkAdminInvited',
|
||||
'CEventNetworkAttemptHostMigration',
|
||||
'CEventNetworkBail',
|
||||
'CEventNetworkCashTransactionLog',
|
||||
'CEventNetworkCheatTriggered',
|
||||
'CEventNetworkClanInviteReceived',
|
||||
'CEventNetworkClanJoined',
|
||||
'CEventNetworkClanKicked',
|
||||
'CEventNetworkClanLeft',
|
||||
'CEventNetworkClanRankChanged',
|
||||
'CEventNetworkCloudEvent',
|
||||
'CEventNetworkCloudFileResponse',
|
||||
'CEventNetworkEmailReceivedEvent',
|
||||
'CEventNetworkEndMatch',
|
||||
'CEventNetworkEndSession',
|
||||
'CEventNetworkEntityDamage',
|
||||
'CEventNetworkFindSession',
|
||||
'CEventNetworkFollowInviteReceived',
|
||||
'CEventNetworkHostMigration',
|
||||
'CEventNetworkHostSession',
|
||||
'CEventNetworkIncrementStat',
|
||||
'CEventNetworkInviteAccepted',
|
||||
'CEventNetworkInviteConfirmed',
|
||||
'CEventNetworkInviteRejected',
|
||||
'CEventNetworkJoinSession',
|
||||
'CEventNetworkJoinSessionResponse',
|
||||
'CEventNetworkOnlinePermissionsUpdated',
|
||||
'CEventNetworkPedLeftBehind',
|
||||
'CEventNetworkPickupRespawned',
|
||||
'CEventNetworkPlayerArrest',
|
||||
'CEventNetworkPlayerCollectedAmbientPickup',
|
||||
'CEventNetworkPlayerCollectedPickup',
|
||||
'CEventNetworkPlayerCollectedPortablePickup',
|
||||
'CEventNetworkPlayerDroppedPortablePickup',
|
||||
'CEventNetworkPlayerEnteredVehicle',
|
||||
'CEventNetworkPlayerJoinScript',
|
||||
'CEventNetworkPlayerLeftScript',
|
||||
'CEventNetworkPlayerScript',
|
||||
'CEventNetworkPlayerSession',
|
||||
'CEventNetworkPlayerSpawn',
|
||||
'CEventNetworkPresenceInvite',
|
||||
'CEventNetworkPresenceInviteRemoved',
|
||||
'CEventNetworkPresenceInviteReply',
|
||||
'CEventNetworkPresenceTriggerEvent',
|
||||
'CEventNetworkPresence_StatUpdate',
|
||||
'CEventNetworkPrimaryClanChanged',
|
||||
'CEventNetworkRequestDelay',
|
||||
'CEventNetworkRosChanged',
|
||||
'CEventNetworkScAdminPlayerUpdated',
|
||||
'CEventNetworkScAdminReceivedCash',
|
||||
'CEventNetworkScriptEvent',
|
||||
'CEventNetworkSessionEvent',
|
||||
'CEventNetworkShopTransaction',
|
||||
'CEventNetworkSignInStateChanged',
|
||||
'CEventNetworkSocialClubAccountLinked',
|
||||
'CEventNetworkSpectateLocal',
|
||||
'CEventNetworkStartMatch',
|
||||
'CEventNetworkStartSession',
|
||||
'CEventNetworkStorePlayerLeft',
|
||||
'CEventNetworkSummon',
|
||||
'CEventNetworkSystemServiceEvent',
|
||||
'CEventNetworkTextMessageReceived',
|
||||
'CEventNetworkTimedExplosion',
|
||||
'CEventNetworkTransitionEvent',
|
||||
'CEventNetworkTransitionGamerInstruction',
|
||||
'CEventNetworkTransitionMemberJoined',
|
||||
'CEventNetworkTransitionMemberLeft',
|
||||
'CEventNetworkTransitionParameterChanged',
|
||||
'CEventNetworkTransitionStarted',
|
||||
'CEventNetworkTransitionStringChanged',
|
||||
'CEventNetworkVehicleUndrivable',
|
||||
'CEventNetworkVoiceConnectionRequested',
|
||||
'CEventNetworkVoiceConnectionResponse',
|
||||
'CEventNetworkVoiceConnectionTerminated',
|
||||
'CEventNetworkVoiceSessionEnded',
|
||||
'CEventNetworkVoiceSessionStarted',
|
||||
'CEventNetworkWithData',
|
||||
'CEventNetwork_InboxMsgReceived',
|
||||
'CEventNewTask',
|
||||
'CEventObjectCollision',
|
||||
'CEventOnFire',
|
||||
'CEventOpenDoor',
|
||||
'CEventPedCollisionWithPed',
|
||||
'CEventPedCollisionWithPlayer',
|
||||
'CEventPedEnteredMyVehicle',
|
||||
'CEventPedJackingMyVehicle',
|
||||
'CEventPedOnCarRoof',
|
||||
'CEventPedSeenDeadPed',
|
||||
'CEventPlayerCollisionWithPed',
|
||||
'CEventPlayerDeath',
|
||||
'CEventPlayerUnableToEnterVehicle',
|
||||
'CEventPotentialBeWalkedInto',
|
||||
'CEventPotentialBlast',
|
||||
'CEventPotentialGetRunOver',
|
||||
'CEventPotentialWalkIntoVehicle',
|
||||
'CEventProvidingCover',
|
||||
'CEventRanOverPed',
|
||||
'CEventReactionEnemyPed',
|
||||
'CEventReactionInvestigateDeadPed',
|
||||
'CEventReactionInvestigateThreat',
|
||||
'CEventRequestHelp',
|
||||
'CEventRequestHelpWithConfrontation',
|
||||
'CEventRespondedToThreat',
|
||||
'CEventScanner',
|
||||
'CEventScenarioForceAction',
|
||||
'CEventScriptCommand',
|
||||
'CEventScriptWithData',
|
||||
'CEventShocking',
|
||||
'CEventShockingBicycleCrash',
|
||||
'CEventShockingBicycleOnPavement',
|
||||
'CEventShockingCarAlarm',
|
||||
'CEventShockingCarChase',
|
||||
'CEventShockingCarCrash',
|
||||
'CEventShockingCarOnCar',
|
||||
'CEventShockingCarPileUp',
|
||||
'CEventShockingDangerousAnimal',
|
||||
'CEventShockingDeadBody',
|
||||
'CEventShockingDrivingOnPavement',
|
||||
'CEventShockingEngineRevved',
|
||||
'CEventShockingExplosion',
|
||||
'CEventShockingFire',
|
||||
'CEventShockingGunFight',
|
||||
'CEventShockingGunshotFired',
|
||||
'CEventShockingHelicopterOverhead',
|
||||
'CEventShockingHornSounded',
|
||||
'CEventShockingInDangerousVehicle',
|
||||
'CEventShockingInjuredPed',
|
||||
'CEventShockingMadDriver',
|
||||
'CEventShockingMadDriverBicycle',
|
||||
'CEventShockingMadDriverExtreme',
|
||||
'CEventShockingMugging',
|
||||
'CEventShockingNonViolentWeaponAimedAt',
|
||||
'CEventShockingParachuterOverhead',
|
||||
'CEventShockingPedKnockedIntoByPlayer',
|
||||
'CEventShockingPedRunOver',
|
||||
'CEventShockingPedShot',
|
||||
'CEventShockingPlaneFlyby',
|
||||
'CEventShockingPotentialBlast',
|
||||
'CEventShockingPropertyDamage',
|
||||
'CEventShockingRunningPed',
|
||||
'CEventShockingRunningStampede',
|
||||
'CEventShockingSeenCarStolen',
|
||||
'CEventShockingSeenConfrontation',
|
||||
'CEventShockingSeenGangFight',
|
||||
'CEventShockingSeenInsult',
|
||||
'CEventShockingSeenMeleeAction',
|
||||
'CEventShockingSeenNiceCar',
|
||||
'CEventShockingSeenPedKilled',
|
||||
'CEventShockingSiren',
|
||||
'CEventShockingStudioBomb',
|
||||
'CEventShockingVehicleTowed',
|
||||
'CEventShockingVisibleWeapon',
|
||||
'CEventShockingWeaponThreat',
|
||||
'CEventShockingWeirdPed',
|
||||
'CEventShockingWeirdPedApproaching',
|
||||
'CEventShoutBlockingLos',
|
||||
'CEventShoutTargetPosition',
|
||||
'CEventShovePed',
|
||||
'CEventSoundBase',
|
||||
'CEventStatChangedValue',
|
||||
'CEventStaticCountReachedMax',
|
||||
'CEventStuckInAir',
|
||||
'CEventSuspiciousActivity',
|
||||
'CEventSwitch2NM',
|
||||
'CEventUnidentifiedPed',
|
||||
'CEventVehicleCollision',
|
||||
'CEventVehicleDamage',
|
||||
'CEventVehicleDamageWeapon',
|
||||
'CEventVehicleOnFire',
|
||||
'CEventWrithe',
|
||||
] as const
|
||||
@@ -0,0 +1,535 @@
|
||||
import type { RPCInstanceClient } from '../core/client'
|
||||
import type { RPCInstanceServer } from '../core/server'
|
||||
import type { RPCInstanceWebview } from '../core/webview'
|
||||
|
||||
/**
|
||||
* Possible environment states for `RPCConfig`
|
||||
*/
|
||||
export type RPCEnvironment = 'server' | 'client' | 'webview'
|
||||
|
||||
export type RPCEnvironmentResolved<T extends RPCEnvironment> =
|
||||
T extends 'server'
|
||||
? RPCInstanceServer
|
||||
: T extends 'client'
|
||||
? RPCInstanceClient
|
||||
: T extends 'webview'
|
||||
? RPCInstanceWebview
|
||||
: never
|
||||
|
||||
/**
|
||||
* `RPCFactory` config.
|
||||
*
|
||||
* If environment does not match will throw `RPCErrors.UNKNOWN_ENVIRONMENT`
|
||||
*/
|
||||
export type RPCConfig<T extends RPCEnvironment | unknown> = {
|
||||
env: T
|
||||
debug?: boolean
|
||||
}
|
||||
|
||||
/** **Internal** */
|
||||
export type RPCEventType = 'event' | 'response'
|
||||
|
||||
/**
|
||||
* **Internal**
|
||||
*
|
||||
* Similar to what Errors look like
|
||||
*/
|
||||
export type RPCState = {
|
||||
event: string
|
||||
uuid: string
|
||||
calledFrom: RPCEnvironment
|
||||
calledTo: RPCEnvironment
|
||||
error: string | null
|
||||
data: unknown[] | null
|
||||
player: number | null
|
||||
type: RPCEventType
|
||||
}
|
||||
|
||||
/**
|
||||
* **Internal**
|
||||
*
|
||||
* `JSON.stringify` version of `RPCState`. Makes TS think this is not type `string` for better dx
|
||||
*/
|
||||
export type RPCStateRaw = string & { __brand: 'RPCStateRaw' }
|
||||
|
||||
/** Internal */
|
||||
export type RPCStateWeb = {
|
||||
origin: RPCEvents
|
||||
data: RPCState
|
||||
}
|
||||
|
||||
/**
|
||||
* **Internal**
|
||||
*
|
||||
* `JSON.stringify` version of `RPCStateWeb`. Makes TS think this is not type `string` for better dx
|
||||
*/
|
||||
export type RPCStateWebRaw = string & { __brand: 'RPCWebStateRaw' }
|
||||
|
||||
/**
|
||||
* **Internal**
|
||||
*
|
||||
* Do not create same listeners to avoid unexpected behaviour
|
||||
*/
|
||||
export enum RPCEvents {
|
||||
LISTENER_SERVER = '__rpc:listenerServer',
|
||||
LISTENER_CLIENT = '__rpc:listenerClient',
|
||||
LISTENER_WEB = '__rpc:listenerWeb',
|
||||
}
|
||||
|
||||
/**
|
||||
* Errors to check against
|
||||
*/
|
||||
export enum RPCErrors {
|
||||
EVENT_NOT_REGISTERED = 'Event not registered',
|
||||
INVALID_DATA = 'Invalid data (possibly broken JSON)',
|
||||
NO_PLAYER = 'No player (failed to resolve from local index)',
|
||||
UNKNOWN_NATIVE = 'Unknown native event (if you are sure this exists - use native handler)',
|
||||
UNKNOWN_ENVIRONMENT = 'Unknown environment (must be either "server", "client" or "webview")',
|
||||
}
|
||||
|
||||
/**
|
||||
* https://docs.fivem.net/docs/scripting-reference/events/server-events/
|
||||
*/
|
||||
export type RPCNativeServerEvents = {
|
||||
entityCreated(handle: number): void
|
||||
entityCreating(handle: number): void
|
||||
entityRemoved(entity: number): void
|
||||
onResourceListRefresh(): void
|
||||
onResourceStart(resource: string): void
|
||||
onResourceStarting(resource: string): void
|
||||
onResourceStop(resource: string): void
|
||||
onServerResourceStart(resource: string): void
|
||||
onServerResourceStop(resource: string): void
|
||||
playerConnecting(
|
||||
playerName: string,
|
||||
setKickReason: (reason: string) => void,
|
||||
deferrals: {
|
||||
defer: () => void
|
||||
done: (failureReason?: string) => void
|
||||
handover: (data: Record<string, unknown>) => void
|
||||
presentCard: (
|
||||
card: string | object,
|
||||
cb?: (data: unknown, rawData: string) => void,
|
||||
) => void
|
||||
update: (message: string) => void
|
||||
},
|
||||
source: number,
|
||||
): void
|
||||
playerEnteredScope(data: { for: string; player: string }): void
|
||||
playerJoining(source: string, oldID: string): void
|
||||
playerLeftScope(data: { for: string; player: string }): void
|
||||
ptFxEvent(
|
||||
sender: number,
|
||||
data: {
|
||||
assetHash: number
|
||||
axisBitset: number
|
||||
effectHash: number
|
||||
entityNetId: number
|
||||
f100: number
|
||||
f105: number
|
||||
f106: number
|
||||
f107: number
|
||||
f109: boolean
|
||||
f110: boolean
|
||||
f111: boolean
|
||||
f92: number
|
||||
isOnEntity: boolean
|
||||
offsetX: number
|
||||
offsetY: number
|
||||
offsetZ: number
|
||||
posX: number
|
||||
posY: number
|
||||
posZ: number
|
||||
rotX: number
|
||||
rotY: number
|
||||
rotZ: number
|
||||
scale: number
|
||||
},
|
||||
): void
|
||||
removeAllWeaponsEvent(sender: number, data: { pedId: number }): void
|
||||
startProjectileEvent(
|
||||
sender: number,
|
||||
data: {
|
||||
commandFireSingleBullet: boolean
|
||||
effectGroup: number
|
||||
firePositionX: number
|
||||
firePositionY: number
|
||||
firePositionZ: number
|
||||
initialPositionX: number
|
||||
initialPositionY: number
|
||||
initialPositionZ: number
|
||||
ownerId: number
|
||||
projectileHash: number
|
||||
targetEntity: number
|
||||
throwTaskSequence: number
|
||||
unk10: number
|
||||
unk11: number
|
||||
unk12: number
|
||||
unk13: number
|
||||
unk14: number
|
||||
unk15: number
|
||||
unk16: number
|
||||
unk3: number
|
||||
unk4: number
|
||||
unk5: number
|
||||
unk6: number
|
||||
unk7: number
|
||||
unk9: number
|
||||
unkX8: number
|
||||
unkY8: number
|
||||
unkZ8: number
|
||||
weaponHash: number
|
||||
},
|
||||
): void
|
||||
weaponDamageEvent(
|
||||
sender: number,
|
||||
data: {
|
||||
actionResultId: number
|
||||
actionResultName: number
|
||||
damageFlags: number
|
||||
damageTime: number
|
||||
damageType: number
|
||||
f104: number
|
||||
f112: boolean
|
||||
f112_1: number
|
||||
f120: number
|
||||
f133: boolean
|
||||
hasActionResult: boolean
|
||||
hasImpactDir: boolean
|
||||
hasVehicleData: boolean
|
||||
hitComponent: number
|
||||
hitEntityWeapon: boolean
|
||||
hitGlobalId: number
|
||||
hitGlobalIds: number[]
|
||||
hitWeaponAmmoAttachment: boolean
|
||||
impactDirX: number
|
||||
impactDirY: number
|
||||
impactDirZ: number
|
||||
isNetTargetPos: boolean
|
||||
localPosX: number
|
||||
localPosY: number
|
||||
localPosZ: number
|
||||
overrideDefaultDamage: boolean
|
||||
parentGlobalId: number
|
||||
silenced: boolean
|
||||
suspensionIndex: number
|
||||
tyreIndex: number
|
||||
weaponDamage: number
|
||||
weaponType: number
|
||||
willKill: boolean
|
||||
},
|
||||
): void
|
||||
}
|
||||
|
||||
/**
|
||||
* https://docs.fivem.net/docs/scripting-reference/events/client-events/
|
||||
*/
|
||||
export type RPCNativeClientEvents = {
|
||||
entityDamaged(
|
||||
victim: number,
|
||||
culprit: number,
|
||||
weapon: number,
|
||||
baseDamage: number,
|
||||
): void
|
||||
gameEventTriggered(
|
||||
name: RPCNativeClientNetworksEvents | string,
|
||||
data: number[],
|
||||
): void
|
||||
mumbleConnected(address: string, reconnecting: boolean): void
|
||||
mumbleDisconnected(address: string): void
|
||||
onClientResourceStart(resource: string): void
|
||||
onClientResourceStop(resource: string): void
|
||||
onResourceStart(resource: string): void
|
||||
onResourceStarting(resource: string): void
|
||||
onResourceStop(resource: string): void
|
||||
populationPedCreating(
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
model: number,
|
||||
overrideCalls: {
|
||||
setModel: (model: string | number) => void
|
||||
setPosition: (x: number, y: number, z: number) => void
|
||||
},
|
||||
): void
|
||||
}
|
||||
|
||||
export type RPCNativeClientNetworksEvents = {
|
||||
[name in RPCNativeClientNetworkEventsNames]: (
|
||||
entities: number[],
|
||||
eventEntity: number,
|
||||
data: unknown[],
|
||||
) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* https://docs.fivem.net/docs/game-references/game-events/
|
||||
*/
|
||||
export type RPCNativeClientNetworkEventsNames =
|
||||
| 'CEventAcquaintancePed'
|
||||
| 'CEventAcquaintancePedDead'
|
||||
| 'CEventAcquaintancePedDislike'
|
||||
| 'CEventAcquaintancePedHate'
|
||||
| 'CEventAcquaintancePedLike'
|
||||
| 'CEventAcquaintancePedWanted'
|
||||
| 'CEventAgitated'
|
||||
| 'CEventAgitatedAction'
|
||||
| 'CEventCallForCover'
|
||||
| 'CEventCarUndriveable'
|
||||
| 'CEventClimbLadderOnRoute'
|
||||
| 'CEventClimbNavMeshOnRoute'
|
||||
| 'CEventCombatTaunt'
|
||||
| 'CEventCommunicateEvent'
|
||||
| 'CEventCopCarBeingStolen'
|
||||
| 'CEventCrimeCryForHelp'
|
||||
| 'CEventCrimeReported'
|
||||
| 'CEventDamage'
|
||||
| 'CEventDataDecisionMaker'
|
||||
| 'CEventDataFileMounter'
|
||||
| 'CEventDataResponseAggressiveRubberneck'
|
||||
| 'CEventDataResponseDeferToScenarioPointFlags'
|
||||
| 'CEventDataResponseFriendlyAimedAt'
|
||||
| 'CEventDataResponseFriendlyNearMiss'
|
||||
| 'CEventDataResponsePlayerDeath'
|
||||
| 'CEventDataResponsePoliceTaskWanted'
|
||||
| 'CEventDataResponseSwatTaskWanted'
|
||||
| 'CEventDataResponseTask'
|
||||
| 'CEventDataResponseTaskAgitated'
|
||||
| 'CEventDataResponseTaskCombat'
|
||||
| 'CEventDataResponseTaskCower'
|
||||
| 'CEventDataResponseTaskCrouch'
|
||||
| 'CEventDataResponseTaskDuckAndCover'
|
||||
| 'CEventDataResponseTaskEscapeBlast'
|
||||
| 'CEventDataResponseTaskEvasiveStep'
|
||||
| 'CEventDataResponseTaskExhaustedFlee'
|
||||
| 'CEventDataResponseTaskExplosion'
|
||||
| 'CEventDataResponseTaskFlee'
|
||||
| 'CEventDataResponseTaskFlyAway'
|
||||
| 'CEventDataResponseTaskGrowlAndFlee'
|
||||
| 'CEventDataResponseTaskGunAimedAt'
|
||||
| 'CEventDataResponseTaskHandsUp'
|
||||
| 'CEventDataResponseTaskHeadTrack'
|
||||
| 'CEventDataResponseTaskLeaveCarAndFlee'
|
||||
| 'CEventDataResponseTaskScenarioFlee'
|
||||
| 'CEventDataResponseTaskSharkAttack'
|
||||
| 'CEventDataResponseTaskShockingEventBackAway'
|
||||
| 'CEventDataResponseTaskShockingEventGoto'
|
||||
| 'CEventDataResponseTaskShockingEventHurryAway'
|
||||
| 'CEventDataResponseTaskShockingEventReact'
|
||||
| 'CEventDataResponseTaskShockingEventReactToAircraft'
|
||||
| 'CEventDataResponseTaskShockingEventStopAndStare'
|
||||
| 'CEventDataResponseTaskShockingEventThreatResponse'
|
||||
| 'CEventDataResponseTaskShockingEventWatch'
|
||||
| 'CEventDataResponseTaskShockingNiceCar'
|
||||
| 'CEventDataResponseTaskShockingPoliceInvestigate'
|
||||
| 'CEventDataResponseTaskThreat'
|
||||
| 'CEventDataResponseTaskTurnToFace'
|
||||
| 'CEventDataResponseTaskWalkAway'
|
||||
| 'CEventDataResponseTaskWalkRoundEntity'
|
||||
| 'CEventDataResponseTaskWalkRoundFire'
|
||||
| 'CEventDeadPedFound'
|
||||
| 'CEventDeath'
|
||||
| 'CEventDecisionMakerResponse'
|
||||
| 'CEventDisturbance'
|
||||
| 'CEventDraggedOutCar'
|
||||
| 'CEventEditableResponse'
|
||||
| 'CEventEncroachingPed'
|
||||
| 'CEventEntityDamaged'
|
||||
| 'CEventEntityDestroyed'
|
||||
| 'CEventExplosion'
|
||||
| 'CEventExplosionHeard'
|
||||
| 'CEventFireNearby'
|
||||
| 'CEventFootStepHeard'
|
||||
| 'CEventFriendlyAimedAt'
|
||||
| 'CEventFriendlyFireNearMiss'
|
||||
| 'CEventGetOutOfWater'
|
||||
| 'CEventGivePedTask'
|
||||
| 'CEventGroupScriptAI'
|
||||
| 'CEventGroupScriptNetwork'
|
||||
| 'CEventGunAimedAt'
|
||||
| 'CEventGunShot'
|
||||
| 'CEventGunShotBulletImpact'
|
||||
| 'CEventGunShotWhizzedBy'
|
||||
| 'CEventHelpAmbientFriend'
|
||||
| 'CEventHurtTransition'
|
||||
| 'CEventInAir'
|
||||
| 'CEventInfo'
|
||||
| 'CEventInfoBase'
|
||||
| 'CEventInjuredCryForHelp'
|
||||
| 'CEventLeaderEnteredCarAsDriver'
|
||||
| 'CEventLeaderExitedCarAsDriver'
|
||||
| 'CEventLeaderHolsteredWeapon'
|
||||
| 'CEventLeaderLeftCover'
|
||||
| 'CEventLeaderUnholsteredWeapon'
|
||||
| 'CEventMeleeAction'
|
||||
| 'CEventMustLeaveBoat'
|
||||
| 'CEventNetworkAdminInvited'
|
||||
| 'CEventNetworkAttemptHostMigration'
|
||||
| 'CEventNetworkBail'
|
||||
| 'CEventNetworkCashTransactionLog'
|
||||
| 'CEventNetworkCheatTriggered'
|
||||
| 'CEventNetworkClanInviteReceived'
|
||||
| 'CEventNetworkClanJoined'
|
||||
| 'CEventNetworkClanKicked'
|
||||
| 'CEventNetworkClanLeft'
|
||||
| 'CEventNetworkClanRankChanged'
|
||||
| 'CEventNetworkCloudEvent'
|
||||
| 'CEventNetworkCloudFileResponse'
|
||||
| 'CEventNetworkEmailReceivedEvent'
|
||||
| 'CEventNetworkEndMatch'
|
||||
| 'CEventNetworkEndSession'
|
||||
| 'CEventNetworkEntityDamage'
|
||||
| 'CEventNetworkFindSession'
|
||||
| 'CEventNetworkFollowInviteReceived'
|
||||
| 'CEventNetworkHostMigration'
|
||||
| 'CEventNetworkHostSession'
|
||||
| 'CEventNetworkIncrementStat'
|
||||
| 'CEventNetworkInviteAccepted'
|
||||
| 'CEventNetworkInviteConfirmed'
|
||||
| 'CEventNetworkInviteRejected'
|
||||
| 'CEventNetworkJoinSession'
|
||||
| 'CEventNetworkJoinSessionResponse'
|
||||
| 'CEventNetworkOnlinePermissionsUpdated'
|
||||
| 'CEventNetworkPedLeftBehind'
|
||||
| 'CEventNetworkPickupRespawned'
|
||||
| 'CEventNetworkPlayerArrest'
|
||||
| 'CEventNetworkPlayerCollectedAmbientPickup'
|
||||
| 'CEventNetworkPlayerCollectedPickup'
|
||||
| 'CEventNetworkPlayerCollectedPortablePickup'
|
||||
| 'CEventNetworkPlayerDroppedPortablePickup'
|
||||
| 'CEventNetworkPlayerEnteredVehicle'
|
||||
| 'CEventNetworkPlayerJoinScript'
|
||||
| 'CEventNetworkPlayerLeftScript'
|
||||
| 'CEventNetworkPlayerScript'
|
||||
| 'CEventNetworkPlayerSession'
|
||||
| 'CEventNetworkPlayerSpawn'
|
||||
| 'CEventNetworkPresenceInvite'
|
||||
| 'CEventNetworkPresenceInviteRemoved'
|
||||
| 'CEventNetworkPresenceInviteReply'
|
||||
| 'CEventNetworkPresenceTriggerEvent'
|
||||
| 'CEventNetworkPresence_StatUpdate'
|
||||
| 'CEventNetworkPrimaryClanChanged'
|
||||
| 'CEventNetworkRequestDelay'
|
||||
| 'CEventNetworkRosChanged'
|
||||
| 'CEventNetworkScAdminPlayerUpdated'
|
||||
| 'CEventNetworkScAdminReceivedCash'
|
||||
| 'CEventNetworkScriptEvent'
|
||||
| 'CEventNetworkSessionEvent'
|
||||
| 'CEventNetworkShopTransaction'
|
||||
| 'CEventNetworkSignInStateChanged'
|
||||
| 'CEventNetworkSocialClubAccountLinked'
|
||||
| 'CEventNetworkSpectateLocal'
|
||||
| 'CEventNetworkStartMatch'
|
||||
| 'CEventNetworkStartSession'
|
||||
| 'CEventNetworkStorePlayerLeft'
|
||||
| 'CEventNetworkSummon'
|
||||
| 'CEventNetworkSystemServiceEvent'
|
||||
| 'CEventNetworkTextMessageReceived'
|
||||
| 'CEventNetworkTimedExplosion'
|
||||
| 'CEventNetworkTransitionEvent'
|
||||
| 'CEventNetworkTransitionGamerInstruction'
|
||||
| 'CEventNetworkTransitionMemberJoined'
|
||||
| 'CEventNetworkTransitionMemberLeft'
|
||||
| 'CEventNetworkTransitionParameterChanged'
|
||||
| 'CEventNetworkTransitionStarted'
|
||||
| 'CEventNetworkTransitionStringChanged'
|
||||
| 'CEventNetworkVehicleUndrivable'
|
||||
| 'CEventNetworkVoiceConnectionRequested'
|
||||
| 'CEventNetworkVoiceConnectionResponse'
|
||||
| 'CEventNetworkVoiceConnectionTerminated'
|
||||
| 'CEventNetworkVoiceSessionEnded'
|
||||
| 'CEventNetworkVoiceSessionStarted'
|
||||
| 'CEventNetworkWithData'
|
||||
| 'CEventNetwork_InboxMsgReceived'
|
||||
| 'CEventNewTask'
|
||||
| 'CEventObjectCollision'
|
||||
| 'CEventOnFire'
|
||||
| 'CEventOpenDoor'
|
||||
| 'CEventPedCollisionWithPed'
|
||||
| 'CEventPedCollisionWithPlayer'
|
||||
| 'CEventPedEnteredMyVehicle'
|
||||
| 'CEventPedJackingMyVehicle'
|
||||
| 'CEventPedOnCarRoof'
|
||||
| 'CEventPedSeenDeadPed'
|
||||
| 'CEventPlayerCollisionWithPed'
|
||||
| 'CEventPlayerDeath'
|
||||
| 'CEventPlayerUnableToEnterVehicle'
|
||||
| 'CEventPotentialBeWalkedInto'
|
||||
| 'CEventPotentialBlast'
|
||||
| 'CEventPotentialGetRunOver'
|
||||
| 'CEventPotentialWalkIntoVehicle'
|
||||
| 'CEventProvidingCover'
|
||||
| 'CEventRanOverPed'
|
||||
| 'CEventReactionEnemyPed'
|
||||
| 'CEventReactionInvestigateDeadPed'
|
||||
| 'CEventReactionInvestigateThreat'
|
||||
| 'CEventRequestHelp'
|
||||
| 'CEventRequestHelpWithConfrontation'
|
||||
| 'CEventRespondedToThreat'
|
||||
| 'CEventScanner'
|
||||
| 'CEventScenarioForceAction'
|
||||
| 'CEventScriptCommand'
|
||||
| 'CEventScriptWithData'
|
||||
| 'CEventShocking'
|
||||
| 'CEventShockingBicycleCrash'
|
||||
| 'CEventShockingBicycleOnPavement'
|
||||
| 'CEventShockingCarAlarm'
|
||||
| 'CEventShockingCarChase'
|
||||
| 'CEventShockingCarCrash'
|
||||
| 'CEventShockingCarOnCar'
|
||||
| 'CEventShockingCarPileUp'
|
||||
| 'CEventShockingDangerousAnimal'
|
||||
| 'CEventShockingDeadBody'
|
||||
| 'CEventShockingDrivingOnPavement'
|
||||
| 'CEventShockingEngineRevved'
|
||||
| 'CEventShockingExplosion'
|
||||
| 'CEventShockingFire'
|
||||
| 'CEventShockingGunFight'
|
||||
| 'CEventShockingGunshotFired'
|
||||
| 'CEventShockingHelicopterOverhead'
|
||||
| 'CEventShockingHornSounded'
|
||||
| 'CEventShockingInDangerousVehicle'
|
||||
| 'CEventShockingInjuredPed'
|
||||
| 'CEventShockingMadDriver'
|
||||
| 'CEventShockingMadDriverBicycle'
|
||||
| 'CEventShockingMadDriverExtreme'
|
||||
| 'CEventShockingMugging'
|
||||
| 'CEventShockingNonViolentWeaponAimedAt'
|
||||
| 'CEventShockingParachuterOverhead'
|
||||
| 'CEventShockingPedKnockedIntoByPlayer'
|
||||
| 'CEventShockingPedRunOver'
|
||||
| 'CEventShockingPedShot'
|
||||
| 'CEventShockingPlaneFlyby'
|
||||
| 'CEventShockingPotentialBlast'
|
||||
| 'CEventShockingPropertyDamage'
|
||||
| 'CEventShockingRunningPed'
|
||||
| 'CEventShockingRunningStampede'
|
||||
| 'CEventShockingSeenCarStolen'
|
||||
| 'CEventShockingSeenConfrontation'
|
||||
| 'CEventShockingSeenGangFight'
|
||||
| 'CEventShockingSeenInsult'
|
||||
| 'CEventShockingSeenMeleeAction'
|
||||
| 'CEventShockingSeenNiceCar'
|
||||
| 'CEventShockingSeenPedKilled'
|
||||
| 'CEventShockingSiren'
|
||||
| 'CEventShockingStudioBomb'
|
||||
| 'CEventShockingVehicleTowed'
|
||||
| 'CEventShockingVisibleWeapon'
|
||||
| 'CEventShockingWeaponThreat'
|
||||
| 'CEventShockingWeirdPed'
|
||||
| 'CEventShockingWeirdPedApproaching'
|
||||
| 'CEventShoutBlockingLos'
|
||||
| 'CEventShoutTargetPosition'
|
||||
| 'CEventShovePed'
|
||||
| 'CEventSoundBase'
|
||||
| 'CEventStatChangedValue'
|
||||
| 'CEventStaticCountReachedMax'
|
||||
| 'CEventStuckInAir'
|
||||
| 'CEventSuspiciousActivity'
|
||||
| 'CEventSwitch2NM'
|
||||
| 'CEventUnidentifiedPed'
|
||||
| 'CEventVehicleCollision'
|
||||
| 'CEventVehicleDamage'
|
||||
| 'CEventVehicleDamageWeapon'
|
||||
| 'CEventVehicleOnFire'
|
||||
| 'CEventWrithe'
|
||||
Reference in New Issue
Block a user