diff --git a/dist/rage-rpc.min.js b/dist/rage-rpc.min.js index b00ee86..10c11b6 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 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(){const e=46656*Math.random()|0,r=46656*Math.random()|0;return("000"+e.toString(36)).slice(-3)+("000"+r.toString(36)).slice(-3)}function c(){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 d}),n.d(r,"call",function(){return m}),n.d(r,"callServer",function(){return v}),n.d(r,"callClient",function(){return h}),n.d(r,"callBrowsers",function(){return y}),n.d(r,"callBrowser",function(){return b});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;function l(e,r,n){const c=t.c(r);e.execute(`var process = window["${s}"]; if(process){ process('${c}'); }else{ ${n?"":`mp.trigger("${s}", '{"ret":1,"id":"${r.id}","err":"${o}","env":"cef"}');`} }`)}function u(e,r){mp.browsers.forEach(n=>l(n,e,r))}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 d(e){if(1!==arguments.length)throw'unregister expects 1 argument: "name"';a.__rpcListeners[e]=void 0}function m(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 m(e,r);case"client":{const o=t.d();return new Promise((i,l)=>{a.__rpcPending[o]={resolve:i,reject:l};const u={req:1,id:o,name:e,env:c,args:r,...n};mp.events.callRemote(s,t.c(u))})}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"'):m(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,player:e};const u={req:1,id:o,name:r,env:c,args:n};e.call(s,[t.c(u)])})}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};const l={req:1,id:o,name:r,env:c,args:n};mp.trigger(s,t.c(l))})}}}function _(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},!1)})}async function w(e,r,n,s={}){switch(c){case"client":{const e=t.d(),c=mp.browsers.length;let a;for(let n=0;n{const c=r=>{const o=r.split(":");o[0]===e&&+o[1]&&(a=t),mp.events.remove(i,c),n()};mp.events.add(i,c),t.execute(`var f = window["${i}"]; mp.trigger("${i}", "${e}:"+((f && f("${r}")) ? 1 : 0));`)}),a)break}if(a)return _(e,a,r,n,s);throw o}case"server":return h(e,"__rpc:callBrowsers",[r,n]);case"cef":return h("__rpc:callBrowsers",[r,n])}}function y(e,r,n){switch(c){case"client":case"cef":return 1!==arguments.length&&2!==arguments.length?Promise.reject('callBrowsers from the client or browser expects 1 or 2 arguments: "name" and optional "args"'):w(void 0,e,r,{});case"server":return 2!==arguments.length&&3!==arguments.length?Promise.reject('callBrowsers from the server expects 2 or 3 arguments: "player", "name", and optional "args"'):w(e,r,n,{})}}function b(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 _(t.d(),e,r,n,{})}a[s]||(a.__rpcListeners={},a.__rpcPending={},a[s]=((e,r)=>{"server"!==c&&(r=e);const n=t.b(r);if(n.req){const r={id:n.id,environment:n.fenv||n.env};"server"===c&&(r.player=e);const o=f(n.name,n.args,r),i={ret:1,id:n.id,env:c};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},!0)}).catch(e=>{u({...i,err:e},!0)});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 r=a.__rpcPending[n.id];if("server"===c&&r.player!==e)return;r&&(n.err?r.reject(n.err):r.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)=>w(e,r,null,{fenv:n.environment})))))}.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 +!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 o=r[t]={i:t,l:!1,exports:{}};return e[t].call(o.exports,o,o.exports,n),o.l=!0,o.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 o in e)n.d(t,o,function(r){return e[r]}.bind(null,o));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(){const e=46656*Math.random()|0,r=46656*Math.random()|0;return("000"+e.toString(36)).slice(-3)+("000"+r.toString(36)).slice(-3)}function o(){return mp.joaat?"server":mp.game&&mp.game.joaat?"client":mp.trigger?"cef":void 0}function c(e){return JSON.stringify(e)}function s(e){return JSON.parse(e)}function i(e){try{e.url}catch(e){return!1}return!0}n.d(r,"e",function(){return t}),n.d(r,"a",function(){return o}),n.d(r,"d",function(){return c}),n.d(r,"c",function(){return s}),n.d(r,"b",function(){return i})},function(e,r,n){"use strict";n.r(r),function(e){n.d(r,"register",function(){return p}),n.d(r,"unregister",function(){return d}),n.d(r,"call",function(){return m}),n.d(r,"callServer",function(){return v}),n.d(r,"callClient",function(){return _}),n.d(r,"callBrowsers",function(){return b}),n.d(r,"callBrowser",function(){return y});var t=n(0);const o=t.a();if(!o)throw"Unknown RAGE environment";const c="PROCEDURE_NOT_FOUND",s="__rpc:id",i="__rpc:process",a="__rpc:exists",l="cef"===o?window:e;if(!l[i])if(l.__rpcListeners={},l.__rpcPending={},l[i]=((e,r)=>{"server"!==o&&(r=e);const n=t.c(r);if(n.req){const c={id:n.id,environment:n.fenv||n.env};"server"===o&&(c.player=e);const s=f(n.name,n.args,c),a={ret:1,id:n.id,env:o};let p;switch(o){case"server":p=(e=>c.player.call(i,[t.d(e)]));break;case"client":if("server"===n.env)p=(e=>mp.events.callRemote(i,t.d(e)));else if("cef"===n.env){const e=n.b&&l.__rpcBrowsers[n.b];v("log",`RECV EVENT FROM BROWSER ${n.b} (EXISTS ${!!e} AND ${!!e&&t.b(e)}) ON CLIENT: ${r}`),p=(r=>e&&t.b(e)&&u(e,r,!0))}break;case"cef":p=(e=>mp.trigger(i,t.d(e)))}p&&s.then(e=>p({...a,res:e})).catch(e=>p({...a,err:e}))}else if(n.ret){const r=l.__rpcPending[n.id];if("server"===o&&r.player!==e)return;r&&(n.err?r.reject(n.err):r.resolve(n.res),l.__rpcPending[n.id]=void 0)}}),"cef"===o)window[a]=(e=>!!l.__rpcListeners[e]);else if(mp.events.add(i,l[i]),"client"===o){p("__rpc:callServer",([e,r],n)=>g(e,r,{fenv:n.environment})),p("__rpc:callBrowsers",([e,r],n)=>h(e,r,null,{fenv:n.environment})),l.__rpcBrowsers={};const e=e=>{const r=t.e();Object.keys(l.__rpcBrowsers).forEach(r=>{l.__rpcBrowsers[r]===e&&delete l.__rpcBrowsers[r]}),l.__rpcBrowsers[r]=e,e.execute(`window['${s}'] = '${r}';`)};mp.browsers.forEach(e),mp.events.add("browserCreated",e)}function u(e,r,n){const o=t.d(r);v("log",`PASSING EVENT TO BROWSER ${o}`),e.execute(`var process = window["${i}"]; if(process){ process('${o}'); }else{ ${n?"":`mp.trigger("${i}", '{"ret":1,"id":"${r.id}","err":"${c}","env":"cef"}');`} }`)}async function f(e,r,n){const t=l.__rpcListeners[e];if(!t)throw c;return t(r,n)}function p(e,r){if(2!==arguments.length)throw'register expects 2 arguments: "name" and "cb"';l.__rpcListeners[e]=r}function d(e){if(1!==arguments.length)throw'unregister expects 1 argument: "name"';l.__rpcListeners[e]=void 0}function m(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:o})}function g(e,r,n={}){switch(o){case"server":return m(e,r);case"client":{const c=t.e();return new Promise((s,a)=>{l.__rpcPending[c]={resolve:s,reject:a};const u={req:1,id:c,name:e,env:o,args:r,...n};mp.events.callRemote(i,t.d(u))})}case"cef":return _("__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 _(e,r,n){switch(o){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"'):m(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 c=t.e();return new Promise((s,a)=>{l.__rpcPending[c]={resolve:s,reject:a,player:e};const u={req:1,id:c,name:r,env:o,args:n};e.call(i,[t.d(u)])})}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 c=t.e();return new Promise((e,a)=>{l.__rpcPending[c]={resolve:e,reject:a};const u={b:l[s],req:1,id:c,name:r,env:o,args:n};mp.trigger(i,t.d(u))})}}}function w(e,r,n,t,c={}){return new Promise((s,i)=>{l.__rpcPending[e]={resolve:s,reject:i},u(r,{req:1,id:e,name:n,env:o,args:t,...c},!1)})}async function h(e,r,n,s={}){switch(o){case"client":{const e=t.e(),o=mp.browsers.length;let i;for(let n=0;n{const o=r=>{const c=r.split(":");c[0]===e&&+c[1]&&(i=t),mp.events.remove(a,o),n()};mp.events.add(a,o),t.execute(`var f = window["${a}"]; mp.trigger("${a}", "${e}:"+((f && f("${r}")) ? 1 : 0));`)}),i)break}if(i)return w(e,i,r,n,s);throw c}case"server":return _(e,"__rpc:callBrowsers",[r,n]);case"cef":return _("__rpc:callBrowsers",[r,n])}}function b(e,r,n){switch(o){case"client":case"cef":return 1!==arguments.length&&2!==arguments.length?Promise.reject('callBrowsers from the client or browser expects 1 or 2 arguments: "name" and optional "args"'):h(void 0,e,r,{});case"server":return 2!==arguments.length&&3!==arguments.length?Promise.reject('callBrowsers from the server expects 2 or 3 arguments: "player", "name", and optional "args"'):h(e,r,n,{})}}function y(e,r,n){if("client"!==o)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.e(),e,r,n,{})}}.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/defs.d.ts b/src/defs.d.ts index ebe18c9..4114709 100644 --- a/src/defs.d.ts +++ b/src/defs.d.ts @@ -10,6 +10,7 @@ declare interface Player { } declare interface Browser { + url: string; execute: (code: string) => void; [property: string]: any; } @@ -23,6 +24,7 @@ declare interface ProcedureListenerInfo { declare interface Event { req?: number; ret?: number; + b?: string; id: string; name?: string; args?: any; diff --git a/src/index.ts b/src/index.ts index 903a3e9..a530239 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,7 @@ if(!environment) throw 'Unknown RAGE environment'; const ERR_NOT_FOUND = 'PROCEDURE_NOT_FOUND'; +const IDENTIFIER = '__rpc:id'; const PROCESS_EVENT = '__rpc:process'; const PROCEDURE_EXISTS = '__rpc:exists'; @@ -30,63 +31,25 @@ if(!glob[PROCESS_EVENT]){ id: data.id, env: environment }; + let ret: (ev: Event) => void; 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 - })]); - }); + case "server": + ret = ev => info.player.call(PROCESS_EVENT, [util.stringifyData(ev)]); 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 - })); - }); + ret = ev => mp.events.callRemote(PROCESS_EVENT, util.stringifyData(ev)); }else if(data.env === "cef"){ - promise.then(res => { - passEventToBrowsers({ - ...part, - res - }, true); - }).catch(err => { - passEventToBrowsers({ - ...part, - err - }, true); - }); + const browser = data.b && glob.__rpcBrowsers[data.b]; + ret = ev => browser && util.isBrowserValid(browser) && passEventToBrowser(browser, ev, true); } break; } case "cef": { - promise.then(res => { - mp.trigger(PROCESS_EVENT, util.stringifyData({ - ...part, - res - })); - }).catch(err => { - mp.trigger(PROCESS_EVENT, util.stringifyData({ - ...part, - err - })); - }); + ret = ev => mp.trigger(PROCESS_EVENT, util.stringifyData(ev)); } } + if(ret) promise.then(res => ret({ ...part, res })).catch(err => ret({ ...part, err })); }else if(data.ret){ // a previously called remote procedure has returned const info = glob.__rpcPending[data.id]; if(environment === "server" && info.player !== player) return; @@ -115,6 +78,19 @@ if(!glob[PROCESS_EVENT]){ fenv: info.environment }); }); + + glob.__rpcBrowsers = {}; + + const initBrowser = (browser: Browser): void => { + const id = util.uid(); + Object.keys(glob.__rpcBrowsers).forEach(key => { + if(glob.__rpcBrowsers[key] === browser) delete glob.__rpcBrowsers[key]; + }); + glob.__rpcBrowsers[id] = browser; + browser.execute(`window['${IDENTIFIER}'] = '${id}';`); + }; + mp.browsers.forEach(initBrowser); + mp.events.add('browserCreated', initBrowser); } } } @@ -258,6 +234,7 @@ export function callClient(player: Player | string, name?: string | any, args?: reject }; const event: Event = { + b: glob[IDENTIFIER], req: 1, id, name, diff --git a/src/util.ts b/src/util.ts index e3194a7..75ed217 100644 --- a/src/util.ts +++ b/src/util.ts @@ -18,4 +18,11 @@ export function stringifyData(data: any): string { export function parseData(data: string): any { return JSON.parse(data); +} + +export function isBrowserValid(browser: Browser): boolean { + try { + browser.url; + }catch(e){ return false; } + return true; } \ No newline at end of file