rpc test
This commit is contained in:
parent
30373960e5
commit
092693acb5
@ -14,7 +14,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"rage-fw-cef": "latest"
|
"rpc": "workspace:^"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^18.2.66",
|
"@types/react": "^18.2.66",
|
||||||
|
@ -8,6 +8,6 @@
|
|||||||
"build": "esbuild src/index.ts --bundle --platform=node --outfile=../../server/client_packages/index.js --format=esm"
|
"build": "esbuild src/index.ts --bundle --platform=node --outfile=../../server/client_packages/index.js --format=esm"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"rage-fw-client": "latest"
|
"rpc": "workspace:^"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
6
apps/rpc/.prettierrc.yaml
Normal file
6
apps/rpc/.prettierrc.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
tabWidth: 4
|
||||||
|
printWidth: 80
|
||||||
|
singleQuote: true
|
||||||
|
semi: false
|
||||||
|
arrowParens: avoid
|
||||||
|
endOfLine: auto
|
9
apps/rpc/index.d.ts
vendored
Normal file
9
apps/rpc/index.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
declare const mp: any
|
||||||
|
|
||||||
|
declare const global: {
|
||||||
|
rpcEvents: Record<string, (...args: any[]) => unknown>
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const window: {
|
||||||
|
rpcEvents: Record<string, (...args: any[]) => unknown>
|
||||||
|
}
|
28
apps/rpc/package.json
Normal file
28
apps/rpc/package.json
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"name": "rpc",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"types": "dist/src/index.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsup",
|
||||||
|
"start": "npx ./dist create"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist/**/*"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"prettier": "^3.3.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5.0.0"
|
||||||
|
},
|
||||||
|
"description": "RageFW RPC",
|
||||||
|
"keywords": [],
|
||||||
|
"author": "SashaGoncharov19",
|
||||||
|
"contributors": [{
|
||||||
|
"name": "rilaxik",
|
||||||
|
"email": "dev.rilaxik@gmail.com",
|
||||||
|
"url": "https://github.com/rilaxik"
|
||||||
|
}],
|
||||||
|
"license": "MIT"
|
||||||
|
}
|
44
apps/rpc/src/browser.ts
Normal file
44
apps/rpc/src/browser.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { Wrapper } from './wrapper'
|
||||||
|
import { Environment, Errors, Events, RPCState, Utils } from './utils'
|
||||||
|
|
||||||
|
class Browser extends Wrapper {
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
public resolveEmitDestination(dataRaw: string) {
|
||||||
|
if (!dataRaw) throw new Error(Errors.NO_DATA)
|
||||||
|
|
||||||
|
let state = Utils.prepareExecution(dataRaw)
|
||||||
|
|
||||||
|
switch (state.calledTo) {
|
||||||
|
case Environment.BROWSER:
|
||||||
|
this.emit(dataRaw)
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
this.emitClient(dataRaw)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private emitClient(dataRaw: string) {
|
||||||
|
mp.trigger(Events.EVENT_LISTENER, dataRaw)
|
||||||
|
}
|
||||||
|
|
||||||
|
private emit(dataRaw: string) {
|
||||||
|
let state = Utils.prepareExecution(dataRaw)
|
||||||
|
|
||||||
|
state = this.verifyEvent_(state)
|
||||||
|
if (state.knownError) {
|
||||||
|
this.triggerError_(state, state.knownError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { eventName, data } = Utils.prepareExecution(dataRaw)
|
||||||
|
this.state_[eventName](...data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const browser = new Browser()
|
||||||
|
export { browser }
|
65
apps/rpc/src/client.ts
Normal file
65
apps/rpc/src/client.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import { Wrapper } from './wrapper'
|
||||||
|
import { Environment, Errors, Events, Utils } from './utils'
|
||||||
|
import type { RPCState } from './utils'
|
||||||
|
|
||||||
|
class Client extends Wrapper {
|
||||||
|
private _browser: any = null
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
set browser(browser: any) {
|
||||||
|
this._browser = browser
|
||||||
|
}
|
||||||
|
|
||||||
|
public resolveEmitDestination(dataRaw: string) {
|
||||||
|
if (!dataRaw) throw new Error(Errors.NO_DATA)
|
||||||
|
|
||||||
|
const state = Utils.prepareExecution(dataRaw)
|
||||||
|
|
||||||
|
switch (state.calledTo) {
|
||||||
|
case Environment.SERVER:
|
||||||
|
this.emitServer(dataRaw)
|
||||||
|
break
|
||||||
|
|
||||||
|
case Environment.BROWSER:
|
||||||
|
this.emitBrowser(dataRaw, state)
|
||||||
|
break
|
||||||
|
|
||||||
|
case Environment.CLIENT:
|
||||||
|
this.emit(state)
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
this.triggerError_(state, Errors.UNKNOWN_ENVIRONMENT)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private emit(state: RPCState) {
|
||||||
|
state = this.verifyEvent_(state)
|
||||||
|
if (state.knownError) {
|
||||||
|
this.triggerError_(state, state.knownError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state_[state.eventName](...state.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
private emitServer(dataRaw: string) {
|
||||||
|
mp.events.callRemote(Events.EVENT_LISTENER, dataRaw)
|
||||||
|
}
|
||||||
|
|
||||||
|
private emitBrowser(dataRaw: string, state: RPCState) {
|
||||||
|
if (!this._browser) {
|
||||||
|
this.triggerError_(state, Errors.NO_BROWSER)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this._browser.call(Events.EVENT_LISTENER, dataRaw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = new Client()
|
||||||
|
export { client }
|
131
apps/rpc/src/index.ts
Normal file
131
apps/rpc/src/index.ts
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import { Wrapper } from './wrapper'
|
||||||
|
import { Environment, Errors, Events, RPCState, Utils } from './utils'
|
||||||
|
import { server } from './server'
|
||||||
|
import { client } from './client'
|
||||||
|
import { browser } from './browser'
|
||||||
|
|
||||||
|
class Rpc extends Wrapper {
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
|
||||||
|
if (this.environment_ === Environment.UNKNOWN)
|
||||||
|
throw new Error(Errors.UNKNOWN_ENVIRONMENT)
|
||||||
|
|
||||||
|
mp.events.add(
|
||||||
|
Events.EVENT_LISTENER,
|
||||||
|
async (player: any, dataRaw: string) => {
|
||||||
|
if (!dataRaw) throw new Error(Errors.NO_DATA)
|
||||||
|
|
||||||
|
switch (this.environment_) {
|
||||||
|
case Environment.SERVER:
|
||||||
|
server.resolveEmitDestination(player, dataRaw)
|
||||||
|
break
|
||||||
|
|
||||||
|
case Environment.CLIENT:
|
||||||
|
dataRaw = player
|
||||||
|
client.resolveEmitDestination(dataRaw)
|
||||||
|
break
|
||||||
|
|
||||||
|
case Environment.BROWSER:
|
||||||
|
dataRaw = player
|
||||||
|
browser.resolveEmitDestination(dataRaw)
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
void { player, dataRaw }
|
||||||
|
break
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public register(
|
||||||
|
eventName: string,
|
||||||
|
cb: (...args: unknown[]) => unknown,
|
||||||
|
): void {
|
||||||
|
Utils.errorUnknownEnvironment(this.environment_)
|
||||||
|
|
||||||
|
this.state_[eventName] = cb
|
||||||
|
}
|
||||||
|
|
||||||
|
public unregister(
|
||||||
|
eventName: string
|
||||||
|
): void {
|
||||||
|
Utils.errorUnknownEnvironment(this.environment_)
|
||||||
|
|
||||||
|
delete this.state_[eventName]
|
||||||
|
}
|
||||||
|
|
||||||
|
public callClient(eventName: string, args: unknown[]) {
|
||||||
|
Utils.errorUnknownEnvironment(this.environment_)
|
||||||
|
|
||||||
|
const state: RPCState = {
|
||||||
|
uuid: Utils.generateUUID(),
|
||||||
|
eventName,
|
||||||
|
calledTo: Environment.CLIENT,
|
||||||
|
calledFrom: this.environment_,
|
||||||
|
knownError: undefined,
|
||||||
|
data: args,
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataRaw = Utils.prepareTransfer(state)
|
||||||
|
|
||||||
|
mp.events.call(Events.EVENT_LISTENER, dataRaw)
|
||||||
|
}
|
||||||
|
|
||||||
|
public callServer(eventName: string, args: unknown[]) {
|
||||||
|
Utils.errorUnknownEnvironment(this.environment_)
|
||||||
|
|
||||||
|
const state: RPCState = {
|
||||||
|
uuid: Utils.generateUUID(),
|
||||||
|
eventName,
|
||||||
|
calledTo: Environment.SERVER,
|
||||||
|
calledFrom: this.environment_,
|
||||||
|
knownError: undefined,
|
||||||
|
data: args,
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataRaw = Utils.prepareTransfer(state)
|
||||||
|
|
||||||
|
mp.events.call(Events.EVENT_LISTENER, dataRaw)
|
||||||
|
}
|
||||||
|
|
||||||
|
public callBrowser(eventName: string, args: unknown[]) {
|
||||||
|
Utils.errorUnknownEnvironment(this.environment_)
|
||||||
|
|
||||||
|
const state: RPCState = {
|
||||||
|
uuid: Utils.generateUUID(),
|
||||||
|
eventName,
|
||||||
|
calledTo: Environment.BROWSER,
|
||||||
|
calledFrom: this.environment_,
|
||||||
|
knownError: undefined,
|
||||||
|
data: args,
|
||||||
|
}
|
||||||
|
|
||||||
|
const dataRaw = Utils.prepareTransfer(state)
|
||||||
|
|
||||||
|
mp.events.call(Events.EVENT_LISTENER, dataRaw)
|
||||||
|
}
|
||||||
|
|
||||||
|
public call(eventName: string, args: unknown[]) {
|
||||||
|
Utils.errorUnknownEnvironment(this.environment_)
|
||||||
|
|
||||||
|
let state: RPCState = {
|
||||||
|
uuid: Utils.generateUUID(),
|
||||||
|
eventName,
|
||||||
|
calledTo: this.environment_,
|
||||||
|
calledFrom: this.environment_,
|
||||||
|
knownError: undefined,
|
||||||
|
data: args,
|
||||||
|
}
|
||||||
|
|
||||||
|
state = this.verifyEvent_(state)
|
||||||
|
if (state.knownError) {
|
||||||
|
this.triggerError_(state, state.knownError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const rpc = new Rpc()
|
||||||
|
export { rpc }
|
44
apps/rpc/src/server.ts
Normal file
44
apps/rpc/src/server.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { Wrapper } from './wrapper'
|
||||||
|
import { Environment, Errors, Events, Utils } from './utils'
|
||||||
|
|
||||||
|
class Server extends Wrapper {
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
public resolveEmitDestination(player: any, dataRaw: string) {
|
||||||
|
if (!dataRaw) throw new Error(Errors.NO_DATA)
|
||||||
|
|
||||||
|
let state = Utils.prepareExecution(dataRaw)
|
||||||
|
|
||||||
|
switch (state.calledTo) {
|
||||||
|
case Environment.SERVER:
|
||||||
|
this.emit(player, dataRaw)
|
||||||
|
break
|
||||||
|
|
||||||
|
default:
|
||||||
|
this.emitClient(player, dataRaw)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private emitClient(player: any, dataRaw: string) {
|
||||||
|
player.call(Events.EVENT_LISTENER, dataRaw)
|
||||||
|
}
|
||||||
|
|
||||||
|
private emit(player: any, dataRaw: string) {
|
||||||
|
let state = Utils.prepareExecution(dataRaw)
|
||||||
|
|
||||||
|
state = this.verifyEvent_(state)
|
||||||
|
if (state.knownError) {
|
||||||
|
this.triggerError_(state, state.knownError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { eventName, data } = Utils.prepareExecution(dataRaw)
|
||||||
|
this.state_[eventName](...data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const server = new Server()
|
||||||
|
export { server }
|
73
apps/rpc/src/utils.ts
Normal file
73
apps/rpc/src/utils.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
export enum Environment {
|
||||||
|
BROWSER = 'BROWSER',
|
||||||
|
CLIENT = 'CLIENT',
|
||||||
|
SERVER = 'SERVER',
|
||||||
|
UNKNOWN = 'UNKNOWN',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Events {
|
||||||
|
EVENT_LISTENER = '__rpc:listener',
|
||||||
|
EVENT_RESPONSE = '__rpc:response',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum Errors {
|
||||||
|
EVENT_NOT_REGISTERED = 'Event not registered',
|
||||||
|
UNKNOWN_ENVIRONMENT = 'Unknown environment',
|
||||||
|
NO_DATA = 'No data',
|
||||||
|
NO_BROWSER = 'You need to initialize browser first',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type RPCState = {
|
||||||
|
eventName: string
|
||||||
|
uuid: string
|
||||||
|
knownError?: string
|
||||||
|
data?: any
|
||||||
|
calledFrom: Environment
|
||||||
|
calledTo: Environment
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Utils {
|
||||||
|
public static getEnvironment(): Environment {
|
||||||
|
if ('joaat' in mp) return Environment.SERVER
|
||||||
|
if ('game' in mp && 'joaat' in (mp.game as { joaat?: unknown }))
|
||||||
|
return Environment.CLIENT
|
||||||
|
if ('mp' in window) return Environment.BROWSER
|
||||||
|
return Environment.UNKNOWN
|
||||||
|
}
|
||||||
|
|
||||||
|
public static prepareExecution(data: string): RPCState {
|
||||||
|
return JSON.parse(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static prepareTransfer(data: RPCState): string {
|
||||||
|
return JSON.stringify(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static generateUUID(): string {
|
||||||
|
let uuid = '',
|
||||||
|
random
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
public static generateResponseEventName(uuid: string): string {
|
||||||
|
return `${Events.EVENT_RESPONSE}_${uuid}`
|
||||||
|
}
|
||||||
|
|
||||||
|
public static errorUnknownEnvironment(environment: Environment) {
|
||||||
|
if (environment === Environment.UNKNOWN)
|
||||||
|
throw new Error(Errors.UNKNOWN_ENVIRONMENT)
|
||||||
|
}
|
||||||
|
}
|
41
apps/rpc/src/wrapper.ts
Normal file
41
apps/rpc/src/wrapper.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { Environment, Errors, RPCState, Utils } from './utils'
|
||||||
|
|
||||||
|
export class Wrapper {
|
||||||
|
protected environment_ = Utils.getEnvironment()
|
||||||
|
protected state_ =
|
||||||
|
this.environment_ === Environment.BROWSER
|
||||||
|
? (window.rpcEvents = {} as Record<
|
||||||
|
string,
|
||||||
|
(...args: any[]) => unknown
|
||||||
|
>)
|
||||||
|
: (global.rpcEvents = {} as Record<
|
||||||
|
string,
|
||||||
|
(...args: any[]) => unknown
|
||||||
|
>)
|
||||||
|
|
||||||
|
protected verifyEvent_(data: string | RPCState): RPCState {
|
||||||
|
let rpcData =
|
||||||
|
typeof data === 'string' ? Utils.prepareExecution(data) : data
|
||||||
|
|
||||||
|
if (!this.state_[rpcData.eventName]) {
|
||||||
|
rpcData.knownError = Errors.EVENT_NOT_REGISTERED
|
||||||
|
}
|
||||||
|
|
||||||
|
return rpcData
|
||||||
|
}
|
||||||
|
|
||||||
|
protected triggerError_(rpcData: RPCState, error?: any) {
|
||||||
|
const errorMessage = [
|
||||||
|
`${rpcData.knownError}`,
|
||||||
|
`Caller: ${rpcData.calledFrom}`,
|
||||||
|
`Receiver: ${this.environment_}`,
|
||||||
|
`Event: ${rpcData.eventName}`,
|
||||||
|
]
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
errorMessage.push(`Additional Info: ${error}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(errorMessage.join('\n | '))
|
||||||
|
}
|
||||||
|
}
|
25
apps/rpc/tsconfig.json
Normal file
25
apps/rpc/tsconfig.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es6",
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"lib": ["ES6"],
|
||||||
|
"declaration": true,
|
||||||
|
"declarationMap": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
|
||||||
|
"outDir": "bin",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
|
||||||
|
"strict": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*",
|
||||||
|
"./index.d.ts"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules"
|
||||||
|
]
|
||||||
|
}
|
11
apps/rpc/tsup.config.ts
Normal file
11
apps/rpc/tsup.config.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { defineConfig } from 'tsup'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
entry: ['src/index.ts'],
|
||||||
|
outDir: './dist',
|
||||||
|
format: ['cjs'],
|
||||||
|
experimentalDts: true,
|
||||||
|
splitting: false,
|
||||||
|
sourcemap: false,
|
||||||
|
clean: true,
|
||||||
|
})
|
@ -8,6 +8,6 @@
|
|||||||
"build": "esbuild src/index.ts --bundle --platform=node --target=node10.4 --outfile=../../server/packages/server/index.js"
|
"build": "esbuild src/index.ts --bundle --platform=node --target=node10.4 --outfile=../../server/packages/server/index.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"rage-fw-server": "latest"
|
"rpc": "workspace:^"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,11 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"server:update": "cd server && rage-win64.exe",
|
"server:update": "cd server && rage-win64.exe",
|
||||||
|
|
||||||
|
"i:client": "cd apps/client && pnpm i rpc",
|
||||||
|
"i:server": "cd apps/server && pnpm i rpc",
|
||||||
|
"i:cef": "cd apps/cef && pnpm i rpc",
|
||||||
|
"i:all": "pnpm i:client && pnpm i:server && pnpm i:cef",
|
||||||
|
|
||||||
"build:client": "cd apps/client && pnpm build",
|
"build:client": "cd apps/client && pnpm build",
|
||||||
"build:server": "cd apps/server && pnpm build",
|
"build:server": "cd apps/server && pnpm build",
|
||||||
"build:cef": "cd apps/cef && pnpm build",
|
"build:cef": "cd apps/cef && pnpm build",
|
||||||
|
@ -3,3 +3,4 @@ packages:
|
|||||||
- "apps/client"
|
- "apps/client"
|
||||||
- "apps/server"
|
- "apps/server"
|
||||||
- "apps/shared"
|
- "apps/shared"
|
||||||
|
- "apps/rpc"
|
Loading…
Reference in New Issue
Block a user