- rpc generics
- some internal types to expose from rpc
This commit is contained in:
Danya H 2024-10-03 23:11:39 +01:00
parent f8dd4d9fce
commit 67be91538b
8 changed files with 110 additions and 50 deletions

View File

@ -5,7 +5,7 @@ function App() {
const [data, setData] = useState<unknown>('') const [data, setData] = useState<unknown>('')
useEffect(() => { useEffect(() => {
rpc.callClient('cefReady', []) rpc.callClient('cefReady')
rpc.register('customCefEvent', args => setData(args)) rpc.register('customCefEvent', args => setData(args))
}, []) }, [])

28
apps/rpc/index.d.ts vendored
View File

@ -1,8 +1,22 @@
import '@ragempcommunity/types-cef' export {}
import '@ragempcommunity/types-client'
import '@ragempcommunity/types-server'
declare const mp: Mp interface Mp {
declare const global: Record<string, (...args: any[]) => unknown> trigger(event: string, data?: any): void
declare const window: Record<string, (...args: any[]) => unknown> events: {
declare const console: { log: (...args: any[]) => void } add(event: string, data: any): void
call(event: string, data: any): void
callRemote(event: string, data: any): void
}
joaat?: unknown
game: {
joaat?: unknown
}
}
declare global {
const mp: Mp
const global: Record<string, (...args: any[]) => unknown>
interface Window {
[p: string]: any
}
}

View File

@ -12,9 +12,6 @@
], ],
"devDependencies": { "devDependencies": {
"@microsoft/api-extractor": "^7.47.9", "@microsoft/api-extractor": "^7.47.9",
"@ragempcommunity/types-client": "^2.1.8",
"@ragempcommunity/types-server": "^2.1.8",
"@ragempcommunity/types-cef": "^2.1.8",
"prettier": "^3.3.2", "prettier": "^3.3.2",
"tsup": "^8.3.0" "tsup": "^8.3.0"
}, },

View File

