framework rpc init
This commit is contained in:
		
							parent
							
								
									f08291eace
								
							
						
					
					
						commit
						f285f9c103
					
				
							
								
								
									
										6
									
								
								rpc/.prettierrc.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								rpc/.prettierrc.yaml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | ||||
| tabWidth: 4 | ||||
| printWidth: 80 | ||||
| singleQuote: true | ||||
| semi: false | ||||
| arrowParens: avoid | ||||
| endOfLine: auto | ||||
							
								
								
									
										22
									
								
								rpc/package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								rpc/package.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| { | ||||
|     "name": "rpc", | ||||
|     "version": "0.1.0", | ||||
|     "main": "dist/index.js", | ||||
|     "types": "dist/src/index.d.ts", | ||||
|     "scripts": { | ||||
|         "watch": "tsc -w", | ||||
|         "build": "tsup", | ||||
|         "start": "npx ./dist create" | ||||
|     }, | ||||
|     "files": [ | ||||
|         "dist/**/*" | ||||
|     ], | ||||
|     "description": "CLI to scaffold a template project for RageFW", | ||||
|     "keywords": [], | ||||
|     "author": "rilaxik", | ||||
|     "license": "ISC", | ||||
|     "devDependencies": { | ||||
|         "prettier": "^3.3.2", | ||||
|         "typescript": "^5.4.5" | ||||
|     } | ||||
| } | ||||
							
								
								
									
										2
									
								
								rpc/src/events.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								rpc/src/events.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,2 @@ | ||||
| export const EVENT_LISTENER = '__rpc:listener' | ||||
| export const EVENT_RESPONSE = '__rpc:response' | ||||
							
								
								
									
										61
									
								
								rpc/src/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								rpc/src/index.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,61 @@ | ||||
| import { Environment, utils } from './utils' | ||||
| import { EVENT_LISTENER } from './events' | ||||
| 
 | ||||
| import { client } from './modules/client' | ||||
| import { server } from './modules/server' | ||||
| 
 | ||||
| const environment = utils.getEnvironment() | ||||
| 
 | ||||
| const state = environment === Environment.CEF ? window : global | ||||
| 
 | ||||
