hbengine 0.2.3
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 +20 -0
- package/engine-cli/connect.mjs +73 -0
- package/engine-cli/hbengine.mjs +565 -0
- package/engine-cli/package.json +1 -0
- package/engine-cli/templates.mjs +14 -0
- package/engine-conformance/analyse-worker.mjs +12 -0
- package/engine-conformance/analyse.mjs +4458 -0
- package/engine-conformance/bundle.mjs +208 -0
- package/engine-conformance/clock.mjs +294 -0
- package/engine-conformance/csp.go +56 -0
- package/engine-conformance/csp.json +6 -0
- package/engine-conformance/csp.mjs +20 -0
- package/engine-conformance/gzip-sizes.mjs +9 -0
- package/engine-conformance/package.json +1 -0
- package/engine-conformance/picture.mjs +55 -0
- package/engine-conformance/report.go +349 -0
- package/engine-conformance/run.mjs +1217 -0
- package/engine-conformance/scan.mjs +476 -0
- package/engine-conformance/testdata/clock-overlay/bundle/assets.json +1 -0
- package/engine-conformance/testdata/clock-overlay/bundle/game.css +17 -0
- package/engine-conformance/testdata/clock-overlay/bundle/game.js +83 -0
- package/engine-conformance/testdata/clock-overlay/bundle/hbtest.json +4 -0
- package/engine-conformance/testdata/clock-overlay/bundle/index.html +34 -0
- package/engine-conformance/testdata/clock-probe/bundle/assets.json +1 -0
- package/engine-conformance/testdata/clock-probe/bundle/game.css +17 -0
- package/engine-conformance/testdata/clock-probe/bundle/game.js +65 -0
- package/engine-conformance/testdata/clock-probe/bundle/hbtest.json +4 -0
- package/engine-conformance/testdata/clock-probe/bundle/index.html +35 -0
- package/engine-conformance/testdata/clock-probe/bundle/probe-worker.js +46 -0
- package/engine-conformance/testdata/clock-probe/bundle/probe.js +210 -0
- package/engine-conformance/testdata/clock-probe/math/dice.tsv +100 -0
- package/engine-conformance/testdata/clock-probe/math/manifest.json +2925 -0
- package/engine-conformance/testdata/clock-worker/bundle/assets.json +1 -0
- package/engine-conformance/testdata/clock-worker/bundle/clock-worker.js +14 -0
- package/engine-conformance/testdata/clock-worker/bundle/game.css +17 -0
- package/engine-conformance/testdata/clock-worker/bundle/game.js +61 -0
- package/engine-conformance/testdata/clock-worker/bundle/hbtest.json +4 -0
- package/engine-conformance/testdata/clock-worker/bundle/index.html +35 -0
- package/engine-conformance/testdata/clock-worker/math/dice.tsv +100 -0
- package/engine-conformance/testdata/clock-worker/math/manifest.json +2925 -0
- package/engine-conformance/travel.mjs +1209 -0
- package/engine-conformance/vendor/acorn/LICENSE +21 -0
- package/engine-conformance/vendor/acorn/acorn.mjs +6266 -0
- package/engine-conformance/wasm-releases.json +168 -0
- package/engine-examples/README.md +23 -0
- package/engine-examples/coin/AGENTS.md +3 -0
- package/engine-examples/coin/README.md +7 -0
- package/engine-examples/coin/bundle/assets.json +1 -0
- package/engine-examples/coin/bundle/game.css +5 -0
- package/engine-examples/coin/bundle/game.js +19 -0
- package/engine-examples/coin/bundle/hbtest.json +5 -0
- package/engine-examples/coin/bundle/index.html +16 -0
- package/engine-examples/coin/math/coin.tsv +2 -0
- package/engine-examples/coin/math/manifest.json +17 -0
- package/engine-examples/crash/AGENTS.md +3 -0
- package/engine-examples/crash/README.md +14 -0
- package/engine-examples/crash/bundle/assets.json +1 -0
- package/engine-examples/crash/bundle/game.css +8 -0
- package/engine-examples/crash/bundle/game.js +90 -0
- package/engine-examples/crash/bundle/hbtest.json +5 -0
- package/engine-examples/crash/bundle/index.html +20 -0
- package/engine-examples/crash/math/crash-01.tsv +2 -0
- package/engine-examples/crash/math/crash-02.tsv +2 -0
- package/engine-examples/crash/math/crash-03.tsv +2 -0
- package/engine-examples/crash/math/crash-04.tsv +2 -0
- package/engine-examples/crash/math/crash-05.tsv +2 -0
- package/engine-examples/crash/math/crash-06.tsv +2 -0
- package/engine-examples/crash/math/crash-07.tsv +2 -0
- package/engine-examples/crash/math/crash-08.tsv +2 -0
- package/engine-examples/crash/math/crash-09.tsv +2 -0
- package/engine-examples/crash/math/crash-10.tsv +2 -0
- package/engine-examples/crash/math/crash-11.tsv +2 -0
- package/engine-examples/crash/math/crash-12.tsv +2 -0
- package/engine-examples/crash/math/crash-13.tsv +2 -0
- package/engine-examples/crash/math/crash-14.tsv +2 -0
- package/engine-examples/crash/math/crash-15.tsv +2 -0
- package/engine-examples/crash/math/crash-16.tsv +2 -0
- package/engine-examples/crash/math/crash-17.tsv +2 -0
- package/engine-examples/crash/math/crash-18.tsv +2 -0
- package/engine-examples/crash/math/crash-19.tsv +2 -0
- package/engine-examples/crash/math/crash-20.tsv +2 -0
- package/engine-examples/crash/math/crash-21.tsv +2 -0
- package/engine-examples/crash/math/crash-22.tsv +2 -0
- package/engine-examples/crash/math/crash-23.tsv +2 -0
- package/engine-examples/crash/math/crash-24.tsv +2 -0
- package/engine-examples/crash/math/crash-25.tsv +2 -0
- package/engine-examples/crash/math/crash-26.tsv +2 -0
- package/engine-examples/crash/math/crash-27.tsv +2 -0
- package/engine-examples/crash/math/crash-28.tsv +2 -0
- package/engine-examples/crash/math/crash-29.tsv +2 -0
- package/engine-examples/crash/math/crash-30.tsv +2 -0
- package/engine-examples/crash/math/crash-31.tsv +2 -0
- package/engine-examples/crash/math/crash-32.tsv +2 -0
- package/engine-examples/crash/math/crash-33.tsv +2 -0
- package/engine-examples/crash/math/crash-34.tsv +2 -0
- package/engine-examples/crash/math/crash-35.tsv +2 -0
- package/engine-examples/crash/math/crash-36.tsv +2 -0
- package/engine-examples/crash/math/crash-37.tsv +2 -0
- package/engine-examples/crash/math/crash-38.tsv +2 -0
- package/engine-examples/crash/math/crash-39.tsv +2 -0
- package/engine-examples/crash/math/crash-40.tsv +2 -0
- package/engine-examples/crash/math/crash-41.tsv +2 -0
- package/engine-examples/crash/math/crash-42.tsv +2 -0
- package/engine-examples/crash/math/crash-43.tsv +2 -0
- package/engine-examples/crash/math/crash-44.tsv +2 -0
- package/engine-examples/crash/math/crash-45.tsv +2 -0
- package/engine-examples/crash/math/crash-46.tsv +2 -0
- package/engine-examples/crash/math/crash-47.tsv +2 -0
- package/engine-examples/crash/math/crash-48.tsv +2 -0
- package/engine-examples/crash/math/crash-49.tsv +2 -0
- package/engine-examples/crash/math/crash-50.tsv +2 -0
- package/engine-examples/crash/math/crash-51.tsv +2 -0
- package/engine-examples/crash/math/crash-52.tsv +2 -0
- package/engine-examples/crash/math/crash-53.tsv +2 -0
- package/engine-examples/crash/math/crash-54.tsv +2 -0
- package/engine-examples/crash/math/crash-55.tsv +2 -0
- package/engine-examples/crash/math/crash-56.tsv +2 -0
- package/engine-examples/crash/math/crash-57.tsv +2 -0
- package/engine-examples/crash/math/crash-58.tsv +2 -0
- package/engine-examples/crash/math/crash-59.tsv +2 -0
- package/engine-examples/crash/math/crash-60.tsv +2 -0
- package/engine-examples/crash/math/crash-61.tsv +2 -0
- package/engine-examples/crash/math/crash-62.tsv +2 -0
- package/engine-examples/crash/math/crash-63.tsv +2 -0
- package/engine-examples/crash/math/crash-64.tsv +2 -0
- package/engine-examples/crash/math/gen.mjs +31 -0
- package/engine-examples/crash/math/manifest.json +1 -0
- package/engine-examples/dice/AGENTS.md +3 -0
- package/engine-examples/dice/README.md +25 -0
- package/engine-examples/dice/bundle/assets.json +1 -0
- package/engine-examples/dice/bundle/game.css +17 -0
- package/engine-examples/dice/bundle/game.js +61 -0
- package/engine-examples/dice/bundle/hbtest.json +4 -0
- package/engine-examples/dice/bundle/index.html +34 -0
- package/engine-examples/dice/math/dice.tsv +100 -0
- package/engine-examples/dice/math/manifest.json +2925 -0
- package/engine-examples/keno/AGENTS.md +3 -0
- package/engine-examples/keno/README.md +11 -0
- package/engine-examples/keno/bundle/assets.json +1 -0
- package/engine-examples/keno/bundle/game.css +14 -0
- package/engine-examples/keno/bundle/game.js +86 -0
- package/engine-examples/keno/bundle/hbtest.json +4 -0
- package/engine-examples/keno/bundle/i18n.json +342 -0
- package/engine-examples/keno/bundle/index.html +20 -0
- package/engine-examples/keno/bundle/model.json +1 -0
- package/engine-examples/keno/math/gen.mjs +35 -0
- package/engine-examples/keno/math/keno-picks-1.tsv +2 -0
- package/engine-examples/keno/math/keno-picks-10.tsv +11 -0
- package/engine-examples/keno/math/keno-picks-2.tsv +3 -0
- package/engine-examples/keno/math/keno-picks-3.tsv +4 -0
- package/engine-examples/keno/math/keno-picks-4.tsv +5 -0
- package/engine-examples/keno/math/keno-picks-5.tsv +6 -0
- package/engine-examples/keno/math/keno-picks-6.tsv +7 -0
- package/engine-examples/keno/math/keno-picks-7.tsv +8 -0
- package/engine-examples/keno/math/keno-picks-8.tsv +9 -0
- package/engine-examples/keno/math/keno-picks-9.tsv +10 -0
- package/engine-examples/keno/math/manifest.json +1 -0
- package/engine-examples/keno/math-kit/pack.mjs +165 -0
- package/engine-examples/keno/math-kit/package.json +8 -0
- package/engine-examples/keno/math-kit/slot.mjs +94 -0
- package/engine-examples/keno/math-kit/tumble.mjs +94 -0
- package/engine-examples/lines/AGENTS.md +3 -0
- package/engine-examples/lines/README.md +20 -0
- package/engine-examples/lines/bundle/assets.json +1 -0
- package/engine-examples/lines/bundle/game.css +16 -0
- package/engine-examples/lines/bundle/game.js +125 -0
- package/engine-examples/lines/bundle/hbtest.json +4 -0
- package/engine-examples/lines/bundle/i18n.json +242 -0
- package/engine-examples/lines/bundle/index.html +23 -0
- package/engine-examples/lines/bundle/model.json +1 -0
- package/engine-examples/lines/math/gen.mjs +54 -0
- package/engine-examples/lines/math-kit/pack.mjs +165 -0
- package/engine-examples/lines/math-kit/package.json +8 -0
- package/engine-examples/lines/math-kit/slot.mjs +94 -0
- package/engine-examples/lines/math-kit/tumble.mjs +94 -0
- package/engine-examples/mines/AGENTS.md +3 -0
- package/engine-examples/mines/README.md +13 -0
- package/engine-examples/mines/bundle/assets.json +1 -0
- package/engine-examples/mines/bundle/game.css +12 -0
- package/engine-examples/mines/bundle/game.js +58 -0
- package/engine-examples/mines/bundle/hbtest.json +8 -0
- package/engine-examples/mines/bundle/index.html +16 -0
- package/engine-examples/mines/math/gen.mjs +40 -0
- package/engine-examples/mines/math/manifest.json +1 -0
- package/engine-examples/mines/math/mines-1-1.tsv +2 -0
- package/engine-examples/mines/math/mines-1-2.tsv +2 -0
- package/engine-examples/mines/math/mines-1-22.tsv +2 -0
- package/engine-examples/mines/math/mines-1-23.tsv +2 -0
- package/engine-examples/mines/math/mines-1-24.tsv +2 -0
- package/engine-examples/mines/math/mines-1-3.tsv +2 -0
- package/engine-examples/mines/math/mines-10-1.tsv +2 -0
- package/engine-examples/mines/math/mines-10-10.tsv +2 -0
- package/engine-examples/mines/math/mines-10-11.tsv +2 -0
- package/engine-examples/mines/math/mines-10-12.tsv +2 -0
- package/engine-examples/mines/math/mines-10-13.tsv +2 -0
- package/engine-examples/mines/math/mines-10-14.tsv +2 -0
- package/engine-examples/mines/math/mines-10-15.tsv +2 -0
- package/engine-examples/mines/math/mines-10-2.tsv +2 -0
- package/engine-examples/mines/math/mines-10-3.tsv +2 -0
- package/engine-examples/mines/math/mines-10-4.tsv +2 -0
- package/engine-examples/mines/math/mines-10-5.tsv +2 -0
- package/engine-examples/mines/math/mines-10-6.tsv +2 -0
- package/engine-examples/mines/math/mines-10-7.tsv +2 -0
- package/engine-examples/mines/math/mines-10-8.tsv +2 -0
- package/engine-examples/mines/math/mines-10-9.tsv +2 -0
- package/engine-examples/mines/math/mines-11-1.tsv +2 -0
- package/engine-examples/mines/math/mines-11-10.tsv +2 -0
- package/engine-examples/mines/math/mines-11-11.tsv +2 -0
- package/engine-examples/mines/math/mines-11-12.tsv +2 -0
- package/engine-examples/mines/math/mines-11-13.tsv +2 -0
- package/engine-examples/mines/math/mines-11-14.tsv +2 -0
- package/engine-examples/mines/math/mines-11-2.tsv +2 -0
- package/engine-examples/mines/math/mines-11-3.tsv +2 -0
- package/engine-examples/mines/math/mines-11-4.tsv +2 -0
- package/engine-examples/mines/math/mines-11-5.tsv +2 -0
- package/engine-examples/mines/math/mines-11-6.tsv +2 -0
- package/engine-examples/mines/math/mines-11-7.tsv +2 -0
- package/engine-examples/mines/math/mines-11-8.tsv +2 -0
- package/engine-examples/mines/math/mines-11-9.tsv +2 -0
- package/engine-examples/mines/math/mines-12-1.tsv +2 -0
- package/engine-examples/mines/math/mines-12-10.tsv +2 -0
- package/engine-examples/mines/math/mines-12-11.tsv +2 -0
- package/engine-examples/mines/math/mines-12-12.tsv +2 -0
- package/engine-examples/mines/math/mines-12-13.tsv +2 -0
- package/engine-examples/mines/math/mines-12-2.tsv +2 -0
- package/engine-examples/mines/math/mines-12-3.tsv +2 -0
- package/engine-examples/mines/math/mines-12-4.tsv +2 -0
- package/engine-examples/mines/math/mines-12-5.tsv +2 -0
- package/engine-examples/mines/math/mines-12-6.tsv +2 -0
- package/engine-examples/mines/math/mines-12-7.tsv +2 -0
- package/engine-examples/mines/math/mines-12-8.tsv +2 -0
- package/engine-examples/mines/math/mines-12-9.tsv +2 -0
- package/engine-examples/mines/math/mines-13-1.tsv +2 -0
- package/engine-examples/mines/math/mines-13-10.tsv +2 -0
- package/engine-examples/mines/math/mines-13-11.tsv +2 -0
- package/engine-examples/mines/math/mines-13-12.tsv +2 -0
- package/engine-examples/mines/math/mines-13-2.tsv +2 -0
- package/engine-examples/mines/math/mines-13-3.tsv +2 -0
- package/engine-examples/mines/math/mines-13-4.tsv +2 -0
- package/engine-examples/mines/math/mines-13-5.tsv +2 -0
- package/engine-examples/mines/math/mines-13-6.tsv +2 -0
- package/engine-examples/mines/math/mines-13-7.tsv +2 -0
- package/engine-examples/mines/math/mines-13-8.tsv +2 -0
- package/engine-examples/mines/math/mines-13-9.tsv +2 -0
- package/engine-examples/mines/math/mines-14-1.tsv +2 -0
- package/engine-examples/mines/math/mines-14-10.tsv +2 -0
- package/engine-examples/mines/math/mines-14-11.tsv +2 -0
- package/engine-examples/mines/math/mines-14-2.tsv +2 -0
- package/engine-examples/mines/math/mines-14-3.tsv +2 -0
- package/engine-examples/mines/math/mines-14-4.tsv +2 -0
- package/engine-examples/mines/math/mines-14-5.tsv +2 -0
- package/engine-examples/mines/math/mines-14-6.tsv +2 -0
- package/engine-examples/mines/math/mines-14-7.tsv +2 -0
- package/engine-examples/mines/math/mines-14-8.tsv +2 -0
- package/engine-examples/mines/math/mines-14-9.tsv +2 -0
- package/engine-examples/mines/math/mines-15-1.tsv +2 -0
- package/engine-examples/mines/math/mines-15-10.tsv +2 -0
- package/engine-examples/mines/math/mines-15-2.tsv +2 -0
- package/engine-examples/mines/math/mines-15-3.tsv +2 -0
- package/engine-examples/mines/math/mines-15-4.tsv +2 -0
- package/engine-examples/mines/math/mines-15-5.tsv +2 -0
- package/engine-examples/mines/math/mines-15-6.tsv +2 -0
- package/engine-examples/mines/math/mines-15-7.tsv +2 -0
- package/engine-examples/mines/math/mines-15-8.tsv +2 -0
- package/engine-examples/mines/math/mines-15-9.tsv +2 -0
- package/engine-examples/mines/math/mines-16-1.tsv +2 -0
- package/engine-examples/mines/math/mines-16-2.tsv +2 -0
- package/engine-examples/mines/math/mines-16-3.tsv +2 -0
- package/engine-examples/mines/math/mines-16-4.tsv +2 -0
- package/engine-examples/mines/math/mines-16-5.tsv +2 -0
- package/engine-examples/mines/math/mines-16-6.tsv +2 -0
- package/engine-examples/mines/math/mines-16-7.tsv +2 -0
- package/engine-examples/mines/math/mines-16-8.tsv +2 -0
- package/engine-examples/mines/math/mines-16-9.tsv +2 -0
- package/engine-examples/mines/math/mines-17-1.tsv +2 -0
- package/engine-examples/mines/math/mines-17-2.tsv +2 -0
- package/engine-examples/mines/math/mines-17-3.tsv +2 -0
- package/engine-examples/mines/math/mines-17-4.tsv +2 -0
- package/engine-examples/mines/math/mines-17-5.tsv +2 -0
- package/engine-examples/mines/math/mines-17-6.tsv +2 -0
- package/engine-examples/mines/math/mines-17-7.tsv +2 -0
- package/engine-examples/mines/math/mines-17-8.tsv +2 -0
- package/engine-examples/mines/math/mines-18-1.tsv +2 -0
- package/engine-examples/mines/math/mines-18-2.tsv +2 -0
- package/engine-examples/mines/math/mines-18-3.tsv +2 -0
- package/engine-examples/mines/math/mines-18-4.tsv +2 -0
- package/engine-examples/mines/math/mines-18-5.tsv +2 -0
- package/engine-examples/mines/math/mines-18-6.tsv +2 -0
- package/engine-examples/mines/math/mines-18-7.tsv +2 -0
- package/engine-examples/mines/math/mines-19-1.tsv +2 -0
- package/engine-examples/mines/math/mines-19-2.tsv +2 -0
- package/engine-examples/mines/math/mines-19-3.tsv +2 -0
- package/engine-examples/mines/math/mines-19-4.tsv +2 -0
- package/engine-examples/mines/math/mines-19-5.tsv +2 -0
- package/engine-examples/mines/math/mines-19-6.tsv +2 -0
- package/engine-examples/mines/math/mines-2-1.tsv +2 -0
- package/engine-examples/mines/math/mines-2-19.tsv +2 -0
- package/engine-examples/mines/math/mines-2-2.tsv +2 -0
- package/engine-examples/mines/math/mines-2-20.tsv +2 -0
- package/engine-examples/mines/math/mines-2-21.tsv +2 -0
- package/engine-examples/mines/math/mines-2-22.tsv +2 -0
- package/engine-examples/mines/math/mines-2-23.tsv +2 -0
- package/engine-examples/mines/math/mines-2-3.tsv +2 -0
- package/engine-examples/mines/math/mines-2-4.tsv +2 -0
- package/engine-examples/mines/math/mines-2-5.tsv +2 -0
- package/engine-examples/mines/math/mines-20-1.tsv +2 -0
- package/engine-examples/mines/math/mines-20-2.tsv +2 -0
- package/engine-examples/mines/math/mines-20-3.tsv +2 -0
- package/engine-examples/mines/math/mines-20-4.tsv +2 -0
- package/engine-examples/mines/math/mines-20-5.tsv +2 -0
- package/engine-examples/mines/math/mines-21-1.tsv +2 -0
- package/engine-examples/mines/math/mines-21-2.tsv +2 -0
- package/engine-examples/mines/math/mines-21-3.tsv +2 -0
- package/engine-examples/mines/math/mines-21-4.tsv +2 -0
- package/engine-examples/mines/math/mines-22-1.tsv +2 -0
- package/engine-examples/mines/math/mines-22-2.tsv +2 -0
- package/engine-examples/mines/math/mines-22-3.tsv +2 -0
- package/engine-examples/mines/math/mines-23-1.tsv +2 -0
- package/engine-examples/mines/math/mines-23-2.tsv +2 -0
- package/engine-examples/mines/math/mines-24-1.tsv +2 -0
- package/engine-examples/mines/math/mines-3-1.tsv +2 -0
- package/engine-examples/mines/math/mines-3-17.tsv +2 -0
- package/engine-examples/mines/math/mines-3-18.tsv +2 -0
- package/engine-examples/mines/math/mines-3-19.tsv +2 -0
- package/engine-examples/mines/math/mines-3-2.tsv +2 -0
- package/engine-examples/mines/math/mines-3-20.tsv +2 -0
- package/engine-examples/mines/math/mines-3-21.tsv +2 -0
- package/engine-examples/mines/math/mines-3-22.tsv +2 -0
- package/engine-examples/mines/math/mines-3-3.tsv +2 -0
- package/engine-examples/mines/math/mines-3-4.tsv +2 -0
- package/engine-examples/mines/math/mines-3-5.tsv +2 -0
- package/engine-examples/mines/math/mines-3-6.tsv +2 -0
- package/engine-examples/mines/math/mines-4-1.tsv +2 -0
- package/engine-examples/mines/math/mines-4-13.tsv +2 -0
- package/engine-examples/mines/math/mines-4-14.tsv +2 -0
- package/engine-examples/mines/math/mines-4-15.tsv +2 -0
- package/engine-examples/mines/math/mines-4-16.tsv +2 -0
- package/engine-examples/mines/math/mines-4-17.tsv +2 -0
- package/engine-examples/mines/math/mines-4-18.tsv +2 -0
- package/engine-examples/mines/math/mines-4-19.tsv +2 -0
- package/engine-examples/mines/math/mines-4-2.tsv +2 -0
- package/engine-examples/mines/math/mines-4-20.tsv +2 -0
- package/engine-examples/mines/math/mines-4-21.tsv +2 -0
- package/engine-examples/mines/math/mines-4-3.tsv +2 -0
- package/engine-examples/mines/math/mines-4-4.tsv +2 -0
- package/engine-examples/mines/math/mines-4-5.tsv +2 -0
- package/engine-examples/mines/math/mines-4-6.tsv +2 -0
- package/engine-examples/mines/math/mines-4-7.tsv +2 -0
- package/engine-examples/mines/math/mines-4-8.tsv +2 -0
- package/engine-examples/mines/math/mines-4-9.tsv +2 -0
- package/engine-examples/mines/math/mines-5-1.tsv +2 -0
- package/engine-examples/mines/math/mines-5-10.tsv +2 -0
- package/engine-examples/mines/math/mines-5-11.tsv +2 -0
- package/engine-examples/mines/math/mines-5-12.tsv +2 -0
- package/engine-examples/mines/math/mines-5-13.tsv +2 -0
- package/engine-examples/mines/math/mines-5-14.tsv +2 -0
- package/engine-examples/mines/math/mines-5-15.tsv +2 -0
- package/engine-examples/mines/math/mines-5-16.tsv +2 -0
- package/engine-examples/mines/math/mines-5-17.tsv +2 -0
- package/engine-examples/mines/math/mines-5-18.tsv +2 -0
- package/engine-examples/mines/math/mines-5-19.tsv +2 -0
- package/engine-examples/mines/math/mines-5-2.tsv +2 -0
- package/engine-examples/mines/math/mines-5-20.tsv +2 -0
- package/engine-examples/mines/math/mines-5-3.tsv +2 -0
- package/engine-examples/mines/math/mines-5-4.tsv +2 -0
- package/engine-examples/mines/math/mines-5-5.tsv +2 -0
- package/engine-examples/mines/math/mines-5-6.tsv +2 -0
- package/engine-examples/mines/math/mines-5-7.tsv +2 -0
- package/engine-examples/mines/math/mines-5-8.tsv +2 -0
- package/engine-examples/mines/math/mines-5-9.tsv +2 -0
- package/engine-examples/mines/math/mines-6-1.tsv +2 -0
- package/engine-examples/mines/math/mines-6-10.tsv +2 -0
- package/engine-examples/mines/math/mines-6-11.tsv +2 -0
- package/engine-examples/mines/math/mines-6-12.tsv +2 -0
- package/engine-examples/mines/math/mines-6-13.tsv +2 -0
- package/engine-examples/mines/math/mines-6-14.tsv +2 -0
- package/engine-examples/mines/math/mines-6-15.tsv +2 -0
- package/engine-examples/mines/math/mines-6-16.tsv +2 -0
- package/engine-examples/mines/math/mines-6-17.tsv +2 -0
- package/engine-examples/mines/math/mines-6-18.tsv +2 -0
- package/engine-examples/mines/math/mines-6-19.tsv +2 -0
- package/engine-examples/mines/math/mines-6-2.tsv +2 -0
- package/engine-examples/mines/math/mines-6-3.tsv +2 -0
- package/engine-examples/mines/math/mines-6-4.tsv +2 -0
- package/engine-examples/mines/math/mines-6-5.tsv +2 -0
- package/engine-examples/mines/math/mines-6-6.tsv +2 -0
- package/engine-examples/mines/math/mines-6-7.tsv +2 -0
- package/engine-examples/mines/math/mines-6-8.tsv +2 -0
- package/engine-examples/mines/math/mines-6-9.tsv +2 -0
- package/engine-examples/mines/math/mines-7-1.tsv +2 -0
- package/engine-examples/mines/math/mines-7-10.tsv +2 -0
- package/engine-examples/mines/math/mines-7-11.tsv +2 -0
- package/engine-examples/mines/math/mines-7-12.tsv +2 -0
- package/engine-examples/mines/math/mines-7-13.tsv +2 -0
- package/engine-examples/mines/math/mines-7-14.tsv +2 -0
- package/engine-examples/mines/math/mines-7-15.tsv +2 -0
- package/engine-examples/mines/math/mines-7-16.tsv +2 -0
- package/engine-examples/mines/math/mines-7-17.tsv +2 -0
- package/engine-examples/mines/math/mines-7-18.tsv +2 -0
- package/engine-examples/mines/math/mines-7-2.tsv +2 -0
- package/engine-examples/mines/math/mines-7-3.tsv +2 -0
- package/engine-examples/mines/math/mines-7-4.tsv +2 -0
- package/engine-examples/mines/math/mines-7-5.tsv +2 -0
- package/engine-examples/mines/math/mines-7-6.tsv +2 -0
- package/engine-examples/mines/math/mines-7-7.tsv +2 -0
- package/engine-examples/mines/math/mines-7-8.tsv +2 -0
- package/engine-examples/mines/math/mines-7-9.tsv +2 -0
- package/engine-examples/mines/math/mines-8-1.tsv +2 -0
- package/engine-examples/mines/math/mines-8-10.tsv +2 -0
- package/engine-examples/mines/math/mines-8-11.tsv +2 -0
- package/engine-examples/mines/math/mines-8-12.tsv +2 -0
- package/engine-examples/mines/math/mines-8-13.tsv +2 -0
- package/engine-examples/mines/math/mines-8-14.tsv +2 -0
- package/engine-examples/mines/math/mines-8-15.tsv +2 -0
- package/engine-examples/mines/math/mines-8-16.tsv +2 -0
- package/engine-examples/mines/math/mines-8-17.tsv +2 -0
- package/engine-examples/mines/math/mines-8-2.tsv +2 -0
- package/engine-examples/mines/math/mines-8-3.tsv +2 -0
- package/engine-examples/mines/math/mines-8-4.tsv +2 -0
- package/engine-examples/mines/math/mines-8-5.tsv +2 -0
- package/engine-examples/mines/math/mines-8-6.tsv +2 -0
- package/engine-examples/mines/math/mines-8-7.tsv +2 -0
- package/engine-examples/mines/math/mines-8-8.tsv +2 -0
- package/engine-examples/mines/math/mines-8-9.tsv +2 -0
- package/engine-examples/mines/math/mines-9-1.tsv +2 -0
- package/engine-examples/mines/math/mines-9-10.tsv +2 -0
- package/engine-examples/mines/math/mines-9-11.tsv +2 -0
- package/engine-examples/mines/math/mines-9-12.tsv +2 -0
- package/engine-examples/mines/math/mines-9-13.tsv +2 -0
- package/engine-examples/mines/math/mines-9-14.tsv +2 -0
- package/engine-examples/mines/math/mines-9-15.tsv +2 -0
- package/engine-examples/mines/math/mines-9-16.tsv +2 -0
- package/engine-examples/mines/math/mines-9-2.tsv +2 -0
- package/engine-examples/mines/math/mines-9-3.tsv +2 -0
- package/engine-examples/mines/math/mines-9-4.tsv +2 -0
- package/engine-examples/mines/math/mines-9-5.tsv +2 -0
- package/engine-examples/mines/math/mines-9-6.tsv +2 -0
- package/engine-examples/mines/math/mines-9-7.tsv +2 -0
- package/engine-examples/mines/math/mines-9-8.tsv +2 -0
- package/engine-examples/mines/math/mines-9-9.tsv +2 -0
- package/engine-examples/plinko/AGENTS.md +3 -0
- package/engine-examples/plinko/README.md +9 -0
- package/engine-examples/plinko/bundle/assets.json +1 -0
- package/engine-examples/plinko/bundle/game.css +5 -0
- package/engine-examples/plinko/bundle/game.js +92 -0
- package/engine-examples/plinko/bundle/hbtest.json +4 -0
- package/engine-examples/plinko/bundle/index.html +16 -0
- package/engine-examples/plinko/bundle/model.json +1 -0
- package/engine-examples/plinko/math/gen.mjs +40 -0
- package/engine-examples/plinko/math/manifest.json +1 -0
- package/engine-examples/plinko/math/plinko-12-high.tsv +13 -0
- package/engine-examples/plinko/math/plinko-12-low.tsv +13 -0
- package/engine-examples/plinko/math/plinko-12-medium.tsv +13 -0
- package/engine-examples/plinko/math/plinko-16-high.tsv +17 -0
- package/engine-examples/plinko/math/plinko-16-low.tsv +17 -0
- package/engine-examples/plinko/math/plinko-16-medium.tsv +17 -0
- package/engine-examples/plinko/math/plinko-8-high.tsv +9 -0
- package/engine-examples/plinko/math/plinko-8-low.tsv +9 -0
- package/engine-examples/plinko/math/plinko-8-medium.tsv +9 -0
- package/engine-examples/plinko/math-kit/pack.mjs +165 -0
- package/engine-examples/plinko/math-kit/package.json +8 -0
- package/engine-examples/plinko/math-kit/slot.mjs +94 -0
- package/engine-examples/plinko/math-kit/tumble.mjs +94 -0
- package/engine-examples/slot/AGENTS.md +3 -0
- package/engine-examples/slot/README.md +26 -0
- package/engine-examples/slot/bundle/assets.json +1 -0
- package/engine-examples/slot/bundle/game.css +19 -0
- package/engine-examples/slot/bundle/game.js +42 -0
- package/engine-examples/slot/bundle/hbtest.json +4 -0
- package/engine-examples/slot/bundle/index.html +23 -0
- package/engine-examples/slot/math/gen.mjs +79 -0
- package/engine-examples/slot/math/manifest.json +1 -0
- package/engine-examples/slot/math/slot.tsv +110592 -0
- package/engine-examples/tower/AGENTS.md +3 -0
- package/engine-examples/tower/README.md +21 -0
- package/engine-examples/tower/bundle/assets.json +1 -0
- package/engine-examples/tower/bundle/game.css +8 -0
- package/engine-examples/tower/bundle/game.js +37 -0
- package/engine-examples/tower/bundle/hbtest.json +5 -0
- package/engine-examples/tower/bundle/index.html +16 -0
- package/engine-examples/tower/math/floor.tsv +4 -0
- package/engine-examples/tower/math/manifest.json +61 -0
- package/engine-examples/tumble/AGENTS.md +3 -0
- package/engine-examples/tumble/README.md +18 -0
- package/engine-examples/tumble/bundle/assets.json +1 -0
- package/engine-examples/tumble/bundle/game.css +13 -0
- package/engine-examples/tumble/bundle/game.js +119 -0
- package/engine-examples/tumble/bundle/hbtest.json +4 -0
- package/engine-examples/tumble/bundle/i18n.json +122 -0
- package/engine-examples/tumble/bundle/index.html +20 -0
- package/engine-examples/tumble/bundle/model.json +1 -0
- package/engine-examples/tumble/math/gen.mjs +38 -0
- package/engine-examples/tumble/math-kit/pack.mjs +165 -0
- package/engine-examples/tumble/math-kit/package.json +8 -0
- package/engine-examples/tumble/math-kit/slot.mjs +94 -0
- package/engine-examples/tumble/math-kit/tumble.mjs +94 -0
- package/engine-examples/wheel/README.md +14 -0
- package/engine-examples/wheel/bundle/assets.json +1 -0
- package/engine-examples/wheel/bundle/game.css +7 -0
- package/engine-examples/wheel/bundle/game.js +51 -0
- package/engine-examples/wheel/bundle/hbtest.json +4 -0
- package/engine-examples/wheel/bundle/index.html +19 -0
- package/engine-examples/wheel/math/high.tsv +24 -0
- package/engine-examples/wheel/math/low.tsv +24 -0
- package/engine-examples/wheel/math/manifest.json +48 -0
- package/engine-examples/wheel/math/medium.tsv +24 -0
- package/engine-math/pack.mjs +165 -0
- package/engine-math/package.json +1 -0
- package/engine-math/slot.mjs +94 -0
- package/engine-math/tumble.mjs +94 -0
- package/engine-mcp/package.json +1 -0
- package/engine-mcp/server.mjs +151 -0
- package/engine-sdk/GUIDE.md +855 -0
- package/engine-sdk/assets.js +92 -0
- package/engine-sdk/autoplay.js +82 -0
- package/engine-sdk/bet-field.js +391 -0
- package/engine-sdk/kit/fiat-coins.js +51 -0
- package/engine-sdk/kit/numeric-input.js +116 -0
- package/engine-sdk/kit/wallet-format.js +423 -0
- package/engine-sdk/marks.js +22 -0
- package/engine-sdk/messages.js +289 -0
- package/engine-sdk/money.js +89 -0
- package/engine-sdk/package.json +1 -0
- package/engine-sdk/protocol.js +263 -0
- package/engine-sdk/sdk.d.ts +138 -0
- package/engine-sdk/sdk.js +330 -0
- package/engine-sdk/stake.js +68 -0
- package/engine-sdk/ui.d.ts +36 -0
- package/engine-sdk/ui.js +340 -0
- package/engine-shell/core/faults.js +127 -0
- package/engine-shell/core/rgs.js +193 -0
- package/engine-shell/core/router.js +917 -0
- package/engine-shell/host/boot.js +408 -0
- package/engine-shell/host/engine-v1.js +438 -0
- package/engine-shell/host/fair.js +285 -0
- package/engine-shell/host/fonts/SOURCE.json +11 -0
- package/engine-shell/host/fonts/figtree-400.woff2 +0 -0
- package/engine-shell/host/fonts/figtree-500.woff2 +0 -0
- package/engine-shell/host/fonts/figtree-600.woff2 +0 -0
- package/engine-shell/host/fonts/red-hat-display-700.woff2 +0 -0
- package/engine-shell/host/index.html +230 -0
- package/engine-shell/host/page.js +809 -0
- package/engine-shell/host/sandbox-panel.js +681 -0
- package/engine-shell/host/sandbox-scenarios.js +554 -0
- package/engine-shell/host/site-bridge.js +87 -0
- package/engine-shell/host/strings.js +916 -0
- package/engine-shell/package.json +1 -0
- package/engine-shell/verifiers/badcat/coin.tsv +2 -0
- package/engine-shell/verifiers/badcat/dice-2.tsv +11 -0
- package/engine-shell/verifiers/badcat/dice-5.tsv +26 -0
- package/engine-shell/verifiers/badcat/dice-9.tsv +46 -0
- package/engine-shell/verifiers/badcat/manifest.json +3344 -0
- package/engine-shell/verifiers/badcat/rps.tsv +3 -0
- package/engine-shell/verifiers/badcat/triple.tsv +66 -0
- package/engine-shell/verifiers/engine-verify.js +358 -0
- package/engine-shell/verifiers/package.json +5 -0
- package/engine-shell/verifiers/parity/manifest.json +27 -0
- package/engine-shell/verifiers/parity/wide.tsv +5 -0
- package/engine-shell/verifiers/parity-vectors.json +244 -0
- package/engine-shell/verifiers/vectors.json +100 -0
- package/package.json +10 -0
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shell page: the frame the site launches for an engine game.
|
|
3
|
+
*
|
|
4
|
+
* launch URL <games host>[/housegames]/engine/play/<slug>/?otp=…¤cy=…
|
|
5
|
+
* (from House Studio's sandbox, play.housestudio.io: &ticket=… too)
|
|
6
|
+
* 1. the one-time code is taken off the address bar and exchanged at
|
|
7
|
+
* <prefix>/direct/session for a short player token (the House
|
|
8
|
+
* Originals' launch, unchanged);
|
|
9
|
+
* 2. the engine session is minted with it and kept in memory only;
|
|
10
|
+
* 3. the game's bundle is framed sandboxed (no network, no storage, no
|
|
11
|
+
* session) and the router connects the two;
|
|
12
|
+
* 4. the site hears the same bridge messages Coin Flip sends.
|
|
13
|
+
*
|
|
14
|
+
* Every endpoint is this page's own origin, under the path the page itself
|
|
15
|
+
* was served from (`/housegames` on the site, nothing on games.housebets.*):
|
|
16
|
+
* nothing in the query can point the shell anywhere else (the PR #211 rule).
|
|
17
|
+
*/
|
|
18
|
+
import { createRgs } from '../core/rgs.js';
|
|
19
|
+
import { createFaults } from '../core/faults.js';
|
|
20
|
+
import { createRouter } from '../core/router.js';
|
|
21
|
+
import { createSiteBridge } from './site-bridge.js';
|
|
22
|
+
import { wordsFor } from './strings.js';
|
|
23
|
+
import { moneyFor } from '../../engine-sdk/money.js';
|
|
24
|
+
|
|
25
|
+
export const API = () => {
|
|
26
|
+
const at = location.pathname.indexOf('/engine/play/');
|
|
27
|
+
return location.origin + (at < 0 ? '/housegames' : location.pathname.slice(0, at));
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const shouldLoadSandboxPanel = (env) => env === 'sandbox';
|
|
31
|
+
|
|
32
|
+
async function post(path, body, headers = {}) {
|
|
33
|
+
const ctrl = new AbortController();
|
|
34
|
+
const timer = setTimeout(() => ctrl.abort(), 10_000);
|
|
35
|
+
try {
|
|
36
|
+
const res = await fetch(API() + path, {
|
|
37
|
+
method: 'POST',
|
|
38
|
+
headers: { 'Content-Type': 'application/json', ...headers },
|
|
39
|
+
body: JSON.stringify(body),
|
|
40
|
+
credentials: 'omit',
|
|
41
|
+
cache: 'no-store',
|
|
42
|
+
signal: ctrl.signal,
|
|
43
|
+
});
|
|
44
|
+
const json = await res.json().catch(() => null);
|
|
45
|
+
if (!res.ok || !json) throw new Error((json && json.error) || `HTTP ${res.status}`);
|
|
46
|
+
return json;
|
|
47
|
+
} finally {
|
|
48
|
+
clearTimeout(timer);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// The site that frames the shell. In production the shell is served from the
|
|
53
|
+
// site's own origin; anywhere else the framing page must be one of the sites
|
|
54
|
+
// below (or, for a shell running on this machine, a page on this machine).
|
|
55
|
+
// Never taken from the URL.
|
|
56
|
+
const SITES = ['https://housebets.com', 'https://www.housebets.com', 'https://beta.housebets.com',
|
|
57
|
+
'https://housebets.dev', 'https://www.housebets.dev', 'https://arbitrium.housebets.dev']; // staging
|
|
58
|
+
function siteOrigin() {
|
|
59
|
+
let ref = null;
|
|
60
|
+
try { ref = document.referrer ? new URL(document.referrer).origin : null; } catch { ref = null; }
|
|
61
|
+
const local = /^(localhost|127\.0\.0\.1)$/.test(location.hostname) && ref && /^http:\/\/(localhost|127\.0\.0\.1):\d+$/.test(ref);
|
|
62
|
+
if (ref === location.origin || SITES.includes(ref) || local) return ref;
|
|
63
|
+
return location.origin;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// The platform's currency payload (the object our own games receive as
|
|
67
|
+
// currentlycurrency) says how the player sees amounts: in the coin, or in a
|
|
68
|
+
// fiat at the platform's rate. The game gets it as `init.wallet`, the shape
|
|
69
|
+
// the Originals' wallet-format.js reads, cut to the fields that shape uses (no
|
|
70
|
+
// picture URLs: the SDK carries our marks). Money stays the server's: the coin
|
|
71
|
+
// must be the session's, and a stake's precision never exceeds what the server
|
|
72
|
+
// takes for it. A payload that disagrees is dropped for the plain coin.
|
|
73
|
+
const CODE = /^[A-Z0-9]{2,10}$/;
|
|
74
|
+
const cleanCode = (v) => (typeof v === 'string' && CODE.test(v.trim().toUpperCase()) ? v.trim().toUpperCase() : null);
|
|
75
|
+
const whole = (v) => (Number.isSafeInteger(v) && v >= 0 ? v : null);
|
|
76
|
+
export function walletFrom(raw, currency) {
|
|
77
|
+
const plain = { symbol: currency.symbol, name: currency.symbol, isFiat: false, rate: 1, betPrecision: Math.min(8, currency.decimals) };
|
|
78
|
+
let p;
|
|
79
|
+
try { p = raw ? JSON.parse(raw) : null; } catch { return plain; }
|
|
80
|
+
if (!p || typeof p !== 'object' || Array.isArray(p)) return plain;
|
|
81
|
+
// In a fiat display the platform renames `symbol` to the fiat and keeps
|
|
82
|
+
// the coin in `name`; `code` is the coin's id, the session's.
|
|
83
|
+
const coin = cleanCode(p.name) || cleanCode(p.symbol);
|
|
84
|
+
if (coin !== String(currency.symbol).toUpperCase() || (p.code !== undefined && p.code !== currency.code)) return plain;
|
|
85
|
+
const own = whole(p.betPrecision) ?? whole(p.decimals) ?? whole(p.precision) ?? 8;
|
|
86
|
+
const rate = Number(p.rate);
|
|
87
|
+
const fiat = p.isFiat === true && Number.isFinite(rate) && rate > 0 ? cleanCode(p.fiat) || cleanCode(p.currentFiat) || cleanCode(p.symbol) : null;
|
|
88
|
+
return {
|
|
89
|
+
symbol: fiat || coin,
|
|
90
|
+
name: coin,
|
|
91
|
+
isFiat: !!fiat,
|
|
92
|
+
...(fiat ? { currentFiat: fiat } : {}),
|
|
93
|
+
rate: fiat ? rate : 1,
|
|
94
|
+
betPrecision: Math.min(8, own, currency.decimals),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
99
|
+
const NIL_UUID = '00000000-0000-0000-0000-000000000000';
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* What the site's `housebets-game-control` envelope asks, read as the
|
|
103
|
+
* Originals read it (limbo host-controls.js): a bare action is the bar's
|
|
104
|
+
* button — `provably-fair` opens the dialog on the player's seeds,
|
|
105
|
+
* `site-input` is the site page reporting a trusted press or key the player
|
|
106
|
+
* made on it, outside the game frame (the click gate shadows the window's
|
|
107
|
+
* activation for it, as for a press on the shell's own controls: SPEC §5),
|
|
108
|
+
* the rest goes to the game as a control; an envelope with any of the bet history's
|
|
109
|
+
* round fields (gameType, roundId, requestId) is the history asking for one
|
|
110
|
+
* round, and only a whole one counts — this game's catalog slug, a round id
|
|
111
|
+
* that is a UUID and not nil, a request id that is a UUID — because a
|
|
112
|
+
* half-filled envelope opened on the seeds would show the player a screen
|
|
113
|
+
* they could take for the verification they asked for. `provably-fair-cancel`
|
|
114
|
+
* names the request to abandon. Returns {fairness: roundId|null, requestId?},
|
|
115
|
+
* {cancel: requestId}, {input: true}, {control: action}, or null for nothing.
|
|
116
|
+
*/
|
|
117
|
+
export function siteCommand(d, gameId) {
|
|
118
|
+
if (!d || typeof d !== 'object' || typeof d.action !== 'string') return null;
|
|
119
|
+
const historical = 'gameType' in d || 'roundId' in d || 'requestId' in d;
|
|
120
|
+
const requestId = String(d.requestId || '').toLowerCase();
|
|
121
|
+
if (d.action === 'provably-fair-cancel') {
|
|
122
|
+
return historical && d.gameType === gameId && UUID.test(requestId) ? { cancel: requestId } : null;
|
|
123
|
+
}
|
|
124
|
+
if (!historical) {
|
|
125
|
+
if (d.action === 'provably-fair') return { fairness: null };
|
|
126
|
+
if (d.action === 'site-input') return { input: true };
|
|
127
|
+
return { control: d.action };
|
|
128
|
+
}
|
|
129
|
+
if (d.action !== 'provably-fair' || d.gameType !== gameId) return null;
|
|
130
|
+
const roundId = String(d.roundId || '').toLowerCase();
|
|
131
|
+
if (!UUID.test(roundId) || roundId === NIL_UUID || !UUID.test(requestId)) return null;
|
|
132
|
+
return { fairness: roundId, requestId };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* The dialog's openers. The site's bar, the bet history and the shell's own
|
|
137
|
+
* button are the player's gesture: the dialog opens afresh, on the seeds or
|
|
138
|
+
* on the history's round. The game's `fairness.open` is untrusted code
|
|
139
|
+
* asking: the dialog is told (`fromGame`) and gives up nothing the player
|
|
140
|
+
* has typed or started in it.
|
|
141
|
+
*/
|
|
142
|
+
export function fairnessOpeners(ui, rgs, ctx) {
|
|
143
|
+
const open = (roundId, fromGame, requestId) => ui.fairness(rgs, roundId, { ...ctx, fromGame, requestId: requestId || null });
|
|
144
|
+
return {
|
|
145
|
+
player: (roundId = null, requestId = null) => open(roundId, false, requestId),
|
|
146
|
+
game: (roundId) => open(roundId, true, null),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* The site's document, when the shell may listen to it: the shell is served
|
|
152
|
+
* from the site's own origin in production (www.housebets.com/housegames/
|
|
153
|
+
* engine/play/…), so it is a same-origin frame of the site page, and a
|
|
154
|
+
* browser hands a press anywhere in that page — the chat, the header — to the
|
|
155
|
+
* shell's window as activation too (the HTML activation notification reaches
|
|
156
|
+
* every same-origin frame of the clicked page). The shell therefore watches
|
|
157
|
+
* the site's presses and focus moves as it watches its own. A shell on an
|
|
158
|
+
* origin of its own (a stand, the games host of ARCHITECTURE §7) gets no such
|
|
159
|
+
* activation and needs no such listener: the parent refuses its document, and
|
|
160
|
+
* that refusal is the proof. Null when there is no site around the shell, or
|
|
161
|
+
* when it is another origin.
|
|
162
|
+
*/
|
|
163
|
+
export function siteDocument(win, parentOrigin) {
|
|
164
|
+
if (!win || win.parent === win || parentOrigin !== win.location.origin) return null;
|
|
165
|
+
try {
|
|
166
|
+
const doc = win.parent.document;
|
|
167
|
+
return doc && typeof doc.addEventListener === 'function' ? doc : null;
|
|
168
|
+
} catch {
|
|
169
|
+
return null; // cross-origin after all: its activation never reaches this window
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Where the page asks for its game's descriptor. A launch from House Studio's
|
|
175
|
+
* sandbox (play.housestudio.io, ADR 0005) carries a ticket, without which the
|
|
176
|
+
* studio names no version that is not live yet: it goes with the question. The
|
|
177
|
+
* casino's launch carries none and asks as it always has.
|
|
178
|
+
*/
|
|
179
|
+
export function descriptorURL(ticket) {
|
|
180
|
+
return ticket ? `./descriptor.json?ticket=${encodeURIComponent(ticket)}` : './descriptor.json';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export async function boot({ ui }) {
|
|
184
|
+
const params = new URLSearchParams(location.search);
|
|
185
|
+
const otp = params.get('otp');
|
|
186
|
+
const ticket = params.get('ticket');
|
|
187
|
+
const currency = Number(params.get('currency'));
|
|
188
|
+
const payload = params.get('currencyPayload');
|
|
189
|
+
// The player's language, as the site names it on the launch (our own
|
|
190
|
+
// games read the same `lang`), else the browser's. The page's own words
|
|
191
|
+
// (the Provably Fair dialog) are in it from here on.
|
|
192
|
+
const locale = (params.get('lang') || params.get('locale') || navigator.language || 'en').slice(0, 12);
|
|
193
|
+
ui.locale(locale);
|
|
194
|
+
// The code is single-use; it leaves the address bar before anything else.
|
|
195
|
+
history.replaceState(null, '', location.pathname);
|
|
196
|
+
const slug = location.pathname.split('/').filter(Boolean).at(-1);
|
|
197
|
+
if (!otp || !Number.isSafeInteger(currency) || currency <= 0 || !/^[a-z0-9][a-z0-9-]{1,23}$/.test(slug)) {
|
|
198
|
+
return ui.fail('This game was opened without a launch. Open it from the casino.');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
let descriptor, token, session;
|
|
202
|
+
try {
|
|
203
|
+
// Where the game's bundle lives and how the site names the game. In
|
|
204
|
+
// production this comes with the game's live version (in the sandbox,
|
|
205
|
+
// the newest that passed, for this launch's ticket); without one the
|
|
206
|
+
// studio says why (not live, or no front end kept for it).
|
|
207
|
+
const res = await fetch(descriptorURL(ticket), { cache: 'no-store' });
|
|
208
|
+
descriptor = await res.json().catch(() => null);
|
|
209
|
+
if (!res.ok || !descriptor || !descriptor.bundleUrl) {
|
|
210
|
+
return ui.fail((descriptor && descriptor.error) || 'This game is not available right now. Please try again later.');
|
|
211
|
+
}
|
|
212
|
+
({ token } = await post('/direct/session', { otp }));
|
|
213
|
+
// No origin binding: the session id never leaves this page's memory
|
|
214
|
+
// (browsers send no Origin on same-origin GETs, which would fail it).
|
|
215
|
+
session = await post('/engine/v1/sessions', { game: slug, currency }, { 'X-Token': token });
|
|
216
|
+
} catch (e) {
|
|
217
|
+
return ui.fail('The game could not start. Please reopen it.', e);
|
|
218
|
+
}
|
|
219
|
+
token = null;
|
|
220
|
+
|
|
221
|
+
const faults = createFaults(fetch);
|
|
222
|
+
const rgs = createRgs({ base: API() + '/engine/v1', sessionId: session.sessionId, fetch: faults.fetch });
|
|
223
|
+
const auth = await rgs.authenticate(locale);
|
|
224
|
+
if (!auth.ok) return ui.fail('The game could not start. Please reopen it.', auth.error);
|
|
225
|
+
// The front end the studio named and the maths the round server plays must be
|
|
226
|
+
// one version: for the seconds a new version takes to reach both, the game waits.
|
|
227
|
+
if (typeof descriptor.mathHash === 'string' && descriptor.mathHash && auth.body.mathHash !== descriptor.mathHash) {
|
|
228
|
+
return ui.fail('This game is being updated. Please reopen it in a moment.');
|
|
229
|
+
}
|
|
230
|
+
// One launch, counted on the engine with the game's errors and abandoned rounds
|
|
231
|
+
// (the router sends those): the studio's Reports show the counts per day.
|
|
232
|
+
rgs.telemetry('boot');
|
|
233
|
+
// The studio dashboard's sandbox launcher can force the first round to a
|
|
234
|
+
// row of the table (?force=12, or 3,0,0 for a ladder's steps) to test a
|
|
235
|
+
// big win or a feature on demand. The server allows it only in the sandbox.
|
|
236
|
+
const force = params.get('force');
|
|
237
|
+
if (force && auth.body.env === 'sandbox' && /^\d{1,7}(,\d{1,7}){0,63}$/.test(force)) {
|
|
238
|
+
const f = await rgs.force(force.split(',').map(Number));
|
|
239
|
+
if (!f.ok) console.warn('force refused', f.error);
|
|
240
|
+
}
|
|
241
|
+
// How amounts are shown (the player's fiat display, as the site shows the
|
|
242
|
+
// header); money itself is in the wallet currency the server confirmed.
|
|
243
|
+
const init = { ...auth.body, wallet: walletFrom(payload, auth.body.currency), locale };
|
|
244
|
+
// How money is shown, the same as the site's header: the autoplay
|
|
245
|
+
// confirmation shows the stake and the totals in the player's coin, and
|
|
246
|
+
// the bet it is for by the labels the maths names (the id when it names none).
|
|
247
|
+
const money = moneyFor(init.wallet);
|
|
248
|
+
const betLine = (modeId, betId) => {
|
|
249
|
+
const modes = Array.isArray(init.modes) ? init.modes : [];
|
|
250
|
+
const label = (x, id) => (x && typeof x.label === 'string' && x.label ? x.label : id);
|
|
251
|
+
const mode = modes.find((m) => m && m.id === modeId);
|
|
252
|
+
const bet = label(mode && Array.isArray(mode.bets) ? mode.bets.find((b) => b && b.id === betId) : null, betId);
|
|
253
|
+
return modes.length > 1 ? `${label(mode, modeId)} · ${bet}` : bet;
|
|
254
|
+
};
|
|
255
|
+
// The page reads the player's way: right to left for Arabic, Hebrew,
|
|
256
|
+
// Persian, Urdu; the game frame inherits it.
|
|
257
|
+
document.documentElement.lang = locale;
|
|
258
|
+
document.documentElement.dir = /^(ar|he|fa|ur)\b/i.test(locale) ? 'rtl' : 'ltr';
|
|
259
|
+
|
|
260
|
+
const parentOrigin = window.parent !== window ? siteOrigin() : null;
|
|
261
|
+
const bridge = createSiteBridge({ parent: window.parent, parentOrigin, gameId: descriptor.catalogSlug, currencyId: currency });
|
|
262
|
+
const frame = ui.frame(descriptor.bundleUrl);
|
|
263
|
+
// The Provably Fair dialog, the shell's own: on the player's seeds (the
|
|
264
|
+
// site's bar, the shell's button) or on a round (the game's fairness.open,
|
|
265
|
+
// the bet history's envelope). It verifies against the maths the shell is
|
|
266
|
+
// running unless the round names another version, shows money as the
|
|
267
|
+
// wallet shows it, and holds the rotation while a round is open; the
|
|
268
|
+
// bets go on behind it, so it is told when a round starts and ends.
|
|
269
|
+
const fairness = fairnessOpeners(ui, rgs, { game: slug, mathHash: init.mathHash, wallet: init.wallet, roundOpen: () => router.roundOpen() });
|
|
270
|
+
// The click gate reads this window's user activation (a click inside the
|
|
271
|
+
// game frame reaches it — and, with the shell on the site's origin, so does
|
|
272
|
+
// a click anywhere in the site page: those are shadowed below) and the
|
|
273
|
+
// document's focus (only a click in the frame moves it there: router.js
|
|
274
|
+
// browserFocusInGame, its default; a focus the game moved there by script
|
|
275
|
+
// is told apart below); its refusals, and the autoplay control, speak the
|
|
276
|
+
// player's language.
|
|
277
|
+
const router = createRouter({
|
|
278
|
+
self: window,
|
|
279
|
+
gameWindow: () => frame.contentWindow,
|
|
280
|
+
gameOrigin: new URL(descriptor.bundleUrl).origin,
|
|
281
|
+
rgs,
|
|
282
|
+
init,
|
|
283
|
+
words: wordsFor(locale),
|
|
284
|
+
site: {
|
|
285
|
+
// The shell's own autoplay control over the game: rounds left and
|
|
286
|
+
// Stop, in this document, where the game cannot cover or press it.
|
|
287
|
+
autoplay: ui.autoplay,
|
|
288
|
+
// The autoplay confirmation: the router hands the capped terms in
|
|
289
|
+
// wallet units; the shell shows them in the player's coin, as the
|
|
290
|
+
// header does, and starts nothing until the player presses Start.
|
|
291
|
+
autoplayConfirm: (t) => {
|
|
292
|
+
const m = (n) => `${money.format(n)} ${money.unit}`;
|
|
293
|
+
ui.autoplayConfirm({ bet: betLine(t.mode, t.bet), rounds: t.rounds, stake: m(t.stake), total: m(t.total), stopProfit: t.stopOnProfit ? m(t.stopOnProfit) : null, stopLoss: m(t.stopOnLoss) });
|
|
294
|
+
},
|
|
295
|
+
// The router took the confirmation down (its deadline, the reality
|
|
296
|
+
// check, the session over): the dialog closes with the game refused.
|
|
297
|
+
autoplayDismiss: ui.autoplayDismiss,
|
|
298
|
+
roundStart: (r) => {
|
|
299
|
+
bridge.roundStart(r);
|
|
300
|
+
ui.fairnessRoundChanged(false);
|
|
301
|
+
},
|
|
302
|
+
roundEnd: (r, round) => {
|
|
303
|
+
bridge.roundEnd(r);
|
|
304
|
+
ui.round({ ...r, round });
|
|
305
|
+
ui.fairnessRoundChanged(true);
|
|
306
|
+
},
|
|
307
|
+
ready: () => {
|
|
308
|
+
ui.ready();
|
|
309
|
+
bridge.ready();
|
|
310
|
+
},
|
|
311
|
+
loading: ui.loading,
|
|
312
|
+
fairness: fairness.game,
|
|
313
|
+
error: (message) => console.warn('[game]', message),
|
|
314
|
+
ended: () => ui.fail('This game session has ended. Please reopen the game.'),
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
// The game asks for its config (hello) on every page it loads; the router
|
|
318
|
+
// answers. Nothing waits on the frame's load event, which a heavy page
|
|
319
|
+
// fires late. The site's bar: Provably Fair is the shell's own dialog on
|
|
320
|
+
// the player's seeds, the same for every game as for every House
|
|
321
|
+
// Original, open before the first round too (the hash is what the player
|
|
322
|
+
// checks before playing); the bet history's envelope opens it on that
|
|
323
|
+
// round (siteCommand); the site's report of the player's own press or key
|
|
324
|
+
// on its page (`site-input`) shadows this window's activation exactly as
|
|
325
|
+
// a press on the shell's controls does — the shell shares the site's
|
|
326
|
+
// origin, so that press armed this window too, and a bundle that stole
|
|
327
|
+
// the focus back by script must get no bet out of it; the rest is the
|
|
328
|
+
// game's.
|
|
329
|
+
bridge.listen((d) => {
|
|
330
|
+
const cmd = siteCommand(d, descriptor.catalogSlug);
|
|
331
|
+
if (!cmd) return;
|
|
332
|
+
if (cmd.cancel) ui.fairnessCancel(cmd.cancel);
|
|
333
|
+
else if (cmd.input) router.shellInput();
|
|
334
|
+
else if (cmd.control) router.tell('control', { action: cmd.control });
|
|
335
|
+
else fairness.player(cmd.fairness, cmd.requestId);
|
|
336
|
+
});
|
|
337
|
+
// No site around the shell (opened directly, or a stand): the shell
|
|
338
|
+
// shows the button the site's bar would.
|
|
339
|
+
if (window.parent === window) ui.fairButton(() => fairness.player());
|
|
340
|
+
// The player's Stop on the shell's autoplay control ends the session at once.
|
|
341
|
+
ui.autoplayStop(() => router.autoplayStop());
|
|
342
|
+
// The confirmation's Start/Cancel: a real click on the shell's own button
|
|
343
|
+
// starts the session; anything else refuses it. The game cannot reach these.
|
|
344
|
+
ui.onAutoplayConfirm((ok) => (ok ? router.confirmAutoplay() : router.cancelAutoplay()));
|
|
345
|
+
// A press or a key on the shell's own controls arms this window's activation
|
|
346
|
+
// as a game click does: the router is told, and takes no play on it meanwhile.
|
|
347
|
+
ui.onShellInput(() => router.shellInput());
|
|
348
|
+
// So does a press anywhere in the site's page when the shell is a
|
|
349
|
+
// same-origin frame of it (production): the chat, the header, a button of
|
|
350
|
+
// the site's. Its presses are shadowed the same way, so a chat click or a
|
|
351
|
+
// word typed there buys the game nothing (siteDocument says when the site's
|
|
352
|
+
// document may be listened to; a cross-origin site gives this window no
|
|
353
|
+
// activation and no document).
|
|
354
|
+
const siteDoc = siteDocument(window, parentOrigin);
|
|
355
|
+
if (siteDoc) ui.onShellInput(() => router.shellInput(), siteDoc);
|
|
356
|
+
// The focus must be the player's: a game can move it into its own frame by
|
|
357
|
+
// script, and the player's next keystrokes (meant for the chat) would then
|
|
358
|
+
// land in the game, granting it activation. The page watches the focus
|
|
359
|
+
// entering and leaving the frame (on this document's moves and the site's,
|
|
360
|
+
// the presses on either, and by reading document.activeElement besides);
|
|
361
|
+
// the router says whether the entry was the player's — the activation
|
|
362
|
+
// live, no shell or site press, no scripted focus and no exit from the
|
|
363
|
+
// frame within its lifetime (a game can take the focus back the moment
|
|
364
|
+
// the player tabs out of it, with that Tab's activation still live), or
|
|
365
|
+
// the player's own Tab key just before it (the page saw the key) — and a
|
|
366
|
+
// focus that was not is blurred straight back out of the frame and buys no
|
|
367
|
+
// play until the player focuses the frame themselves.
|
|
368
|
+
ui.watchGameFocus((viaTab, sinceExitMs) => router.frameFocused(viaTab, sinceExitMs), siteDoc ? [document, siteDoc] : [document]);
|
|
369
|
+
// A round left open as the page goes is counted as abandoned (pagehide also
|
|
370
|
+
// fires on a close, which beforeunload may not on a phone), and an autoplay
|
|
371
|
+
// session ends with the page — as it does when the page is merely hidden
|
|
372
|
+
// (another tab, the phone locked): a run is for a player who is looking.
|
|
373
|
+
window.addEventListener('pagehide', router.unload);
|
|
374
|
+
document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'hidden') router.hidden(); });
|
|
375
|
+
if (shouldLoadSandboxPanel(auth.body.env)) {
|
|
376
|
+
import('./sandbox-panel.js').then(({ createSandboxPanel }) => createSandboxPanel({
|
|
377
|
+
rgs,
|
|
378
|
+
router,
|
|
379
|
+
faults,
|
|
380
|
+
api: API(),
|
|
381
|
+
frame,
|
|
382
|
+
slug,
|
|
383
|
+
mathHash: init.mathHash,
|
|
384
|
+
money,
|
|
385
|
+
refresh: async () => {
|
|
386
|
+
const refreshed = await rgs.authenticate(locale);
|
|
387
|
+
if (refreshed.ok) {
|
|
388
|
+
router.refreshInit({ ...refreshed.body, wallet: walletFrom(payload, refreshed.body.currency), locale });
|
|
389
|
+
}
|
|
390
|
+
return refreshed;
|
|
391
|
+
},
|
|
392
|
+
})).catch((error) => console.warn('[sandbox panel]', error));
|
|
393
|
+
}
|
|
394
|
+
// The reality check the server asks for: every N seconds of play the
|
|
395
|
+
// game is paused and the player told how long it has been.
|
|
396
|
+
const every = init.rg && init.rg.realityCheckSec > 0 ? init.rg.realityCheckSec * 1000 : 0;
|
|
397
|
+
if (every) {
|
|
398
|
+
const started = Date.now();
|
|
399
|
+
const check = async () => {
|
|
400
|
+
router.tell('pause', { reason: 'reality-check' });
|
|
401
|
+
await ui.realityCheck(Math.round((Date.now() - started) / 60000));
|
|
402
|
+
router.tell('resume', {});
|
|
403
|
+
setTimeout(check, every);
|
|
404
|
+
};
|
|
405
|
+
setTimeout(check, every);
|
|
406
|
+
}
|
|
407
|
+
ui.started({ game: init.game, mathHash: init.mathHash, env: init.env, currency: init.currency });
|
|
408
|
+
}
|