@energy8platform/game-engine 0.15.2 → 0.17.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.
Files changed (55) hide show
  1. package/README.md +22 -12
  2. package/dist/assets.d.ts +2 -14
  3. package/dist/core.cjs.js +16 -201
  4. package/dist/core.cjs.js.map +1 -1
  5. package/dist/core.d.ts +4 -35
  6. package/dist/core.esm.js +12 -197
  7. package/dist/core.esm.js.map +1 -1
  8. package/dist/debug.cjs.js +5 -233
  9. package/dist/debug.cjs.js.map +1 -1
  10. package/dist/debug.d.ts +2 -140
  11. package/dist/debug.esm.js +2 -233
  12. package/dist/debug.esm.js.map +1 -1
  13. package/dist/index.cjs.js +21 -432
  14. package/dist/index.cjs.js.map +1 -1
  15. package/dist/index.d.ts +10 -195
  16. package/dist/index.esm.js +14 -429
  17. package/dist/index.esm.js.map +1 -1
  18. package/dist/lua.cjs.js +7 -1493
  19. package/dist/lua.cjs.js.map +1 -1
  20. package/dist/lua.d.ts +1 -403
  21. package/dist/lua.esm.js +1 -1483
  22. package/dist/lua.esm.js.map +1 -1
  23. package/dist/react.cjs.js.map +1 -1
  24. package/dist/react.d.ts +4 -35
  25. package/dist/react.esm.js.map +1 -1
  26. package/dist/vite.cjs.js +19 -175
  27. package/dist/vite.cjs.js.map +1 -1
  28. package/dist/vite.d.ts +8 -3
  29. package/dist/vite.esm.js +10 -173
  30. package/dist/vite.esm.js.map +1 -1
  31. package/package.json +6 -16
  32. package/src/core/GameApplication.ts +14 -18
  33. package/src/debug/index.ts +4 -2
  34. package/src/index.ts +3 -3
  35. package/src/loading/LoadingScene.ts +1 -1
  36. package/src/loading/index.ts +11 -2
  37. package/src/lua/index.ts +3 -31
  38. package/src/types.ts +16 -41
  39. package/src/vite/index.ts +13 -196
  40. package/bin/simulate.ts +0 -139
  41. package/scripts/install-simulate.mjs +0 -101
  42. package/src/debug/DevBridge.ts +0 -305
  43. package/src/loading/CSSPreloader.ts +0 -129
  44. package/src/loading/logo.ts +0 -95
  45. package/src/lua/ActionRouter.ts +0 -132
  46. package/src/lua/LuaEngine.ts +0 -412
  47. package/src/lua/LuaEngineAPI.ts +0 -314
  48. package/src/lua/NativeSimulationRunner.ts +0 -367
  49. package/src/lua/ParallelSimulationRunner.ts +0 -156
  50. package/src/lua/PersistentState.ts +0 -80
  51. package/src/lua/SessionManager.ts +0 -227
  52. package/src/lua/SimulationRunner.ts +0 -192
  53. package/src/lua/SimulationWorker.ts +0 -44
  54. package/src/lua/fengari.d.ts +0 -10
  55. package/src/lua/types.ts +0 -149
package/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # @energy8platform/game-engine
2
2
 
