Add initial client<-->server RPC

This commit is contained in:
Micah Allen 2018-11-01 15:05:48 -04:00
parent 1edbf7ffa4
commit dc0d914b27
3 changed files with 161 additions and 46 deletions

View File

@ -1,20 +1,20 @@
{ {
"name": "rage-eventbus", "name": "rage-rpc",
"version": "0.0.1", "version": "0.0.1",
"description": "An asynchronous event bus for RAGE Multiplayer", "description": "An asynchronous RPC implementation for RAGE Multiplayer",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/micaww/rage-eventbus.git" "url": "git+https://github.com/micaww/rage-rpc.git"
}, },
"author": "micaww", "author": "micaww",
"license": "ISC", "license": "ISC",
"bugs": { "bugs": {
"url": "https://github.com/micaww/rage-eventbus/issues" "url": "https://github.com/micaww/rage-rpc/issues"
}, },
"homepage": "https://github.com/micaww/rage-eventbus#readme", "homepage": "https://github.com/micaww/rage-rpc#readme",
"dependencies": {} "dependencies": {}
} }

View File

@ -1,63 +1,163 @@
const util = require('./util.js'); const util = require('./util.js');
//const isClient = !!mp.game.joaat; const environment = util.getEnvironment();
//const isCEF = !!mp.trigger; if(!environment) throw 'Unknown RAGE environment';
const PROCESS_EVENT = '__rpc:process';
const rpc = {};
const listeners = {}; const listeners = {};
const pending = {};
/*mp.events.add('rbus:process', (data) => { async function callProcedure(name, args, info){
if(!listeners[name]) throw 'PROCEDURE_NOT_FOUND';
return listeners[name](args, info);
}
});*/ const processEvent = (...args) => {
let data = args[0];
if(environment === "server") data = args[1];
data = util.parseData(data);
const rbus = {}; if(data.req){ // someone is trying to remotely call a procedure
const info = {
id: data.id,
environment: data.env
};
if(environment === "server") info.player = args[0];
const promise = callProcedure(data.name, data.args, info);
switch(environment){
case "server": {
promise.then(res => {
info.player.call(PROCESS_EVENT, [util.stringifyData({
ret: 1,
id: data.id,
res
})]);
}).catch(err => {
info.player.call(PROCESS_EVENT, [util.stringifyData({
ret: 1,
id: data.id,
err
})]);
});
break;
}
case "client": {
promise.then(res => {
mp.events.callRemote(PROCESS_EVENT, util.stringifyData({
ret: 1,
id: data.id,
res
}));
}).catch(err => {
mp.events.callRemote(PROCESS_EVENT, util.stringifyData({
ret: 1,
id: data.id,
err
}));
});
break;
}
}
}else if(data.ret){ // a previously called remote procedure has returned
const info = pending[data.id];
if(info){
if(data.err) info.reject(data.err);
else info.resolve(data.res);
pending[data.id] = undefined;
}
}
};
mp.events.add(PROCESS_EVENT, processEvent);
/** /**
* Register an event listener. * Register a procedure.
* @param {string} eventName - The name of the event. * @param {string} name - The name of the procedure.
* @param {function} cb - The event'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.
*/ */
rbus.on = (eventName, cb) => { rpc.register = (name, cb) => {
if(!listeners[eventName]) listeners[eventName] = []; listeners[name] = cb;
listeners[eventName].push(cb);
}; };
/** /**
* Unregister an event listener. * Unregister a procedure.
* @param {string} eventName - The name of the event. * @param {string} name - The name of the procedure.
* @param {function} cb - The callback that was registered with `on`.
*/ */
rbus.off = (eventName, cb) => { rpc.unregister = (name) => {
if(!listeners[eventName]) return; listeners[name] = undefined;
listeners[eventName] = listeners[eventName].filter(listener => listener !== cb);
}; };
/** /**
* Calls a local event listener. * Calls a local procedure.
* @param {string} eventName - The name of the event. * @param {string} name - The name of the locally registered procedure.
* @returns {Promise} - The result from the local event listener. * @param args - Any parameters for the procedure.
* @returns {Promise} - The result from the procedure.
*/ */
rbus.send = (eventName) => { rpc.call = (name, args) => callProcedure(name, args, { environment });
if(!listeners[eventName] || !listeners[eventName].length) return Promise.reject('NO_LISTENERS');
return Promise.resolve(listeners[eventName][0]()); /**
* Calls a remote procedure registered on the server.
* @param {string} name - The name of the registered procedure.
* @param args - Any parameters for the procedure.
* @returns {Promise} - The result from the procedure.
*/
rpc.callServer = (name, args) => {
switch(environment){
case "server": {
return rpc.call(name, args);
}
case "client": {
const id = util.uid();
return new Promise((resolve, reject) => {
pending[id] = {
resolve,
reject
};
mp.events.callRemote(PROCESS_EVENT, util.stringifyData({
req: 1,
id,
name,
env: environment,
args
}));
});
}
}
}; };
/** /**
* Calls a remote event listener residing on the server. * Calls a remote procedure registered on the client.
* @param {string} eventName - The name of the event. * @param player - The player to call the procedure on.
* @returns {Promise} - The result from the remote event listener. * @param {string} name - The name of the registered procedure.
* @param args - Any parameters for the procedure.
* @returns {Promise} - The result from the procedure.
*/ */
rbus.sendServer = (eventName) => { rpc.callClient = (player, name, args) => {
switch(environment){
case "client": {
if(player === mp.players.local) return rpc.call(name, args);
else return Promise.reject('Only the server can RPC to other clients.');
}
case "server": {
const id = util.uid();
return new Promise((resolve, reject) => {
pending[id] = {
resolve,
reject
};
player.call(PROCESS_EVENT, [util.stringifyData({
req: 1,
id,
name,
env: environment,
args
})]);
});
}
}
}; };
/** module.exports = rpc;
* Calls a remote event listener residing on the client.
* @param player - The player to send to
* @param {string} eventName - The name of the event
* @returns {Promise} - The result from the remote event listener
*/
rbus.sendClient = (player, eventName) => {
};
module.exports = rbus;

View File

@ -3,9 +3,24 @@ const util = {};
util.uid = () => { util.uid = () => {
let firstPart = (Math.random() * 46656) | 0; let firstPart = (Math.random() * 46656) | 0;
let secondPart = (Math.random() * 46656) | 0; let secondPart = (Math.random() * 46656) | 0;
firstPart = ("000" + firstPart.toString(36)).slice(-3); firstPart = ('000' + firstPart.toString(36)).slice(-3);
secondPart = ("000" + secondPart.toString(36)).slice(-3); secondPart = ('000' + secondPart.toString(36)).slice(-3);
return firstPart + secondPart; return firstPart + secondPart;
}; };
util.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';
};
util.stringifyData = (data) => {
return JSON.stringify(data);
};
util.parseData = (data) => {
return JSON.parse(data);
};
module.exports = util; module.exports = util;