| class rpc { | ||||
|     constructor() { | ||||
|         if (environment === Environment.UNKNOWN) return | ||||
| 
 | ||||
|         mp.events.add(EVENT_LISTENER, async (player: any, request: string) => { | ||||
|             switch (environment) { | ||||
|                 case Environment.SERVER: | ||||
|                     await server.listenEvent(player, request) | ||||
|                     break | ||||
| 
 | ||||
|                 case Environment.CLIENT: | ||||
|                     request = player | ||||
| 
 | ||||
|                     await client.listenEvent(request) | ||||
|                     break | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     public register<Callback extends any[] = unknown[], Return = unknown>( | ||||
|         eventName: string, | ||||
|         cb: (...args: Callback) => Return, | ||||
|     ) { | ||||
|         if (environment === Environment.UNKNOWN) return | ||||
|         state[eventName] = cb | ||||
|     } | ||||
| 
 | ||||
|     public async callClient<Args extends any[] = unknown[], Return = unknown>( | ||||
|         player: any, | ||||
|         eventName: string, | ||||
|         ...args: Args | ||||
|     ): Promise<Return | unknown> { | ||||
|         if (environment === Environment.UNKNOWN) return | ||||
|         if (environment === Environment.SERVER) { | ||||
|             return client.executeClient(player, eventName, args) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public async callServer<Args extends any[] = unknown[], Return = unknown>( | ||||
|         eventName: string, | ||||
|         ...args: Args | ||||
|     ): Promise<Return | unknown> { | ||||
|         if (environment === Environment.UNKNOWN) return | ||||
|         if (environment === Environment.CLIENT) { | ||||
|             return server.executeServer(eventName, args) | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| const testRpc = new rpc() | ||||
| export { testRpc } | ||||
							
								
								
									
										94
									
								
								rpc/src/modules/client.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								rpc/src/modules/client.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,94 @@ | ||||
| import { EVENT_LISTENER } from '../events' | ||||
| import { Wrapper } from './wrapper' | ||||
| import { RPCState } from '../utils' | ||||
| 
 | ||||
| class Client extends Wrapper { | ||||
|     private sendResponseToServer(data: RPCState) { | ||||
|         const eventName = this._utils.generateResponseEventName(data.uuid) | ||||
|         const preparedData = this._utils.prepareForTransfer(data) | ||||
|         mp.events.callRemote(eventName, preparedData) | ||||
|     } | ||||
| 
 | ||||
|     public async listenEvent(data: string) { | ||||
|         const rpcData = this._verifyEvent(data) | ||||
| 
 | ||||
|         if (rpcData.knownError) { | ||||
|             this._triggerError(rpcData) | ||||
|             return | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             const fnResponse = await this._state[rpcData.eventName]( | ||||
|                 ...rpcData.data, | ||||
|             ) | ||||
|             const response = { | ||||
|                 ...rpcData, | ||||
|                 data: fnResponse, | ||||
|             } | ||||
| 
 | ||||
|             this.sendResponseToServer(response) | ||||
|         } catch (e) { | ||||
|             this._triggerError(rpcData, e) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private handleClientServerReturn( | ||||
|         uuid: string, | ||||
|         resolve: (value: unknown) => void, | ||||
|         reject: (reason?: any) => void, | ||||
|     ) { | ||||
|         const responseEvent = this._utils.generateResponseEventName(uuid) | ||||
|         const timeoutDuration = 1000 * 10 | ||||
| 
 | ||||
|         const timeoutID = setTimeout(() => { | ||||
|             reject(new Error('Timeout ended')) | ||||
|             mp.events.remove(responseEvent) | ||||
|         }, timeoutDuration) | ||||
| 
 | ||||
|         const handler = (_: any, response: string) => { | ||||
|             const { knownError, data } = this._utils.prepareForExecute(response) | ||||
| 
 | ||||
|             if (knownError) | ||||
|                 try { | ||||
|                     clearTimeout(timeoutID) | ||||
|                     reject(knownError) | ||||
|                     return | ||||
|                 } catch (e) {} | ||||
| 
 | ||||
|             resolve(data) | ||||
|             mp.events.remove(responseEvent) | ||||
| 
 | ||||
|             try { | ||||
|                 clearTimeout(timeoutID) | ||||
|             } catch (e) {} | ||||
|         } | ||||
| 
 | ||||
|         mp.events.add(responseEvent, handler) | ||||
|     } | ||||
| 
 | ||||
|     public async executeClient< | ||||
|         Args extends any[] = unknown[], | ||||
|         Return = unknown, | ||||
|     >( | ||||
|         player: any, | ||||
|         eventName: string, | ||||
|         ...args: Args | ||||
|     ): Promise<Return | unknown> { | ||||
|         return new Promise((resolve, reject) => { | ||||
|             const uuid = this._utils.generateUUID() | ||||
| 
 | ||||
|             const data: RPCState = { | ||||
|                 uuid, | ||||
|                 eventName, | ||||
|                 calledFrom: this._environment, | ||||
|                 data: args, | ||||
|             } | ||||
| 
 | ||||
|             player.call(EVENT_LISTENER, [this._utils.prepareForTransfer(data)]) | ||||
| 
 | ||||
|             this.handleClientServerReturn(uuid, resolve, reject) | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export const client = new Client() | ||||
							
								
								
									
										95
									
								
								rpc/src/modules/server.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								rpc/src/modules/server.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,95 @@ | ||||
| import { Wrapper } from './wrapper' | ||||
| import { RPCState, utils } from '../utils' | ||||
| import { EVENT_LISTENER } from '../events' | ||||
| 
 | ||||
| class Server extends Wrapper { | ||||
|     private sendResponseToClient(player: any, data: RPCState) { | ||||
|         const eventName = this._utils.generateResponseEventName(data.uuid) | ||||
|         const preparedData = this._utils.prepareForTransfer(data) | ||||
| 
 | ||||
|         player.call(eventName, [preparedData]) | ||||
|     } | ||||
| 
 | ||||
|     public async listenEvent(player: any, data: string) { | ||||
|         const rpcData = this._verifyEvent(data) | ||||
| 
 | ||||
|         if (rpcData.knownError) { | ||||
|             this._triggerError(rpcData) | ||||
|             return | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             const fnResponse = await this._state[rpcData.eventName]( | ||||
|                 ...rpcData.data, | ||||
|             ) | ||||
|             const response = { | ||||
|                 ...rpcData, | ||||
|                 data: fnResponse, | ||||
|             } | ||||
| 
 | ||||
|             this.sendResponseToClient(player, response) | ||||
|         } catch (e) { | ||||
|             this._triggerError(rpcData, e) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     private handleServerClientReturn( | ||||
|         uuid: string, | ||||
|         resolve: (value: unknown) => void, | ||||
|         reject: (reason?: any) => void, | ||||
|     ) { | ||||
|         const responseEvent = this._utils.generateResponseEventName(uuid) | ||||
|         const timeoutDuration = 1000 * 10 | ||||
| 
 | ||||
|         const timeoutID = setTimeout(() => { | ||||
|             reject(new Error('Timeout ended')) | ||||
|             mp.events.remove(responseEvent) | ||||
|         }, timeoutDuration) | ||||
| 
 | ||||
|         const handler = (response: string) => { | ||||
|             const { knownError, data } = this._utils.prepareForExecute(response) | ||||
| 
 | ||||
|             if (knownError) { | ||||
|                 try { | ||||
|                     clearTimeout(timeoutID) | ||||
|                     reject(knownError) | ||||
|                     return | ||||
|                 } catch (e) {} | ||||
|             } | ||||
| 
 | ||||
|             resolve(data) | ||||
|             mp.events.remove(responseEvent) | ||||
| 
 | ||||
|             try { | ||||
|                 clearTimeout(timeoutID) | ||||
|             } catch (e) {} | ||||
|         } | ||||
| 
 | ||||
|         mp.events.add(responseEvent, handler) | ||||
|     } | ||||
| 
 | ||||
|     public async executeServer< | ||||
|         Args extends any[] = unknown[], | ||||
|         Return = unknown, | ||||
|     >(eventName: string, ...args: Args): Promise<Return | unknown> { | ||||
|         return new Promise((resolve, reject) => { | ||||
|             const uuid = this._utils.generateUUID() | ||||
| 
 | ||||
|             const data: RPCState = { | ||||
|                 uuid, | ||||
|                 eventName, | ||||
|                 calledFrom: this._environment, | ||||
|                 data: args, | ||||
|             } | ||||
| 
 | ||||
|             mp.events.callRemote( | ||||
|                 EVENT_LISTENER, | ||||
|                 this._utils.prepareForTransfer(data), | ||||
|             ) | ||||
| 
 | ||||
|             this.handleServerClientReturn(uuid, resolve, reject) | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export const server = new Server() | ||||
							
								
								
									
										32
									
								
								rpc/src/modules/wrapper.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								rpc/src/modules/wrapper.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,32 @@ | ||||
| import { Environment, Errors, RPCState, utils } from '../utils' | ||||
| 
 | ||||
| export class Wrapper { | ||||
|     public _utils = utils | ||||
|     public _environment = utils.getEnvironment() | ||||
|     public _state = this._environment === Environment.CEF ? window : global | ||||
| 
 | ||||
|     public _verifyEvent(data: string): RPCState { | ||||
|         const rpcData = utils.prepareForExecute(data) | ||||
| 
 | ||||
|         if (!this._state[rpcData.eventName]) { | ||||
|             rpcData.knownError = Errors.EVENT_NOT_REGISTERED | ||||
|         } | ||||
| 
 | ||||
|         return rpcData | ||||
|     } | ||||
| 
 | ||||
|     public _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(' | ')) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										13
									
								
								rpc/src/types.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								rpc/src/types.d.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,13 @@ | ||||
| declare const mp: any | ||||
| declare const console: any | ||||
| 
 | ||||
| declare const setTimeout: (fn: Function, time: number) => number | ||||
| declare const clearTimeout: (id: number) => void | ||||
| 
 | ||||
| declare const global: { | ||||
|     [p: string]: (...args: any[]) => unknown | ||||
| } | ||||
| 
 | ||||
| declare const window: { | ||||
|     [p: string]: (...args: any[]) => unknown | ||||
| } | ||||
							
								
								
									
										62
									
								
								rpc/src/utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								rpc/src/utils.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,62 @@ | ||||
| import { EVENT_RESPONSE } from './events' | ||||
| 
 | ||||
| export enum Environment { | ||||
|     CEF = 'CEF', | ||||
|     CLIENT = 'CLIENT', | ||||
|     SERVER = 'SERVER', | ||||
|     UNKNOWN = 'UNKNOWN', | ||||
| } | ||||
| 
 | ||||
| export enum Errors { | ||||
|     EVENT_NOT_REGISTERED = 'Event not registered', | ||||
| } | ||||
| 
 | ||||
| export type RPCState = { | ||||
|     eventName: string | ||||
|     uuid: string | ||||
|     knownError?: string | ||||
|     data?: any | ||||
|     calledFrom: Environment | ||||
| } | ||||
| 
 | ||||
| class Utils { | ||||
|     public getEnvironment(): Environment { | ||||
|         if (mp.joaat) return Environment.SERVER | ||||
|         if (mp.game && mp.game.joaat) return Environment.CLIENT | ||||
|         if ('mp' in window) return Environment.CEF | ||||
|         return Environment.UNKNOWN | ||||
|     } | ||||
| 
 | ||||
|     public prepareForExecute(data: string): RPCState { | ||||
|         return JSON.parse(data) | ||||
|     } | ||||
| 
 | ||||
|     public prepareForTransfer(data: RPCState): string { | ||||
|         return JSON.stringify(data) | ||||
|     } | ||||
| 
 | ||||
|     public 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 generateResponseEventName(uuid: string): string { | ||||
|         return `${EVENT_RESPONSE}_${uuid}` | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| export const utils = new Utils() | ||||
							
								
								
									
										24
									
								
								rpc/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								rpc/tsconfig.json
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | ||||
| { | ||||
|   "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/**/*" | ||||
|   ], | ||||
|   "exclude": [ | ||||
|     "node_modules" | ||||
|   ] | ||||
| } | ||||
							
								
								
									
										12
									
								
								rpc/tsup.config.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								rpc/tsup.config.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,12 @@ | ||||
| import { defineConfig } from 'tsup' | ||||
| 
 | ||||
| export default defineConfig({ | ||||
|     entry: ['src/index.ts'], | ||||
|     outDir: './dist', | ||||
|     format: ['cjs'], | ||||
|     noExternal: ['rage-rpc'], | ||||
|     experimentalDts: true, | ||||
|     splitting: false, | ||||
|     sourcemap: false, | ||||
|     clean: true, | ||||
| }) | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user