Add event system

This commit is contained in:
micah 2019-06-02 14:44:53 -04:00
parent d0fbc6993d
commit d6cd63b201

View File

@ -9,12 +9,15 @@ const IDENTIFIER = '__rpc:id';
const PROCESS_EVENT = '__rpc:process'; const PROCESS_EVENT = '__rpc:process';
const BROWSER_REGISTER = '__rpc:browserRegister'; const BROWSER_REGISTER = '__rpc:browserRegister';
const BROWSER_UNREGISTER = '__rpc:browserUnregister'; const BROWSER_UNREGISTER = '__rpc:browserUnregister';
const TRIGGER_EVENT = '__rpc:triggerEvent';
const TRIGGER_EVENT_BROWSERS = '__rpc:triggerEventBrowsers';
const glob = environment === "cef" ? window : global; const glob = environment === 'cef' ? window : global;
if(!glob[PROCESS_EVENT]){ if(!glob[PROCESS_EVENT]){
glob.__rpcListeners = {}; glob.__rpcListeners = {};
glob.__rpcPending = {}; glob.__rpcPending = {};
glob.__rpcEvListeners = {};
glob[PROCESS_EVENT] = (player: Player | string, rawData?: string) => { glob[PROCESS_EVENT] = (player: Player | string, rawData?: string) => {
if(environment !== "server") rawData = player as string; if(environment !== "server") rawData = player as string;
@ -103,18 +106,26 @@ if(!glob[PROCESS_EVENT]){
const [browserId, name] = JSON.parse(data); const [browserId, name] = JSON.parse(data);
if(glob.__rpcBrowserProcedures[name] === browserId) delete glob.__rpcBrowserProcedures[name]; if(glob.__rpcBrowserProcedures[name] === browserId) delete glob.__rpcBrowserProcedures[name];
}); });
register(TRIGGER_EVENT_BROWSERS, ([name, args], info) => {
Object.values(glob.__rpcBrowsers).forEach(browser => {
_callBrowser(browser, TRIGGER_EVENT, [name, args], { fenv: info.environment, noRet: 1 });
});
});
} }
}else{ }else{
if(typeof glob[IDENTIFIER] === 'undefined'){ if(typeof glob[IDENTIFIER] === 'undefined'){
glob[IDENTIFIER] = new Promise(resolve => { glob[IDENTIFIER] = new Promise(resolve => {
if (window.name) { if (window.name) {
resolve(window.name); resolve(window.name);
} else { }else{
glob[IDENTIFIER+':resolve'] = resolve; glob[IDENTIFIER+':resolve'] = resolve;
} }
}); });
} }
} }
register(TRIGGER_EVENT, ([name, args], info) => callEvent(name, args, info));
} }
function passEventToBrowser(browser: Browser, data: Event, ignoreNotFound: boolean): void { function passEventToBrowser(browser: Browser, data: Event, ignoreNotFound: boolean): void {
@ -301,11 +312,12 @@ export function callClient(player: Player | string, name?: string | any, args?:
let extraData: any = {}; let extraData: any = {};
if(options.noRet) extraData.noRet = 1; if(options.noRet) extraData.noRet = 1;
return util.promiseTimeout(_callClient(typeof player === 'object' ? player : null, name, args, extraData), options.timeout); return util.promiseTimeout(_callClient(player as Player, name, args, extraData), options.timeout);
} }
function _callBrowser(id: string, browser: Browser, name: string, args?: any, extraData: any = {}): Promise<any> { function _callBrowser(browser: Browser, name: string, args?: any, extraData: any = {}): Promise<any> {
return new Promise(resolve => { return new Promise(resolve => {
const id = util.uid();
if(!extraData.noRet){ if(!extraData.noRet){
glob.__rpcPending[id] = { glob.__rpcPending[id] = {
resolve resolve
@ -325,12 +337,11 @@ function _callBrowser(id: string, browser: Browser, name: string, args?: any, ex
function _callBrowsers(player: Player, name: string, args?: any, extraData: any = {}): Promise<any> { function _callBrowsers(player: Player, name: string, args?: any, extraData: any = {}): Promise<any> {
switch(environment){ switch(environment){
case 'client': case 'client':
const id = util.uid();
const browserId = glob.__rpcBrowserProcedures[name]; const browserId = glob.__rpcBrowserProcedures[name];
if(!browserId) return util.promiseReject(ERR_NOT_FOUND); if(!browserId) return util.promiseReject(ERR_NOT_FOUND);
const browser = glob.__rpcBrowsers[browserId]; const browser = glob.__rpcBrowsers[browserId];
if(!browser || !util.isBrowserValid(browser)) return util.promiseReject(ERR_NOT_FOUND); if(!browser || !util.isBrowserValid(browser)) return util.promiseReject(ERR_NOT_FOUND);
return _callBrowser(id, browser, name, args, extraData); return _callBrowser(browser, name, args, extraData);
case 'server': case 'server':
return _callClient(player, '__rpc:callBrowsers', [name, args, +extraData.noRet], extraData); return _callClient(player, '__rpc:callBrowsers', [name, args, +extraData.noRet], extraData);
case 'cef': case 'cef':
@ -389,12 +400,148 @@ export function callBrowsers(player: Player | string, name?: string | any, args?
export function callBrowser(browser: Browser, name: string, args?: any, options: CallOptions = {}): Promise<any> { export function callBrowser(browser: Browser, name: string, args?: any, options: CallOptions = {}): Promise<any> {
if(environment !== 'client') return util.promiseReject('callBrowser can only be used in the client environment'); if(environment !== 'client') return util.promiseReject('callBrowser can only be used in the client environment');
if(arguments.length < 2 || arguments.length > 4) return util.promiseReject('callBrowser expects 2 to 4 arguments: "browser", "name", optional "args", and optional "options"'); if(arguments.length < 2 || arguments.length > 4) return util.promiseReject('callBrowser expects 2 to 4 arguments: "browser", "name", optional "args", and optional "options"');
const id = util.uid();
let extraData: any = {}; let extraData: any = {};
if(options.noRet) extraData.noRet = 1; if(options.noRet) extraData.noRet = 1;
return util.promiseTimeout(_callBrowser(id, browser, name, args, extraData), options.timeout); return util.promiseTimeout(_callBrowser(browser, name, args, extraData), options.timeout);
}
function callEvent(name: string, args: any, info: ProcedureListenerInfo){
const listeners = glob.__rpcEvListeners[name];
if(listeners){
listeners.forEach(listener => listener(args, info));
}
}
/**
* Register an event handler.
* @param {string} name - The name of the event.
* @param cb - The callback for the event.
*/
export function on(name: string, cb: ProcedureListener){
if(arguments.length !== 2) throw 'on expects 2 arguments: "name" and "cb"';
const listeners = glob.__rpcEvListeners[name] || new Set();
listeners.add(cb);
glob.__rpcEvListeners[name] = listeners;
}
/**
* Unregister an event handler.
* @param {string} name - The name of the event.
* @param cb - The callback for the event.
*/
export function off(name: string, cb: ProcedureListener){
if(arguments.length !== 2) throw 'off expects 2 arguments: "name" and "cb"';
const listeners = glob.__rpcEvListeners[name];
if(listeners){
listeners.delete(cb);
}
}
/**
* Triggers a local event. Only events registered in the same context will be triggered.
*
* Can be called from any environment.
*
* @param name - The name of the locally registered event.
* @param args - Any parameters for the event.
*/
export function trigger(name: string, args?: any){
if(arguments.length < 1 || arguments.length > 2) throw 'trigger expects 1 or 2 arguments: "name", and optional "args"';
callEvent(name, args, { environment });
}
/**
* Triggers an event registered on the client.
*
* Can be called from any environment.
*
* @param player - The player to call the procedure on.
* @param name - The name of the event.
* @param args - Any parameters for the event.
*/
export function triggerClient(player: Player | string, name?: string | any, args?: any){
switch(environment){
case 'client': {
args = name;
name = player;
player = null;
if((arguments.length < 1 || arguments.length > 2) || typeof name !== 'string') throw 'triggerClient from the client expects 1 or 2 arguments: "name", and optional "args"';
break;
}
case 'server': {
if((arguments.length < 2 || arguments.length > 3) || typeof player !== 'object') throw 'triggerClient from the server expects 2 or 3 arguments: "player", "name", and optional "args"';
break;
}
case 'cef': {
args = name;
name = player;
player = null;
if((arguments.length < 1 || arguments.length > 2) || typeof name !== 'string') throw 'triggerClient from the browser expects 1 or 2 arguments: "name", and optional "args"';
break;
}
}
_callClient(player as Player, TRIGGER_EVENT, [name, args], { noRet: 1 });
}
/**
* Triggers an event registered on the server.
*
* Can be called from any environment.
*
* @param name - The name of the event.
* @param args - Any parameters for the event.
*/
export function triggerServer(name: string, args?: any){
if(arguments.length < 1 || arguments.length > 2) throw 'triggerServer expects 1 or 2 arguments: "name", and optional "args"';
_callServer(TRIGGER_EVENT, [name, args], { noRet: 1 });
}
/**
* Triggers an event registered in any browser context.
*
* Can be called from any environment.
*
* @param player - The player to call the procedure on.
* @param name - The name of the event.
* @param args - Any parameters for the event.
*/
export function triggerBrowsers(player: Player | string, name?: string | any, args?: any){
switch(environment){
case 'client':
case 'cef':
args = name;
name = player;
player = null;
if(arguments.length < 1 || arguments.length > 2) throw 'triggerBrowsers from the client or browser expects 1 or 2 arguments: "name", and optional "args"';
break;
case 'server':
if(arguments.length < 2 || arguments.length > 3) throw 'triggerBrowsers from the server expects 2 or 3 arguments: "player", "name", and optional "args"';
break;
}
_callClient(player as Player, TRIGGER_EVENT_BROWSERS, [name, args], { noRet: 1 });
}
/**
* Triggers an event registered in a specific browser instance.
*
* Client-side environment only.
*
* @param browser - The browser instance.
* @param name - The name of the event.
* @param args - Any parameters for the event.
*/
export function triggerBrowser(browser: Browser, name: string, args?: any){
if(environment !== 'client') throw 'callBrowser can only be used in the client environment';
if(arguments.length < 2 || arguments.length > 4) throw 'callBrowser expects 2 or 3 arguments: "browser", "name", and optional "args"';
_callBrowser(browser, TRIGGER_EVENT, [name, args], { noRet: 1});
} }
export default { export default {
@ -404,5 +551,12 @@ export default {
callServer, callServer,
callClient, callClient,
callBrowsers, callBrowsers,
callBrowser callBrowser,
on,
off,
trigger,
triggerServer,
triggerClient,
triggerBrowsers,
triggerBrowser
}; };