chore(rpc): promote Wrapper to BaseInstance

This commit is contained in:
2026-09-28 02:05:40 +01:00
parent 571d113386
commit 558f04567a
6 changed files with 156 additions and 367 deletions
@@ -1,9 +1,10 @@
import { Emitter } from '../utils/emitter' import { Emitter, type Handler } from '../utils/emitter'
import { import {
handlerErrorMessage, handlerErrorMessage,
notRegisteredMessage, notRegisteredMessage,
RPCError, RPCError,
} from '../utils/errors' } from '../utils/errors'
import { generateUUID } from '../utils/funcs'
import { Pending } from '../utils/pending' import { Pending } from '../utils/pending'
import { import {
type RPCConfig, type RPCConfig,
@@ -12,21 +13,84 @@ import {
type RPCState, type RPCState,
} from '../utils/types' } from '../utils/types'
export class Wrapper { /** Shared plumbing of the server, client and webview instances */
protected env: RPCEnvironment export class RPCInstanceBase {
protected _emitterLocal: Emitter protected readonly env: RPCEnvironment
protected _pending: Pending protected readonly debug: boolean
protected debug: boolean protected readonly _emitterLocal = new Emitter()
protected console: Console protected readonly _pending: Pending
constructor(cfg: RPCConfig<RPCEnvironment>) { constructor(cfg: RPCConfig<RPCEnvironment>) {
this.env = cfg.env this.env = cfg.env
this._emitterLocal = new Emitter()
this._pending = new Pending(cfg.timeout ?? 5000)
this.debug = cfg.debug ?? false this.debug = cfg.debug ?? false
this.console = console this._pending = new Pending(cfg.timeout ?? 5000)
} }
// ===== LISTENERS =====
/** Registers `cb` for `event` on `emitter`; `method` is only used for logs */
protected listen(
emitter: Emitter,
method: string,
event: string,
cb: Handler,
): this {
this.log(`${method} ${event}`)
emitter.on(event, cb)
return this
}
protected unlisten(emitter: Emitter, method: string, event: string): this {
this.log(`${method} ${event}`)
emitter.off(event)
return this
}
// ===== OUTGOING =====
/** Builds an event payload sent from this environment to `to` */
protected request(
event: string,
to: RPCEnvironment,
args: unknown[],
player: number | null,
): RPCState {
return {
event,
uuid: generateUUID(),
calledFrom: this.env,
calledTo: to,
error: null,
data: args,
player,
type: 'event',
}
}
/** Calls a listener registered with `onSelf` in this environment */
protected emitLocal<R>(event: string, args: unknown[]): Promise<R> {
this.log(`emitSelf ${event}`)
if (!this._emitterLocal.has(event)) {
throw new RPCError(
RPCErrors.EVENT_NOT_REGISTERED,
notRegisteredMessage(event, this.env, this.env),
{ event, uuid: '', from: this.env, to: this.env },
)
}
return this._emitterLocal.emit<R>(event, ...args)
}
/** Settles the call waiting for `response`; ignores late or unexpected ones */
protected settle(response: RPCState): void {
const found = response.error
? this._pending.reject(response.uuid, RPCError.fromResponse(response))
: this._pending.resolve(response.uuid, response.data?.[0])
if (!found) this.logIgnored(response)
}
// ===== INCOMING =====
/** /**
* Runs the listener for `request` and builds the response to send back. * Runs the listener for `request` and builds the response to send back.
* Never throws: a missing listener or a thrown error ends up in `error`. * Never throws: a missing listener or a thrown error ends up in `error`.
@@ -57,7 +121,7 @@ export class Wrapper {
return this.response(request, [data], null) return this.response(request, [data], null)
} catch (e) { } catch (e) {
// keep the stack visible where the listener lives // keep the stack visible where the listener lives
this.console.error(e) console.error(e)
return this.errorResponse( return this.errorResponse(
request, request,
new RPCError( new RPCError(
@@ -83,33 +147,18 @@ export class Wrapper {
}) })
} }
/** Settles the call waiting for `response`; ignores late or unexpected ones */ // ===== LOGS =====
protected settle(response: RPCState): void {
const found = response.error
? this._pending.reject(response.uuid, RPCError.fromResponse(response))
: this._pending.resolve(response.uuid, response.data?.[0])
if (!found) this.logIgnored(response) /** Debug-only log, prefixed with the environment */
} protected log(message: string): void {
if (this.debug) console.log(`[RPC]:${this.env}:${message}`)
/** Throws if no local listener is registered for `event` */
protected assertListener(emitter: Emitter, event: string): void {
if (!emitter.has(event)) {
throw new RPCError(
RPCErrors.EVENT_NOT_REGISTERED,
notRegisteredMessage(event, this.env, this.env),
{ event, uuid: '', from: this.env, to: this.env },
)
}
} }
protected logIgnored(response: RPCState): void { protected logIgnored(response: RPCState): void {
if (this.debug) { this.log(
this.console.log( `ignored response ${response.event} ${response.uuid} (no pending call, possibly timed out)`,
`[RPC]:ignored response ${response.event} ${response.uuid} (no pending call, possibly timed out)`,
) )
} }
}
private response( private response(
request: RPCState, request: RPCState,
+26 -109
View File
@@ -2,7 +2,7 @@ import type * as s from '@entityseven/fivem-rpc-shared-types'
import { Emitter } from '../utils/emitter' import { Emitter } from '../utils/emitter'
import { RPCError } from '../utils/errors' import { RPCError } from '../utils/errors'
import { generateUUID, parse, stringify, stringifyWeb } from '../utils/funcs' import { parse, stringify, stringifyWeb } from '../utils/funcs'
import { import {
NATIVE_CLIENT_EVENTS, NATIVE_CLIENT_EVENTS,
NATIVE_CLIENT_NETWORK_EVENTS, NATIVE_CLIENT_NETWORK_EVENTS,
@@ -17,9 +17,9 @@ import {
type RPCStateRaw, type RPCStateRaw,
type RPCStateWeb, type RPCStateWeb,
} from '../utils/types' } from '../utils/types'
import { Wrapper } from './wrapper' import { RPCInstanceBase } from './base'
export class RPCInstanceClient extends Wrapper { export class RPCInstanceClient extends RPCInstanceBase {
private readonly _emitterServer: Emitter private readonly _emitterServer: Emitter
private readonly _emitterWeb: Emitter private readonly _emitterWeb: Emitter
@@ -29,7 +29,7 @@ export class RPCInstanceClient extends Wrapper {
this._emitterServer = new Emitter() this._emitterServer = new Emitter()
this._emitterWeb = new Emitter() this._emitterWeb = new Emitter()
this.console.log('[RPC] Initialized Client') console.log('[RPC] Initialized Client')
onNet(RPCEvents.LISTENER_SERVER, this._handleServer.bind(this)) onNet(RPCEvents.LISTENER_SERVER, this._handleServer.bind(this))
RegisterNuiCallbackType(RPCEvents.LISTENER_WEB) RegisterNuiCallbackType(RPCEvents.LISTENER_WEB)
@@ -55,11 +55,9 @@ export class RPCInstanceClient extends Wrapper {
} }
const payload = parse(payloadRaw) const payload = parse(payloadRaw)
if (this.debug) { this.log(
this.console.log( `accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
`[RPC]:client:accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
) )
}
if (payload.type === 'event') { if (payload.type === 'event') {
if (payload.calledTo === 'client') { if (payload.calledTo === 'client') {
@@ -88,11 +86,9 @@ export class RPCInstanceClient extends Wrapper {
} }
private async _handleWeb(payload: RPCState): Promise<unknown> { private async _handleWeb(payload: RPCState): Promise<unknown> {
if (this.debug) { this.log(
this.console.log( `accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
`[RPC]:client:accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
) )
}
if (payload.type === 'event') { if (payload.type === 'event') {
if (payload.calledTo === 'client') { if (payload.calledTo === 'client') {
@@ -134,25 +130,13 @@ export class RPCInstanceClient extends Wrapper {
...args: CallbackArguments ...args: CallbackArguments
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>, ) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
): this { ): this {
if (this.debug) { return this.listen(this._emitterServer, 'onServer', eventName, cb)
this.console.log(`[RPC]:onServer ${eventName}`)
}
this._emitterServer.on(eventName, cb)
return this
} }
public offServer<EventName extends keyof s.RPCEvents_ServerClient>( public offServer<EventName extends keyof s.RPCEvents_ServerClient>(
eventName: EventName, eventName: EventName,
): this { ): this {
if (this.debug) { return this.unlisten(this._emitterServer, 'offServer', eventName)
this.console.log(`[RPC]:offServer ${eventName}`)
}
this._emitterServer.off(eventName)
return this
} }
public async emitServer< public async emitServer<
@@ -160,16 +144,12 @@ export class RPCInstanceClient extends Wrapper {
Arguments extends Parameters<s.RPCEvents_ClientServer[EventName]>, Arguments extends Parameters<s.RPCEvents_ClientServer[EventName]>,
Response extends ReturnType<s.RPCEvents_ClientServer[EventName]>, Response extends ReturnType<s.RPCEvents_ClientServer[EventName]>,
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> { >(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
const payload: RPCState = { const payload = this.request(
event: eventName, eventName,
uuid: generateUUID(), 'server',
calledFrom: 'client', args,
calledTo: 'server', GetPlayerServerId(PlayerId()),
error: null, )
data: args.length ? args : null,
player: GetPlayerServerId(PlayerId()),
type: 'event',
}
emitNet(RPCEvents.LISTENER_CLIENT, stringify(payload)) emitNet(RPCEvents.LISTENER_CLIENT, stringify(payload))
@@ -188,25 +168,13 @@ export class RPCInstanceClient extends Wrapper {
...args: CallbackArguments ...args: CallbackArguments
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>, ) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
): this { ): this {
if (this.debug) { return this.listen(this._emitterWeb, 'onWebview', eventName, cb)
this.console.log(`[RPC]:onWebview ${eventName}`)
}
this._emitterWeb.on(eventName, cb)
return this
} }
public offWebview<EventName extends keyof s.RPCEvents_WebviewClient>( public offWebview<EventName extends keyof s.RPCEvents_WebviewClient>(
eventName: EventName, eventName: EventName,
): this { ): this {
if (this.debug) { return this.unlisten(this._emitterWeb, 'offWebview', eventName)
this.console.log(`[RPC]:offWebview ${eventName}`)
}
this._emitterWeb.off(eventName)
return this
} }
public async emitWebview< public async emitWebview<
@@ -214,16 +182,7 @@ export class RPCInstanceClient extends Wrapper {
Arguments extends Parameters<s.RPCEvents_ClientWebview[EventName]>, Arguments extends Parameters<s.RPCEvents_ClientWebview[EventName]>,
Response extends ReturnType<s.RPCEvents_ClientWebview[EventName]>, Response extends ReturnType<s.RPCEvents_ClientWebview[EventName]>,
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> { >(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
const payload: RPCState = { const payload = this.request(eventName, 'webview', args, PlayerId())
event: eventName,
uuid: generateUUID(),
calledFrom: 'client',
calledTo: 'webview',
error: null,
data: args.length ? args : null,
player: PlayerId(),
type: 'event',
}
this._sendWebMessage({ this._sendWebMessage({
origin: RPCEvents.LISTENER_CLIENT, origin: RPCEvents.LISTENER_CLIENT,
@@ -245,25 +204,13 @@ export class RPCInstanceClient extends Wrapper {
...args: CallbackArguments ...args: CallbackArguments
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>, ) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
): this { ): this {
if (this.debug) { return this.listen(this._emitterLocal, 'onSelf', eventName, cb)
this.console.log(`[RPC]:onSelf ${eventName}`)
}
this._emitterLocal.on(eventName, cb)
return this
} }
public offSelf<EventName extends keyof s.RPCEvents_Client>( public offSelf<EventName extends keyof s.RPCEvents_Client>(
eventName: EventName, eventName: EventName,
): this { ): this {
if (this.debug) { return this.unlisten(this._emitterLocal, 'offSelf', eventName)
this.console.log(`[RPC]:offSelf ${eventName}`)
}
this._emitterLocal.off(eventName)
return this
} }
public async emitSelf< public async emitSelf<
@@ -271,29 +218,7 @@ export class RPCInstanceClient extends Wrapper {
Arguments extends Parameters<s.RPCEvents_Client[EventName]>, Arguments extends Parameters<s.RPCEvents_Client[EventName]>,
Response extends ReturnType<s.RPCEvents_Client[EventName]>, Response extends ReturnType<s.RPCEvents_Client[EventName]>,
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> { >(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
const payload: RPCState = { return this.emitLocal<Awaited<Response>>(eventName, args)
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.assertListener(this._emitterLocal, payload.event)
return await this._emitterLocal.emit<Awaited<Response>>(
payload.event,
...(payload.data && payload.data.length > 0 ? payload.data : []),
)
} }
// ===== OTHER ===== // ===== OTHER =====
@@ -305,9 +230,7 @@ export class RPCInstanceClient extends Wrapper {
command: CommandName, command: CommandName,
cb: (player: number, args: CallbackArguments, commandRaw: string) => void, cb: (player: number, args: CallbackArguments, commandRaw: string) => void,
): this { ): this {
if (this.debug) { this.log(`onCommand ${command}`)
this.console.log(`[RPC]:onCommand ${command}`)
}
RegisterCommand(command, cb, false) RegisterCommand(command, cb, false)
@@ -322,9 +245,7 @@ export class RPCInstanceClient extends Wrapper {
throw new RPCError(RPCErrors.UNKNOWN_NATIVE, RPCErrors.UNKNOWN_NATIVE) throw new RPCError(RPCErrors.UNKNOWN_NATIVE, RPCErrors.UNKNOWN_NATIVE)
} }
if (this.debug) { this.log(`onNativeEvent ${eventName}`)
this.console.log(`[RPC]:onNativeEvent ${eventName}`)
}
on(eventName, cb) on(eventName, cb)
@@ -341,9 +262,7 @@ export class RPCInstanceClient extends Wrapper {
throw new RPCError(RPCErrors.UNKNOWN_NATIVE, RPCErrors.UNKNOWN_NATIVE) throw new RPCError(RPCErrors.UNKNOWN_NATIVE, RPCErrors.UNKNOWN_NATIVE)
} }
if (this.debug) { this.log(`onNativeNetworkEvent ${eventName}`)
this.console.log(`[RPC]:onNativeNetworkEvent ${eventName}`)
}
on(eventName, cb) on(eventName, cb)
@@ -351,9 +270,7 @@ export class RPCInstanceClient extends Wrapper {
} }
public setWebviewFocus(hasFocus: boolean, hasCursor: boolean): this { public setWebviewFocus(hasFocus: boolean, hasCursor: boolean): this {
if (this.debug) { this.log(`setWebviewFocus ${hasFocus} ${hasCursor}`)
this.console.log(`[RPC]:setWebviewFocus ${hasFocus} ${hasCursor}`)
}
SetNuiFocus(hasFocus, hasCursor) SetNuiFocus(hasFocus, hasCursor)
+22 -116
View File
@@ -2,19 +2,18 @@ import type * as s from '@entityseven/fivem-rpc-shared-types'
import { Emitter } from '../utils/emitter' import { Emitter } from '../utils/emitter'
import { RPCError } from '../utils/errors' import { RPCError } from '../utils/errors'
import { generateUUID, parse, stringify } from '../utils/funcs' import { parse, stringify } from '../utils/funcs'
import { NATIVE_SERVER_EVENTS } from '../utils/native' import { NATIVE_SERVER_EVENTS } from '../utils/native'
import { import {
type RPCConfig, type RPCConfig,
RPCErrors, RPCErrors,
RPCEvents, RPCEvents,
type RPCNativeServerEvents, type RPCNativeServerEvents,
type RPCState,
type RPCStateRaw, type RPCStateRaw,
} from '../utils/types' } from '../utils/types'
import { Wrapper } from './wrapper' import { RPCInstanceBase } from './base'
export class RPCInstanceServer extends Wrapper { export class RPCInstanceServer extends RPCInstanceBase {
private readonly _emitterClient: Emitter private readonly _emitterClient: Emitter
private readonly _emitterWeb: Emitter private readonly _emitterWeb: Emitter
@@ -24,7 +23,7 @@ export class RPCInstanceServer extends Wrapper {
this._emitterClient = new Emitter() this._emitterClient = new Emitter()
this._emitterWeb = new Emitter() this._emitterWeb = new Emitter()
this.console.log('[RPC] Initialized Server') console.log('[RPC] Initialized Server')
onNet(RPCEvents.LISTENER_CLIENT, this._handleClient.bind(this)) onNet(RPCEvents.LISTENER_CLIENT, this._handleClient.bind(this))
onNet(RPCEvents.LISTENER_WEB, this._handleWeb.bind(this)) onNet(RPCEvents.LISTENER_WEB, this._handleWeb.bind(this))
@@ -40,17 +39,15 @@ export class RPCInstanceServer extends Wrapper {
} }
const payload = parse(payloadRaw) const payload = parse(payloadRaw)
if (this.debug) { this.log(
this.console.log( `accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
`[RPC]:server:accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
) )
}
if (payload.calledFrom === 'client') { if (payload.calledFrom === 'client') {
if (payload.type === 'event') { if (payload.type === 'event') {
if (payload.player === null || payload.player === -1) { if (payload.player === null || payload.player === -1) {
// nobody to reply to, the caller times out // nobody to reply to, the caller times out
this.console.error( console.error(
new RPCError( new RPCError(
RPCErrors.NO_PLAYER, RPCErrors.NO_PLAYER,
`${RPCErrors.NO_PLAYER}: "${payload.event}" from ${payload.calledFrom}`, `${RPCErrors.NO_PLAYER}: "${payload.event}" from ${payload.calledFrom}`,
@@ -81,17 +78,15 @@ export class RPCInstanceServer extends Wrapper {
} }
const payload = parse(payloadRaw) const payload = parse(payloadRaw)
if (this.debug) { this.log(
this.console.log( `accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
`[RPC]:server:accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
) )
}
if (payload.calledFrom === 'webview') { if (payload.calledFrom === 'webview') {
if (payload.type === 'event') { if (payload.type === 'event') {
if (payload.player === null || payload.player === -1) { if (payload.player === null || payload.player === -1) {
// nobody to reply to, the caller times out // nobody to reply to, the caller times out
this.console.error( console.error(
new RPCError( new RPCError(
RPCErrors.NO_PLAYER, RPCErrors.NO_PLAYER,
`${RPCErrors.NO_PLAYER}: "${payload.event}" from ${payload.calledFrom}`, `${RPCErrors.NO_PLAYER}: "${payload.event}" from ${payload.calledFrom}`,
@@ -127,25 +122,13 @@ export class RPCInstanceServer extends Wrapper {
...args: CallbackArguments ...args: CallbackArguments
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>, ) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
): this { ): this {
if (this.debug) { return this.listen(this._emitterClient, 'onClient', eventName, cb)
this.console.log(`[RPC]:onClient ${eventName}`)
}
this._emitterClient.on(eventName, cb)
return this
} }
public offClient<EventName extends keyof s.RPCEvents_ClientServer>( public offClient<EventName extends keyof s.RPCEvents_ClientServer>(
eventName: EventName, eventName: EventName,
): this { ): this {
if (this.debug) { return this.unlisten(this._emitterClient, 'offClient', eventName)
this.console.log(`[RPC]:offClient ${eventName}`)
}
this._emitterClient.off(eventName)
return this
} }
public async emitClient< public async emitClient<
@@ -157,16 +140,7 @@ export class RPCInstanceServer extends Wrapper {
eventName: EventName, eventName: EventName,
...args: Arguments ...args: Arguments
): Promise<Awaited<Response>> { ): Promise<Awaited<Response>> {
const payload: RPCState = { const payload = this.request(eventName, 'client', args, player)
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)) emitNet(RPCEvents.LISTENER_SERVER, player, stringify(payload))
@@ -177,16 +151,7 @@ export class RPCInstanceServer extends Wrapper {
EventName extends keyof s.RPCEvents_ServerClient, EventName extends keyof s.RPCEvents_ServerClient,
Arguments extends Parameters<s.RPCEvents_ServerClient[EventName]>, Arguments extends Parameters<s.RPCEvents_ServerClient[EventName]>,
>(eventName: EventName, ...args: Arguments): Promise<void> { >(eventName: EventName, ...args: Arguments): Promise<void> {
const payload: RPCState = { const payload = this.request(eventName, 'client', args, -1)
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)) emitNet(RPCEvents.LISTENER_SERVER, -1, stringify(payload))
} }
@@ -204,25 +169,13 @@ export class RPCInstanceServer extends Wrapper {
...args: CallbackArguments ...args: CallbackArguments
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>, ) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
): this { ): this {
if (this.debug) { return this.listen(this._emitterWeb, 'onWebview', eventName, cb)
this.console.log(`[RPC]:onWebview ${eventName}`)
}
this._emitterWeb.on(eventName, cb)
return this
} }
public offWebview<EventName extends keyof s.RPCEvents_WebviewServer>( public offWebview<EventName extends keyof s.RPCEvents_WebviewServer>(
eventName: EventName, eventName: EventName,
): this { ): this {
if (this.debug) { return this.unlisten(this._emitterWeb, 'offWebview', eventName)
this.console.log(`[RPC]:offWebview ${eventName}`)
}
this._emitterWeb.off(eventName)
return this
} }
public async emitWebview< public async emitWebview<
@@ -234,16 +187,7 @@ export class RPCInstanceServer extends Wrapper {
eventName: EventName, eventName: EventName,
...args: Arguments ...args: Arguments
): Promise<Awaited<Response>> { ): Promise<Awaited<Response>> {
const payload: RPCState = { const payload = this.request(eventName, 'webview', args, player)
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)) emitNet(RPCEvents.LISTENER_SERVER, player, stringify(payload))
@@ -262,25 +206,13 @@ export class RPCInstanceServer extends Wrapper {
...args: CallbackArguments ...args: CallbackArguments
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>, ) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
): this { ): this {
if (this.debug) { return this.listen(this._emitterLocal, 'onSelf', eventName, cb)
this.console.log(`[RPC]:onSelf ${eventName}`)
}
this._emitterLocal.on(eventName, cb)
return this
} }
public offSelf<EventName extends keyof s.RPCEvents_Server>( public offSelf<EventName extends keyof s.RPCEvents_Server>(
eventName: EventName, eventName: EventName,
): this { ): this {
if (this.debug) { return this.unlisten(this._emitterLocal, 'offSelf', eventName)
this.console.log(`[RPC]:offSelf ${eventName}`)
}
this._emitterLocal.off(eventName)
return this
} }
public async emitSelf< public async emitSelf<
@@ -288,29 +220,7 @@ export class RPCInstanceServer extends Wrapper {
Arguments extends Parameters<s.RPCEvents_Server[EventName]>, Arguments extends Parameters<s.RPCEvents_Server[EventName]>,
Response extends ReturnType<s.RPCEvents_Server[EventName]>, Response extends ReturnType<s.RPCEvents_Server[EventName]>,
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> { >(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
const payload: RPCState = { return this.emitLocal<Awaited<Response>>(eventName, args)
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.assertListener(this._emitterLocal, payload.event)
return await this._emitterLocal.emit<Awaited<Response>>(
payload.event,
...(payload.data && payload.data.length > 0 ? payload.data : []),
)
} }
// ===== OTHER ===== // ===== OTHER =====
@@ -323,9 +233,7 @@ export class RPCInstanceServer extends Wrapper {
cb: (player: number, args: CallbackArguments, commandRaw: string) => void, cb: (player: number, args: CallbackArguments, commandRaw: string) => void,
restricted = false, restricted = false,
): this { ): this {
if (this.debug) { this.log(`onCommand ${command}`)
this.console.log(`[RPC]:onCommand ${command}`)
}
RegisterCommand(command, cb, restricted) RegisterCommand(command, cb, restricted)
@@ -340,9 +248,7 @@ export class RPCInstanceServer extends Wrapper {
throw new RPCError(RPCErrors.UNKNOWN_NATIVE, RPCErrors.UNKNOWN_NATIVE) throw new RPCError(RPCErrors.UNKNOWN_NATIVE, RPCErrors.UNKNOWN_NATIVE)
} }
if (this.debug) { this.log(`onNativeEvent ${eventName}`)
this.console.log(`[RPC]:onNativeEvent ${eventName}`)
}
on(eventName, cb) on(eventName, cb)
+17 -97
View File
@@ -1,7 +1,7 @@
import type * as s from '@entityseven/fivem-rpc-shared-types' import type * as s from '@entityseven/fivem-rpc-shared-types'
import { Emitter } from '../utils/emitter' import { Emitter } from '../utils/emitter'
import { generateUUID, stringify } from '../utils/funcs' import { stringify } from '../utils/funcs'
import { import {
RPCEvents, RPCEvents,
type RPCConfig, type RPCConfig,
@@ -9,9 +9,9 @@ import {
type RPCStateRaw, type RPCStateRaw,
type RPCStateWeb, type RPCStateWeb,
} from '../utils/types' } from '../utils/types'
import { Wrapper } from './wrapper' import { RPCInstanceBase } from './base'
export class RPCInstanceWebview extends Wrapper { export class RPCInstanceWebview extends RPCInstanceBase {
private readonly _emitterClient: Emitter private readonly _emitterClient: Emitter
private readonly _emitterServer: Emitter private readonly _emitterServer: Emitter
@@ -21,7 +21,7 @@ export class RPCInstanceWebview extends Wrapper {
this._emitterClient = new Emitter() this._emitterClient = new Emitter()
this._emitterServer = new Emitter() this._emitterServer = new Emitter()
this.console.log('[RPC] Initialized Webview') console.log('[RPC] Initialized Webview')
window.addEventListener('message', (e: MessageEvent<RPCStateWeb>) => { window.addEventListener('message', (e: MessageEvent<RPCStateWeb>) => {
if (e.data.origin === RPCEvents.LISTENER_CLIENT) { if (e.data.origin === RPCEvents.LISTENER_CLIENT) {
@@ -36,11 +36,9 @@ export class RPCInstanceWebview extends Wrapper {
// ===== HANDLERS ===== // ===== HANDLERS =====
private async _handleClient(payload: RPCState) { private async _handleClient(payload: RPCState) {
if (this.debug) { this.log(
this.console.log( `accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
`[RPC]:webview:accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
) )
}
if (payload.calledFrom === 'client' && payload.type === 'event') { if (payload.calledFrom === 'client' && payload.type === 'event') {
const response = await this.dispatch(this._emitterClient, payload) const response = await this.dispatch(this._emitterClient, payload)
@@ -50,11 +48,9 @@ export class RPCInstanceWebview extends Wrapper {
} }
private async _handleServer(payload: RPCState) { private async _handleServer(payload: RPCState) {
if (this.debug) { this.log(
this.console.log( `accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
`[RPC]:webview:accepted ${payload.type} ${payload.event} from ${payload.calledFrom}`,
) )
}
if (payload.calledFrom === 'server' && payload.type === 'event') { if (payload.calledFrom === 'server' && payload.type === 'event') {
const response = await this.dispatch(this._emitterServer, payload) const response = await this.dispatch(this._emitterServer, payload)
@@ -75,25 +71,13 @@ export class RPCInstanceWebview extends Wrapper {
...args: CallbackArguments ...args: CallbackArguments
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>, ) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
): this { ): this {
if (this.debug) { return this.listen(this._emitterClient, 'onClient', eventName, cb)
this.console.log(`[RPC]:onClient ${eventName}`)
}
this._emitterClient.on(eventName, cb)
return this
} }
public offClient<EventName extends keyof s.RPCEvents_ClientWebview>( public offClient<EventName extends keyof s.RPCEvents_ClientWebview>(
eventName: EventName, eventName: EventName,
): this { ): this {
if (this.debug) { return this.unlisten(this._emitterClient, 'offClient', eventName)
this.console.log(`[RPC]:offClient ${eventName}`)
}
this._emitterClient.off(eventName)
return this
} }
public async emitClient< public async emitClient<
@@ -101,16 +85,7 @@ export class RPCInstanceWebview extends Wrapper {
Arguments extends Parameters<s.RPCEvents_WebviewClient[EventName]>, Arguments extends Parameters<s.RPCEvents_WebviewClient[EventName]>,
Response extends ReturnType<s.RPCEvents_WebviewClient[EventName]>, Response extends ReturnType<s.RPCEvents_WebviewClient[EventName]>,
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> { >(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
const payload: RPCState = { const payload = this.request(eventName, 'client', args, null)
event: eventName,
uuid: generateUUID(),
calledFrom: 'webview',
calledTo: 'client',
error: null,
data: args.length ? args : null,
player: null,
type: 'event',
}
return this._request<Awaited<Response>>(payload) return this._request<Awaited<Response>>(payload)
} }
@@ -127,25 +102,13 @@ export class RPCInstanceWebview extends Wrapper {
...args: CallbackArguments ...args: CallbackArguments
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>, ) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
): this { ): this {
if (this.debug) { return this.listen(this._emitterServer, 'onServer', eventName, cb)
this.console.log(`[RPC]:onServer ${eventName}`)
}
this._emitterServer.on(eventName, cb)
return this
} }
public offServer<EventName extends keyof s.RPCEvents_ServerWebview>( public offServer<EventName extends keyof s.RPCEvents_ServerWebview>(
eventName: EventName, eventName: EventName,
): RPCInstanceWebview { ): RPCInstanceWebview {
if (this.debug) { return this.unlisten(this._emitterServer, 'offServer', eventName)
this.console.log(`[RPC]:offServer ${eventName}`)
}
this._emitterServer.off(eventName)
return this
} }
public async emitServer< public async emitServer<
@@ -153,16 +116,7 @@ export class RPCInstanceWebview extends Wrapper {
Arguments extends Parameters<s.RPCEvents_WebviewServer[EventName]>, Arguments extends Parameters<s.RPCEvents_WebviewServer[EventName]>,
Response extends ReturnType<s.RPCEvents_WebviewServer[EventName]>, Response extends ReturnType<s.RPCEvents_WebviewServer[EventName]>,
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> { >(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
const payload: RPCState = { const payload = this.request(eventName, 'server', args, null)
event: eventName,
uuid: generateUUID(),
calledFrom: 'webview',
calledTo: 'server',
error: null,
data: args.length ? args : null,
player: null,
type: 'event',
}
return this._request<Awaited<Response>>(payload) return this._request<Awaited<Response>>(payload)
} }
@@ -179,25 +133,13 @@ export class RPCInstanceWebview extends Wrapper {
...args: CallbackArguments ...args: CallbackArguments
) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>, ) => Awaited<CallbackReturn> | Promise<Awaited<CallbackReturn>>,
): this { ): this {
if (this.debug) { return this.listen(this._emitterLocal, 'onSelf', eventName, cb)
this.console.log(`[RPC]:onSelf ${eventName}`)
}
this._emitterLocal.on(eventName, cb)
return this
} }
public offSelf<EventName extends keyof s.RPCEvents_Webview>( public offSelf<EventName extends keyof s.RPCEvents_Webview>(
eventName: EventName, eventName: EventName,
): this { ): this {
if (this.debug) { return this.unlisten(this._emitterLocal, 'offSelf', eventName)
this.console.log(`[RPC]:offSelf ${eventName}`)
}
this._emitterLocal.off(eventName)
return this
} }
public async emitSelf< public async emitSelf<
@@ -205,29 +147,7 @@ export class RPCInstanceWebview extends Wrapper {
Arguments extends Parameters<s.RPCEvents_Webview[EventName]>, Arguments extends Parameters<s.RPCEvents_Webview[EventName]>,
Response extends ReturnType<s.RPCEvents_Webview[EventName]>, Response extends ReturnType<s.RPCEvents_Webview[EventName]>,
>(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> { >(eventName: EventName, ...args: Arguments): Promise<Awaited<Response>> {
const payload: RPCState = { return this.emitLocal<Awaited<Response>>(eventName, args)
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.assertListener(this._emitterLocal, payload.event)
return await this._emitterLocal.emit<Awaited<Response>>(
payload.event,
...(payload.data && payload.data.length > 0 ? payload.data : []),
)
} }
// ===== UTILS ===== // ===== UTILS =====
+2 -5
View File
@@ -1,7 +1,6 @@
import { RPCInstanceClient } from './core/client' import { RPCInstanceClient } from './core/client'
import { RPCInstanceServer } from './core/server' import { RPCInstanceServer } from './core/server'
import { RPCInstanceWebview } from './core/webview' import { RPCInstanceWebview } from './core/webview'
import { Wrapper } from './core/wrapper'
import { RPCError } from './utils/errors' import { RPCError } from './utils/errors'
import { import {
type RPCConfig, type RPCConfig,
@@ -27,7 +26,7 @@ import {
* *
* @class * @class
*/ */
class RPCFactory<T extends RPCEnvironment> extends Wrapper { class RPCFactory<T extends RPCEnvironment> {
private readonly operator: private readonly operator:
| RPCInstanceServer | RPCInstanceServer
| RPCInstanceClient | RPCInstanceClient
@@ -40,9 +39,7 @@ class RPCFactory<T extends RPCEnvironment> extends Wrapper {
* @param {boolean} opts.debug - Show additional logs * @param {boolean} opts.debug - Show additional logs
*/ */
constructor(opts: RPCConfig<T>) { constructor(opts: RPCConfig<T>) {
super(opts) console.log('[RPC] Initializing...')
this.console.log('[RPC] Initializing...')
switch (opts.env) { switch (opts.env) {
case 'server': case 'server':
+1 -1
View File
@@ -4,7 +4,7 @@ import { RPCErrors } from './types'
* Accepts any callback. Argument types are enforced by the typed `on*`/`emit*` * Accepts any callback. Argument types are enforced by the typed `on*`/`emit*`
* methods that wrap the emitter, not here. * methods that wrap the emitter, not here.
*/ */
type Handler = (...args: never[]) => unknown export type Handler = (...args: never[]) => unknown
/** One handler per event: registering an event again replaces its handler. */ /** One handler per event: registering an event again replaces its handler. */
export class Emitter { export class Emitter {