2018-11-02 05:10:24 +00:00
import * as util from './util.js' ;
2018-11-01 13:31:53 +00:00
2018-11-01 19:05:48 +00:00
const environment = util . getEnvironment ( ) ;
if ( ! environment ) throw 'Unknown RAGE environment' ;
2018-11-02 04:36:03 +00:00
const ERR _NOT _FOUND = 'PROCEDURE_NOT_FOUND' ;
2018-11-01 19:05:48 +00:00
const PROCESS _EVENT = '__rpc:process' ;
2018-11-01 22:47:57 +00:00
const PROCEDURE _EXISTS = '__rpc:exists' ;
2018-11-01 19:05:48 +00:00
2018-11-01 13:31:53 +00:00
const listeners = { } ;
2018-11-01 19:05:48 +00:00
const pending = { } ;
2018-11-01 13:31:53 +00:00
2018-11-01 22:47:57 +00:00
let passEventToBrowser , passEventToBrowsers ;
2018-11-01 20:54:32 +00:00
if ( environment === "client" ) {
2018-11-02 04:36:03 +00:00
passEventToBrowser = ( browser , data ) => {
const raw = util . stringifyData ( data ) ;
browser . execute ( ` var process = window[" ${ PROCESS _EVENT } "]; if(process){ process(' ${ raw } '); }else{ mp.trigger(" ${ PROCESS _EVENT } ", '{"ret":1,"id":" ${ data . id } ","err":" ${ ERR _NOT _FOUND } "}'); } ` ) ;
2018-11-01 22:47:57 +00:00
} ;
2018-11-02 04:36:03 +00:00
passEventToBrowsers = ( data ) => {
mp . browsers . forEach ( browser => passEventToBrowser ( browser , data ) ) ;
2018-11-01 20:54:32 +00:00
} ;
}
2018-11-01 22:47:57 +00:00
async function callProcedure ( name , args , info ) {
2018-11-02 04:36:03 +00:00
if ( ! listeners [ name ] ) throw ERR _NOT _FOUND ;
2018-11-01 22:47:57 +00:00
return listeners [ name ] ( args , info ) ;
}
2018-11-01 19:05:48 +00:00
const processEvent = ( ... args ) => {
2018-11-01 20:40:00 +00:00
let rawData = args [ 0 ] ;
if ( environment === "server" ) rawData = args [ 1 ] ;
const data = util . parseData ( rawData ) ;
2018-11-01 19:05:48 +00:00
if ( data . req ) { // someone is trying to remotely call a procedure
const info = {
id : data . id ,
2018-11-02 00:48:56 +00:00
environment : data . fenv || data . env
2018-11-01 19:05:48 +00:00
} ;
if ( environment === "server" ) info . player = args [ 0 ] ;
const promise = callProcedure ( data . name , data . args , info ) ;
2018-11-01 22:47:57 +00:00
const part = {
ret : 1 ,
id : data . id
} ;
2018-11-01 19:05:48 +00:00
switch ( environment ) {
case "server" : {
promise . then ( res => {
info . player . call ( PROCESS _EVENT , [ util . stringifyData ( {
2018-11-01 20:40:00 +00:00
... part ,
2018-11-01 19:05:48 +00:00
res
} ) ] ) ;
} ) . catch ( err => {
info . player . call ( PROCESS _EVENT , [ util . stringifyData ( {
2018-11-01 20:40:00 +00:00
... part ,
2018-11-01 19:05:48 +00:00
err
} ) ] ) ;
} ) ;
break ;
}
case "client" : {
2018-11-01 20:54:32 +00:00
if ( data . env === "server" ) {
promise . then ( res => {
mp . events . callRemote ( PROCESS _EVENT , util . stringifyData ( {
... part ,
res
} ) ) ;
} ) . catch ( err => {
mp . events . callRemote ( PROCESS _EVENT , util . stringifyData ( {
... part ,
err
} ) ) ;
} ) ;
} else if ( data . env === "cef" ) {
promise . then ( res => {
2018-11-02 04:36:03 +00:00
passEventToBrowsers ( {
2018-11-01 20:54:32 +00:00
... part ,
res
2018-11-02 04:36:03 +00:00
} ) ;
2018-11-01 20:54:32 +00:00
} ) . catch ( err => {
2018-11-02 04:36:03 +00:00
passEventToBrowsers ( {
2018-11-01 20:54:32 +00:00
... part ,
err
2018-11-02 04:36:03 +00:00
} ) ;
2018-11-01 20:54:32 +00:00
} ) ;
}
2018-11-01 19:05:48 +00:00
break ;
}
2018-11-01 22:47:57 +00:00
case "cef" : {
promise . then ( res => {
mp . trigger ( PROCESS _EVENT , util . stringifyData ( {
... part ,
res
} ) ) ;
} ) . catch ( err => {
mp . trigger ( PROCESS _EVENT , util . stringifyData ( {
... part ,
err
} ) ) ;
} ) ;
}
2018-11-01 19:05:48 +00:00
}
} 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 ;
}
}
} ;
2018-11-01 13:31:53 +00:00
2018-11-01 20:40:00 +00:00
if ( environment === "cef" ) {
window [ PROCESS _EVENT ] = processEvent ;
2018-11-01 22:47:57 +00:00
window [ PROCEDURE _EXISTS ] = name => ! ! listeners [ name ] ;
2018-11-01 20:40:00 +00:00
} else {
mp . events . add ( PROCESS _EVENT , processEvent ) ;
}
2018-11-01 13:31:53 +00:00
/ * *
2018-11-01 19:05:48 +00:00
* Register a 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 .
2018-11-01 13:31:53 +00:00
* /
2018-11-02 05:10:24 +00:00
export function register ( name , cb ) {
2018-11-02 04:36:03 +00:00
if ( arguments . length !== 2 ) throw 'register expects 2 arguments: "name" and "cb"' ;
2018-11-01 19:05:48 +00:00
listeners [ name ] = cb ;
2018-11-02 05:10:24 +00:00
}
2018-11-01 13:31:53 +00:00
/ * *
2018-11-01 19:05:48 +00:00
* Unregister a procedure .
* @ param { string } name - The name of the procedure .
2018-11-01 13:31:53 +00:00
* /
2018-11-02 05:10:24 +00:00
export function unregister ( name ) {
2018-11-02 04:36:03 +00:00
if ( arguments . length !== 1 ) throw 'unregister expects 1 argument: "name"' ;
2018-11-01 19:05:48 +00:00
listeners [ name ] = undefined ;
2018-11-02 05:10:24 +00:00
}
2018-11-01 13:31:53 +00:00
/ * *
2018-11-02 04:36:03 +00:00
* Calls a local procedure . Only procedures registered in the same context will be resolved .
*
* Can be called from any environment .
*
2018-11-01 19:05:48 +00:00
* @ param { string } name - The name of the locally registered procedure .
* @ param args - Any parameters for the procedure .
* @ returns { Promise } - The result from the procedure .
2018-11-01 13:31:53 +00:00
* /
2018-11-02 05:10:24 +00:00
export function call ( name , args ) {
2018-11-02 04:36:03 +00:00
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 } ) ;
2018-11-02 05:10:24 +00:00
}
2018-11-01 13:31:53 +00:00
2018-11-02 05:10:24 +00:00
function _callServer ( name , args , extraData ) {
2018-11-01 19:05:48 +00:00
switch ( environment ) {
case "server" : {
2018-11-02 05:10:24 +00:00
return call ( name , args ) ;
2018-11-01 19:05:48 +00:00
}
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 ,
2018-11-02 00:48:56 +00:00
args ,
... extraData
2018-11-01 19:05:48 +00:00
} ) ) ;
} ) ;
}
2018-11-01 20:40:00 +00:00
case "cef" : {
2018-11-02 05:10:24 +00:00
return callClient ( '__rpc:callServer' , [ name , args ] ) ;
2018-11-01 20:40:00 +00:00
}
2018-11-01 19:05:48 +00:00
}
2018-11-02 00:48:56 +00:00
}
/ * *
* Calls a remote procedure registered on the server .
2018-11-02 04:36:03 +00:00
*
* Can be called from any environment .
*
2018-11-02 00:48:56 +00:00
* @ param { string } name - The name of the registered procedure .
* @ param args - Any parameters for the procedure .
* @ returns { Promise } - The result from the procedure .
* /
2018-11-02 05:10:24 +00:00
export function callServer ( name , args ) {
2018-11-02 04:36:03 +00:00
if ( arguments . length !== 1 && arguments . length !== 2 ) return Promise . reject ( 'callServer expects 1 or 2 arguments: "name" and optional "args"' ) ;
2018-11-02 05:10:24 +00:00
return _callServer ( name , args , { } ) ;
}
2018-11-01 13:31:53 +00:00
/ * *
2018-11-01 19:05:48 +00:00
* Calls a remote procedure registered on the client .
2018-11-02 04:36:03 +00:00
*
* Can be called from any environment .
*
* @ param player - The player to call the procedure on .
2018-11-01 19:05:48 +00:00
* @ param { string } name - The name of the registered procedure .
* @ param args - Any parameters for the procedure .
* @ returns { Promise } - The result from the procedure .
2018-11-01 13:31:53 +00:00
* /
2018-11-01 22:59:45 +00:00
// serverside
// callClient(player, name, args)
//
// clientside or cef
// callClient(name, args)
2018-11-02 05:10:24 +00:00
export function callClient ( player , name , args ) {
2018-11-01 19:05:48 +00:00
switch ( environment ) {
case "client" : {
2018-11-02 04:36:03 +00:00
args = name ;
name = player ;
if ( ( arguments . length !== 1 && arguments . length !== 2 ) || typeof name !== "string" ) return Promise . reject ( 'callClient from the client expects 1 or 2 arguments: "name" and optional "args"' ) ;
2018-11-02 05:10:24 +00:00
return call ( name , args ) ;
2018-11-01 19:05:48 +00:00
}
case "server" : {
2018-11-02 04:36:03 +00:00
if ( ( arguments . length !== 2 && arguments . length !== 3 ) || typeof player !== "object" ) return Promise . reject ( 'callClient from the server expects 2 or 3 arguments: "player", "name", and optional "args"' ) ;
2018-11-01 19:05:48 +00:00
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
} ) ] ) ;
} ) ;
}
2018-11-01 20:54:32 +00:00
case "cef" : {
2018-11-02 04:36:03 +00:00
args = name ;
name = player ;
if ( ( arguments . length !== 1 && arguments . length !== 2 ) || typeof name !== "string" ) return Promise . reject ( 'callClient from the browser expects 1 or 2 arguments: "name" and optional "args"' ) ;
2018-11-01 20:54:32 +00:00
const id = util . uid ( ) ;
return new Promise ( ( resolve , reject ) => {
pending [ id ] = {
resolve ,
reject
} ;
mp . trigger ( PROCESS _EVENT , util . stringifyData ( {
req : 1 ,
id ,
name ,
env : environment ,
args
} ) ) ;
} ) ;
}
2018-11-01 19:05:48 +00:00
}
2018-11-02 05:10:24 +00:00
}
2018-11-01 13:31:53 +00:00
2018-11-02 05:10:24 +00:00
function _callBrowser ( id , browser , name , args , extraData ) {
2018-11-02 02:01:11 +00:00
return new Promise ( ( resolve , reject ) => {
pending [ id ] = {
resolve ,
reject
} ;
2018-11-02 04:36:03 +00:00
passEventToBrowser ( browser , {
2018-11-02 02:01:11 +00:00
req : 1 ,
id ,
name ,
env : environment ,
args ,
... extraData
2018-11-02 04:36:03 +00:00
} ) ;
2018-11-02 02:01:11 +00:00
} ) ;
}
2018-11-02 05:10:24 +00:00
async function _callBrowsers ( player , name , args , extraData ) {
2018-11-02 02:01:11 +00:00
switch ( environment ) {
case "client" : {
args = name ;
name = player ;
const id = util . uid ( ) ;
const numBrowsers = mp . browsers . length ;
let browser ;
for ( let i = 0 ; i < numBrowsers ; i ++ ) {
const b = mp . browsers . at ( i ) ;
await new Promise ( resolve => {
const existsHandler = str => {
const parts = str . split ( ':' ) ;
if ( parts [ 0 ] === id ) {
if ( + parts [ 1 ] ) {
browser = b ;
}
}
mp . events . remove ( PROCEDURE _EXISTS , existsHandler ) ;
resolve ( ) ;
} ;
mp . events . add ( PROCEDURE _EXISTS , existsHandler ) ;
b . execute ( ` var f = window[" ${ PROCEDURE _EXISTS } "]; mp.trigger(" ${ PROCEDURE _EXISTS } ", " ${ id } :"+((f && f(" ${ name } ")) ? 1 : 0)); ` ) ;
} ) ;
if ( browser ) break ;
}
2018-11-02 05:10:24 +00:00
if ( browser ) return _callBrowser ( id , browser , name , args , extraData ) ;
2018-11-02 04:36:03 +00:00
throw ERR _NOT _FOUND ;
2018-11-02 02:01:11 +00:00
}
case "server" : {
2018-11-02 05:10:24 +00:00
return callClient ( player , '__rpc:callBrowsers' , [ name , args ] ) ;
2018-11-02 02:01:11 +00:00
}
case "cef" : {
args = name ;
name = player ;
2018-11-02 05:10:24 +00:00
return callClient ( '__rpc:callBrowsers' , [ name , args ] ) ;
2018-11-02 02:01:11 +00:00
}
}
}
2018-11-01 22:59:45 +00:00
/ * *
* Calls a remote procedure registered in any browser context .
2018-11-02 02:01:11 +00:00
*
* Can be called from any environment .
*
* @ param { object } [ player ] - The player to call the procedure on .
2018-11-01 22:59:45 +00:00
* @ param { string } name - The name of the registered procedure .
* @ param args - Any parameters for the procedure .
* @ returns { Promise } - The result from the procedure .
* /
2018-11-02 05:10:24 +00:00
export function callBrowsers ( player , name , args ) {
2018-11-02 02:01:11 +00:00
switch ( environment ) {
case "client" :
2018-11-02 04:36:03 +00:00
if ( arguments . length !== 1 && arguments . length !== 2 ) return Promise . reject ( 'callBrowsers from the client expects 1 or 2 arguments: "name" and optional "args"' ) ;
2018-11-02 02:01:11 +00:00
break ;
case "server" :
2018-11-02 04:36:03 +00:00
if ( arguments . length !== 2 && arguments . length !== 3 ) return Promise . reject ( 'callBrowsers from the server expects 2 or 3 arguments: "player", "name", and optional "args"' ) ;
2018-11-02 02:01:11 +00:00
break ;
case "cef" :
2018-11-02 04:36:03 +00:00
if ( arguments . length !== 1 && arguments . length !== 2 ) return Promise . reject ( 'callBrowsers from the browser expects 1 or 2 arguments: "name" and optional "args"' ) ;
2018-11-02 02:01:11 +00:00
break ;
2018-11-01 22:47:57 +00:00
}
2018-11-02 05:10:24 +00:00
return _callBrowsers ( player , name , args , { } ) ;
}
2018-11-02 02:01:11 +00:00
/ * *
* Calls a remote procedure registered in a specific browser instance .
*
* Client - side environment only .
*
* @ param { object } browser - The browser instance .
* @ param { string } name - The name of the registered procedure .
* @ param args - Any parameters for the procedure .
* @ returns { Promise } - The result from the procedure .
* /
2018-11-02 05:10:24 +00:00
export function callBrowser ( browser , name , args ) {
2018-11-02 04:36:03 +00:00
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"' ) ;
2018-11-02 02:01:11 +00:00
const id = util . uid ( ) ;
2018-11-02 05:10:24 +00:00
return _callBrowser ( id , browser , name , args , { } ) ;
}
2018-11-01 22:47:57 +00:00
2018-11-02 00:48:56 +00:00
// set up internal pass-through events
if ( environment === "client" ) {
2018-11-02 05:10:24 +00:00
register ( '__rpc:callServer' , ( [ name , args ] , info ) => {
return _callServer ( name , args , {
2018-11-02 00:48:56 +00:00
fenv : info . environment
} ) ;
} ) ;
2018-11-02 05:10:24 +00:00
register ( '__rpc:callBrowsers' , ( [ name , args ] , info ) => {
return _callBrowsers ( name , args , null , {
2018-11-02 02:01:11 +00:00
fenv : info . environment
} ) ;
} ) ;
2018-11-02 05:10:24 +00:00
}