Use Typescript

This commit is contained in:
Micah Allen 2019-01-10 18:57:44 -05:00
parent 65241d1c07
commit 94ffe6c474
11 changed files with 1377 additions and 93 deletions

5
.babelrc Normal file
View File

@ -0,0 +1,5 @@
{
"presets": [
"@babel/preset-typescript"
]
}

29
dist/rage-rpc.d.ts vendored Normal file
View File

@ -0,0 +1,29 @@
export as namespace rpc;
export function register(name: string, cb: ProcedureListener): void;
export function unregister(name: string): void;
export function call(name: string, args?: any): Promise<any>;
export function callServer(name: string, args?: any): Promise<any>;
export function callClient(player: Player, name: string, args?: any): Promise<any>;
export function callClient(name: string, args?: any): Promise<any>;
export function callBrowsers(player: Player, name: string, args?: any): Promise<any>;
export function callBrowsers(name: string, args?: any): Promise<any>;
export function callBrowser(browser: Browser, name: string, args?: any): Promise<any>;
export interface Player {
call: (eventName: string, args?: any[]) => void;
[property: string]: any;
}
export interface Browser {
execute: (code: string) => void;
[property: string]: any;
}
export interface ProcedureListenerInfo {
environment: string;
id?: string;
player?: Player;
}
export type ProcedureListener = (args: any, info: ProcedureListenerInfo) => any;

File diff suppressed because one or more lines are too long

1198
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -3,10 +3,12 @@
"version": "0.0.3", "version": "0.0.3",
"description": "An asynchronous RPC implementation for RAGE Multiplayer", "description": "An asynchronous RPC implementation for RAGE Multiplayer",
"main": "dist/rage-rpc.min.js", "main": "dist/rage-rpc.min.js",
"types": "dist/rage-rpc.d.ts",
"scripts": { "scripts": {
"build": "webpack-cli --config ./webpack.config.js", "build": "webpack-cli --config ./webpack.config.js",
"test": "echo \"Error: no test specified\" && exit 1" "type-check": "tsc"
}, },
"files": ["dist"],
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/micaww/rage-rpc.git" "url": "git+https://github.com/micaww/rage-rpc.git"
@ -19,7 +21,12 @@
"homepage": "https://github.com/micaww/rage-rpc#readme", "homepage": "https://github.com/micaww/rage-rpc#readme",
"dependencies": {}, "dependencies": {},
"devDependencies": { "devDependencies": {
"webpack-cli": "^3.1.2", "@babel/core": "^7.2.2",
"webpack": "^4.23.1" "@babel/preset-env": "^7.2.3",
"@babel/preset-typescript": "^7.1.0",
"babel-loader": "^8.0.5",
"typescript": "^3.2.2",
"webpack": "^4.23.1",
"webpack-cli": "^3.1.2"
} }
} }

33
src/defs.d.ts vendored Normal file
View File

@ -0,0 +1,33 @@
declare var mp: any;
declare var global: any;
declare var window: any;
declare type ProcedureListener = (args: any, info: ProcedureListenerInfo) => any;
declare interface Player {
call: (eventName: string, args?: any[]) => void;
[property: string]: any;
}
declare interface Browser {
execute: (code: string) => void;
[property: string]: any;
}
declare interface ProcedureListenerInfo {
environment: string;
id?: string;
player?: Player;
}
declare interface Event {
req?: number;
ret?: number;
id: string;
name?: string;
args?: any;
env: string;
fenv?: string;
res?: any;
err?: any;
}

View File

