@zaisma/game-server 1.4.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/bench/brand/.assetsignore +4 -0
- package/bench/brand/README.md +51 -0
- package/bench/brand/_headers +13 -0
- package/bench/brand/app-icon-1024.png +0 -0
- package/bench/brand/app-icon-192.png +0 -0
- package/bench/brand/app-icon-512.png +0 -0
- package/bench/brand/apple-touch-icon.png +0 -0
- package/bench/brand/cover-guide/coin-cascade-busy-fine.webp +0 -0
- package/bench/brand/cover-guide/gem-grid-cover.webp +0 -0
- package/bench/brand/cover-guide/gem-grid-screenshot.webp +0 -0
- package/bench/brand/cover-guide/good-arena.webp +0 -0
- package/bench/brand/cover-guide/good-four-in-a-row.webp +0 -0
- package/bench/brand/cover-guide/lava-leap-extra-text.webp +0 -0
- package/bench/brand/cover-guide/lava-leap-fake-reward.webp +0 -0
- package/bench/brand/cover-guide/lava-leap-fine.webp +0 -0
- package/bench/brand/cover-guide/lava-leap-overlay.webp +0 -0
- package/bench/brand/cover-guide/screenshot-fine.webp +0 -0
- package/bench/brand/cover-guide/screenshot-not-fine.webp +0 -0
- package/bench/brand/design/covers/ashen-crown.webp +0 -0
- package/bench/brand/design/covers/frostline.webp +0 -0
- package/bench/brand/design/covers/glasswing.webp +0 -0
- package/bench/brand/design/covers/hollow-tide.webp +0 -0
- package/bench/brand/design/covers/iron-vigil.webp +0 -0
- package/bench/brand/design/covers/kestrel.webp +0 -0
- package/bench/brand/design/covers/neon-drift.webp +0 -0
- package/bench/brand/design/covers/verdant.webp +0 -0
- package/bench/brand/favicon-32.png +0 -0
- package/bench/brand/favicon.ico +0 -0
- package/bench/brand/favicon.svg +1 -0
- package/bench/brand/flags/LICENSE +21 -0
- package/bench/brand/flags/ad.png +0 -0
- package/bench/brand/flags/ae.png +0 -0
- package/bench/brand/flags/af.png +0 -0
- package/bench/brand/flags/ag.png +0 -0
- package/bench/brand/flags/ai.png +0 -0
- package/bench/brand/flags/al.png +0 -0
- package/bench/brand/flags/am.png +0 -0
- package/bench/brand/flags/ao.png +0 -0
- package/bench/brand/flags/aq.png +0 -0
- package/bench/brand/flags/ar.png +0 -0
- package/bench/brand/flags/as.png +0 -0
- package/bench/brand/flags/at.png +0 -0
- package/bench/brand/flags/au.png +0 -0
- package/bench/brand/flags/aw.png +0 -0
- package/bench/brand/flags/ax.png +0 -0
- package/bench/brand/flags/az.png +0 -0
- package/bench/brand/flags/ba.png +0 -0
- package/bench/brand/flags/bb.png +0 -0
- package/bench/brand/flags/bd.png +0 -0
- package/bench/brand/flags/be.png +0 -0
- package/bench/brand/flags/bf.png +0 -0
- package/bench/brand/flags/bg.png +0 -0
- package/bench/brand/flags/bh.png +0 -0
- package/bench/brand/flags/bi.png +0 -0
- package/bench/brand/flags/bj.png +0 -0
- package/bench/brand/flags/bl.png +0 -0
- package/bench/brand/flags/bm.png +0 -0
- package/bench/brand/flags/bn.png +0 -0
- package/bench/brand/flags/bo.png +0 -0
- package/bench/brand/flags/bq.png +0 -0
- package/bench/brand/flags/br.png +0 -0
- package/bench/brand/flags/bs.png +0 -0
- package/bench/brand/flags/bt.png +0 -0
- package/bench/brand/flags/bv.png +0 -0
- package/bench/brand/flags/bw.png +0 -0
- package/bench/brand/flags/by.png +0 -0
- package/bench/brand/flags/bz.png +0 -0
- package/bench/brand/flags/ca.png +0 -0
- package/bench/brand/flags/cc.png +0 -0
- package/bench/brand/flags/cd.png +0 -0
- package/bench/brand/flags/cf.png +0 -0
- package/bench/brand/flags/cg.png +0 -0
- package/bench/brand/flags/ch.png +0 -0
- package/bench/brand/flags/ci.png +0 -0
- package/bench/brand/flags/ck.png +0 -0
- package/bench/brand/flags/cl.png +0 -0
- package/bench/brand/flags/cm.png +0 -0
- package/bench/brand/flags/cn.png +0 -0
- package/bench/brand/flags/co.png +0 -0
- package/bench/brand/flags/cp.png +0 -0
- package/bench/brand/flags/cr.png +0 -0
- package/bench/brand/flags/cu.png +0 -0
- package/bench/brand/flags/cv.png +0 -0
- package/bench/brand/flags/cw.png +0 -0
- package/bench/brand/flags/cx.png +0 -0
- package/bench/brand/flags/cy.png +0 -0
- package/bench/brand/flags/cz.png +0 -0
- package/bench/brand/flags/de.png +0 -0
- package/bench/brand/flags/dg.png +0 -0
- package/bench/brand/flags/dj.png +0 -0
- package/bench/brand/flags/dk.png +0 -0
- package/bench/brand/flags/dm.png +0 -0
- package/bench/brand/flags/do.png +0 -0
- package/bench/brand/flags/dz.png +0 -0
- package/bench/brand/flags/ec.png +0 -0
- package/bench/brand/flags/ee.png +0 -0
- package/bench/brand/flags/eg.png +0 -0
- package/bench/brand/flags/eh.png +0 -0
- package/bench/brand/flags/er.png +0 -0
- package/bench/brand/flags/es.png +0 -0
- package/bench/brand/flags/et.png +0 -0
- package/bench/brand/flags/eu.png +0 -0
- package/bench/brand/flags/fi.png +0 -0
- package/bench/brand/flags/fj.png +0 -0
- package/bench/brand/flags/fk.png +0 -0
- package/bench/brand/flags/fm.png +0 -0
- package/bench/brand/flags/fo.png +0 -0
- package/bench/brand/flags/fr.png +0 -0
- package/bench/brand/flags/ga.png +0 -0
- package/bench/brand/flags/gb.png +0 -0
- package/bench/brand/flags/gd.png +0 -0
- package/bench/brand/flags/ge.png +0 -0
- package/bench/brand/flags/gf.png +0 -0
- package/bench/brand/flags/gg.png +0 -0
- package/bench/brand/flags/gh.png +0 -0
- package/bench/brand/flags/gi.png +0 -0
- package/bench/brand/flags/gl.png +0 -0
- package/bench/brand/flags/gm.png +0 -0
- package/bench/brand/flags/gn.png +0 -0
- package/bench/brand/flags/gp.png +0 -0
- package/bench/brand/flags/gq.png +0 -0
- package/bench/brand/flags/gr.png +0 -0
- package/bench/brand/flags/gs.png +0 -0
- package/bench/brand/flags/gt.png +0 -0
- package/bench/brand/flags/gu.png +0 -0
- package/bench/brand/flags/gw.png +0 -0
- package/bench/brand/flags/gy.png +0 -0
- package/bench/brand/flags/hk.png +0 -0
- package/bench/brand/flags/hm.png +0 -0
- package/bench/brand/flags/hn.png +0 -0
- package/bench/brand/flags/hr.png +0 -0
- package/bench/brand/flags/ht.png +0 -0
- package/bench/brand/flags/hu.png +0 -0
- package/bench/brand/flags/ic.png +0 -0
- package/bench/brand/flags/id.png +0 -0
- package/bench/brand/flags/ie.png +0 -0
- package/bench/brand/flags/il.png +0 -0
- package/bench/brand/flags/im.png +0 -0
- package/bench/brand/flags/in.png +0 -0
- package/bench/brand/flags/io.png +0 -0
- package/bench/brand/flags/iq.png +0 -0
- package/bench/brand/flags/ir.png +0 -0
- package/bench/brand/flags/is.png +0 -0
- package/bench/brand/flags/it.png +0 -0
- package/bench/brand/flags/je.png +0 -0
- package/bench/brand/flags/jm.png +0 -0
- package/bench/brand/flags/jo.png +0 -0
- package/bench/brand/flags/jp.png +0 -0
- package/bench/brand/flags/ke.png +0 -0
- package/bench/brand/flags/kg.png +0 -0
- package/bench/brand/flags/kh.png +0 -0
- package/bench/brand/flags/ki.png +0 -0
- package/bench/brand/flags/km.png +0 -0
- package/bench/brand/flags/kn.png +0 -0
- package/bench/brand/flags/kp.png +0 -0
- package/bench/brand/flags/kr.png +0 -0
- package/bench/brand/flags/kw.png +0 -0
- package/bench/brand/flags/ky.png +0 -0
- package/bench/brand/flags/kz.png +0 -0
- package/bench/brand/flags/la.png +0 -0
- package/bench/brand/flags/lb.png +0 -0
- package/bench/brand/flags/lc.png +0 -0
- package/bench/brand/flags/li.png +0 -0
- package/bench/brand/flags/lk.png +0 -0
- package/bench/brand/flags/lr.png +0 -0
- package/bench/brand/flags/ls.png +0 -0
- package/bench/brand/flags/lt.png +0 -0
- package/bench/brand/flags/lu.png +0 -0
- package/bench/brand/flags/lv.png +0 -0
- package/bench/brand/flags/ly.png +0 -0
- package/bench/brand/flags/ma.png +0 -0
- package/bench/brand/flags/mc.png +0 -0
- package/bench/brand/flags/md.png +0 -0
- package/bench/brand/flags/me.png +0 -0
- package/bench/brand/flags/mf.png +0 -0
- package/bench/brand/flags/mg.png +0 -0
- package/bench/brand/flags/mh.png +0 -0
- package/bench/brand/flags/mk.png +0 -0
- package/bench/brand/flags/ml.png +0 -0
- package/bench/brand/flags/mm.png +0 -0
- package/bench/brand/flags/mn.png +0 -0
- package/bench/brand/flags/mo.png +0 -0
- package/bench/brand/flags/mp.png +0 -0
- package/bench/brand/flags/mq.png +0 -0
- package/bench/brand/flags/mr.png +0 -0
- package/bench/brand/flags/ms.png +0 -0
- package/bench/brand/flags/mt.png +0 -0
- package/bench/brand/flags/mu.png +0 -0
- package/bench/brand/flags/mv.png +0 -0
- package/bench/brand/flags/mw.png +0 -0
- package/bench/brand/flags/mx.png +0 -0
- package/bench/brand/flags/my.png +0 -0
- package/bench/brand/flags/mz.png +0 -0
- package/bench/brand/flags/na.png +0 -0
- package/bench/brand/flags/nc.png +0 -0
- package/bench/brand/flags/ne.png +0 -0
- package/bench/brand/flags/nf.png +0 -0
- package/bench/brand/flags/ng.png +0 -0
- package/bench/brand/flags/ni.png +0 -0
- package/bench/brand/flags/nl.png +0 -0
- package/bench/brand/flags/no.png +0 -0
- package/bench/brand/flags/np.png +0 -0
- package/bench/brand/flags/nr.png +0 -0
- package/bench/brand/flags/nu.png +0 -0
- package/bench/brand/flags/nz.png +0 -0
- package/bench/brand/flags/om.png +0 -0
- package/bench/brand/flags/pa.png +0 -0
- package/bench/brand/flags/pc.png +0 -0
- package/bench/brand/flags/pe.png +0 -0
- package/bench/brand/flags/pf.png +0 -0
- package/bench/brand/flags/pg.png +0 -0
- package/bench/brand/flags/ph.png +0 -0
- package/bench/brand/flags/pk.png +0 -0
- package/bench/brand/flags/pl.png +0 -0
- package/bench/brand/flags/pm.png +0 -0
- package/bench/brand/flags/pn.png +0 -0
- package/bench/brand/flags/pr.png +0 -0
- package/bench/brand/flags/ps.png +0 -0
- package/bench/brand/flags/pt.png +0 -0
- package/bench/brand/flags/pw.png +0 -0
- package/bench/brand/flags/py.png +0 -0
- package/bench/brand/flags/qa.png +0 -0
- package/bench/brand/flags/re.png +0 -0
- package/bench/brand/flags/ro.png +0 -0
- package/bench/brand/flags/rs.png +0 -0
- package/bench/brand/flags/ru.png +0 -0
- package/bench/brand/flags/rw.png +0 -0
- package/bench/brand/flags/sa.png +0 -0
- package/bench/brand/flags/sb.png +0 -0
- package/bench/brand/flags/sc.png +0 -0
- package/bench/brand/flags/sd.png +0 -0
- package/bench/brand/flags/se.png +0 -0
- package/bench/brand/flags/sg.png +0 -0
- package/bench/brand/flags/sh.png +0 -0
- package/bench/brand/flags/si.png +0 -0
- package/bench/brand/flags/sj.png +0 -0
- package/bench/brand/flags/sk.png +0 -0
- package/bench/brand/flags/sl.png +0 -0
- package/bench/brand/flags/sm.png +0 -0
- package/bench/brand/flags/sn.png +0 -0
- package/bench/brand/flags/so.png +0 -0
- package/bench/brand/flags/sr.png +0 -0
- package/bench/brand/flags/ss.png +0 -0
- package/bench/brand/flags/st.png +0 -0
- package/bench/brand/flags/sv.png +0 -0
- package/bench/brand/flags/sx.png +0 -0
- package/bench/brand/flags/sy.png +0 -0
- package/bench/brand/flags/sz.png +0 -0
- package/bench/brand/flags/tc.png +0 -0
- package/bench/brand/flags/td.png +0 -0
- package/bench/brand/flags/tf.png +0 -0
- package/bench/brand/flags/tg.png +0 -0
- package/bench/brand/flags/th.png +0 -0
- package/bench/brand/flags/tj.png +0 -0
- package/bench/brand/flags/tk.png +0 -0
- package/bench/brand/flags/tl.png +0 -0
- package/bench/brand/flags/tm.png +0 -0
- package/bench/brand/flags/tn.png +0 -0
- package/bench/brand/flags/to.png +0 -0
- package/bench/brand/flags/tr.png +0 -0
- package/bench/brand/flags/tt.png +0 -0
- package/bench/brand/flags/tv.png +0 -0
- package/bench/brand/flags/tw.png +0 -0
- package/bench/brand/flags/tz.png +0 -0
- package/bench/brand/flags/ua.png +0 -0
- package/bench/brand/flags/ug.png +0 -0
- package/bench/brand/flags/um.png +0 -0
- package/bench/brand/flags/un.png +0 -0
- package/bench/brand/flags/us.png +0 -0
- package/bench/brand/flags/uy.png +0 -0
- package/bench/brand/flags/uz.png +0 -0
- package/bench/brand/flags/va.png +0 -0
- package/bench/brand/flags/vc.png +0 -0
- package/bench/brand/flags/ve.png +0 -0
- package/bench/brand/flags/vg.png +0 -0
- package/bench/brand/flags/vi.png +0 -0
- package/bench/brand/flags/vn.png +0 -0
- package/bench/brand/flags/vu.png +0 -0
- package/bench/brand/flags/wf.png +0 -0
- package/bench/brand/flags/ws.png +0 -0
- package/bench/brand/flags/xk.png +0 -0
- package/bench/brand/flags/xx.png +0 -0
- package/bench/brand/flags/ye.png +0 -0
- package/bench/brand/flags/yt.png +0 -0
- package/bench/brand/flags/za.png +0 -0
- package/bench/brand/flags/zm.png +0 -0
- package/bench/brand/flags/zw.png +0 -0
- package/bench/brand/fonts/OFL.txt +93 -0
- package/bench/brand/fonts/jost.7726a5cd.woff2 +0 -0
- package/bench/brand/loader/gif/zaisma-loader-black-128.gif +0 -0
- package/bench/brand/loader/gif/zaisma-loader-black-512.gif +0 -0
- package/bench/brand/loader/gif/zaisma-loader-white-128.gif +0 -0
- package/bench/brand/loader/gif/zaisma-loader-white-512.gif +0 -0
- package/bench/brand/loader/png/zaisma-loader-black-128.png +0 -0
- package/bench/brand/loader/png/zaisma-loader-black-512.png +0 -0
- package/bench/brand/loader/png/zaisma-loader-white-128.png +0 -0
- package/bench/brand/loader/png/zaisma-loader-white-512.png +0 -0
- package/bench/brand/loader/webp/zaisma-loader-black-128.webp +0 -0
- package/bench/brand/loader/webp/zaisma-loader-black-512.webp +0 -0
- package/bench/brand/loader/webp/zaisma-loader-white-128.webp +0 -0
- package/bench/brand/loader/webp/zaisma-loader-white-512.webp +0 -0
- package/bench/brand/loader/zaisma-loader-black.svg +1 -0
- package/bench/brand/loader/zaisma-loader-white.svg +1 -0
- package/bench/brand/loader/zaisma-loader.svg +1 -0
- package/bench/brand/sign-in/city-day-phone.webp +0 -0
- package/bench/brand/sign-in/city-day.webp +0 -0
- package/bench/brand/sign-in/city-night-phone.webp +0 -0
- package/bench/brand/sign-in/city-night.webp +0 -0
- package/bench/brand/zaisma-app-icon.svg +1 -0
- package/bench/brand/zaisma-mark-black-2048.png +0 -0
- package/bench/brand/zaisma-mark-black.svg +1 -0
- package/bench/brand/zaisma-mark-metal-2048.png +0 -0
- package/bench/brand/zaisma-mark-metal.svg +1 -0
- package/bench/brand/zaisma-mark-small-black.svg +1 -0
- package/bench/brand/zaisma-mark-small-white.svg +1 -0
- package/bench/brand/zaisma-mark-tricolor.svg +1 -0
- package/bench/brand/zaisma-mark-white-2048.png +0 -0
- package/bench/brand/zaisma-mark-white.svg +1 -0
- package/bench/brand/zaisma-wordmark-black-2400.png +0 -0
- package/bench/brand/zaisma-wordmark-black.svg +1 -0
- package/bench/brand/zaisma-wordmark-metal-2400.png +0 -0
- package/bench/brand/zaisma-wordmark-metal.svg +1 -0
- package/bench/brand/zaisma-wordmark-white-2400.png +0 -0
- package/bench/brand/zaisma-wordmark-white.svg +1 -0
- package/bench/glass.js +46466 -0
- package/bin/local-play-bench.d.ts +8 -0
- package/bin/local-play-bench.js +218 -0
- package/bin/local-play-certificate.js +99 -0
- package/bin/local-play-runner.js +43 -0
- package/bin/local-play.d.ts +41 -0
- package/bin/local-play.js +1567 -0
- package/bin/node.js +66 -1
- package/package.json +1 -1
- package/sdk/picture.js +97 -0
- package/sdk/relay.js +269 -0
- package/sdk/saves.js +884 -0
- package/sdk/zaisma.d.ts +1720 -0
- package/sdk/zaisma.js +2374 -0
- package/src/qr-code.d.ts +20 -0
- package/src/qr-code.js +319 -0
- package/src/room.js +1 -1
package/bin/node.js
CHANGED
|
@@ -691,7 +691,7 @@ function binaryBytes(received) {
|
|
|
691
691
|
return new Uint8Array(received.buffer.slice(received.byteOffset, received.byteOffset + received.byteLength));
|
|
692
692
|
}
|
|
693
693
|
|
|
694
|
-
const USAGE = "Usage: zaisma-game-server <server.ts | relay> --game <slug> [--port 8080] [--origin <url>,<url>] [--issuer-url https://rooms.zaisma.com] [--database-file zaisma-database.sqlite] [--room-state-file zaisma-room-states.sqlite]";
|
|
694
|
+
const USAGE = "Usage: zaisma-game-server <server.ts | relay> --game <slug> [--port 8080] [--origin <url>,<url>] [--issuer-url https://rooms.zaisma.com] [--database-file zaisma-database.sqlite] [--room-state-file zaisma-room-states.sqlite]\n or: zaisma-game-server play-locally [folder] [--port 4800] [--game <slug>] [--on-network] [--no-open] [--json]";
|
|
695
695
|
const FLAGS = ["game", "port", "origin", "issuer-url", "database-file", "room-state-file"];
|
|
696
696
|
|
|
697
697
|
/** The command line's flags by name, or null for one this runner doesn't take (or one missing its value). */
|
|
@@ -705,8 +705,73 @@ function flagsFrom(argumentList) {
|
|
|
705
705
|
return flags;
|
|
706
706
|
}
|
|
707
707
|
|
|
708
|
+
/** play-locally's flags: whether each takes a value. */
|
|
709
|
+
const PLAY_LOCALLY_FLAGS = { port: true, game: true, "on-network": false, "no-open": false, json: false };
|
|
710
|
+
|
|
711
|
+
/** play-locally [folder] and its flags, or null with the refusal printed: said plainly, before anything listens. */
|
|
712
|
+
function playLocallyArguments(argumentList) {
|
|
713
|
+
const refuse = (message, fix) => {
|
|
714
|
+
console.error(`${message} ${fix} (bad_option)`);
|
|
715
|
+
return null;
|
|
716
|
+
};
|
|
717
|
+
const flags = {};
|
|
718
|
+
let folder = null;
|
|
719
|
+
for (let i = 0; i < argumentList.length; i++) {
|
|
720
|
+
const given = argumentList[i];
|
|
721
|
+
if (!given.startsWith("--")) {
|
|
722
|
+
if (folder !== null) return refuse(`play-locally takes one folder, and got ${folder} and ${given}.`, "Give the folder of your game, the one with index.html at the top.");
|
|
723
|
+
folder = given;
|
|
724
|
+
continue;
|
|
725
|
+
}
|
|
726
|
+
const name = given.slice(2);
|
|
727
|
+
if (!(name in PLAY_LOCALLY_FLAGS)) return refuse(`${given} isn't a play-locally flag.`, `play-locally takes: ${Object.keys(PLAY_LOCALLY_FLAGS).map((each) => `--${each}`).join(", ")}.`);
|
|
728
|
+
if (!PLAY_LOCALLY_FLAGS[name]) {
|
|
729
|
+
flags[name] = true;
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
const flagValue = argumentList[++i];
|
|
733
|
+
if (flagValue === undefined || flagValue.startsWith("--")) return refuse(`${given} needs a value.`, name === "port" ? "Give the first of four ports, like --port 4800." : "Give your game's slug, like --game sky-hopper.");
|
|
734
|
+
flags[name] = flagValue;
|
|
735
|
+
}
|
|
736
|
+
const portNumber = flags.port === undefined ? 4800 : Number(flags.port);
|
|
737
|
+
if (!Number.isInteger(portNumber) || portNumber < 1 || portNumber > 65532) return refuse("--port isn't a whole number from 1 to 65532.", "Give the first of four ports, like --port 4800: the bench, the game's site, the stand-in rooms and your game server take it and the next three.");
|
|
738
|
+
if (flags.game !== undefined && !/^[a-z0-9-]{1,40}$/.test(flags.game)) return refuse("--game isn't a game's slug.", "A slug is 1 to 40 lowercase letters, digits or hyphens, like sky-hopper.");
|
|
739
|
+
return { folder: folder ?? ".", portNumber, gameSlug: flags.game ?? null, isOnNetwork: flags["on-network"] === true, isOpening: flags["no-open"] !== true, isJson: flags.json === true };
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
/** zaisma-game-server play-locally: your game and its game server on this computer, with a test bench (bin/local-play.js). */
|
|
743
|
+
async function playLocally(argumentList) {
|
|
744
|
+
const given = playLocallyArguments(argumentList);
|
|
745
|
+
if (!given) process.exit(1);
|
|
746
|
+
const { startLocalPlay, terminalQr } = await import("./local-play.js");
|
|
747
|
+
const say = given.isJson ? (line) => console.error(line) : (line) => console.log(line);
|
|
748
|
+
let running;
|
|
749
|
+
try {
|
|
750
|
+
running = await startLocalPlay({ gameFolder: given.folder, port: given.portNumber, isOnNetwork: given.isOnNetwork, gameSlug: given.gameSlug, log: say });
|
|
751
|
+
} catch (error) {
|
|
752
|
+
console.error(error instanceof GameServerError ? `${error.message} ${error.fix ?? ""} (${error.code})` : error);
|
|
753
|
+
process.exit(1);
|
|
754
|
+
}
|
|
755
|
+
for (const signal of ["SIGTERM", "SIGINT"]) process.once(signal, () => running.close().then(() => process.exit(0)));
|
|
756
|
+
if (given.isJson) console.log(JSON.stringify({ bench_url: running.benchUrl, game_site_url: running.gameSiteUrl, rooms_url: running.roomsUrl, network_url: running.networkUrl }));
|
|
757
|
+
say(`Playing ${given.gameSlug ?? "your game"} on this computer. The test bench is at ${running.benchUrl}`);
|
|
758
|
+
say("Each player plays on Zaisma's own game page. Add a player in the dev bar to play with yourself in another tab. A change to a game file reloads the game, and a change to the game server starts it again with its rooms kept.");
|
|
759
|
+
say("Nothing here talks to Zaisma. What the bench keeps is in .zaisma-local/ in the game's folder: add it to your .gitignore. Press Ctrl-C to stop.");
|
|
760
|
+
if (running.networkUrl) {
|
|
761
|
+
say(`On a phone on the same Wi-Fi: ${running.networkUrl}`);
|
|
762
|
+
say(terminalQr(running.networkUrl));
|
|
763
|
+
say("Your phone will say the connection isn't private, because the bench signs its own certificate. Continue to the site.");
|
|
764
|
+
}
|
|
765
|
+
if (given.isOpening) {
|
|
766
|
+
const { spawn } = await import("node:child_process");
|
|
767
|
+
const opener = process.platform === "darwin" ? ["open", [running.benchUrl]] : process.platform === "win32" ? ["cmd", ["/c", "start", "", running.benchUrl]] : ["xdg-open", [running.benchUrl]];
|
|
768
|
+
spawn(opener[0], opener[1], { stdio: "ignore", detached: true }).on("error", () => {}).unref();
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
708
772
|
// Run as a command. An npm bin is a symlink to this file, so both sides are resolved before they're compared.
|
|
709
773
|
async function runCommand() {
|
|
774
|
+
if (process.argv[2] === "play-locally") return playLocally(process.argv.slice(3));
|
|
710
775
|
const file = process.argv[2];
|
|
711
776
|
const flags = file ? flagsFrom(process.argv.slice(3)) : null;
|
|
712
777
|
if (!file || file.startsWith("--") || !flags?.game) {
|
package/package.json
CHANGED
package/sdk/picture.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Report pictures. Zaisma adds this to every game page. When a player opens Report on the Zaisma
|
|
3
|
+
* game page, the page asks for one picture of the game as it is right now: the game's biggest
|
|
4
|
+
* canvas, copied straight after the game draws a frame (so WebGL games work too), shrunk and
|
|
5
|
+
* handed back (unless it's all one color). Nothing is taken unless the Zaisma page asks, and nothing leaves the player's
|
|
6
|
+
* browser unless they send the report with the picture. Games that don't draw on a canvas give
|
|
7
|
+
* no picture. Plain JavaScript, no dependencies, MIT licensed.
|
|
8
|
+
*/
|
|
9
|
+
(function () {
|
|
10
|
+
"use strict";
|
|
11
|
+
var site = typeof __ZAISMA_PICTURE__ === "object" && __ZAISMA_PICTURE__ ? __ZAISMA_PICTURE__.site : "";
|
|
12
|
+
if (!site || window.parent === window) return;
|
|
13
|
+
var MAXIMUM_SIDE_PIXEL_COUNT = 1280;
|
|
14
|
+
var waiting = [];
|
|
15
|
+
|
|
16
|
+
function biggestCanvas() {
|
|
17
|
+
var best = null;
|
|
18
|
+
var bestPixelCount = 0;
|
|
19
|
+
var all = document.getElementsByTagName("canvas");
|
|
20
|
+
for (var i = 0; i < all.length; i++) {
|
|
21
|
+
var pixelCount = all[i].width * all[i].height;
|
|
22
|
+
if (pixelCount > bestPixelCount && all[i].getClientRects().length) {
|
|
23
|
+
best = all[i];
|
|
24
|
+
bestPixelCount = pixelCount;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return best;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// A canvas that's all one color (a title screen drawn in HTML over it, a game still loading) shows nothing worth sending.
|
|
31
|
+
function blank(canvas) {
|
|
32
|
+
var small = document.createElement("canvas");
|
|
33
|
+
small.width = 32;
|
|
34
|
+
small.height = 18;
|
|
35
|
+
var canvasContext = small.getContext("2d");
|
|
36
|
+
canvasContext.drawImage(canvas, 0, 0, 32, 18);
|
|
37
|
+
var pixelBytes = canvasContext.getImageData(0, 0, 32, 18).data;
|
|
38
|
+
var darkestByte = 255, lightestByte = 0;
|
|
39
|
+
for (var i = 0; i < pixelBytes.length; i += 4) {
|
|
40
|
+
var lightnessByte = (pixelBytes[i] * 3 + pixelBytes[i + 1] * 6 + pixelBytes[i + 2]) / 10;
|
|
41
|
+
if (lightnessByte < darkestByte) darkestByte = lightnessByte;
|
|
42
|
+
if (lightnessByte > lightestByte) lightestByte = lightnessByte;
|
|
43
|
+
}
|
|
44
|
+
return lightestByte - darkestByte < 10;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function picture() {
|
|
48
|
+
try {
|
|
49
|
+
var from = biggestCanvas();
|
|
50
|
+
if (!from) return null;
|
|
51
|
+
var longestSidePixelCount = Math.max(from.width, from.height);
|
|
52
|
+
var shrunkSidePixelCount = Math.min(longestSidePixelCount, MAXIMUM_SIDE_PIXEL_COUNT);
|
|
53
|
+
var to = document.createElement("canvas");
|
|
54
|
+
to.width = Math.max(1, Math.round((from.width * shrunkSidePixelCount) / longestSidePixelCount));
|
|
55
|
+
to.height = Math.max(1, Math.round((from.height * shrunkSidePixelCount) / longestSidePixelCount));
|
|
56
|
+
var canvasContext = to.getContext("2d");
|
|
57
|
+
canvasContext.fillStyle = "#000";
|
|
58
|
+
canvasContext.fillRect(0, 0, to.width, to.height);
|
|
59
|
+
canvasContext.drawImage(from, 0, 0, to.width, to.height);
|
|
60
|
+
return blank(to) ? null : to.toDataURL("image/jpeg", 0.8);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
return null; // A canvas showing pictures from another site can't be copied.
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function answer() {
|
|
67
|
+
if (!waiting.length) return;
|
|
68
|
+
var pictureUrl = picture();
|
|
69
|
+
waiting.splice(0).forEach(function (id) {
|
|
70
|
+
window.parent.postMessage({ type: "zaisma:picture", id: id, picture: pictureUrl }, site);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Copy inside the game's own frame callback: after it has drawn, before the browser clears a WebGL canvas.
|
|
75
|
+
var nativeFrame = window.requestAnimationFrame;
|
|
76
|
+
if (nativeFrame) {
|
|
77
|
+
window.requestAnimationFrame = function (callback) {
|
|
78
|
+
return nativeFrame.call(window, function (frameTimeMs) {
|
|
79
|
+
try {
|
|
80
|
+
callback(frameTimeMs);
|
|
81
|
+
} finally {
|
|
82
|
+
answer();
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
window.addEventListener("message", function (event) {
|
|
89
|
+
if (event.origin !== site || event.source !== window.parent || !event.data || event.data.type !== "zaisma:picture-request") return;
|
|
90
|
+
var id = String(event.data.id);
|
|
91
|
+
waiting.push(id);
|
|
92
|
+
// A game that isn't drawing right now (paused, between turns) is copied straight away.
|
|
93
|
+
setTimeout(function () {
|
|
94
|
+
if (waiting.indexOf(id) !== -1) answer();
|
|
95
|
+
}, 200);
|
|
96
|
+
});
|
|
97
|
+
})();
|
package/sdk/relay.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Zaisma's relay. Zaisma adds this first to every game page. A connection between two players'
|
|
3
|
+
* browsers (WebRTC, what PeerJS and Godot use) would show each the other's internet address, so
|
|
4
|
+
* every one goes through Cloudflare's relay (TURN) instead: players only ever see Cloudflare's
|
|
5
|
+
* address, and connections also get through school and office networks where direct ones fail.
|
|
6
|
+
* The game's own WebRTC code works unchanged: this wraps RTCPeerConnection so each connection uses
|
|
7
|
+
* only the relay, with Zaisma's relay servers, whatever servers the game asks for (the game's own
|
|
8
|
+
* relay stays when it's one of the game's listed servers). Plain JavaScript, no dependencies, MIT licensed.
|
|
9
|
+
*/
|
|
10
|
+
(function (window) {
|
|
11
|
+
"use strict";
|
|
12
|
+
var config = typeof __ZAISMA_RELAY__ === "object" && __ZAISMA_RELAY__ ? __ZAISMA_RELAY__ : {};
|
|
13
|
+
var NAMES = ["RTCPeerConnection", "webkitRTCPeerConnection"];
|
|
14
|
+
var ownRelayHosts = (config.ownRelayHosts || []).map(function (host) { return String(host).toLowerCase(); });
|
|
15
|
+
/** Ask again this long after the relay couldn't be had, not on every connection. */
|
|
16
|
+
var RETRY_AFTER_MS = 60 * 1000;
|
|
17
|
+
/** Credentials with less left than this are fetched anew. */
|
|
18
|
+
var RENEW_BEFORE_MS = 10 * 60 * 1000;
|
|
19
|
+
var storageKey = "zaisma:relay:" + config.game;
|
|
20
|
+
|
|
21
|
+
/** What this window wrapped: our getters (so nothing is wrapped twice), and each original's wrapper. */
|
|
22
|
+
var getters = new WeakSet();
|
|
23
|
+
var wrappers = new WeakMap();
|
|
24
|
+
/** The configuration the game asked each connection for, and the relay servers it was given. */
|
|
25
|
+
var askedConfigurations = new WeakMap();
|
|
26
|
+
var givenServers = new WeakMap();
|
|
27
|
+
|
|
28
|
+
// ---- Zaisma's relay servers for this tab ----
|
|
29
|
+
|
|
30
|
+
var kept = null; // { iceServers, expiresAtMs }
|
|
31
|
+
var fetching = null;
|
|
32
|
+
var failedAtMs = 0;
|
|
33
|
+
var hasWarned = false;
|
|
34
|
+
|
|
35
|
+
function warn(message) {
|
|
36
|
+
if (hasWarned) return;
|
|
37
|
+
hasWarned = true;
|
|
38
|
+
window.console.warn("Zaisma: connections between players go only through Zaisma's relay, so they don't connect right now. " + message);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isFresh(credentials) {
|
|
42
|
+
return Boolean(credentials && credentials.iceServers && credentials.expiresAtMs - Date.now() > RENEW_BEFORE_MS);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function storedCredentials() {
|
|
46
|
+
try {
|
|
47
|
+
var stored = JSON.parse(window.sessionStorage.getItem(storageKey) || "null");
|
|
48
|
+
return isFresh(stored) ? stored : null;
|
|
49
|
+
} catch (error) {
|
|
50
|
+
return null; // storage off: fetch new ones
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** The game page's key to player passes, which also gives relay credentials (the SDK asks for it the same way). */
|
|
55
|
+
function roomKey() {
|
|
56
|
+
return new Promise(function (resolve, reject) {
|
|
57
|
+
if (!config.site || !config.rooms || window.parent === window) return reject(new Error("Connections between players work when the game is played on Zaisma."));
|
|
58
|
+
var timerNumber = window.setTimeout(function () {
|
|
59
|
+
window.removeEventListener("message", onMessage);
|
|
60
|
+
reject(new Error("Zaisma couldn't check this browser. Reload the game to try again."));
|
|
61
|
+
}, 30000);
|
|
62
|
+
function onMessage(event) {
|
|
63
|
+
if (event.origin !== config.site || event.source !== window.parent || !event.data || event.data.type !== "zaisma:room-key") return;
|
|
64
|
+
window.removeEventListener("message", onMessage);
|
|
65
|
+
window.clearTimeout(timerNumber);
|
|
66
|
+
if (typeof event.data.key === "string") resolve(event.data.key);
|
|
67
|
+
else reject(new Error("Zaisma couldn't check this browser, or this game doesn't use online play. Reload the game to try again."));
|
|
68
|
+
}
|
|
69
|
+
window.addEventListener("message", onMessage);
|
|
70
|
+
window.parent.postMessage({ type: "zaisma:room-key" }, config.site);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Zaisma's relay servers, as iceServers: [] when there are none to be had (connections then don't connect, rather than go direct). */
|
|
75
|
+
function relayServers() {
|
|
76
|
+
if (isFresh(kept)) return Promise.resolve(kept.iceServers);
|
|
77
|
+
var stored = storedCredentials();
|
|
78
|
+
if (stored) {
|
|
79
|
+
kept = stored;
|
|
80
|
+
return Promise.resolve(kept.iceServers);
|
|
81
|
+
}
|
|
82
|
+
if (Date.now() - failedAtMs < RETRY_AFTER_MS) return Promise.resolve([]);
|
|
83
|
+
if (!fetching) {
|
|
84
|
+
fetching = roomKey()
|
|
85
|
+
.then(function (key) {
|
|
86
|
+
// text/plain keeps this a simple request, so the browser needs no preflight.
|
|
87
|
+
return window.fetch(config.rooms + "/v1/relay", { method: "POST", headers: { "Content-Type": "text/plain" }, body: JSON.stringify({ gameSlug: config.game, roomKey: key }) });
|
|
88
|
+
})
|
|
89
|
+
.then(function (response) {
|
|
90
|
+
return response.json().then(function (answer) {
|
|
91
|
+
if (!response.ok || !answer || !Array.isArray(answer.iceServers)) throw new Error((answer && answer.error && answer.error.message) || "Zaisma's relay didn't answer.");
|
|
92
|
+
return answer;
|
|
93
|
+
});
|
|
94
|
+
})
|
|
95
|
+
.then(
|
|
96
|
+
function (answer) {
|
|
97
|
+
fetching = null;
|
|
98
|
+
kept = { iceServers: answer.iceServers, expiresAtMs: Date.parse(answer.expiresAt) };
|
|
99
|
+
try { window.sessionStorage.setItem(storageKey, JSON.stringify(kept)); } catch (error) { /* fine */ }
|
|
100
|
+
return kept.iceServers;
|
|
101
|
+
},
|
|
102
|
+
function (error) {
|
|
103
|
+
fetching = null;
|
|
104
|
+
failedAtMs = Date.now();
|
|
105
|
+
warn(error && error.message ? error.message : "");
|
|
106
|
+
return [];
|
|
107
|
+
},
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
return fetching;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ---- What each connection is given ----
|
|
114
|
+
|
|
115
|
+
/** The game's own relay servers among its listed servers: turn: and turns: addresses on those hosts, nothing else. */
|
|
116
|
+
function ownRelays(iceServers) {
|
|
117
|
+
var out = [];
|
|
118
|
+
(Array.isArray(iceServers) ? iceServers : []).forEach(function (server) {
|
|
119
|
+
if (!server || typeof server !== "object") return;
|
|
120
|
+
var urls = [].concat(server.urls !== undefined ? server.urls : server.url !== undefined ? server.url : []).filter(function (address) {
|
|
121
|
+
var host = /^turns?:(\[[^\]]+\]|[^:?\/]+)/i.exec(String(address));
|
|
122
|
+
return host !== null && ownRelayHosts.indexOf(host[1].toLowerCase()) !== -1;
|
|
123
|
+
});
|
|
124
|
+
if (urls.length) out.push({ urls: urls, username: server.username, credential: server.credential });
|
|
125
|
+
});
|
|
126
|
+
return out;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** A configuration from base that only relays, through Zaisma's servers and the game's own relay. */
|
|
130
|
+
function relayedConfiguration(base, asked, servers) {
|
|
131
|
+
var out = {};
|
|
132
|
+
for (var name in base) if (Object.prototype.hasOwnProperty.call(base, name)) out[name] = base[name];
|
|
133
|
+
out.iceTransportPolicy = "relay";
|
|
134
|
+
out.iceServers = servers.concat(ownRelays(asked.iceServers));
|
|
135
|
+
// Gathering ahead of time waits until there are servers to gather from.
|
|
136
|
+
if (!servers.length) out.iceCandidatePoolSize = 0;
|
|
137
|
+
else if (asked.iceCandidatePoolSize !== undefined) out.iceCandidatePoolSize = asked.iceCandidatePoolSize;
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function define(target, name, method) {
|
|
142
|
+
Object.defineProperty(target, name, { value: method, writable: true, configurable: true, enumerable: false });
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** The wrapper for one window's RTCPeerConnection, and its prototype's calls made to relay first. */
|
|
146
|
+
function wrap(Native) {
|
|
147
|
+
var proto = Native.prototype;
|
|
148
|
+
var nativeSetConfiguration = proto.setConfiguration;
|
|
149
|
+
var nativeGetConfiguration = proto.getConfiguration;
|
|
150
|
+
|
|
151
|
+
function RTCPeerConnection(configuration) {
|
|
152
|
+
if (!new.target) throw new TypeError("Failed to construct 'RTCPeerConnection': Please use the 'new' operator.");
|
|
153
|
+
var asked = configuration && typeof configuration === "object" ? configuration : {};
|
|
154
|
+
var servers = isFresh(kept) ? kept.iceServers : [];
|
|
155
|
+
var parts = Array.prototype.slice.call(arguments);
|
|
156
|
+
parts[0] = relayedConfiguration(asked, asked, servers);
|
|
157
|
+
var connection = Reflect.construct(Native, parts, new.target);
|
|
158
|
+
askedConfigurations.set(connection, asked);
|
|
159
|
+
if (servers.length) givenServers.set(connection, servers);
|
|
160
|
+
// Fetched now, so they're there by the game's first offer or answer.
|
|
161
|
+
if (!servers.length) relayServers();
|
|
162
|
+
return connection;
|
|
163
|
+
}
|
|
164
|
+
RTCPeerConnection.prototype = proto;
|
|
165
|
+
Object.setPrototypeOf(RTCPeerConnection, Native);
|
|
166
|
+
define(proto, "constructor", RTCPeerConnection);
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Before a connection gathers addresses (its first offer or answer): Zaisma's relay servers, and
|
|
170
|
+
* a check that it only relays. Covers every connection in this window, however it was made.
|
|
171
|
+
*/
|
|
172
|
+
function relayFirst(connection) {
|
|
173
|
+
return relayServers().then(function (servers) {
|
|
174
|
+
if (connection.signalingState === "closed") return; // the call itself says so
|
|
175
|
+
var current = nativeGetConfiguration.call(connection);
|
|
176
|
+
// Servers it already has stay when new ones couldn't be had.
|
|
177
|
+
var use = servers.length ? servers : givenServers.get(connection) || [];
|
|
178
|
+
if (givenServers.get(connection) !== use || current.iceTransportPolicy !== "relay") {
|
|
179
|
+
try {
|
|
180
|
+
nativeSetConfiguration.call(connection, relayedConfiguration(current, askedConfigurations.get(connection) || current, use));
|
|
181
|
+
givenServers.set(connection, use);
|
|
182
|
+
} catch (error) {
|
|
183
|
+
/* checked just below */
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (nativeGetConfiguration.call(connection).iceTransportPolicy !== "relay") {
|
|
187
|
+
throw new DOMException("On Zaisma, connections between players go only through Zaisma's relay, and this one couldn't be set to.", "InvalidStateError");
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
["createOffer", "createAnswer", "setLocalDescription"].forEach(function (name) {
|
|
192
|
+
var native = proto[name];
|
|
193
|
+
if (typeof native !== "function") return;
|
|
194
|
+
define(proto, name, function () {
|
|
195
|
+
var connection = this;
|
|
196
|
+
var parts = arguments;
|
|
197
|
+
return relayFirst(connection).then(function () { return native.apply(connection, parts); });
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
if (typeof nativeSetConfiguration === "function") {
|
|
201
|
+
// A game changing its servers keeps Zaisma's relay.
|
|
202
|
+
define(proto, "setConfiguration", function (configuration) {
|
|
203
|
+
var asked = configuration && typeof configuration === "object" ? configuration : {};
|
|
204
|
+
askedConfigurations.set(this, asked);
|
|
205
|
+
var servers = givenServers.get(this) || [];
|
|
206
|
+
return nativeSetConfiguration.call(this, relayedConfiguration(asked, asked, servers));
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
wrappers.set(Native, RTCPeerConnection);
|
|
210
|
+
return RTCPeerConnection;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ---- Every window the game can reach ----
|
|
214
|
+
|
|
215
|
+
function install(target) {
|
|
216
|
+
try {
|
|
217
|
+
if (!target) return;
|
|
218
|
+
NAMES.forEach(function (name) {
|
|
219
|
+
var descriptor = Object.getOwnPropertyDescriptor(target, name);
|
|
220
|
+
if (!descriptor || (descriptor.get && getters.has(descriptor.get))) return;
|
|
221
|
+
var Native = target[name];
|
|
222
|
+
if (typeof Native !== "function") return;
|
|
223
|
+
var Relayed = wrappers.get(Native) || wrap(Native);
|
|
224
|
+
var current = Relayed;
|
|
225
|
+
var get = function () { return current; };
|
|
226
|
+
getters.add(get);
|
|
227
|
+
// Libraries like webrtc-adapter put their own wrapper around this one; nothing puts the original back.
|
|
228
|
+
Object.defineProperty(target, name, { configurable: true, enumerable: false, get: get, set: function (assigned) { current = assigned === Native ? Relayed : assigned; } });
|
|
229
|
+
});
|
|
230
|
+
watchFrames(target);
|
|
231
|
+
} catch (error) {
|
|
232
|
+
/* another site's window: nothing of it is reachable from here */
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** A frame the game makes has its own RTCPeerConnection: it's wrapped the moment the game reaches into the frame. */
|
|
237
|
+
function watchFrames(target) {
|
|
238
|
+
["HTMLIFrameElement", "HTMLFrameElement", "HTMLObjectElement"].forEach(function (name) {
|
|
239
|
+
var Element = target[name];
|
|
240
|
+
if (typeof Element !== "function") return;
|
|
241
|
+
["contentWindow", "contentDocument"].forEach(function (property) {
|
|
242
|
+
var descriptor = Object.getOwnPropertyDescriptor(Element.prototype, property);
|
|
243
|
+
if (!descriptor || !descriptor.get || getters.has(descriptor.get)) return;
|
|
244
|
+
var nativeGet = descriptor.get;
|
|
245
|
+
var get = function () {
|
|
246
|
+
var reached = nativeGet.call(this);
|
|
247
|
+
if (reached) install(property === "contentDocument" ? reached.defaultView : reached);
|
|
248
|
+
return reached;
|
|
249
|
+
};
|
|
250
|
+
getters.add(get);
|
|
251
|
+
Object.defineProperty(Element.prototype, property, { configurable: true, enumerable: descriptor.enumerable, get: get });
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
var nativeOpen = target.open;
|
|
255
|
+
if (typeof nativeOpen === "function" && !getters.has(nativeOpen)) {
|
|
256
|
+
var open = function () {
|
|
257
|
+
var opened = nativeOpen.apply(this, arguments);
|
|
258
|
+
install(opened);
|
|
259
|
+
return opened;
|
|
260
|
+
};
|
|
261
|
+
getters.add(open);
|
|
262
|
+
target.open = open;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
install(window);
|
|
267
|
+
// A game that plays online gets its relay servers while it loads, so its first connection doesn't wait.
|
|
268
|
+
if (config.fetchEarly && NAMES.some(function (name) { return typeof window[name] === "function"; })) relayServers();
|
|
269
|
+
})(window);
|