feat pure rpc example
This commit is contained in:
parent
67be91538b
commit
e7e423adc6
12
.gitignore
vendored
12
.gitignore
vendored
@ -1,8 +1,8 @@
|
|||||||
# Editor
|
# Editor
|
||||||
.vscode
|
.vscode/
|
||||||
.idea
|
.idea/
|
||||||
|
|
||||||
.git
|
.git/
|
||||||
|
|
||||||
# Server Files
|
# Server Files
|
||||||
server/bin
|
server/bin
|
||||||
@ -18,4 +18,8 @@ server/ragemp-server.exe
|
|||||||
# Development
|
# Development
|
||||||
node_modules
|
node_modules
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
dist
|
|
||||||
|
# Build
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
out/
|
@ -1,10 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "rage-fw-example-cef",
|
"name": "rage-fw-example-browser",
|
||||||
"version": "0.0.0",
|
"description": "Browser side of Rage-FW example",
|
||||||
"type": "module",
|
|
||||||
"author": "Entity Seven Group",
|
|
||||||
"license": "CC BY-ND",
|
|
||||||
"description": "CEF side of rage-fw example",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
@ -12,9 +8,10 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@rage-fw/shared": "workspace:^",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"rpc": "workspace:^"
|
"rage-fw-rpc": "file:../../../rage-framework/rpc"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^18.2.66",
|
"@types/react": "^18.2.66",
|
||||||
@ -25,7 +22,11 @@
|
|||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.6",
|
"eslint-plugin-react-refresh": "^0.4.6",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.6.3",
|
||||||
"vite": "^5.2.0"
|
"vite": "^5.4.10"
|
||||||
}
|
},
|
||||||
|
"type": "module",
|
||||||
|
"license": "CC BY-ND",
|
||||||
|
"author": "Entity Seven Group",
|
||||||
|
"version": "0.1.0"
|
||||||
}
|
}
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
24
apps/browser/src/App.tsx
Normal file
24
apps/browser/src/App.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
import { events } from '@rage-fw/shared'
|
||||||
|
import { rpc } from '@/lib'
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const [data, setData] = useState<string>('')
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
rpc.register(events.browser.customBrowserEvent, (args: string) => {
|
||||||
|
setData(prev => prev + ' | ' + args)
|
||||||
|
return 'response from cef'
|
||||||
|
})
|
||||||
|
rpc.callClient(events.client.cefReady, ['hello from cef'])
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ width: '100%', color: 'white', textAlign: 'center' }}>
|
||||||
|
<h1>Hello World!</h1>
|
||||||
|
<h2>{data}</h2>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
5
apps/browser/src/lib/index.ts
Normal file
5
apps/browser/src/lib/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { Rpc } from 'rage-fw-rpc'
|
||||||
|
|
||||||
|
export const rpc = new Rpc({
|
||||||
|
debugLogs: true,
|
||||||
|
})
|
@ -22,7 +22,11 @@
|
|||||||
"types": [
|
"types": [
|
||||||
"../../node_modules/@ragempcommunity/types-cef",
|
"../../node_modules/@ragempcommunity/types-cef",
|
||||||
"../shared/declarations/rage-fw-shared-types/"
|
"../shared/declarations/rage-fw-shared-types/"
|
||||||
]
|
],
|
||||||
|
"paths": {
|
||||||
|
"@": ["./src"],
|
||||||
|
"@/*": ["./src/*"],
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src"],
|
||||||
"references": [{ "path": "./tsconfig.node.json" }]
|
"references": [{ "path": "./tsconfig.node.json" }]
|
17
apps/browser/vite.config.ts
Normal file
17
apps/browser/vite.config.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
|
// https://vitejs.dev/config/
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [react()],
|
||||||
|
build: {
|
||||||
|
outDir: '../../server/client_packages/cef',
|
||||||
|
emptyOutDir: true,
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'@': './src',
|
||||||
|
'@/*': './src/*',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
@ -1,20 +0,0 @@
|
|||||||
import { useEffect, useState } from 'react'
|
|
||||||
import { rpc } from 'rpc'
|
|
||||||
|
|
||||||
function App() {
|
|
||||||
const [data, setData] = useState<unknown>('')
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
rpc.callClient('cefReady')
|
|
||||||
rpc.register('customCefEvent', args => setData(args))
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div style={{ width: '100%', color: 'white', textAlign: 'center' }}>
|
|
||||||
<h1>Hello World!</h1>
|
|
||||||
<h2>{data!.toString()}</h2>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default App
|
|
@ -1,11 +0,0 @@
|
|||||||
import { defineConfig } from 'vite'
|
|
||||||
import react from '@vitejs/plugin-react'
|
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
|
||||||
export default defineConfig({
|
|
||||||
plugins: [react()],
|
|
||||||
build: {
|
|
||||||
outDir: '../../server/client_packages/cef',
|
|
||||||
emptyOutDir: true
|
|
||||||
}
|
|
||||||
})
|
|
@ -1,13 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "rage-fw-example-client",
|
"name": "rage-fw-example-client",
|
||||||
"version": "0.1.0",
|
"description": "Client side of Rage-FW example",
|
||||||
"author": "Entity Seven Group",
|
|
||||||
"license": "CC BY-ND",
|
|
||||||
"description": "Client side of rage-fw example",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"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": {
|
||||||
"rpc": "workspace:^"
|
"@rage-fw/shared": "workspace:^",
|
||||||
}
|
"rage-fw-rpc": "file:../../../rage-framework/rpc"
|
||||||
|
},
|
||||||
|
"license": "CC BY-ND",
|
||||||
|
"author": "Entity Seven Group",
|
||||||
|
"version": "0.1.0"
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,22 @@
|
|||||||
import { rpc, client } from 'rpc'
|
import { events } from '@rage-fw/shared'
|
||||||
|
import { rpc } from './lib'
|
||||||
|
|
||||||
client.browser = mp.browsers.new('package://cef/index.html')
|
rpc.browser = mp.browsers.new('package://cef/index.html')
|
||||||
|
|
||||||
rpc.register('cefReady', async () => {
|
rpc.register(events.client.cefReady, async (args: string) => {
|
||||||
mp.console.logInfo('cef to client')
|
mp.console.logInfo(args)
|
||||||
|
const res = (await rpc.callServer(events.server.customServerEvent, [
|
||||||
rpc.callServer('customServerEvent', ['client to server'])
|
'hello from client',
|
||||||
|
])) as string
|
||||||
|
mp.console.logInfo(res)
|
||||||
|
return 'response from client'
|
||||||
})
|
})
|
||||||
|
|
||||||
rpc.register('customClientEvent', async data => {
|
rpc.register(events.client.cefReady, async (args: string) => {
|
||||||
mp.console.logInfo(JSON.stringify(data))
|
mp.console.logInfo(args)
|
||||||
|
const res = (await rpc.callBrowser(events.browser.customBrowserEvent, [
|
||||||
rpc.callBrowser('customCefEvent', ['client to cef'])
|
'hello from client',
|
||||||
|
])) as string
|
||||||
|
mp.console.logInfo(res)
|
||||||
|
return 'response from client'
|
||||||
})
|
})
|
||||||
|
5
apps/client/src/lib/index.ts
Normal file
5
apps/client/src/lib/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { Rpc } from 'rage-fw-rpc'
|
||||||
|
|
||||||
|
export const rpc = new Rpc({
|
||||||
|
debugLogs: true,
|
||||||
|
})
|
@ -1,6 +0,0 @@
|
|||||||
tabWidth: 4
|
|
||||||
printWidth: 80
|
|
||||||
singleQuote: true
|
|
||||||
semi: false
|
|
||||||
arrowParens: avoid
|
|
||||||
endOfLine: auto
|
|
22
apps/rpc/index.d.ts
vendored
22
apps/rpc/index.d.ts
vendored
@ -1,22 +0,0 @@
|
|||||||
export {}
|
|
||||||
|
|
||||||
interface Mp {
|
|
||||||
trigger(event: string, data?: any): void
|
|
||||||
events: {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "rpc",
|
|
||||||
"version": "0.1.0",
|
|
||||||
"main": "src/index.ts",
|
|
||||||
"types": "dist/src/index.d.ts",
|
|
||||||
"scripts": {
|
|
||||||
"build": "tsup",
|
|
||||||
"start": "npx ./dist create"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"dist/**/*"
|
|
||||||
],
|
|
||||||
"devDependencies": {
|
|
||||||
"@microsoft/api-extractor": "^7.47.9",
|
|
||||||
"prettier": "^3.3.2",
|
|
||||||
"tsup": "^8.3.0"
|
|
||||||
},
|
|
||||||
"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"
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
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.LOCAL_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 }
|
|
@ -1,67 +0,0 @@
|
|||||||
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](
|
|
||||||
...(Array.isArray(state.data) ? state.data : []),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private emitServer(dataRaw: string) {
|
|
||||||
mp.events.callRemote(Events.SERVER_EVENT_LISTENER, dataRaw)
|
|
||||||
}
|
|
||||||
|
|
||||||
private emitBrowser(dataRaw: string, state: RPCState) {
|
|
||||||
if (!this._browser) {
|
|
||||||
this.triggerError_(state, Errors.NO_BROWSER)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this._browser.call(Events.LOCAL_EVENT_LISTENER, dataRaw)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const client = new Client()
|
|
||||||
export { client }
|
|
@ -1,225 +0,0 @@
|
|||||||
import { Wrapper } from './wrapper'
|
|
||||||
import {
|
|
||||||
Environment,
|
|
||||||
Errors,
|
|
||||||
Events,
|
|
||||||
RPCState,
|
|
||||||
Utils,
|
|
||||||
type PlayerMp,
|
|
||||||
} from './utils'
|
|
||||||
|
|
||||||
import { server } from './server'
|
|
||||||
import { client } from './client'
|
|
||||||
import { browser } from './browser'
|
|
||||||
|
|
||||||
export { server } from './server'
|
|
||||||
export { client } from './client'
|
|
||||||
export { browser } from './browser'
|
|
||||||
|
|
||||||
class Rpc extends Wrapper {
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
|
|
||||||
if (this.environment_ === Environment.UNKNOWN)
|
|
||||||
throw new Error(Errors.UNKNOWN_ENVIRONMENT)
|
|
||||||
|
|
||||||
mp.events.add(
|
|
||||||
Events.LOCAL_EVENT_LISTENER,
|
|
||||||
async (player: PlayerMp | string, dataRaw: string) => {
|
|
||||||
switch (this.environment_) {
|
|
||||||
case Environment.SERVER:
|
|
||||||
server.resolveEmitDestination(
|
|
||||||
player as PlayerMp,
|
|
||||||
dataRaw,
|
|
||||||
)
|
|
||||||
break
|
|
||||||
|
|
||||||
case Environment.CLIENT:
|
|
||||||
dataRaw = player as string
|
|
||||||
client.resolveEmitDestination(dataRaw)
|
|
||||||
break
|
|
||||||
|
|
||||||
case Environment.BROWSER:
|
|
||||||
dataRaw = player as string
|
|
||||||
browser.resolveEmitDestination(dataRaw)
|
|
||||||
break
|
|
||||||
|
|
||||||
default:
|
|
||||||
void { player, dataRaw }
|
|
||||||
break
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
public register<
|
|
||||||
CallbackArguments extends unknown[] = unknown[],
|
|
||||||
CallbackReturn extends unknown = unknown,
|
|
||||||
EventName extends string = string,
|
|
||||||
>(
|
|
||||||
eventName: EventName,
|
|
||||||
cb: (...args: CallbackArguments) => CallbackReturn,
|
|
||||||
): void {
|
|
||||||
Utils.errorUnknownEnvironment(this.environment_)
|
|
||||||
|
|
||||||
this.state_[eventName] = cb
|
|
||||||
}
|
|
||||||
|
|
||||||
public unregister<EventName extends string = string>(
|
|
||||||
eventName: EventName,
|
|
||||||
): void {
|
|
||||||
Utils.errorUnknownEnvironment(this.environment_)
|
|
||||||
|
|
||||||
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(
|
|
||||||
playerOrEventName: PlayerMp | string,
|
|
||||||
eventNameOrArgs?: string | unknown[],
|
|
||||||
args?: unknown[],
|
|
||||||
): void {
|
|
||||||
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
|
|
||||||
if (this.environment_ === Environment.CLIENT) {
|
|
||||||
this.call(playerOrEventName as string, args as unknown[])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// server
|
|
||||||
if (
|
|
||||||
this.environment_ === Environment.SERVER &&
|
|
||||||
_is1StParamPlayer(playerOrEventName) &&
|
|
||||||
_is2NdParamEventName(eventNameOrArgs)
|
|
||||||
) {
|
|
||||||
const state: RPCState = {
|
|
||||||
uuid: Utils.generateUUID(),
|
|
||||||
eventName: eventNameOrArgs,
|
|
||||||
calledTo: Environment.CLIENT,
|
|
||||||
calledFrom: this.environment_,
|
|
||||||
knownError: undefined,
|
|
||||||
data: args as unknown[],
|
|
||||||
}
|
|
||||||
|
|
||||||
const dataRaw = Utils.prepareTransfer(state)
|
|
||||||
|
|
||||||
playerOrEventName.call(Events.LOCAL_EVENT_LISTENER, [dataRaw])
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// browser
|
|
||||||
if (
|
|
||||||
this.environment_ === Environment.BROWSER &&
|
|
||||||
!_is1StParamPlayer(playerOrEventName) &&
|
|
||||||
!_is2NdParamEventName(eventNameOrArgs)
|
|
||||||
) {
|
|
||||||
const state: RPCState = {
|
|
||||||
uuid: Utils.generateUUID(),
|
|
||||||
eventName: playerOrEventName,
|
|
||||||
calledTo: Environment.CLIENT,
|
|
||||||
calledFrom: this.environment_,
|
|
||||||
knownError: undefined,
|
|
||||||
data: eventNameOrArgs,
|
|
||||||
}
|
|
||||||
|
|
||||||
const dataRaw = Utils.prepareTransfer(state)
|
|
||||||
|
|
||||||
mp.trigger(Events.LOCAL_EVENT_LISTENER, dataRaw)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public callServer<
|
|
||||||
Arguments extends unknown[] = unknown[],
|
|
||||||
EventName extends string = string,
|
|
||||||
>(eventName: EventName, args?: Arguments) {
|
|
||||||
Utils.errorUnknownEnvironment(this.environment_)
|
|
||||||
|
|
||||||
const state: RPCState = {
|
|
||||||
uuid: Utils.generateUUID(),
|
|
||||||
eventName,
|
|
||||||
calledTo: Environment.SERVER,
|
|
||||||
calledFrom: this.environment_,
|
|
||||||
knownError: undefined,
|
|
||||||
data: args,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.calledFrom === Environment.SERVER) {
|
|
||||||
this.callSelf(state)
|
|
||||||
} else {
|
|
||||||
const dataRaw = Utils.prepareTransfer(state)
|
|
||||||
|
|
||||||
mp.events.call(Events.LOCAL_EVENT_LISTENER, dataRaw)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public callBrowser<
|
|
||||||
Arguments extends unknown[] = unknown[],
|
|
||||||
EventName extends string = string,
|
|
||||||
>(eventName: EventName, args?: Arguments) {
|
|
||||||
Utils.errorUnknownEnvironment(this.environment_)
|
|
||||||
|
|
||||||
const state: RPCState = {
|
|
||||||
uuid: Utils.generateUUID(),
|
|
||||||
eventName,
|
|
||||||
calledTo: Environment.BROWSER,
|
|
||||||
calledFrom: this.environment_,
|
|
||||||
knownError: undefined,
|
|
||||||
data: args,
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.calledFrom === Environment.BROWSER) {
|
|
||||||
this.callSelf(state)
|
|
||||||
} else {
|
|
||||||
const dataRaw = Utils.prepareTransfer(state)
|
|
||||||
|
|
||||||
mp.events.call(Events.LOCAL_EVENT_LISTENER, dataRaw)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public call<
|
|
||||||
Arguments extends unknown[] = unknown[],
|
|
||||||
EventName extends string = string,
|
|
||||||
>(eventName: EventName, args?: Arguments) {
|
|
||||||
Utils.errorUnknownEnvironment(this.environment_)
|
|
||||||
|
|
||||||
let state: RPCState = {
|
|
||||||
uuid: Utils.generateUUID(),
|
|
||||||
eventName,
|
|
||||||
calledTo: this.environment_,
|
|
||||||
calledFrom: this.environment_,
|
|
||||||
knownError: undefined,
|
|
||||||
data: args,
|
|
||||||
}
|
|
||||||
|
|
||||||
this.callSelf(state)
|
|
||||||
}
|
|
||||||
|
|
||||||
private callSelf(state: RPCState) {
|
|
||||||
state = this.verifyEvent_(state)
|
|
||||||
if (state.knownError) {
|
|
||||||
this.triggerError_(state, state.knownError)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.state_[state.eventName](...state.data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const rpc = new Rpc()
|
|
||||||
export { rpc }
|
|
@ -1,49 +0,0 @@
|
|||||||
import { Wrapper } from './wrapper'
|
|
||||||
import { Environment, Events, Utils, type PlayerMp } from './utils'
|
|
||||||
|
|
||||||
class Server extends Wrapper {
|
|
||||||
constructor() {
|
|
||||||
super()
|
|
||||||
|
|
||||||
mp.events.add(
|
|
||||||
Events.SERVER_EVENT_LISTENER,
|
|
||||||
async (player: PlayerMp, dataRaw: string) => {
|
|
||||||
this.emit(player, dataRaw)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
public resolveEmitDestination(player: PlayerMp, dataRaw: string) {
|
|
||||||
let state = Utils.prepareExecution(dataRaw)
|
|
||||||
|
|
||||||
switch (state.calledTo) {
|
|
||||||
case Environment.SERVER:
|
|
||||||
this.emit(player, dataRaw)
|
|
||||||
break
|
|
||||||
|
|
||||||
default:
|
|
||||||
this.emitClient(player as PlayerMp, dataRaw)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private emitClient(player: PlayerMp, dataRaw: string) {
|
|
||||||
player.call(Events.LOCAL_EVENT_LISTENER, [dataRaw])
|
|
||||||
}
|
|
||||||
|
|
||||||
private emit(player: PlayerMp, 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](player, ...data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const server = new Server()
|
|
||||||
export { server }
|
|
@ -1,82 +0,0 @@
|
|||||||
export enum Environment {
|
|
||||||
BROWSER = 'BROWSER',
|
|
||||||
CLIENT = 'CLIENT',
|
|
||||||
SERVER = 'SERVER',
|
|
||||||
UNKNOWN = 'UNKNOWN',
|
|
||||||
}
|
|
||||||
|
|
||||||
export enum Events {
|
|
||||||
LOCAL_EVENT_LISTENER = '__rpc:listener',
|
|
||||||
CLIENT_EVENT_LISTENER = '__rpc:clientListener',
|
|
||||||
SERVER_EVENT_LISTENER = '__rpc:serverListener',
|
|
||||||
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 class Utils {
|
|
||||||
public static getEnvironment(): Environment {
|
|
||||||
if ('joaat' in mp) return Environment.SERVER
|
|
||||||
if (
|
|
||||||
'game' in mp &&
|
|
||||||
'joaat' in (mp as { game: { joaat?: unknown } }).game
|
|
||||||
)
|
|
||||||
return Environment.CLIENT
|
|
||||||
if (window && '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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type RPCState = {
|
|
||||||
eventName: string
|
|
||||||
uuid: string
|
|
||||||
knownError?: string
|
|
||||||
data?: any
|
|
||||||
calledFrom: Environment
|
|
||||||
calledTo: Environment
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PlayerMp = {
|
|
||||||
call(event: string, args?: unknown[]): void
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
import { Environment, Errors, RPCState, Utils } from './utils'
|
|
||||||
|
|
||||||
export class Wrapper {
|
|
||||||
protected environment_ = Utils.getEnvironment()
|
|
||||||
protected state_ =
|
|
||||||
this.environment_ === Environment.BROWSER ? window : global
|
|
||||||
|
|
||||||
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 | '))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "es6",
|
|
||||||
"module": "commonjs",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"lib": [
|
|
||||||
"ES6",
|
|
||||||
"dom"
|
|
||||||
],
|
|
||||||
"declaration": true,
|
|
||||||
"declarationMap": true,
|
|
||||||
"sourceMap": true,
|
|
||||||
|
|
||||||
"outDir": "bin",
|
|
||||||
"esModuleInterop": true,
|
|
||||||
|
|
||||||
"strict": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"noImplicitAny": true,
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"src/**/*",
|
|
||||||
"./index.d.ts"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"node_modules",
|
|
||||||
"dist"
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
import { defineConfig } from 'tsup'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
entry: ['src/index.ts'],
|
|
||||||
outDir: './dist',
|
|
||||||
format: ['cjs'],
|
|
||||||
experimentalDts: true,
|
|
||||||
splitting: false,
|
|
||||||
sourcemap: false,
|
|
||||||
clean: true,
|
|
||||||
})
|
|
@ -1,13 +1,14 @@
|
|||||||
{
|
{
|
||||||
"name": "rage-fw-example-server",
|
"name": "rage-fw-example-server",
|
||||||
"version": "0.1.0",
|
"description": "Server side of Rage-FW example",
|
||||||
"author": "Entity Seven Group",
|
|
||||||
"license": "CC BY-ND",
|
|
||||||
"description": "Server side of rage-fw example",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"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": {
|
||||||
"rpc": "workspace:^"
|
"@rage-fw/shared": "workspace:^",
|
||||||
}
|
"rage-fw-rpc": "file:../../../rage-framework/rpc"
|
||||||
|
},
|
||||||
|
"license": "CC BY-ND",
|
||||||
|
"author": "Entity Seven Group",
|
||||||
|
"version": "0.1.0"
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,20 @@
|
|||||||
import { rpc } from 'rpc'
|
import { events } from '@rage-fw/shared'
|
||||||
|
import { rpc } from './lib'
|
||||||
|
|
||||||
rpc.register('playerJoin', async (player: PlayerMp) => {
|
rpc.register('playerJoin', async (player: PlayerMp) => {
|
||||||
console.log(`Connected: ${player.socialClub}`)
|
console.log(`[Server] ${player.socialClub} connected`)
|
||||||
})
|
})
|
||||||
|
|
||||||
rpc.register('customServerEvent', (player: PlayerMp, data) => {
|
rpc.register(
|
||||||
console.log(player, data)
|
events.server.customServerEvent,
|
||||||
|
async (player: PlayerMp, args: string) => {
|
||||||
rpc.callClient(player, 'customClientEvent', ['server to client'])
|
console.log(args)
|
||||||
})
|
const res = await rpc.callClient(
|
||||||
|
player,
|
||||||
|
events.client.customClientEvent,
|
||||||
|
['hello from server'],
|
||||||
|
)
|
||||||
|
console.log(res)
|
||||||
|
return 'response from server'
|
||||||
|
},
|
||||||
|
)
|
||||||
|
5
apps/server/src/lib/index.ts
Normal file
5
apps/server/src/lib/index.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { Rpc } from 'rage-fw-rpc'
|
||||||
|
|
||||||
|
export const rpc = new Rpc({
|
||||||
|
debugLogs: true,
|
||||||
|
})
|
@ -1,13 +0,0 @@
|
|||||||
declare module 'rage-fw-shared-types' {
|
|
||||||
export interface RageFW_ICustomClientEvent {
|
|
||||||
customClientEvent(greetings: string): void
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RageFW_ICustomServerEvent {
|
|
||||||
customServerEvent(greetings: string): void
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RageFW_ICustomCefEvent {
|
|
||||||
customCefEvent(greetings: string): void
|
|
||||||
}
|
|
||||||
}
|
|
12
apps/shared/index.ts
Normal file
12
apps/shared/index.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export const events = {
|
||||||
|
server: {
|
||||||
|
customServerEvent: 'customServerEvent',
|
||||||
|
},
|
||||||
|
client: {
|
||||||
|
cefReady: 'cefReady',
|
||||||
|
customClientEvent: 'customClientEvent',
|
||||||
|
},
|
||||||
|
browser: {
|
||||||
|
customBrowserEvent: 'customBrowserEvent',
|
||||||
|
},
|
||||||
|
}
|
6
apps/shared/package.json
Normal file
6
apps/shared/package.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "@rage-fw/shared",
|
||||||
|
"description": "Shared data for Rage-FW example",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"main": "index.ts"
|
||||||
|
}
|
29
package.json
29
package.json
@ -1,29 +1,30 @@
|
|||||||
{
|
{
|
||||||
"name": "framework-example",
|
"name": "framework-example",
|
||||||
"version": "0.1.0",
|
"description": "This project is an example of RAGE-FW usage",
|
||||||
"author": "Entity Seven Group",
|
"workspaces": [
|
||||||
"license": "CC BY-ND",
|
"apps/*"
|
||||||
"description": "This project is example of RAGE FW usage.",
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"server:update": "cd server && rage-win64.exe",
|
"server:update": "cd server && rage-win64.exe",
|
||||||
|
"server:run": "cd server && ragemp-server.exe",
|
||||||
"i:client": "cd apps/client && pnpm i rpc",
|
"i:client": "cd apps/client && pnpm i",
|
||||||
"i:server": "cd apps/server && pnpm i rpc",
|
"i:server": "cd apps/server && pnpm i",
|
||||||
"i:cef": "cd apps/cef && pnpm i rpc",
|
"i:browser": "cd apps/browser && pnpm i",
|
||||||
"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:browser": "cd apps/cef && pnpm build",
|
||||||
"build:all": "pnpm build:client && pnpm build:server && pnpm build:cef"
|
"build:all": "pnpm build:client && pnpm build:server && pnpm build:browser",
|
||||||
|
"build": "pnpm build:all"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ragempcommunity/types-client": "^2.1.8",
|
"@ragempcommunity/types-client": "^2.1.8",
|
||||||
"@ragempcommunity/types-server": "^2.1.8",
|
"@ragempcommunity/types-server": "^2.1.8",
|
||||||
"@ragempcommunity/types-cef": "^2.1.8",
|
"@ragempcommunity/types-cef": "^2.1.8",
|
||||||
"rage-fw-shared-types": "latest",
|
|
||||||
"esbuild": "^0.21.5",
|
"esbuild": "^0.21.5",
|
||||||
"typescript": "^5.4.5",
|
"typescript": "^5.4.5",
|
||||||
"prettier": "^3.3.2"
|
"prettier": "^3.3.2"
|
||||||
}
|
},
|
||||||
|
"license": "CC BY-ND",
|
||||||
|
"author": "Entity Seven Group",
|
||||||
|
"version": "0.1.0"
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,2 @@
|
|||||||
packages:
|
packages:
|
||||||
- "apps/cef"
|
- "apps/*"
|
||||||
- "apps/client"
|
|
||||||
- "apps/server"
|
|
||||||
- "apps/shared"
|
|
||||||
- "apps/rpc"
|
|
||||||
|
Loading…
Reference in New Issue
Block a user