@ -1,6 +1,12 @@
import { Wrapper } from './wrapper' import { Wrapper } from './wrapper'
import type { Player, PlayerServer } from './utils' import {
import { Environment, Errors, Events, RPCState, Utils } from './utils' Environment,
Errors,
Events,
RPCState,
Utils,
type PlayerMp,
} from './utils'
import { server } from './server' import { server } from './server'
import { client } from './client' import { client } from './client'
@ -19,10 +25,13 @@ class Rpc extends Wrapper {
mp.events.add( mp.events.add(
Events.LOCAL_EVENT_LISTENER, Events.LOCAL_EVENT_LISTENER,
async (player: Player, dataRaw: string) => { async (player: PlayerMp | string, dataRaw: string) => {
switch (this.environment_) { switch (this.environment_) {
case Environment.SERVER: case Environment.SERVER:
server.resolveEmitDestination(player, dataRaw) server.resolveEmitDestination(
player as PlayerMp,
dataRaw,
)
break break
case Environment.CLIENT: case Environment.CLIENT:
@ -43,28 +52,49 @@ class Rpc extends Wrapper {
) )
} }
public register( public register<
eventName: string, CallbackArguments extends unknown[] = unknown[],
cb: (...args: unknown[]) => unknown, CallbackReturn extends unknown = unknown,
EventName extends string = string,
>(
eventName: EventName,
cb: (...args: CallbackArguments) => CallbackReturn,
): void { ): void {
Utils.errorUnknownEnvironment(this.environment_) Utils.errorUnknownEnvironment(this.environment_)
this.state_[eventName] = cb this.state_[eventName] = cb
} }
public unregister(eventName: string): void { public unregister<EventName extends string = string>(
eventName: EventName,
): void {
Utils.errorUnknownEnvironment(this.environment_) Utils.errorUnknownEnvironment(this.environment_)
delete this.state_[eventName] delete this.state_[eventName]
} }
public callClient<
Arguments extends unknown[] = unknown[],
EventName extends string = string,
>(eventName: EventName, args?: Arguments): void
public callClient<
Arguments extends unknown[] = unknown[],
EventName extends string = string,
>(player: PlayerMp, eventName: EventName, args?: Arguments): void
public callClient( public callClient(
playerOrEventName: Player, playerOrEventName: PlayerMp | string,
eventNameOrArgs: string | unknown[], eventNameOrArgs?: string | unknown[],
args?: unknown[], args?: unknown[],
) { ): void {
Utils.errorUnknownEnvironment(this.environment_) Utils.errorUnknownEnvironment(this.environment_)
function _is1StParamPlayer(x: unknown): x is PlayerMp {
return typeof x === 'object'
}
function _is2NdParamEventName(x: unknown): x is string {
return !Array.isArray(x) && typeof x === 'string'
}
// client // client
if (this.environment_ === Environment.CLIENT) { if (this.environment_ === Environment.CLIENT) {
this.call(playerOrEventName as string, args as unknown[]) this.call(playerOrEventName as string, args as unknown[])
@ -72,10 +102,14 @@ class Rpc extends Wrapper {
} }
// server // server
if (this.environment_ === Environment.SERVER) { if (
this.environment_ === Environment.SERVER &&
_is1StParamPlayer(playerOrEventName) &&
_is2NdParamEventName(eventNameOrArgs)
) {
const state: RPCState = { const state: RPCState = {
uuid: Utils.generateUUID(), uuid: Utils.generateUUID(),
eventName: eventNameOrArgs as string, eventName: eventNameOrArgs,
calledTo: Environment.CLIENT, calledTo: Environment.CLIENT,
calledFrom: this.environment_, calledFrom: this.environment_,
knownError: undefined, knownError: undefined,
@ -84,22 +118,23 @@ class Rpc extends Wrapper {
const dataRaw = Utils.prepareTransfer(state) const dataRaw = Utils.prepareTransfer(state)
;(playerOrEventName as PlayerServer).call( playerOrEventName.call(Events.LOCAL_EVENT_LISTENER, [dataRaw])
Events.LOCAL_EVENT_LISTENER,
[dataRaw],
)
return return
} }
// browser // browser
if (this.environment_ === Environment.BROWSER) { if (
this.environment_ === Environment.BROWSER &&
!_is1StParamPlayer(playerOrEventName) &&
!_is2NdParamEventName(eventNameOrArgs)
) {
const state: RPCState = { const state: RPCState = {
uuid: Utils.generateUUID(), uuid: Utils.generateUUID(),
eventName: playerOrEventName as string, eventName: playerOrEventName,
calledTo: Environment.CLIENT, calledTo: Environment.CLIENT,
calledFrom: this.environment_, calledFrom: this.environment_,
knownError: undefined, knownError: undefined,
data: eventNameOrArgs as unknown[], data: eventNameOrArgs,
} }
const dataRaw = Utils.prepareTransfer(state) const dataRaw = Utils.prepareTransfer(state)
@ -109,7 +144,10 @@ class Rpc extends Wrapper {
} }
} }
public callServer(eventName: string, args: unknown[]) { public callServer<
Arguments extends unknown[] = unknown[],
EventName extends string = string,
>(eventName: EventName, args?: Arguments) {
Utils.errorUnknownEnvironment(this.environment_) Utils.errorUnknownEnvironment(this.environment_)
const state: RPCState = { const state: RPCState = {
@ -130,7 +168,10 @@ class Rpc extends Wrapper {
} }
} }
public callBrowser(eventName: string, args: unknown[]) { public callBrowser<
Arguments extends unknown[] = unknown[],
EventName extends string = string,
>(eventName: EventName, args?: Arguments) {
Utils.errorUnknownEnvironment(this.environment_) Utils.errorUnknownEnvironment(this.environment_)
const state: RPCState = { const state: RPCState = {
@ -151,7 +192,10 @@ class Rpc extends Wrapper {
} }
} }
public call(eventName: string, args: unknown[]) { public call<
Arguments extends unknown[] = unknown[],
EventName extends string = string,
>(eventName: EventName, args?: Arguments) {
Utils.errorUnknownEnvironment(this.environment_) Utils.errorUnknownEnvironment(this.environment_)
let state: RPCState = { let state: RPCState = {

View File

@ -1,6 +1,5 @@
import { Wrapper } from './wrapper' import { Wrapper } from './wrapper'
import type { Player, PlayerServer } from './utils' import { Environment, Events, Utils, type PlayerMp } from './utils'
import { Environment, Events, Utils } from './utils'
class Server extends Wrapper { class Server extends Wrapper {
constructor() { constructor() {
@ -8,13 +7,13 @@ class Server extends Wrapper {
mp.events.add( mp.events.add(
Events.SERVER_EVENT_LISTENER, Events.SERVER_EVENT_LISTENER,
async (player: PlayerServer, dataRaw: string) => { async (player: PlayerMp, dataRaw: string) => {
this.emit(player, dataRaw) this.emit(player, dataRaw)
}, },
) )
} }
public resolveEmitDestination(player: Player, dataRaw: string) { public resolveEmitDestination(player: PlayerMp, dataRaw: string) {
let state = Utils.prepareExecution(dataRaw) let state = Utils.prepareExecution(dataRaw)
switch (state.calledTo) { switch (state.calledTo) {
@ -23,16 +22,16 @@ class Server extends Wrapper {
break break
default: default:
this.emitClient(player as PlayerServer, dataRaw) this.emitClient(player as PlayerMp, dataRaw)
break break
} }
} }
private emitClient(player: PlayerServer, dataRaw: string) { private emitClient(player: PlayerMp, dataRaw: string) {
player.call(Events.LOCAL_EVENT_LISTENER, [dataRaw]) player.call(Events.LOCAL_EVENT_LISTENER, [dataRaw])
} }
private emit(player: Player, dataRaw: string) { private emit(player: PlayerMp, dataRaw: string) {
let state = Utils.prepareExecution(dataRaw) let state = Utils.prepareExecution(dataRaw)
state = this.verifyEvent_(state) state = this.verifyEvent_(state)

View File

@ -22,9 +22,12 @@ export enum Errors {
export class Utils { export class Utils {
public static getEnvironment(): Environment { public static getEnvironment(): Environment {
if ('joaat' in mp) return Environment.SERVER if ('joaat' in mp) return Environment.SERVER
if ('game' in mp && 'joaat' in (mp.game as { joaat?: unknown })) if (
'game' in mp &&
'joaat' in (mp as { game: { joaat?: unknown } }).game
)
return Environment.CLIENT return Environment.CLIENT
if ('mp' in window) return Environment.BROWSER if (window && 'mp' in window) return Environment.BROWSER
return Environment.UNKNOWN return Environment.UNKNOWN
} }
@ -74,7 +77,6 @@ export type RPCState = {
calledTo: Environment calledTo: Environment
} }
export interface Player {} export type PlayerMp = {
export interface PlayerServer extends Player { call(event: string, args?: unknown[]): void
call(eventName: string, args: unknown[]): void
} }

View File

@ -3,7 +3,10 @@
"target": "es6", "target": "es6",
"module": "commonjs", "module": "commonjs",
"moduleResolution": "node", "moduleResolution": "node",
"lib": ["ES6"], "lib": [
"ES6",
"dom"
],
"declaration": true, "declaration": true,
"declarationMap": true, "declarationMap": true,
"sourceMap": true, "sourceMap": true,
@ -20,6 +23,7 @@
"./index.d.ts" "./index.d.ts"
], ],
"exclude": [ "exclude": [
"node_modules" "node_modules",
"dist"
] ]
} }

View File

@ -1,10 +1,10 @@
import { rpc } from 'rpc' import { rpc } from 'rpc'
rpc.register('playerJoin', async player => { rpc.register('playerJoin', async (player: PlayerMp) => {
console.log(`Connected: ${player.socialClub}`) console.log(`Connected: ${player.socialClub}`)
}) })
rpc.register('customServerEvent', (player, data) => { rpc.register('customServerEvent', (player: PlayerMp, data) => {
console.log(player, data) console.log(player, data)
rpc.callClient(player, 'customClientEvent', ['server to client']) rpc.callClient(player, 'customClientEvent', ['server to client'])