@ -1,4 +1,4 @@
import * as util from './util.js'; import * as util from './util';
const environment = util.getEnvironment(); const environment = util.getEnvironment();
if(!environment) throw 'Unknown RAGE environment'; if(!environment) throw 'Unknown RAGE environment';
@ -14,17 +14,16 @@ if(!glob[PROCESS_EVENT]){
glob.__rpcListeners = {}; glob.__rpcListeners = {};
glob.__rpcPending = {}; glob.__rpcPending = {};
glob[PROCESS_EVENT] = (...args) => { glob[PROCESS_EVENT] = (player: Player | string, rawData?: string) => {
let rawData = args[0]; if(environment !== "server") rawData = player as string;
if(environment === "server") rawData = args[1]; const data: Event = util.parseData(rawData);
const data = util.parseData(rawData);
if(data.req){ // someone is trying to remotely call a procedure if(data.req){ // someone is trying to remotely call a procedure
const info = { const info: ProcedureListenerInfo = {
id: data.id, id: data.id,
environment: data.fenv || data.env environment: data.fenv || data.env
}; };
if(environment === "server") info.player = args[0]; if(environment === "server") info.player = player as Player;
const promise = callProcedure(data.name, data.args, info); const promise = callProcedure(data.name, data.args, info);
const part = { const part = {
ret: 1, ret: 1,
@ -90,7 +89,7 @@ if(!glob[PROCESS_EVENT]){
} }
}else if(data.ret){ // a previously called remote procedure has returned }else if(data.ret){ // a previously called remote procedure has returned
const info = glob.__rpcPending[data.id]; const info = glob.__rpcPending[data.id];
if(environment === "server" && info.player !== args[0]) return; if(environment === "server" && info.player !== player) return;
if(info){ if(info){
if(data.err) info.reject(data.err); if(data.err) info.reject(data.err);
else info.resolve(data.res); else info.resolve(data.res);
@ -100,7 +99,7 @@ if(!glob[PROCESS_EVENT]){
}; };
if(environment === "cef"){ if(environment === "cef"){
window[PROCEDURE_EXISTS] = name => !!glob.__rpcListeners[name]; window[PROCEDURE_EXISTS] = (name: string) => !!glob.__rpcListeners[name];
}else{ }else{
mp.events.add(PROCESS_EVENT, glob[PROCESS_EVENT]); mp.events.add(PROCESS_EVENT, glob[PROCESS_EVENT]);
@ -120,19 +119,16 @@ if(!glob[PROCESS_EVENT]){
} }
} }
let passEventToBrowser, passEventToBrowsers; function passEventToBrowser(browser: Browser, data: Event, ignore: boolean): void {
if(environment === "client"){ const raw = util.stringifyData(data);
passEventToBrowser = (browser, data, ignore) => { browser.execute(`var process = window["${PROCESS_EVENT}"]; if(process){ process('${raw}'); }else{ ${ignore ? '' : `mp.trigger("${PROCESS_EVENT}", '{"ret":1,"id":"${data.id}","err":"${ERR_NOT_FOUND}","env":"cef"}');`} }`);
const raw = util.stringifyData(data);
browser.execute(`var process = window["${PROCESS_EVENT}"]; if(process){ process('${raw}'); }else{ ${ignore ? '' : `mp.trigger("${PROCESS_EVENT}", '{"ret":1,"id":"${data.id}","err":"${ERR_NOT_FOUND}","env":"cef"}');`} }`);
};
passEventToBrowsers = (data, ignore) => {
mp.browsers.forEach(browser => passEventToBrowser(browser, data, ignore));
};
} }
async function callProcedure(name, args, info){ function passEventToBrowsers(data: Event, ignore: boolean): void {
mp.browsers.forEach((browser: Browser) => passEventToBrowser(browser, data, ignore));
}
async function callProcedure(name: string, args: any, info: ProcedureListenerInfo){
const listener = glob.__rpcListeners[name]; const listener = glob.__rpcListeners[name];
if(!listener) throw ERR_NOT_FOUND; if(!listener) throw ERR_NOT_FOUND;
return listener(args, info); return listener(args, info);
@ -143,7 +139,7 @@ async function callProcedure(name, args, info){
* @param {string} name - The name of the procedure. * @param {string} name - The name of the procedure.
* @param {function} cb - The procedure's callback. The return value will be sent back to the caller. * @param {function} cb - The procedure's callback. The return value will be sent back to the caller.
*/ */
export function register(name, cb){ export function register(name: string, cb: ProcedureListener): void {
if(arguments.length !== 2) throw 'register expects 2 arguments: "name" and "cb"'; if(arguments.length !== 2) throw 'register expects 2 arguments: "name" and "cb"';
glob.__rpcListeners[name] = cb; glob.__rpcListeners[name] = cb;
} }
@ -152,7 +148,7 @@ export function register(name, cb){
* Unregister a procedure. * Unregister a procedure.
* @param {string} name - The name of the procedure. * @param {string} name - The name of the procedure.
*/ */
export function unregister(name){ export function unregister(name: string): void {
if(arguments.length !== 1) throw 'unregister expects 1 argument: "name"'; if(arguments.length !== 1) throw 'unregister expects 1 argument: "name"';
glob.__rpcListeners[name] = undefined; glob.__rpcListeners[name] = undefined;
} }
@ -162,16 +158,16 @@ export function unregister(name){
* *
* Can be called from any environment. * Can be called from any environment.
* *
* @param {string} name - The name of the locally registered procedure. * @param name - The name of the locally registered procedure.
* @param args - Any parameters for the procedure. * @param args - Any parameters for the procedure.
* @returns {Promise} - The result from the procedure. * @returns The result from the procedure.
*/ */
export function call(name, args){ export function call(name: string, args?: any): Promise<any> {
if(arguments.length !== 1 && arguments.length !== 2) return Promise.reject('call expects 1 or 2 arguments: "name" and optional "args"'); if(arguments.length !== 1 && arguments.length !== 2) return Promise.reject('call expects 1 or 2 arguments: "name" and optional "args"');
return callProcedure(name, args, { environment }); return callProcedure(name, args, { environment });
} }
function _callServer(name, args, extraData){ function _callServer(name: string, args?: any, extraData = {}): Promise<any> {
switch(environment){ switch(environment){
case "server": { case "server": {
return call(name, args); return call(name, args);
@ -183,14 +179,15 @@ function _callServer(name, args, extraData){
resolve, resolve,
reject reject
}; };
mp.events.callRemote(PROCESS_EVENT, util.stringifyData({ const event: Event = {
req: 1, req: 1,
id, id,
name, name,
env: environment, env: environment,
args, args,
...extraData ...extraData
})); };
mp.events.callRemote(PROCESS_EVENT, util.stringifyData(event));
}); });
} }
case "cef": { case "cef": {
@ -204,11 +201,11 @@ function _callServer(name, args, extraData){
* *
* Can be called from any environment. * Can be called from any environment.
* *
* @param {string} name - The name of the registered procedure. * @param name - The name of the registered procedure.
* @param args - Any parameters for the procedure. * @param args - Any parameters for the procedure.
* @returns {Promise} - The result from the procedure. * @returns The result from the procedure.
*/ */
export function callServer(name, args){ export function callServer(name: string, args?: any): Promise<any> {
if(arguments.length !== 1 && arguments.length !== 2) return Promise.reject('callServer expects 1 or 2 arguments: "name" and optional "args"'); if(arguments.length !== 1 && arguments.length !== 2) return Promise.reject('callServer expects 1 or 2 arguments: "name" and optional "args"');
return _callServer(name, args, {}); return _callServer(name, args, {});
} }
@ -219,16 +216,11 @@ export function callServer(name, args){
* Can be called from any environment. * Can be called from any environment.
* *
* @param player - The player to call the procedure on. * @param player - The player to call the procedure on.
* @param {string} name - The name of the registered procedure. * @param name - The name of the registered procedure.
* @param args - Any parameters for the procedure. * @param args - Any parameters for the procedure.
* @returns {Promise} - The result from the procedure. * @returns The result from the procedure.
*/ */
// serverside export function callClient(player: Player | string, name?: string | any, args?: any): Promise<any> {
// callClient(player, name, args)
//
// clientside or cef
// callClient(name, args)
export function callClient(player, name, args){
switch(environment){ switch(environment){
case "client": { case "client": {
args = name; args = name;
@ -245,13 +237,14 @@ export function callClient(player, name, args){
reject, reject,
player player
}; };
player.call(PROCESS_EVENT, [util.stringifyData({ const event: Event = {
req: 1, req: 1,
id, id,
name, name,
env: environment, env: environment,
args args
})]); };
player.call(PROCESS_EVENT, [util.stringifyData(event)]);
}); });
} }
case "cef": { case "cef": {
@ -264,19 +257,20 @@ export function callClient(player, name, args){
resolve, resolve,
reject reject
}; };
mp.trigger(PROCESS_EVENT, util.stringifyData({ const event: Event = {
req: 1, req: 1,
id, id,
name, name,
env: environment, env: environment,
args args
})); };
mp.trigger(PROCESS_EVENT, util.stringifyData(event));
}); });
} }
} }
} }
function _callBrowser(id, browser, name, args, extraData){ function _callBrowser(id: string, browser: Browser, name: string, args?: any, extraData = {}){
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
glob.__rpcPending[id] = { glob.__rpcPending[id] = {
resolve, resolve,
@ -292,18 +286,17 @@ function _callBrowser(id, browser, name, args, extraData){
}, false); }, false);
}); });
} }
async function _callBrowsers(player, name, args, extraData){
async function _callBrowsers(player: Player, name: string, args?: any, extraData = {}): Promise<any> {
switch(environment){ switch(environment){
case "client": { case "client": {
args = name;
name = player;
const id = util.uid(); const id = util.uid();
const numBrowsers = mp.browsers.length; const numBrowsers = mp.browsers.length;
let browser; let browser;
for(let i = 0; i < numBrowsers; i++){ for(let i = 0; i < numBrowsers; i++){
const b = mp.browsers.at(i); const b = mp.browsers.at(i);
await new Promise(resolve => { await new Promise(resolve => {
const existsHandler = str => { const existsHandler = (str: string) => {
const parts = str.split(':'); const parts = str.split(':');
if(parts[0] === id){ if(parts[0] === id){
if(+parts[1]){ if(+parts[1]){
@ -325,8 +318,6 @@ async function _callBrowsers(player, name, args, extraData){
return callClient(player, '__rpc:callBrowsers', [name, args]); return callClient(player, '__rpc:callBrowsers', [name, args]);
} }
case "cef": { case "cef": {
args = name;
name = player;
return callClient('__rpc:callBrowsers', [name, args]); return callClient('__rpc:callBrowsers', [name, args]);
} }
} }
@ -337,24 +328,21 @@ async function _callBrowsers(player, name, args, extraData){
* *
* Can be called from any environment. * Can be called from any environment.
* *
* @param {object} [player] - The player to call the procedure on. * @param player - The player to call the procedure on.
* @param {string} name - The name of the registered procedure. * @param name - The name of the registered procedure.
* @param args - Any parameters for the procedure. * @param args - Any parameters for the procedure.
* @returns {Promise} - The result from the procedure. * @returns The result from the procedure.
*/ */
export function callBrowsers(player, name, args){ export function callBrowsers(player: Player | string, name?: string | any, args?: any): Promise<any> {
switch(environment){ switch(environment){
case "client": case "client":
if(arguments.length !== 1 && arguments.length !== 2) return Promise.reject('callBrowsers from the client expects 1 or 2 arguments: "name" and optional "args"'); case "cef":
break; if(arguments.length !== 1 && arguments.length !== 2) return Promise.reject('callBrowsers from the client or browser expects 1 or 2 arguments: "name" and optional "args"');
return _callBrowsers(undefined, player as string, name, {});
case "server": case "server":
if(arguments.length !== 2 && arguments.length !== 3) return Promise.reject('callBrowsers from the server expects 2 or 3 arguments: "player", "name", and optional "args"'); if(arguments.length !== 2 && arguments.length !== 3) return Promise.reject('callBrowsers from the server expects 2 or 3 arguments: "player", "name", and optional "args"');
break; return _callBrowsers(player as Player, name, args, {});
case "cef":
if(arguments.length !== 1 && arguments.length !== 2) return Promise.reject('callBrowsers from the browser expects 1 or 2 arguments: "name" and optional "args"');
break;
} }
return _callBrowsers(player, name, args, {});
} }
/** /**
@ -362,13 +350,13 @@ export function callBrowsers(player, name, args){
* *
* Client-side environment only. * Client-side environment only.
* *
* @param {object} browser - The browser instance. * @param browser - The browser instance.
* @param {string} name - The name of the registered procedure. * @param name - The name of the registered procedure.
* @param args - Any parameters for the procedure. * @param args - Any parameters for the procedure.
* @returns {Promise} - The result from the procedure. * @returns The result from the procedure.
*/ */
export function callBrowser(browser, name, args){ export function callBrowser(browser: Browser, name: string, args?: any): Promise<any> {
if(environment !== "client") return Promise.reject('callBrowser can only be used in the client environment'); if(environment !== 'client') return Promise.reject('callBrowser can only be used in the client environment');
if(arguments.length !== 2 && arguments.length !== 3) return Promise.reject('callBrowser expects 2 or 3 arguments: "browser", "name", and optional "args"'); if(arguments.length !== 2 && arguments.length !== 3) return Promise.reject('callBrowser expects 2 or 3 arguments: "browser", "name", and optional "args"');
const id = util.uid(); const id = util.uid();
return _callBrowser(id, browser, name, args, {}); return _callBrowser(id, browser, name, args, {});

View File

@ -1,22 +0,0 @@
export function uid(){
let firstPart = (Math.random() * 46656) | 0;
let secondPart = (Math.random() * 46656) | 0;
firstPart = ('000' + firstPart.toString(36)).slice(-3);
secondPart = ('000' + secondPart.toString(36)).slice(-3);
return firstPart + secondPart;
}
export function getEnvironment(){
if(!mp) return undefined;
if(mp.joaat) return 'server';
else if(mp.game && mp.game.joaat) return 'client';
else if(mp.trigger) return 'cef';
}
export function stringifyData(data){
return JSON.stringify(data);
}
export function parseData(data){
return JSON.parse(data);
}

21
src/util.ts Normal file
View File

@ -0,0 +1,21 @@
export function uid(): string {
const first = (Math.random() * 46656) | 0;
const second = (Math.random() * 46656) | 0;
const firstPart = ('000' + first.toString(36)).slice(-3);
const secondPart = ('000' + second.toString(36)).slice(-3);
return firstPart + secondPart;
}
export function getEnvironment(): string {
if(mp.joaat) return 'server';
else if(mp.game && mp.game.joaat) return 'client';
else if(mp.trigger) return 'cef';
}
export function stringifyData(data: any): string {
return JSON.stringify(data);
}
export function parseData(data: string): any {
return JSON.parse(data);
}

14
tsconfig.json Normal file
View File

@ -0,0 +1,14 @@
{
"compilerOptions": {
"lib": ["es2015"],
"noEmit": true,
"isolatedModules": true,
"noImplicitAny": true
},
"include": [
"src"
],
"exclude": [
"node_modules"
]
}

View File

@ -1,8 +1,19 @@
const path = require('path'); const path = require('path');
module.exports = { module.exports = {
entry: './src/index.js', entry: './src/index.ts',
mode: 'production', mode: 'production',
module: {
rules: [
{
test: /\.ts$/,
loader: 'babel-loader'
}
]
},
resolve: {
extensions: ['.ts']
},
output: { output: {
path: path.resolve(__dirname, 'dist'), path: path.resolve(__dirname, 'dist'),
filename: 'rage-rpc.min.js', filename: 'rage-rpc.min.js',