diff --git a/dist/rage-rpc.min.js b/dist/rage-rpc.min.js index b2dd513..9f1606e 100644 --- a/dist/rage-rpc.min.js +++ b/dist/rage-rpc.min.js @@ -1 +1 @@ -!function(e,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define([],r):"object"==typeof exports?exports.rpc=r():e.rpc=r()}("undefined"!=typeof self?self:this,function(){return function(e){var r={};function t(n){if(r[n])return r[n].exports;var o=r[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,t),o.l=!0,o.exports}return t.m=e,t.c=r,t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:n})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,r){if(1&r&&(e=t(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(t.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var o in e)t.d(n,o,function(r){return e[r]}.bind(null,o));return n},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},t.p="",t(t.s=0)}([function(e,r,t){"use strict";function n(){let e=46656*Math.random()|0,r=46656*Math.random()|0;return(e=("000"+e.toString(36)).slice(-3))+(r=("000"+r.toString(36)).slice(-3))}function o(e){return JSON.stringify(e)}t.r(r),t.d(r,"register",function(){return d}),t.d(r,"unregister",function(){return v}),t.d(r,"call",function(){return h}),t.d(r,"callServer",function(){return b}),t.d(r,"callClient",function(){return y}),t.d(r,"callBrowsers",function(){return _}),t.d(r,"callBrowser",function(){return P});const c=function(){if(mp)return mp.joaat?"server":mp.game&&mp.game.joaat?"client":mp.trigger?"cef":void 0}();if(!c)throw"Unknown RAGE environment";const s="PROCEDURE_NOT_FOUND",i="__rpc:process",a="__rpc:exists",l={},u={};let f,m;async function p(e,r,t){if(!l[e])throw s;return l[e](r,t)}"client"===c&&(f=((e,r)=>{const t=o(r);e.execute(`var process = window["${i}"]; if(process){ process('${t}'); }else{ mp.trigger("${i}", '{"ret":1,"id":"${r.id}","err":"${s}"}'); }`)}),m=(e=>{mp.browsers.forEach(r=>f(r,e))}));const g=(...e)=>{let r=e[0];"server"===c&&(r=e[1]);const t=function(e){return JSON.parse(e)}(r);if(t.req){const r={id:t.id,environment:t.fenv||t.env};"server"===c&&(r.player=e[0]);const n=p(t.name,t.args,r),s={ret:1,id:t.id};switch(c){case"server":n.then(e=>{r.player.call(i,[o({...s,res:e})])}).catch(e=>{r.player.call(i,[o({...s,err:e})])});break;case"client":"server"===t.env?n.then(e=>{mp.events.callRemote(i,o({...s,res:e}))}).catch(e=>{mp.events.callRemote(i,o({...s,err:e}))}):"cef"===t.env&&n.then(e=>{m({...s,res:e})}).catch(e=>{m({...s,err:e})});break;case"cef":n.then(e=>{mp.trigger(i,o({...s,res:e}))}).catch(e=>{mp.trigger(i,o({...s,err:e}))})}}else if(t.ret){const e=u[t.id];e&&(t.err?e.reject(t.err):e.resolve(t.res),u[t.id]=void 0)}};function d(e,r){if(2!==arguments.length)throw'register expects 2 arguments: "name" and "cb"';l[e]=r}function v(e){if(1!==arguments.length)throw'unregister expects 1 argument: "name"';l[e]=void 0}function h(e,r){return 1!==arguments.length&&2!==arguments.length?Promise.reject('call expects 1 or 2 arguments: "name" and optional "args"'):p(e,r,{environment:c})}function w(e,r,t){switch(c){case"server":return h(e,r);case"client":{const s=n();return new Promise((n,a)=>{u[s]={resolve:n,reject:a},mp.events.callRemote(i,o({req:1,id:s,name:e,env:c,args:r,...t}))})}case"cef":return y("__rpc:callServer",[e,r])}}function b(e,r){return 1!==arguments.length&&2!==arguments.length?Promise.reject('callServer expects 1 or 2 arguments: "name" and optional "args"'):w(e,r,{})}function y(e,r,t){switch(c){case"client":return t=r,r=e,1!==arguments.length&&2!==arguments.length||"string"!=typeof r?Promise.reject('callClient from the client expects 1 or 2 arguments: "name" and optional "args"'):h(r,t);case"server":{if(2!==arguments.length&&3!==arguments.length||"object"!=typeof e)return Promise.reject('callClient from the server expects 2 or 3 arguments: "player", "name", and optional "args"');const s=n();return new Promise((n,a)=>{u[s]={resolve:n,reject:a},e.call(i,[o({req:1,id:s,name:r,env:c,args:t})])})}case"cef":{if(t=r,r=e,1!==arguments.length&&2!==arguments.length||"string"!=typeof r)return Promise.reject('callClient from the browser expects 1 or 2 arguments: "name" and optional "args"');const s=n();return new Promise((e,n)=>{u[s]={resolve:e,reject:n},mp.trigger(i,o({req:1,id:s,name:r,env:c,args:t}))})}}}function j(e,r,t,n,o){return new Promise((s,i)=>{u[e]={resolve:s,reject:i},f(r,{req:1,id:e,name:t,env:c,args:n,...o})})}async function x(e,r,t,o){switch(c){case"client":{t=r,r=e;const c=n(),i=mp.browsers.length;let l;for(let e=0;e{const n=r=>{const o=r.split(":");o[0]===c&&+o[1]&&(l=t),mp.events.remove(a,n),e()};mp.events.add(a,n),t.execute(`var f = window["${a}"]; mp.trigger("${a}", "${c}:"+((f && f("${r}")) ? 1 : 0));`)}),l)break}if(l)return j(c,l,r,t,o);throw s}case"server":return y(e,"__rpc:callBrowsers",[r,t]);case"cef":return t=r,y("__rpc:callBrowsers",[r=e,t])}}function _(e,r,t){switch(c){case"client":if(1!==arguments.length&&2!==arguments.length)return Promise.reject('callBrowsers from the client expects 1 or 2 arguments: "name" and optional "args"');break;case"server":if(2!==arguments.length&&3!==arguments.length)return Promise.reject('callBrowsers from the server expects 2 or 3 arguments: "player", "name", and optional "args"');break;case"cef":if(1!==arguments.length&&2!==arguments.length)return Promise.reject('callBrowsers from the browser expects 1 or 2 arguments: "name" and optional "args"')}return x(e,r,t,{})}function P(e,r,t){if("client"!==c)return Promise.reject("callBrowser can only be used in the client environment");if(2!==arguments.length&&3!==arguments.length)return Promise.reject('callBrowser expects 2 or 3 arguments: "browser", "name", and optional "args"');return j(n(),e,r,t,{})}"cef"===c?(window[i]=g,window[a]=(e=>!!l[e])):mp.events.add(i,g),"client"===c&&(d("__rpc:callServer",([e,r],t)=>w(e,r,{fenv:t.environment})),d("__rpc:callBrowsers",([e,r],t)=>x(e,r,null,{fenv:t.environment})))}])}); \ No newline at end of file +!function(e,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define([],r):"object"==typeof exports?exports.rpc=r():e.rpc=r()}("undefined"!=typeof self?self:this,function(){return function(e){var r={};function n(t){if(r[t])return r[t].exports;var c=r[t]={i:t,l:!1,exports:{}};return e[t].call(c.exports,c,c.exports,n),c.l=!0,c.exports}return n.m=e,n.c=r,n.d=function(e,r,t){n.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,r){if(1&r&&(e=n(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(n.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var c in e)n.d(t,c,function(r){return e[r]}.bind(null,c));return t},n.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(r,"a",r),r},n.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},n.p="",n(n.s=1)}([function(e,r,n){"use strict";function t(){let e=46656*Math.random()|0,r=46656*Math.random()|0;return(e=("000"+e.toString(36)).slice(-3))+(r=("000"+r.toString(36)).slice(-3))}function c(){if(mp)return mp.joaat?"server":mp.game&&mp.game.joaat?"client":mp.trigger?"cef":void 0}function o(e){return JSON.stringify(e)}function s(e){return JSON.parse(e)}n.d(r,"d",function(){return t}),n.d(r,"a",function(){return c}),n.d(r,"c",function(){return o}),n.d(r,"b",function(){return s})},function(e,r,n){"use strict";n.r(r),function(e){n.d(r,"register",function(){return p}),n.d(r,"unregister",function(){return m}),n.d(r,"call",function(){return d}),n.d(r,"callServer",function(){return v}),n.d(r,"callClient",function(){return h}),n.d(r,"callBrowsers",function(){return b}),n.d(r,"callBrowser",function(){return y});var t=n(0);const c=t.a();if(!c)throw"Unknown RAGE environment";const o="PROCEDURE_NOT_FOUND",s="__rpc:process",i="__rpc:exists",a="cef"===c?window:e;let l,u;async function f(e,r,n){const t=a.__rpcListeners[e];if(!t)throw o;return t(r,n)}function p(e,r){if(2!==arguments.length)throw'register expects 2 arguments: "name" and "cb"';a.__rpcListeners[e]=r}function m(e){if(1!==arguments.length)throw'unregister expects 1 argument: "name"';a.__rpcListeners[e]=void 0}function d(e,r){return 1!==arguments.length&&2!==arguments.length?Promise.reject('call expects 1 or 2 arguments: "name" and optional "args"'):f(e,r,{environment:c})}function g(e,r,n){switch(c){case"server":return d(e,r);case"client":{const o=t.d();return new Promise((i,l)=>{a.__rpcPending[o]={resolve:i,reject:l},mp.events.callRemote(s,t.c({req:1,id:o,name:e,env:c,args:r,...n}))})}case"cef":return h("__rpc:callServer",[e,r])}}function v(e,r){return 1!==arguments.length&&2!==arguments.length?Promise.reject('callServer expects 1 or 2 arguments: "name" and optional "args"'):g(e,r,{})}function h(e,r,n){switch(c){case"client":return n=r,r=e,1!==arguments.length&&2!==arguments.length||"string"!=typeof r?Promise.reject('callClient from the client expects 1 or 2 arguments: "name" and optional "args"'):d(r,n);case"server":{if(2!==arguments.length&&3!==arguments.length||"object"!=typeof e)return Promise.reject('callClient from the server expects 2 or 3 arguments: "player", "name", and optional "args"');const o=t.d();return new Promise((i,l)=>{a.__rpcPending[o]={resolve:i,reject:l},e.call(s,[t.c({req:1,id:o,name:r,env:c,args:n})])})}case"cef":{if(n=r,r=e,1!==arguments.length&&2!==arguments.length||"string"!=typeof r)return Promise.reject('callClient from the browser expects 1 or 2 arguments: "name" and optional "args"');const o=t.d();return new Promise((e,i)=>{a.__rpcPending[o]={resolve:e,reject:i},mp.trigger(s,t.c({req:1,id:o,name:r,env:c,args:n}))})}}}function w(e,r,n,t,o){return new Promise((s,i)=>{a.__rpcPending[e]={resolve:s,reject:i},l(r,{req:1,id:e,name:n,env:c,args:t,...o})})}async function _(e,r,n,s){switch(c){case"client":{n=r,r=e;const c=t.d(),a=mp.browsers.length;let l;for(let e=0;e{const t=r=>{const o=r.split(":");o[0]===c&&+o[1]&&(l=n),mp.events.remove(i,t),e()};mp.events.add(i,t),n.execute(`var f = window["${i}"]; mp.trigger("${i}", "${c}:"+((f && f("${r}")) ? 1 : 0));`)}),l)break}if(l)return w(c,l,r,n,s);throw o}case"server":return h(e,"__rpc:callBrowsers",[r,n]);case"cef":return n=r,h("__rpc:callBrowsers",[r=e,n])}}function b(e,r,n){switch(c){case"client":if(1!==arguments.length&&2!==arguments.length)return Promise.reject('callBrowsers from the client expects 1 or 2 arguments: "name" and optional "args"');break;case"server":if(2!==arguments.length&&3!==arguments.length)return Promise.reject('callBrowsers from the server expects 2 or 3 arguments: "player", "name", and optional "args"');break;case"cef":if(1!==arguments.length&&2!==arguments.length)return Promise.reject('callBrowsers from the browser expects 1 or 2 arguments: "name" and optional "args"')}return _(e,r,n,{})}function y(e,r,n){if("client"!==c)return Promise.reject("callBrowser can only be used in the client environment");if(2!==arguments.length&&3!==arguments.length)return Promise.reject('callBrowser expects 2 or 3 arguments: "browser", "name", and optional "args"');return w(t.d(),e,r,n,{})}!a[s]&&(a.__rpcListeners={},a.__rpcPending={},a[s]=((...e)=>{let r=e[0];"server"===c&&(r=e[1]);const n=t.b(r);if(n.req){const r={id:n.id,environment:n.fenv||n.env};"server"===c&&(r.player=e[0]);const o=f(n.name,n.args,r),i={ret:1,id:n.id};switch(c){case"server":o.then(e=>{r.player.call(s,[t.c({...i,res:e})])}).catch(e=>{r.player.call(s,[t.c({...i,err:e})])});break;case"client":"server"===n.env?o.then(e=>{mp.events.callRemote(s,t.c({...i,res:e}))}).catch(e=>{mp.events.callRemote(s,t.c({...i,err:e}))}):"cef"===n.env&&o.then(e=>{u({...i,res:e})}).catch(e=>{u({...i,err:e})});break;case"cef":o.then(e=>{mp.trigger(s,t.c({...i,res:e}))}).catch(e=>{mp.trigger(s,t.c({...i,err:e}))})}}else if(n.ret){const e=a.__rpcPending[n.id];e&&(n.err?e.reject(n.err):e.resolve(n.res),a.__rpcPending[n.id]=void 0)}}),"cef"===c?window[i]=(e=>!!a.__rpcListeners[e]):(mp.events.add(s,a[s]),"client"===c&&(p("__rpc:callServer",([e,r],n)=>g(e,r,{fenv:n.environment})),p("__rpc:callBrowsers",([e,r],n)=>_(e,r,null,{fenv:n.environment}))))),"client"===c&&(l=((e,r)=>{const n=t.c(r);e.execute(`var process = window["${s}"]; if(process){ process('${n}'); }else{ mp.trigger("${s}", '{"ret":1,"id":"${r.id}","err":"${o}"}'); }`)}),u=(e=>{mp.browsers.forEach(r=>l(r,e))}))}.call(this,n(2))},function(e,r){var n;n=function(){return this}();try{n=n||Function("return this")()||(0,eval)("this")}catch(e){"object"==typeof window&&(n=window)}e.exports=n}])}); \ No newline at end of file diff --git a/src/index.js b/src/index.js index e2d08bc..58bc728 100644 --- a/src/index.js +++ b/src/index.js @@ -8,8 +8,118 @@ const ERR_NOT_FOUND = 'PROCEDURE_NOT_FOUND'; const PROCESS_EVENT = '__rpc:process'; const PROCEDURE_EXISTS = '__rpc:exists'; -const listeners = {}; -const pending = {}; +const glob = environment === "cef" ? window : global; + +const init = !glob[PROCESS_EVENT]; + +if(init){ + glob.__rpcListeners = {}; + glob.__rpcPending = {}; + + glob[PROCESS_EVENT] = (...args) => { + let rawData = args[0]; + if(environment === "server") rawData = args[1]; + const data = util.parseData(rawData); + + + if(data.req){ // someone is trying to remotely call a procedure + const info = { + id: data.id, + environment: data.fenv || data.env + }; + if(environment === "server") info.player = args[0]; + const promise = callProcedure(data.name, data.args, info); + const part = { + ret: 1, + id: data.id + }; + switch(environment){ + case "server": { + promise.then(res => { + info.player.call(PROCESS_EVENT, [util.stringifyData({ + ...part, + res + })]); + }).catch(err => { + info.player.call(PROCESS_EVENT, [util.stringifyData({ + ...part, + err + })]); + }); + break; + } + case "client": { + 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 => { + passEventToBrowsers({ + ...part, + res + }); + }).catch(err => { + passEventToBrowsers({ + ...part, + err + }); + }); + } + break; + } + case "cef": { + promise.then(res => { + mp.trigger(PROCESS_EVENT, util.stringifyData({ + ...part, + res + })); + }).catch(err => { + mp.trigger(PROCESS_EVENT, util.stringifyData({ + ...part, + err + })); + }); + } + } + }else if(data.ret){ // a previously called remote procedure has returned + const info = glob.__rpcPending[data.id]; + if(info){ + if(data.err) info.reject(data.err); + else info.resolve(data.res); + glob.__rpcPending[data.id] = undefined; + } + } + }; + + if(environment === "cef"){ + window[PROCEDURE_EXISTS] = name => !!glob.__rpcListeners[name]; + }else{ + mp.events.add(PROCESS_EVENT, glob[PROCESS_EVENT]); + + if(environment === "client"){ + // set up internal pass-through events + register('__rpc:callServer', ([name, args], info) => { + return _callServer(name, args, { + fenv: info.environment + }); + }); + register('__rpc:callBrowsers', ([name, args], info) => { + return _callBrowsers(name, args, null, { + fenv: info.environment + }); + }); + } + } +} let passEventToBrowser, passEventToBrowsers; if(environment === "client"){ @@ -24,98 +134,9 @@ if(environment === "client"){ } async function callProcedure(name, args, info){ - if(!listeners[name]) throw ERR_NOT_FOUND; - return listeners[name](args, info); -} - -const processEvent = (...args) => { - let rawData = args[0]; - if(environment === "server") rawData = args[1]; - const data = util.parseData(rawData); - - if(data.req){ // someone is trying to remotely call a procedure - const info = { - id: data.id, - environment: data.fenv || data.env - }; - if(environment === "server") info.player = args[0]; - const promise = callProcedure(data.name, data.args, info); - const part = { - ret: 1, - id: data.id - }; - switch(environment){ - case "server": { - promise.then(res => { - info.player.call(PROCESS_EVENT, [util.stringifyData({ - ...part, - res - })]); - }).catch(err => { - info.player.call(PROCESS_EVENT, [util.stringifyData({ - ...part, - err - })]); - }); - break; - } - case "client": { - 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 => { - passEventToBrowsers({ - ...part, - res - }); - }).catch(err => { - passEventToBrowsers({ - ...part, - err - }); - }); - } - break; - } - case "cef": { - promise.then(res => { - mp.trigger(PROCESS_EVENT, util.stringifyData({ - ...part, - res - })); - }).catch(err => { - mp.trigger(PROCESS_EVENT, util.stringifyData({ - ...part, - err - })); - }); - } - } - }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; - } - } -}; - -if(environment === "cef"){ - window[PROCESS_EVENT] = processEvent; - window[PROCEDURE_EXISTS] = name => !!listeners[name]; -}else{ - mp.events.add(PROCESS_EVENT, processEvent); + const listener = glob.__rpcListeners[name]; + if(!listener) throw ERR_NOT_FOUND; + return listener(args, info); } /** @@ -125,7 +146,7 @@ if(environment === "cef"){ */ export function register(name, cb){ if(arguments.length !== 2) throw 'register expects 2 arguments: "name" and "cb"'; - listeners[name] = cb; + glob.__rpcListeners[name] = cb; } /** @@ -134,7 +155,7 @@ export function register(name, cb){ */ export function unregister(name){ if(arguments.length !== 1) throw 'unregister expects 1 argument: "name"'; - listeners[name] = undefined; + glob.__rpcListeners[name] = undefined; } /** @@ -159,7 +180,7 @@ function _callServer(name, args, extraData){ case "client": { const id = util.uid(); return new Promise((resolve, reject) => { - pending[id] = { + glob.__rpcPending[id] = { resolve, reject }; @@ -220,7 +241,7 @@ export function callClient(player, name, args){ 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"'); const id = util.uid(); return new Promise((resolve, reject) => { - pending[id] = { + glob.__rpcPending[id] = { resolve, reject }; @@ -239,7 +260,7 @@ export function callClient(player, name, args){ 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"'); const id = util.uid(); return new Promise((resolve, reject) => { - pending[id] = { + glob.__rpcPending[id] = { resolve, reject }; @@ -257,7 +278,7 @@ export function callClient(player, name, args){ function _callBrowser(id, browser, name, args, extraData){ return new Promise((resolve, reject) => { - pending[id] = { + glob.__rpcPending[id] = { resolve, reject }; @@ -351,18 +372,4 @@ export function callBrowser(browser, name, 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(); return _callBrowser(id, browser, name, args, {}); -} - -// set up internal pass-through events -if(environment === "client"){ - register('__rpc:callServer', ([name, args], info) => { - return _callServer(name, args, { - fenv: info.environment - }); - }); - register('__rpc:callBrowsers', ([name, args], info) => { - return _callBrowsers(name, args, null, { - fenv: info.environment - }); - }); } \ No newline at end of file