3
- A casino game engine built on [PixiJS v8](https://pixijs.com/) and [@energy8platform/game-sdk](https://github.com/energy8platform/game-sdk). Provides scene management, responsive scaling, audio, state machines, tweens, UI components, Lua scripting, and React integration for developing slot machines, card games, and other iGaming titles.
3
+ A casino game engine built on [PixiJS v8](https://pixijs.com/) and [@energy8platform/game-sdk](https://github.com/energy8platform/game-sdk). Provides scene management, responsive scaling, audio, state machines, tweens, UI components, and React integration for developing slot machines, card games, and other iGaming titles.
4
+
5
+ > **Building on a different renderer?** The platform-specific bits (Lua engine, RTP simulation, DevBridge, branded loading screen, SDK session orchestration) live in the renderer-agnostic [`@energy8platform/platform-core`](../platform-core/README.md). `game-engine` is the Pixi front-end on top of it; Phaser, Three.js, and custom-engine consumers depend only on `platform-core`.
4
6
 
5
7
  ---
6
8
 
@@ -31,14 +33,15 @@ A casino game engine built on [PixiJS v8](https://pixijs.com/) and [@energy8plat
31
33
  ## Quick Start
32
34
 
33
35
  ```bash
34
- # Install dependencies
36
+ # Install dependencies. platform-core comes in transitively as a
37
+ # regular dependency of game-engine — you don't list it explicitly.
35
38
  npm install pixi.js @energy8platform/game-sdk @energy8platform/game-engine
36
39
 
37
40
  # Optional peer dependencies
38
41
  npm install @pixi/sound # Audio
39
42
  npm install @esotericsoftware/spine-pixi-v8 # Spine animations
40
43
  npm install react react-dom react-reconciler # React integration
41
- npm install fengari # Lua engine
44
+ npm install fengari # Lua engine (in dev/sim)
42
45
  ```
43
46
 
44
47
  ```typescript
@@ -91,7 +94,9 @@ bootstrap();
91
94
  | `@esotericsoftware/spine-pixi-v8` | `~4.2.0` | Optional — Spine animations |
92
95
  | `react`, `react-dom` | `>=18.0.0` | Optional — ReactScene |
93
96
  | `react-reconciler` | `>=0.29.0` | Optional — ReactScene (custom PixiJS reconciler) |
94
- | `fengari` | `^0.1.4` | Optional — Lua engine |
97
+ | `fengari` | `^0.1.4` | Optional — Lua engine (peer of `platform-core`, install only if you use Lua locally) |
98
+
99
+ `@energy8platform/platform-core` is a regular dependency (not a peer) — npm pulls it in automatically when you install `game-engine`.
95
100
 
96
101
  ### Sub-path Exports
97
102
 
@@ -710,6 +715,8 @@ export default defineGameConfig({
710
715
 
711
716
  Runs platform Lua game scripts in the browser via `fengari` (Lua 5.3, pure JS). Replicates server-side execution for development — no backend required.
712
717
 
718
+ > The Lua engine, simulation runners, and game-definition types ship from [`@energy8platform/platform-core`](../platform-core/README.md). `game-engine` re-exports them at `@energy8platform/game-engine/lua` so existing import paths keep working.
719
+
713
720
  ### DevBridge Integration (recommended)
714
721
 
715
722
  ```typescript
@@ -764,20 +771,20 @@ engine.destroy();
764
771
 
765
772
  ### RTP Simulation (CLI)
766
773
 
767
- Run the same Lua script from `dev.config.ts` through millions of iterations to verify math:
774
+ Run the same Lua script from `dev.config.ts` through millions of iterations to verify math. The CLI ships with [`@energy8platform/platform-core`](../platform-core/README.md) (a transitive dep when you install `game-engine`):
768
775
 
769
776
  ```bash
770
777
  # Regular spins (1M iterations, default)
771
- npx game-engine-simulate
778
+ npx platform-core-simulate
772
779
 
773
780
  # Buy bonus simulation
774
- npx game-engine-simulate --action buy_bonus
781
+ npx platform-core-simulate --action buy_bonus
775
782
 
776
- # Ante bet
777
- npx game-engine-simulate --params '{"ante_bet":true}'
783
+ # Ante bet (v5: regular action, not a params flag)
784
+ npx platform-core-simulate --action ante_spin
778
785
 
779
786
  # Custom parameters
780
- npx game-engine-simulate --iterations 5000000 --bet 1 --seed 42 --config ./dev.config.ts
787
+ npx platform-core-simulate --iterations 5000000 --bet 1 --seed 42 --config ./dev.config.ts
781
788
  ```
782
789
 
783
790
  Output matches the platform's server-side simulation format:
@@ -824,7 +831,7 @@ console.log(formatSimulationResult(result));
824
831
 
825
832
  ## DevBridge
826
833
 
827
- Simulates a casino host for local development using SDK's `Bridge` in `devMode` (shared `MemoryChannel`, no iframe needed).
834
+ Simulates a casino host for local development using SDK's `Bridge` in `devMode` (shared `MemoryChannel`, no iframe needed). DevBridge is implemented in [`@energy8platform/platform-core`](../platform-core/README.md); the imports below resolve through the re-export shim shipped by `game-engine`.
828
835
 
829
836
  ```typescript
830
837
  import { DevBridge } from '@energy8platform/game-engine/debug';
@@ -847,10 +854,13 @@ bridge.setBalance(5000);
847
854
  bridge.destroy();
848
855
  ```
849
856
 
850
- **Handled messages:** `GAME_READY`, `PLAY_REQUEST`, `PLAY_RESULT_ACK`, `GET_BALANCE`, `GET_STATE`, `OPEN_DEPOSIT`.
857
+ **Handled (in):** `GAME_READY`, `PLAY_REQUEST`, `PLAY_RESULT_ACK`, `GET_BALANCE`, `GET_STATE`, `OPEN_DEPOSIT`.
858
+ **Emitted (out):** `INIT`, `PLAY_RESULT`, `PLAY_ERROR`, `BALANCE_UPDATE`, `STATE_RESPONSE`.
851
859
 
852
860
  > With the Vite plugin (`devBridge: true`), DevBridge is injected automatically before your app entry point.
853
861
 
862
+ > **Error codes & session contract.** In Lua mode, DevBridge emits the same `PLAY_ERROR` codes the platform does (`INVALID_AMOUNT`, `INSUFFICIENT_FUNDS`, `ACTIVE_SESSION_EXISTS`, `NO_ACTIVE_SESSION`, `SESSION_EXPIRED`, `ENGINE_ERROR`, …) — see the [platform-core DevBridge section](../platform-core/README.md#platform-parity-behavior-lua-mode) for the full table and the contract details (server-generated round IDs, `session.history`, `STATE_RESPONSE` shape, `creditPending` semantics).
863
+
854
864
  ---
855
865
 
856
866
  ## React Integration
package/dist/assets.d.ts CHANGED
@@ -1,16 +1,5 @@
1
- interface AssetEntry {
2
- alias: string;
3
- src: string | string[];
4
- /** Optional data to pass to the loader */
5
- data?: Record<string, unknown>;
6
- }
7
- interface AssetBundle {
8
- name: string;
9
- assets: AssetEntry[];
10
- }
11
- interface AssetManifest {
12
- bundles: AssetBundle[];
13
- }
1
+ import { AssetManifest } from '@energy8platform/platform-core';
2
+ export { AssetBundle, AssetEntry, AssetManifest } from '@energy8platform/platform-core';
14
3
 
15
4
  /**
16
5
  * Manages game asset loading with progress tracking, bundle support, and
@@ -94,4 +83,3 @@ declare class AssetManager {
94
83
  }
95
84
 
96
85
  export { AssetManager };
97
- export type { AssetBundle, AssetEntry, AssetManifest };
package/dist/core.cjs.js CHANGED
@@ -1,7 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var pixi_js = require('pixi.js');
4
- var gameSdk = require('@energy8platform/game-sdk');
4
+ var platformCore = require('@energy8platform/platform-core');
5
+ var loading = require('@energy8platform/platform-core/loading');
5
6
 
6
7
  // ─── Scale Modes ───────────────────────────────────────────
7
8
  var ScaleMode;
@@ -1429,81 +1430,12 @@ class Scene {
1429
1430
  }
1430
1431
  }
1431
1432
 
1432
- /**
1433
- * Shared Energy8 SVG logo with an embedded loader bar.
1434
- *
1435
- * The loader bar fill is controlled via a `<clipPath>` whose `<rect>` width
1436
- * is animatable. Different consumers customise gradient IDs and the clip
1437
- * element's ID/class to avoid collisions when both CSSPreloader and
1438
- * LoadingScene appear in the same DOM.
1439
- */
1440
- /** SVG path data for the Energy8 wordmark — reused across loaders */
1441
- const WORDMARK_PATHS = `
1442
- <path d="m241 81.75h-19.28c-1.77 0-6.73 4.98-7.43 6.99l-4.36 12.22c-0.49 1.37 0.05 2.92 1.06 4.32-2.07 1.19-3.69 3.08-4.36 5.43l-3.25 10.41c-0.86 2.89 2.39 6.63 4.31 6.63h19.28c1.96 0 7.4-5.56 7.96-7.51l2.96-10.22c0.63-2.25 0.1-3.98-1.22-4.99 2.55-1.56 3.86-4.14 4.55-6.31l2.77-9.31c0.74-2.57-1.37-7.66-2.99-7.66zm-13.36 28.31-2.27 7.03h-8.28l2.58-8.28h8.28l-0.31 1.25zm4.06-16.97-2.11 6.7h-7.04l2.25-7.34h7.26l-0.36 0.64z" fill="url(#GID0)"/>
1443
- <path d="m202.5 81.75-9.31 14.97-2.32-14.97h-11.82l4.32 25.15-0.57 4.91-8.64 26.44 15.31-12.76 5.63-16.48 19.96-27.26h-12.56z" fill="url(#GID1)"/>
1444
- <path d="m174.2 81.75h-19.78l-5.75 5.16-10.79 33.2c-0.77 2.53 2.48 6.93 4.87 6.93h17.38c2.63 0 7.85-5.34 8.32-6.83l5.37-18.14h-15.17l-2.2 7.64h3.78l-2.25 7.2h-8.01l7.1-25.52h7.58l-1.48 8.4 12.78-5.98c1.28-0.63 1.97-3.99 1.61-6.61-0.36-2.34-1.64-5.45-3.36-5.45z" fill="url(#GID2)"/>
1445
- <path d="m140.6 81.75h-70.6l-5.36 19.37-4.26-19.37h-46.76l2.95 5.88-10.58 39.28h26.84l2.95-9.52-15.63-0.13 2.55-8.34h8.74l8.47-9.81h-14.61l2.11-7.3h15.47l2.54-8.71 2.58 4.74-11.4 39.07h11.05l6.46-21.49 8.84 36.33 19.18-55.67-1.83-3.36 3.68 4.09-12.07 40.1h28.18l3.39-10.31h-17.01l2.67-8.03h9.98l7.58-9.52h-14.28l1.93-6.6h14.61l3.25-9.73 2.81 5.12-11.3 38.89h11.05l5.23-17.81h1.62l1.48 17.6h10.69l-1.48-16.81c4.75-1.28 7.52-5.9 8.64-9.81l2.95-11.3c0.86-2.73-1.43-6.85-3.3-6.85zm-9.8 17.3h-8.69l2.54-7.84h8.35l-2.2 7.84z" fill="url(#GID3)"/>
1446
- <path d="m205.9 148.9h-122.6l-2.61-3.12h-32.4l-2.51 3.12h-1.59c-5.34 0-7.94 4.88-7.94 7.65v0.03c0 4.2 3.55 7.6 7.74 7.6h103.6l2.11 3.12h36.09l1.82-3.12h18.3c5.25 0 6.64-5.3 6.64-7.35v-0.25c0-4.23-2.9-7.68-6.64-7.68zm-0.7 12.83h-160.6c-3.69 0-6.11-2.58-6.11-5.47v-0.03c0-2.89 2.1-5.47 5.61-5.47h161.1c3.45 0 4.89 3.12 4.89 5.65v0.17c0 2.57-2.11 5.15-4.89 5.15z" fill="url(#GID4)"/>`;
1447
- /** Gradient definitions template (gradient IDs are replaced per-consumer) */
1448
- const GRADIENT_DEFS = `
1449
- <linearGradient id="GID0" x1="223.7" x2="223.7" y1="81.75" y2="127.8" gradientUnits="userSpaceOnUse">
1450
- <stop stop-color="#663BA6"/><stop stop-color="#7939C2" offset=".349"/><stop stop-color="#8A2FC0" offset=".6615"/><stop stop-color="#791BA3" offset="1"/>
1451
- </linearGradient>
1452
- <linearGradient id="GID1" x1="194.6" x2="194.6" y1="81.75" y2="138.3" gradientUnits="userSpaceOnUse">
1453
- <stop stop-color="#663BA6"/><stop stop-color="#7939C2" offset=".349"/><stop stop-color="#8A2FC0" offset=".6615"/><stop stop-color="#791BA3" offset="1"/>
1454
- </linearGradient>
1455
- <linearGradient id="GID2" x1="157.8" x2="157.8" y1="81.75" y2="127" gradientUnits="userSpaceOnUse">
1456
- <stop stop-color="#663BA6"/><stop stop-color="#7939C2" offset=".349"/><stop stop-color="#8A2FC0" offset=".6615"/><stop stop-color="#791BA3" offset="1"/>
1457
- </linearGradient>
1458
- <linearGradient id="GID3" x1="79.96" x2="79.96" y1="81.75" y2="141.8" gradientUnits="userSpaceOnUse">
1459
- <stop stop-color="#663BA6"/><stop stop-color="#7939C2" offset=".349"/><stop stop-color="#8A2FC0" offset=".6615"/><stop stop-color="#791BA3" offset="1"/>
1460
- </linearGradient>
1461
- <linearGradient id="GID4" x1="36.18" x2="212.5" y1="156.6" y2="156.6" gradientUnits="userSpaceOnUse">
1462
- <stop stop-color="#316FB0"/><stop stop-color="#1FCDE6" offset=".5"/><stop stop-color="#29FEE7" offset="1"/>
1463
- </linearGradient>
1464
- <linearGradient id="GID5" x1="40.27" x2="208.2" y1="156.4" y2="156.4" gradientUnits="userSpaceOnUse">
1465
- <stop stop-color="#316FB0"/><stop stop-color="#1FCDE6" offset=".5"/><stop stop-color="#29FEE7" offset="1"/>
1466
- </linearGradient>`;
1467
- /** Max width of the loader bar in SVG units */
1468
- const LOADER_BAR_MAX_WIDTH = 174;
1469
- /**
1470
- * Build the Energy8 SVG logo with a loader bar, using unique IDs.
1471
- *
1472
- * @param opts - Configuration to avoid element ID collisions
1473
- * @returns SVG markup string
1474
- */
1475
- function buildLogoSVG(opts) {
1476
- const { idPrefix, svgClass, svgStyle, clipRectClass, clipRectId, textId, textContent, textClass } = opts;
1477
- // Replace gradient ID placeholders with prefixed versions
1478
- const paths = WORDMARK_PATHS.replace(/GID(\d)/g, `${idPrefix}$1`);
1479
- const defs = GRADIENT_DEFS.replace(/GID(\d)/g, `${idPrefix}$1`);
1480
- const clipId = `${idPrefix}-loader-clip`;
1481
- const fillGradientId = `${idPrefix}5`;
1482
- const classAttr = svgClass ? ` class="${svgClass}"` : '';
1483
- const styleAttr = svgStyle ? ` style="${svgStyle}"` : '';
1484
- const rectClassAttr = clipRectClass ? ` class="${clipRectClass}"` : '';
1485
- const rectIdAttr = clipRectId ? ` id="${clipRectId}"` : '';
1486
- const txtIdAttr = textId ? ` id="${textId}"` : '';
1487
- const txtClassAttr = textClass ? ` class="${textClass}"` : '';
1488
- return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 250 200" fill="none"${classAttr}${styleAttr}>
1489
- ${paths}
1490
- <clipPath id="${clipId}">
1491
- <rect${rectIdAttr} x="37" y="148" width="0" height="20"${rectClassAttr}/>
1492
- </clipPath>
1493
- <path d="m204.5 152.6h-159.8c-2.78 0-4.45 1.69-4.45 3.99v0.11c0 2.04 1.42 3.43 3.64 3.43h160.6c2.88 0 3.67-2.07 3.67-3.43v-0.25c0-2.04-1.48-3.85-3.67-3.85z" fill="url(#${fillGradientId})" clip-path="url(#${clipId})"/>
1494
- <text${txtIdAttr} x="125" y="196" text-anchor="middle" fill="rgba(255,255,255,0.6)" font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif" font-size="8" font-weight="600" letter-spacing="1.5"${txtClassAttr}>${textContent ?? 'Loading...'}</text>
1495
- <defs>
1496
- ${defs}
1497
- </defs>
1498
- </svg>`;
1499
- }
1500
-
1501
1433
  /**
1502
1434
  * Build the loading scene variant of the logo SVG.
1503
1435
  * Uses unique IDs (prefixed with 'ls') to avoid collisions with CSSPreloader.
1504
1436
  */
1505
1437
  function buildLoadingLogoSVG() {
1506
- return buildLogoSVG({
1438
+ return loading.buildLogoSVG({
1507
1439
  idPrefix: 'ls',
1508
1440
  svgStyle: 'width:100%;height:auto;',
1509
1441
  clipRectId: 'ge-loader-rect',
@@ -1684,7 +1616,7 @@ class LoadingScene extends Scene {
1684
1616
  // ─── Progress ──────────────────────────────────────────
1685
1617
  updateLoaderBar(progress) {
1686
1618
  if (this._loaderRect) {
1687
- this._loaderRect.setAttribute('width', String(LOADER_BAR_MAX_WIDTH * progress));
1619
+ this._loaderRect.setAttribute('width', String(loading.LOADER_BAR_MAX_WIDTH * progress));
1688
1620
  }
1689
1621
  if (this._percentEl) {
1690
1622
  const pct = Math.round(progress * 100);
@@ -1766,120 +1698,6 @@ class LoadingScene extends Scene {
1766
1698
  }
1767
1699
  }
1768
1700
 
1769
- const PRELOADER_ID = '__ge-css-preloader__';
1770
- /**
1771
- * Inline SVG logo with animated loader bar.
1772
- * The `#loader` path acts as the progress fill — animated via clipPath.
1773
- */
1774
- const LOGO_SVG = buildLogoSVG({
1775
- idPrefix: 'pl',
1776
- svgClass: 'ge-logo-svg',
1777
- clipRectClass: 'ge-clip-rect',
1778
- textClass: 'ge-preloader-svg-text',
1779
- });
1780
- /**
1781
- * Creates a lightweight CSS-only preloader that appears instantly,
1782
- * BEFORE PixiJS/WebGL is initialized.
1783
- *
1784
- * Displays the Energy8 logo SVG with an animated loader bar.
1785
- */
1786
- function createCSSPreloader(container, config) {
1787
- if (document.getElementById(PRELOADER_ID))
1788
- return;
1789
- const bgColor = typeof config?.backgroundColor === 'string'
1790
- ? config.backgroundColor
1791
- : typeof config?.backgroundColor === 'number'
1792
- ? `#${config.backgroundColor.toString(16).padStart(6, '0')}`
1793
- : '#0a0a1a';
1794
- const bgGradient = config?.backgroundGradient ?? `linear-gradient(135deg, ${bgColor} 0%, #1a1a3e 100%)`;
1795
- const customHTML = config?.cssPreloaderHTML ?? '';
1796
- const el = document.createElement('div');
1797
- el.id = PRELOADER_ID;
1798
- el.innerHTML = customHTML || `
1799
- <div class="ge-preloader-content">
1800
- ${LOGO_SVG}
1801
- </div>
1802
- `;
1803
- const style = document.createElement('style');
1804
- style.textContent = `
1805
- #${PRELOADER_ID} {
1806
- position: absolute;
1807
- top: 0; left: 0;
1808
- width: 100%; height: 100%;
1809
- background: ${bgGradient};
1810
- display: flex;
1811
- align-items: center;
1812
- justify-content: center;
1813
- z-index: 10000;
1814
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
1815
- transition: opacity 0.4s ease-out;
1816
- }
1817
-
1818
- #${PRELOADER_ID}.ge-preloader-hidden {
1819
- opacity: 0;
1820
- pointer-events: none;
1821
- }
1822
-
1823
- .ge-preloader-content {
1824
- display: flex;
1825
- flex-direction: column;
1826
- align-items: center;
1827
- width: 80%;
1828
- max-width: 700px;
1829
- }
1830
-
1831
- .ge-logo-svg {
1832
- width: 100%;
1833
- height: auto;
1834
- filter: drop-shadow(0 0 30px rgba(121, 57, 194, 0.4));
1835
- }
1836
-
1837
- /* Animate the loader clip-rect to shimmer while waiting */
1838
- .ge-clip-rect {
1839
- animation: ge-loader-fill 2s ease-in-out infinite;
1840
- }
1841
-
1842
- @keyframes ge-loader-fill {
1843
- 0% { width: 0; }
1844
- 50% { width: 174; }
1845
- 100% { width: 0; }
1846
- }
1847
-
1848
- /* Animate the SVG text opacity */
1849
- .ge-preloader-svg-text {
1850
- animation: ge-pulse 1.5s ease-in-out infinite;
1851
- }
1852
-
1853
- @keyframes ge-pulse {
1854
- 0%, 100% { opacity: 0.4; }
1855
- 50% { opacity: 1; }
1856
- }
1857
- `;
1858
- container.style.position = container.style.position || 'relative';
1859
- container.appendChild(style);
1860
- container.appendChild(el);
1861
- }
1862
- /**
1863
- * Remove the CSS preloader with a smooth fade-out transition.
1864
- */
1865
- function removeCSSPreloader(container) {
1866
- const el = document.getElementById(PRELOADER_ID);
1867
- if (!el)
1868
- return;
1869
- el.classList.add('ge-preloader-hidden');
1870
- // Remove after transition
1871
- el.addEventListener('transitionend', () => {
1872
- el.remove();
1873
- // Also remove the style element
1874
- const styles = container.querySelectorAll('style');
1875
- for (const style of styles) {
1876
- if (style.textContent?.includes(PRELOADER_ID)) {
1877
- style.remove();
1878
- }
1879
- }
1880
- });
1881
- }
1882
-
1883
1701
  /**
1884
1702
  * FPS overlay for debugging performance.
1885
1703
  *
@@ -2021,6 +1839,8 @@ class GameApplication extends EventEmitter {
2021
1839
  fpsOverlay = null;
2022
1840
  /** Data received from SDK initialization */
2023
1841
  initData = null;
1842
+ /** Platform session (SDK + optional DevBridge). null until start() runs. */
1843
+ platformSession = null;
2024
1844
  /** Configuration */
2025
1845
  config;
2026
1846
  // ─── Private state ──────────────────────────────────────
@@ -2077,7 +1897,7 @@ class GameApplication extends EventEmitter {
2077
1897
  // 1. Resolve container element
2078
1898
  this._container = this.resolveContainer();
2079
1899
  // 2. Show CSS preloader immediately (before PixiJS)
2080
- createCSSPreloader(this._container, this.config.loading);
1900
+ loading.createCSSPreloader(this._container, this.config.loading);
2081
1901
  // 3. Initialize PixiJS
2082
1902
  await this.initPixi();
2083
1903
  // 4. Initialize SDK (if enabled)
@@ -2088,7 +1908,7 @@ class GameApplication extends EventEmitter {
2088
1908
  this.initSubSystems();
2089
1909
  this.emit('initialized');
2090
1910
  // 7. Remove CSS preloader, show Canvas loading screen
2091
- removeCSSPreloader(this._container);
1911
+ loading.removeCSSPreloader(this._container);
2092
1912
  // 8. Load assets with loading screen
2093
1913
  await this.loadAssets(firstScene, sceneData);
2094
1914
  this.emit('loaded');
@@ -2114,7 +1934,7 @@ class GameApplication extends EventEmitter {
2114
1934
  this.input?.destroy();
2115
1935
  this.audio?.destroy();
2116
1936
  this.viewport?.destroy();
2117
- this.sdk?.destroy();
1937
+ this.platformSession?.destroy();
2118
1938
  this.app?.destroy(true, { children: true, texture: true });
2119
1939
  this.emit('destroyed');
2120
1940
  this.removeAllListeners();
@@ -2150,20 +1970,15 @@ class GameApplication extends EventEmitter {
2150
1970
  this.app.canvas.style.height = '100%';
2151
1971
  }
2152
1972
  async initSDK() {
2153
- if (this.config.sdk === false) {
2154
- // Offline / development mode no SDK
2155
- this.initData = null;
2156
- return;
2157
- }
2158
- const sdkOpts = typeof this.config.sdk === 'object' ? this.config.sdk : {};
2159
- this.sdk = new gameSdk.CasinoGameSDK(sdkOpts);
2160
- // Perform the handshake
2161
- this.initData = await this.sdk.ready();
2162
- // Forward SDK events
2163
- this.sdk.on('error', (err) => {
1973
+ // Delegate the SDK handshake (and any optional in-process DevBridge
1974
+ // wiring) to platform-core. The session forwards SDK events upward.
1975
+ this.platformSession = await platformCore.createPlatformSession({ sdk: this.config.sdk });
1976
+ this.sdk = this.platformSession.sdk;
1977
+ this.initData = this.platformSession.initData;
1978
+ this.platformSession.on('error', (err) => {
2164
1979
  this.emit('error', err);
2165
1980
  });
2166
- this.sdk.on('balanceUpdate', (data) => {
1981
+ this.platformSession.on('balanceUpdate', (data) => {
2167
1982
  this.emit('balanceUpdate', data);
2168
1983
  });
2169
1984
  }