@energy8platform/platform-core 0.30.10 → 0.31.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 +1 -1
- package/dist/index.cjs.js +246 -29
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +180 -2
- package/dist/index.esm.js +241 -30
- package/dist/index.esm.js.map +1 -1
- package/dist/loading.cjs.js +246 -29
- package/dist/loading.cjs.js.map +1 -1
- package/dist/loading.d.ts +180 -2
- package/dist/loading.esm.js +241 -30
- package/dist/loading.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +7 -0
- package/src/loading/ExternalOverlay.ts +249 -0
- package/src/loading/index.ts +20 -1
- package/src/loading/variants/voidmoon.ts +38 -29
- package/src/types.ts +55 -0
package/dist/index.d.ts
CHANGED
|
@@ -504,7 +504,61 @@ interface AssetBundle {
|
|
|
504
504
|
interface AssetManifest {
|
|
505
505
|
bundles: AssetBundle[];
|
|
506
506
|
}
|
|
507
|
+
/**
|
|
508
|
+
* A loading overlay the GAME owns, covering the gap BEFORE the engine's own loading screen exists:
|
|
509
|
+
* from the browser's first paint to the first frame the engine paints. It does not replace the
|
|
510
|
+
* loading screen — see `LoadingScreenConfig.externalOverlay`.
|
|
511
|
+
*
|
|
512
|
+
* Described structurally on purpose. The implementation this exists for is Artube's
|
|
513
|
+
* `LoaderViewController` (vendored at `@energy8platform/artube-bridge/loader`), whose markup their
|
|
514
|
+
* Vite plugin injects into `index.html` so the overlay is painted before our bundle is even
|
|
515
|
+
* fetched. Naming that type here would tie every consumer of platform-core to the Artube packages;
|
|
516
|
+
* the shape is satisfied with no adapter, and nothing in `@energy8platform/platform-core` knows
|
|
517
|
+
* Artube exists.
|
|
518
|
+
*
|
|
519
|
+
* The engine's use of it, in order:
|
|
520
|
+
* - `showLoader()` once, at the very start of boot, before anything that can throw;
|
|
521
|
+
* - `updateProgress(0..100)` at boot milestones (Pixi up, SDK handshake done, subsystems up) —
|
|
522
|
+
* real progress through the gap, not the asset loading that follows;
|
|
523
|
+
* - `hideLoader()` the moment the engine's loading screen has painted its first frame — AND on
|
|
524
|
+
* every failure path, so a boot that throws can never leave the overlay on screen forever.
|
|
525
|
+
*/
|
|
526
|
+
interface ExternalLoadingOverlay {
|
|
527
|
+
/** Reveal the overlay / its progress affordance. Called once, at the start of boot. */
|
|
528
|
+
showLoader(): void;
|
|
529
|
+
/** Loading progress as a PERCENTAGE, 0–100 (what `ILoaderViewController` expects — the engine's
|
|
530
|
+
* internal 0..1 fraction is converted at the seam). */
|
|
531
|
+
updateProgress(value: number): void;
|
|
532
|
+
/** Dismiss the overlay. Called once; must tolerate being called after a failed boot. */
|
|
533
|
+
hideLoader(): void;
|
|
534
|
+
}
|
|
507
535
|
interface LoadingScreenConfig {
|
|
536
|
+
/**
|
|
537
|
+
* A game-supplied loading overlay covering ONLY the gap before the engine's own loading screen
|
|
538
|
+
* paints — Artube's branded loader is the case this exists for. It is not a replacement: once the
|
|
539
|
+
* loading screen has painted its first frame, the overlay is dismissed and the rest of the boot
|
|
540
|
+
* (brand, progress bar, tap-to-start, minimum display time) is exactly what it is on every other
|
|
541
|
+
* target. Every other option in this object therefore still applies.
|
|
542
|
+
*
|
|
543
|
+
* The two never stack visibly: the built-in preloader is mounted only at the hand-over, and it
|
|
544
|
+
* covers the external overlay (z-index) from the frame it appears in, so there is no gap, no
|
|
545
|
+
* flash and no bare background at the seam.
|
|
546
|
+
*/
|
|
547
|
+
externalOverlay?: ExternalLoadingOverlay;
|
|
548
|
+
/**
|
|
549
|
+
* How long {@link externalOverlay} is guaranteed on screen, in ms, measured from the boot's very
|
|
550
|
+
* first step. Default 1500.
|
|
551
|
+
*
|
|
552
|
+
* The gap this overlay covers is short — a warm boot hands over in a few hundred milliseconds —
|
|
553
|
+
* and a partner's branding that flashes past in under a second has not been shown. The floor also
|
|
554
|
+
* gives a two-phase overlay (Artube's) room to reach its second phase and settle there instead of
|
|
555
|
+
* being cut mid-crossfade. It costs a slow boot nothing: the hand-over happens later than this
|
|
556
|
+
* anyway.
|
|
557
|
+
*
|
|
558
|
+
* Distinct from {@link minDisplayTime}, which is the minimum for the engine's OWN loading screen
|
|
559
|
+
* and is measured from the hand-over. A game that sets both is asking for the sum.
|
|
560
|
+
*/
|
|
561
|
+
externalOverlayMinDisplayTime?: number;
|
|
508
562
|
/**
|
|
509
563
|
* Which visual identity the CSS preloader renders. Defaults to `'energy8'`;
|
|
510
564
|
* an unknown value falls back to the default. Ignored when `cssPreloaderHTML`
|
|
@@ -544,6 +598,130 @@ declare function setCSSPreloaderProgress(progress: number): void;
|
|
|
544
598
|
declare function waitCSSPreloaderTap(): Promise<void>;
|
|
545
599
|
declare function removeCSSPreloader(_container: HTMLElement): Promise<void>;
|
|
546
600
|
|
|
601
|
+
/**
|
|
602
|
+
* A loading overlay the GAME owns, covering the gap the engine cannot: from the
|
|
603
|
+
* browser's first paint until the engine's own loading screen is on screen.
|
|
604
|
+
*
|
|
605
|
+
* The case this exists for is Artube. Their platform ships a branded loader
|
|
606
|
+
* whose markup a Vite plugin injects into `index.html`, so it is painted before
|
|
607
|
+
* the game bundle has even been fetched — earlier than any engine code can
|
|
608
|
+
* possibly run. That window (bundle download → Pixi init → SDK handshake) is
|
|
609
|
+
* otherwise a blank page.
|
|
610
|
+
*
|
|
611
|
+
* ── Where it stops ──────────────────────────────────────────────────────────
|
|
612
|
+
* It stops once BOTH are true: the engine's own loading screen has painted its
|
|
613
|
+
* first frame, and the overlay has had its guaranteed time on screen
|
|
614
|
+
* ({@link DEFAULT_EXTERNAL_MIN_DISPLAY_MS}, `loading.externalOverlayMinDisplayTime`).
|
|
615
|
+
* The gap alone is often only a few hundred milliseconds, which is not long
|
|
616
|
+
* enough for a partner's branding to register — let alone for their two-phase
|
|
617
|
+
* screen to reach its second phase.
|
|
618
|
+
*
|
|
619
|
+
* From there the player gets the engine's brand, the engine's progress bar and
|
|
620
|
+
* the engine's tap-to-start — exactly as on every other target. The external
|
|
621
|
+
* overlay covers the gap; it does not replace the loading screen, and it has no
|
|
622
|
+
* say in the tap gate: by then the player is looking at OUR screen.
|
|
623
|
+
*
|
|
624
|
+
* (This inverts an earlier design in which the external overlay REPLACED the
|
|
625
|
+
* CSS preloader for the whole boot. Handing over at the first frame keeps the
|
|
626
|
+
* game's own loading identity on every platform and leaves the built-in
|
|
627
|
+
* preloader's code path untouched — `CSSPreloader.ts` knows nothing about any
|
|
628
|
+
* of this, so non-Artube targets cannot be affected by a change here.)
|
|
629
|
+
*
|
|
630
|
+
* ── Structural, not nominal ─────────────────────────────────────────────────
|
|
631
|
+
* Nothing in `@energy8platform` names Artube's controller. The game passes an
|
|
632
|
+
* instance; we describe the shape (`ExternalLoadingOverlay`). Artube's vendored
|
|
633
|
+
* `LoaderViewController` (`@energy8platform/artube-bridge/loader`) satisfies it
|
|
634
|
+
* with no adapter.
|
|
635
|
+
*
|
|
636
|
+
* ── Module-level state ──────────────────────────────────────────────────────
|
|
637
|
+
* Singleton, like the CSS preloader next door, and for the same reason: the
|
|
638
|
+
* three call sites (boot, hand-over, failure path) are in different files and
|
|
639
|
+
* there is exactly one loading screen per page.
|
|
640
|
+
*/
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* How long a game-supplied overlay is guaranteed on screen, measured from
|
|
644
|
+
* adoption (which is the first thing the boot does, so in practice from the
|
|
645
|
+
* page's first paint).
|
|
646
|
+
*
|
|
647
|
+
* Without a floor the overlay lives exactly as long as the gap it covers, and
|
|
648
|
+
* that gap is short: a warm local boot handed over at ~500ms and dismissed
|
|
649
|
+
* their screen at ~840ms. A partner's branding that flashes past in under a
|
|
650
|
+
* second has not been shown. 1500ms is the user's number, and it is also the
|
|
651
|
+
* smallest one that fits their two-phase screen whole — a 500ms crossfade into
|
|
652
|
+
* the branded phase plus time to read it — with room for the 300ms dismissal
|
|
653
|
+
* fade on top.
|
|
654
|
+
*
|
|
655
|
+
* Overridable per game: `loading.externalOverlayMinDisplayTime`.
|
|
656
|
+
*/
|
|
657
|
+
declare const DEFAULT_EXTERNAL_MIN_DISPLAY_MS = 1500;
|
|
658
|
+
/** Whether a game-supplied overlay is currently on screen and owned by us. */
|
|
659
|
+
declare function hasExternalOverlay(): boolean;
|
|
660
|
+
/**
|
|
661
|
+
* Take ownership of the game's overlay: from here on the engine is responsible
|
|
662
|
+
* for taking it down, including when the boot fails.
|
|
663
|
+
*
|
|
664
|
+
* Called BEFORE anything in the boot that can throw — notably before the
|
|
665
|
+
* container selector is resolved. Until the engine has adopted it, nothing can
|
|
666
|
+
* dismiss it, and Artube's is already on screen from `index.html`.
|
|
667
|
+
*
|
|
668
|
+
* `showLoader()` is Artube's "reveal the progress affordance"; it does not
|
|
669
|
+
* switch their two-phase screen to the branded phase — only progress does, see
|
|
670
|
+
* {@link advanceExternalOverlay}.
|
|
671
|
+
*
|
|
672
|
+
* @param minDisplayMillis how long the overlay is owed on screen before the
|
|
673
|
+
* hand-over may dismiss it (`loading.externalOverlayMinDisplayTime`); defaults
|
|
674
|
+
* to {@link DEFAULT_EXTERNAL_MIN_DISPLAY_MS}. Adoption is where the clock
|
|
675
|
+
* starts, so it is taken here rather than read at the hand-over.
|
|
676
|
+
*/
|
|
677
|
+
declare function adoptExternalOverlay(overlay: ExternalLoadingOverlay, minDisplayMillis?: number): void;
|
|
678
|
+
/**
|
|
679
|
+
* Report boot progress, as a 0..1 fraction, to a game-supplied overlay.
|
|
680
|
+
*
|
|
681
|
+
* NOT the asset-loading progress the built-in preloader shows — that belongs to
|
|
682
|
+
* the engine's own loading screen, which by then has taken over. This is the
|
|
683
|
+
* pre-first-frame boot: bundle up, Pixi up, SDK handshake done.
|
|
684
|
+
*
|
|
685
|
+
* Sending it is deliberate, not decorative: Artube's loader only crossfades to
|
|
686
|
+
* its BRANDED phase on the first value above zero, so without progress the
|
|
687
|
+
* player never sees the half of their screen the integration exists to show.
|
|
688
|
+
* Values go out as they arrive — the guarantee that the crossfade has room to
|
|
689
|
+
* finish is the dismissal floor ({@link externalOverlayHold}), not a delay
|
|
690
|
+
* here. An earlier design held the first value back for 800ms instead; the
|
|
691
|
+
* floor makes that redundant, and one timer on this overlay is the whole point.
|
|
692
|
+
*
|
|
693
|
+
* Values are clamped to 0..1, kept monotonic, and converted to the PERCENTAGE
|
|
694
|
+
* (0–100) that `ILoaderViewController.updateProgress` documents.
|
|
695
|
+
*/
|
|
696
|
+
declare function advanceExternalOverlay(fraction: number): void;
|
|
697
|
+
/**
|
|
698
|
+
* Wait until the overlay has had its guaranteed time on screen. The hand-over
|
|
699
|
+
* awaits this BEFORE it mounts the engine's own loading screen, so the two
|
|
700
|
+
* timelines do not overlap: their screen is whole and undisturbed for the whole
|
|
701
|
+
* window, then ours mounts, paints, and only then takes over.
|
|
702
|
+
*
|
|
703
|
+
* Resolves immediately when no overlay is adopted (every non-Artube target) or
|
|
704
|
+
* when the floor has already passed (any boot slower than it, which is the
|
|
705
|
+
* normal case in production — this costs nothing there).
|
|
706
|
+
*
|
|
707
|
+
* NOT part of the failure path on purpose: {@link releaseExternalOverlay} is
|
|
708
|
+
* synchronous and immediate, so a boot that throws takes the overlay down at
|
|
709
|
+
* once instead of stranding the player behind a courtesy delay. A release also
|
|
710
|
+
* settles a wait already in progress.
|
|
711
|
+
*/
|
|
712
|
+
declare function externalOverlayHold(): Promise<void>;
|
|
713
|
+
/**
|
|
714
|
+
* Dismiss the overlay and forget it. Idempotent — this is reached both on the
|
|
715
|
+
* normal hand-over and defensively from the failure paths, and a second
|
|
716
|
+
* `hideLoader()` must not fire.
|
|
717
|
+
*
|
|
718
|
+
* @returns whether this call is the one that dismissed it. Callers that can run
|
|
719
|
+
* BEFORE adoption (`createSlotGame`'s `fatal`, which refuses some launches
|
|
720
|
+
* before `GameApplication` exists) use the `false` to fall back to hiding the
|
|
721
|
+
* game's overlay directly.
|
|
722
|
+
*/
|
|
723
|
+
declare function releaseExternalOverlay(): boolean;
|
|
724
|
+
|
|
547
725
|
/**
|
|
548
726
|
* Shared Energy8 SVG logo with a loader bar underneath.
|
|
549
727
|
*
|
|
@@ -700,5 +878,5 @@ interface NativeSimulationResult extends SimulationResult {
|
|
|
700
878
|
volatilityLabel?: string;
|
|
701
879
|
}
|
|
702
880
|
|
|
703
|
-
export { DevBridge, EventEmitter, LOADER_BAR_MAX_WIDTH, PlatformSession, buildLogoSVG, createCSSPreloader, createPlatformSession, removeCSSPreloader, setCSSPreloaderProgress, waitCSSPreloaderTap };
|
|
704
|
-
export type { ActionDefinition, AssetBundle, AssetEntry, AssetManifest, BetLevelsConfig, DevBridgeConfig, DistributionBucket, GameDefinition, LoadingScreenConfig, LuaEngineConfig, LuaPlayResult, MaxWinConfig, NativeSimulationConfig, NativeSimulationResult, PersistentStateConfig, PlatformSessionConfig, PlatformSessionEvents, ReplayConfig, ReplayLaunch, SDKOptions, SessionConfig, SimulationConfig, SimulationRawAccumulators, SimulationResult, StageStats, TransitionRule };
|
|
881
|
+
export { DEFAULT_EXTERNAL_MIN_DISPLAY_MS, DevBridge, EventEmitter, LOADER_BAR_MAX_WIDTH, PlatformSession, adoptExternalOverlay, advanceExternalOverlay, buildLogoSVG, createCSSPreloader, createPlatformSession, externalOverlayHold, hasExternalOverlay, releaseExternalOverlay, removeCSSPreloader, setCSSPreloaderProgress, waitCSSPreloaderTap };
|
|
882
|
+
export type { ActionDefinition, AssetBundle, AssetEntry, AssetManifest, BetLevelsConfig, DevBridgeConfig, DistributionBucket, ExternalLoadingOverlay, GameDefinition, LoadingScreenConfig, LuaEngineConfig, LuaPlayResult, MaxWinConfig, NativeSimulationConfig, NativeSimulationResult, PersistentStateConfig, PlatformSessionConfig, PlatformSessionEvents, ReplayConfig, ReplayLaunch, SDKOptions, SessionConfig, SimulationConfig, SimulationRawAccumulators, SimulationResult, StageStats, TransitionRule };
|
package/dist/index.esm.js
CHANGED
|
@@ -998,40 +998,48 @@ const slottechVariant = {
|
|
|
998
998
|
/** Element ids the lifecycle handle binds to. */
|
|
999
999
|
const RECT_ID = 'ge-vm-loader-rect';
|
|
1000
1000
|
const TEXT_ID = 'ge-vm-loader-text';
|
|
1001
|
-
/**
|
|
1002
|
-
const
|
|
1001
|
+
/** Left edge (SVG units) of the loader bar — aligned with the "V" of the wordmark. */
|
|
1002
|
+
const LOADER_BAR_X = 249.79;
|
|
1003
|
+
/** Max width (SVG units) of the voidmoon loader bar fill. Spans the wordmark: "V" → end of the final "N". */
|
|
1004
|
+
const LOADER_BAR_MAX_WIDTH = 519;
|
|
1003
1005
|
/**
|
|
1004
|
-
*
|
|
1005
|
-
*
|
|
1006
|
-
*
|
|
1007
|
-
*
|
|
1008
|
-
*
|
|
1006
|
+
* voidmoon logo — the official lockup, embedded verbatim as SVG outlines:
|
|
1007
|
+
* the crescent-moon-and-spark mark on the left, the "VOIDMOON" wordmark on the
|
|
1008
|
+
* right, all in #f5f5f5 (filled *and* stroked, exactly as exported — the stroke
|
|
1009
|
+
* is what gives the thin glyphs their weight). The export's `.st0` / `.st1`
|
|
1010
|
+
* classes are inlined as presentation attributes on two wrapper groups so the
|
|
1011
|
+
* logo carries no global CSS into the host page.
|
|
1012
|
+
*
|
|
1013
|
+
* The loader bar + status text are added beneath the lockup in the same
|
|
1014
|
+
* viewBox space; the viewBox is taller than the artwork (140.2) to make room.
|
|
1009
1015
|
*/
|
|
1010
|
-
const LOGO_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="
|
|
1016
|
+
const LOGO_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 769.28 215" class="ge-vm-logo-svg" style="overflow:visible" role="img">
|
|
1011
1017
|
<title>voidmoon</title>
|
|
1012
|
-
<g
|
|
1013
|
-
<
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1018
|
+
<g fill="#f5f5f5" stroke="#f5f5f5" stroke-miterlimit="10">
|
|
1019
|
+
<polygon points="274.76 98.02 249.79 48.07 256.03 48.07 274.76 85.54 293.49 48.07 299.74 48.07 274.76 98.02"/>
|
|
1020
|
+
<path d="M325.51,70.2h6.24c1.12-8.22,7.63-14.65,15.83-15.85v-6.24c-11.99,1.24-20.85,10.36-22.07,22.1ZM353.82,48.1v6.24c9.03,1.32,15.48,9.27,15.48,18.67,0,10.33-8.4,18.73-18.73,18.73-9.33,0-17.41-6.36-18.82-15.3h-6.24c1.49,12.45,11.94,21.55,25.06,21.55,14.14,0,24.97-11.14,24.97-24.97,0-12.92-8.88-23.58-21.72-24.91Z"/>
|
|
1021
|
+
<rect x="400.39" y="48.08" width="6.24" height="49.95"/>
|
|
1022
|
+
<path d="M437.62,54.32l-6.14-6.24h19.14c13.15,0,23.81,10.85,23.81,24.22v.63c0,13.91-10.99,25.1-24.54,25.1-9.62,0-18.41,0-18.41,0l6.14-6.24h12.27c10.17,0,18.41-8.39,18.41-18.73h0c0-10.34-8.24-18.73-18.41-18.73h-12.27Z"/>
|
|
1023
|
+
<polygon points="499.59 98.02 505.83 98.02 505.85 63.24 524.56 79.29 543.29 64.35 543.29 98.02 549.54 98.02 549.54 48.07 524.62 70.2 499.59 48.07 499.59 98.02"/>
|
|
1024
|
+
<polygon points="725.07 68.91 731.32 74.56 731.32 98.02 725.07 98.02 725.07 68.91"/>
|
|
1025
|
+
<polygon points="768.78 48.07 768.78 98.01 762.53 92.38 753.31 84.06 753.31 84.05 725.07 58.51 725.07 48.1 731.32 53.73 762.53 81.87 762.53 48.07 768.78 48.07"/>
|
|
1026
|
+
<path d="M602.55,48.03v6.24c8.22,1.12,14.65,7.63,15.85,15.83h6.24c-1.24-11.99-10.36-20.85-22.1-22.07ZM624.65,76.34h-6.24c-1.32,9.03-9.27,15.48-18.67,15.48-10.33,0-18.73-8.4-18.73-18.73,0-9.33,6.36-17.41,15.3-18.82v-6.24c-12.45,1.49-21.55,11.94-21.55,25.06,0,14.14,11.14,24.97,24.97,24.97,12.92,0,23.58-8.88,24.91-21.72Z"/>
|
|
1027
|
+
<path d="M699.92,75.89h-6.24c-1.12,8.22-7.63,14.65-15.83,15.85v6.24c11.99-1.24,20.85-10.36,22.07-22.1ZM671.6,97.99v-6.24c-9.03-1.32-15.48-9.27-15.48-18.67,0-10.33,8.4-18.73,18.73-18.73,9.33,0,17.41,6.36,18.82,15.3h6.24c-1.49-12.45-11.94-21.55-25.06-21.55-14.14,0-24.97,11.14-24.97,24.97,0,12.92,8.88,23.58,21.72,24.91Z"/>
|
|
1028
|
+
</g>
|
|
1029
|
+
<g fill="#f5f5f5" stroke="#f5f5f5" stroke-miterlimit="10" stroke-width="2">
|
|
1030
|
+
<path d="M199.39,73.47h.01c-1.76,36.6-31.98,65.73-69.01,65.73s-67.52-29.39-69.04-66.2c-.9-.53-1.53-1.47-1.63-2.57l-22.61,2.67c-1.75.2-3.12,1.6-3.3,3.35l-2.47,24.86-3.11-24.95c-.21-1.7-1.56-3.06-3.28-3.25L.12,70.11l25.29-1.97c1.83-.14,3.28-1.57,3.46-3.38l2.47-25.88,2.47,25.88c.18,1.81,1.63,3.24,3.45,3.38l22.45,1.74c.06-1.14.7-2.13,1.64-2.68C62.87,30.38,93.2,1,130.39,1s67.25,29.12,69.01,65.72h0s-.01,0-.01,0c-1.74-30.76-27.24-55.15-58.43-55.15s-58.52,26.2-58.52,58.52,26.2,58.52,58.52,58.52,56.69-24.4,58.43-55.15Z"/>
|
|
1031
|
+
<path d="M202.84,70.1c0,1.86-1.51,3.38-3.37,3.38-.02,0-.05,0-.07,0,.02-.25.02-.49.03-.74-.01.25-.03.49-.04.74-1.83-.04-3.3-1.53-3.3-3.37s1.47-3.34,3.3-3.37c.01.25.03.49.04.74-.01-.25-.01-.49-.03-.74.02,0,.05,0,.07,0,1.86,0,3.37,1.51,3.37,3.38Z"/>
|
|
1032
|
+
<line x1="232.26" y1="70.1" x2="202.84" y2="70.1"/>
|
|
1033
|
+
<line x1="196.09" y1="70.1" x2="166.58" y2="70.1"/>
|
|
1026
1034
|
</g>
|
|
1027
1035
|
|
|
1028
|
-
<rect x="
|
|
1036
|
+
<rect x="${LOADER_BAR_X}" y="170" width="${LOADER_BAR_MAX_WIDTH}" height="6.5" rx="3.25" fill="rgba(255,255,255,0.12)"/>
|
|
1029
1037
|
<clipPath id="vm-loader-clip">
|
|
1030
|
-
<rect id="${RECT_ID}" x="
|
|
1038
|
+
<rect id="${RECT_ID}" x="${LOADER_BAR_X}" y="170" width="0" height="6.5" rx="3.25" class="ge-vm-clip-rect"/>
|
|
1031
1039
|
</clipPath>
|
|
1032
|
-
<rect x="
|
|
1040
|
+
<rect x="${LOADER_BAR_X}" y="170" width="${LOADER_BAR_MAX_WIDTH}" height="6.5" rx="3.25" fill="#9D63FE" clip-path="url(#vm-loader-clip)"/>
|
|
1033
1041
|
|
|
1034
|
-
<text id="${TEXT_ID}" x="
|
|
1042
|
+
<text id="${TEXT_ID}" x="${LOADER_BAR_X + LOADER_BAR_MAX_WIDTH / 2}" y="205" text-anchor="middle" class="ge-vm-text">Loading...</text>
|
|
1035
1043
|
</svg>`;
|
|
1036
1044
|
const voidmoonVariant = {
|
|
1037
1045
|
buildContentHTML() {
|
|
@@ -1063,7 +1071,7 @@ const voidmoonVariant = {
|
|
|
1063
1071
|
|
|
1064
1072
|
@keyframes ge-vm-fill {
|
|
1065
1073
|
0% { width: 0; }
|
|
1066
|
-
50% { width:
|
|
1074
|
+
50% { width: 519px; }
|
|
1067
1075
|
100% { width: 0; }
|
|
1068
1076
|
}
|
|
1069
1077
|
|
|
@@ -1075,9 +1083,9 @@ const voidmoonVariant = {
|
|
|
1075
1083
|
.ge-vm-text {
|
|
1076
1084
|
fill: rgba(255, 255, 255, 0.6);
|
|
1077
1085
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
1078
|
-
font-size:
|
|
1086
|
+
font-size: 14px;
|
|
1079
1087
|
font-weight: 600;
|
|
1080
|
-
letter-spacing:
|
|
1088
|
+
letter-spacing: 2.2px;
|
|
1081
1089
|
animation: ge-vm-pulse 1.5s ease-in-out infinite;
|
|
1082
1090
|
}
|
|
1083
1091
|
|
|
@@ -1463,5 +1471,208 @@ function removeCSSPreloader(_container) {
|
|
|
1463
1471
|
});
|
|
1464
1472
|
}
|
|
1465
1473
|
|
|
1466
|
-
|
|
1474
|
+
let adopted = null;
|
|
1475
|
+
/** Last value pushed, so a repeated milestone cannot walk the bar backwards. */
|
|
1476
|
+
let lastProgress = 0;
|
|
1477
|
+
/** When the overlay was adopted — the origin the minimum display time is measured from. */
|
|
1478
|
+
let adoptedAt = 0;
|
|
1479
|
+
/** How long this overlay is owed on screen; see {@link DEFAULT_EXTERNAL_MIN_DISPLAY_MS}. */
|
|
1480
|
+
let minDisplayMs = 0;
|
|
1481
|
+
/** When the first progress went out, i.e. when their phase crossfade started. 0 = not yet. */
|
|
1482
|
+
let crossfadeStartedAt = 0;
|
|
1483
|
+
/** The one pending timer: the hand-over waiting out the floor below. */
|
|
1484
|
+
let holdTimer = null;
|
|
1485
|
+
let holdResolve = null;
|
|
1486
|
+
/**
|
|
1487
|
+
* How long a game-supplied overlay is guaranteed on screen, measured from
|
|
1488
|
+
* adoption (which is the first thing the boot does, so in practice from the
|
|
1489
|
+
* page's first paint).
|
|
1490
|
+
*
|
|
1491
|
+
* Without a floor the overlay lives exactly as long as the gap it covers, and
|
|
1492
|
+
* that gap is short: a warm local boot handed over at ~500ms and dismissed
|
|
1493
|
+
* their screen at ~840ms. A partner's branding that flashes past in under a
|
|
1494
|
+
* second has not been shown. 1500ms is the user's number, and it is also the
|
|
1495
|
+
* smallest one that fits their two-phase screen whole — a 500ms crossfade into
|
|
1496
|
+
* the branded phase plus time to read it — with room for the 300ms dismissal
|
|
1497
|
+
* fade on top.
|
|
1498
|
+
*
|
|
1499
|
+
* Overridable per game: `loading.externalOverlayMinDisplayTime`.
|
|
1500
|
+
*/
|
|
1501
|
+
const DEFAULT_EXTERNAL_MIN_DISPLAY_MS = 1500;
|
|
1502
|
+
/**
|
|
1503
|
+
* Artube's partner→branded crossfade, `transition: opacity 0.5s ease` in the
|
|
1504
|
+
* markup their Vite plugin injects (vendored verbatim in
|
|
1505
|
+
* `@energy8platform/artube-server/vite`). It is triggered by the first
|
|
1506
|
+
* `updateProgress` above zero.
|
|
1507
|
+
*
|
|
1508
|
+
* The floor below is extended to cover it because a crossfade that starts and
|
|
1509
|
+
* then gets cut is worse than one that never starts: the player sees a
|
|
1510
|
+
* half-formed gradient and a bar that never fills — the three-screens-at-once
|
|
1511
|
+
* artefact this integration hit once and rejected (evidence:
|
|
1512
|
+
* `rejected-immediate-progress-01-Lartube_P_C.png`). Extending the floor is
|
|
1513
|
+
* bounded by construction: only the FIRST progress starts it, and every
|
|
1514
|
+
* progress value comes from a boot milestone that precedes the hand-over.
|
|
1515
|
+
*/
|
|
1516
|
+
const PHASE_CROSSFADE_MS = 500;
|
|
1517
|
+
/**
|
|
1518
|
+
* Call into the game's overlay without letting it break the boot. A third-party
|
|
1519
|
+
* controller is outside our control (Artube's throws if its markup is missing,
|
|
1520
|
+
* for one), and none of these calls is worth failing a game over — least of all
|
|
1521
|
+
* `hideLoader`, where a throw escaping the teardown path is exactly how an
|
|
1522
|
+
* overlay ends up stranded on screen forever.
|
|
1523
|
+
*/
|
|
1524
|
+
function guard(method, fn) {
|
|
1525
|
+
try {
|
|
1526
|
+
fn();
|
|
1527
|
+
}
|
|
1528
|
+
catch (err) {
|
|
1529
|
+
console.warn(`[GameEngine] loading overlay ${method}() threw`, err);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
/** Whether a game-supplied overlay is currently on screen and owned by us. */
|
|
1533
|
+
function hasExternalOverlay() {
|
|
1534
|
+
return adopted !== null;
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Take ownership of the game's overlay: from here on the engine is responsible
|
|
1538
|
+
* for taking it down, including when the boot fails.
|
|
1539
|
+
*
|
|
1540
|
+
* Called BEFORE anything in the boot that can throw — notably before the
|
|
1541
|
+
* container selector is resolved. Until the engine has adopted it, nothing can
|
|
1542
|
+
* dismiss it, and Artube's is already on screen from `index.html`.
|
|
1543
|
+
*
|
|
1544
|
+
* `showLoader()` is Artube's "reveal the progress affordance"; it does not
|
|
1545
|
+
* switch their two-phase screen to the branded phase — only progress does, see
|
|
1546
|
+
* {@link advanceExternalOverlay}.
|
|
1547
|
+
*
|
|
1548
|
+
* @param minDisplayMillis how long the overlay is owed on screen before the
|
|
1549
|
+
* hand-over may dismiss it (`loading.externalOverlayMinDisplayTime`); defaults
|
|
1550
|
+
* to {@link DEFAULT_EXTERNAL_MIN_DISPLAY_MS}. Adoption is where the clock
|
|
1551
|
+
* starts, so it is taken here rather than read at the hand-over.
|
|
1552
|
+
*/
|
|
1553
|
+
function adoptExternalOverlay(overlay, minDisplayMillis) {
|
|
1554
|
+
if (adopted)
|
|
1555
|
+
return; // idempotent: boot step 0 and step 2 both reach here
|
|
1556
|
+
adopted = overlay;
|
|
1557
|
+
lastProgress = 0;
|
|
1558
|
+
crossfadeStartedAt = 0;
|
|
1559
|
+
minDisplayMs =
|
|
1560
|
+
typeof minDisplayMillis === 'number' && Number.isFinite(minDisplayMillis)
|
|
1561
|
+
? Math.max(0, minDisplayMillis)
|
|
1562
|
+
: DEFAULT_EXTERNAL_MIN_DISPLAY_MS;
|
|
1563
|
+
adoptedAt = Date.now();
|
|
1564
|
+
guard('showLoader', () => overlay.showLoader());
|
|
1565
|
+
}
|
|
1566
|
+
/**
|
|
1567
|
+
* Report boot progress, as a 0..1 fraction, to a game-supplied overlay.
|
|
1568
|
+
*
|
|
1569
|
+
* NOT the asset-loading progress the built-in preloader shows — that belongs to
|
|
1570
|
+
* the engine's own loading screen, which by then has taken over. This is the
|
|
1571
|
+
* pre-first-frame boot: bundle up, Pixi up, SDK handshake done.
|
|
1572
|
+
*
|
|
1573
|
+
* Sending it is deliberate, not decorative: Artube's loader only crossfades to
|
|
1574
|
+
* its BRANDED phase on the first value above zero, so without progress the
|
|
1575
|
+
* player never sees the half of their screen the integration exists to show.
|
|
1576
|
+
* Values go out as they arrive — the guarantee that the crossfade has room to
|
|
1577
|
+
* finish is the dismissal floor ({@link externalOverlayHold}), not a delay
|
|
1578
|
+
* here. An earlier design held the first value back for 800ms instead; the
|
|
1579
|
+
* floor makes that redundant, and one timer on this overlay is the whole point.
|
|
1580
|
+
*
|
|
1581
|
+
* Values are clamped to 0..1, kept monotonic, and converted to the PERCENTAGE
|
|
1582
|
+
* (0–100) that `ILoaderViewController.updateProgress` documents.
|
|
1583
|
+
*/
|
|
1584
|
+
function advanceExternalOverlay(fraction) {
|
|
1585
|
+
const overlay = adopted;
|
|
1586
|
+
if (!overlay)
|
|
1587
|
+
return;
|
|
1588
|
+
const clamped = Number.isFinite(fraction) ? Math.max(0, Math.min(1, fraction)) : 0;
|
|
1589
|
+
if (clamped <= lastProgress)
|
|
1590
|
+
return;
|
|
1591
|
+
lastProgress = clamped;
|
|
1592
|
+
// The first non-zero value is what starts their partner→branded crossfade, so
|
|
1593
|
+
// it is also what the floor has to cover.
|
|
1594
|
+
if (clamped > 0 && crossfadeStartedAt === 0)
|
|
1595
|
+
crossfadeStartedAt = Date.now();
|
|
1596
|
+
guard('updateProgress', () => overlay.updateProgress(clamped * 100));
|
|
1597
|
+
}
|
|
1598
|
+
/**
|
|
1599
|
+
* Milliseconds still owed to the overlay before the hand-over may dismiss it:
|
|
1600
|
+
* the minimum display time, extended when needed so a phase crossfade that has
|
|
1601
|
+
* started can finish. Zero once both are satisfied — and zero when there is no
|
|
1602
|
+
* overlay, so nothing on a normal target ever waits.
|
|
1603
|
+
*/
|
|
1604
|
+
function remainingHoldMs() {
|
|
1605
|
+
if (!adopted)
|
|
1606
|
+
return 0;
|
|
1607
|
+
const now = Date.now();
|
|
1608
|
+
const floor = Math.max(adoptedAt + minDisplayMs, crossfadeStartedAt === 0 ? 0 : crossfadeStartedAt + PHASE_CROSSFADE_MS);
|
|
1609
|
+
return Math.max(0, floor - now);
|
|
1610
|
+
}
|
|
1611
|
+
/**
|
|
1612
|
+
* Wait until the overlay has had its guaranteed time on screen. The hand-over
|
|
1613
|
+
* awaits this BEFORE it mounts the engine's own loading screen, so the two
|
|
1614
|
+
* timelines do not overlap: their screen is whole and undisturbed for the whole
|
|
1615
|
+
* window, then ours mounts, paints, and only then takes over.
|
|
1616
|
+
*
|
|
1617
|
+
* Resolves immediately when no overlay is adopted (every non-Artube target) or
|
|
1618
|
+
* when the floor has already passed (any boot slower than it, which is the
|
|
1619
|
+
* normal case in production — this costs nothing there).
|
|
1620
|
+
*
|
|
1621
|
+
* NOT part of the failure path on purpose: {@link releaseExternalOverlay} is
|
|
1622
|
+
* synchronous and immediate, so a boot that throws takes the overlay down at
|
|
1623
|
+
* once instead of stranding the player behind a courtesy delay. A release also
|
|
1624
|
+
* settles a wait already in progress.
|
|
1625
|
+
*/
|
|
1626
|
+
function externalOverlayHold() {
|
|
1627
|
+
// Reaching here means the boot is done and the only thing left is the wait, so the bar is
|
|
1628
|
+
// finished too. Without this it freezes at the last boot milestone (0.85 live) and their screen
|
|
1629
|
+
// is taken away with the bar still short — a bar that never fills, which is the same artefact in
|
|
1630
|
+
// a different costume. Only when a bar is actually on screen: if no progress ever went out the
|
|
1631
|
+
// player is still on the first phase, and starting a crossfade at the hand-over is precisely
|
|
1632
|
+
// what must not happen.
|
|
1633
|
+
if (crossfadeStartedAt !== 0)
|
|
1634
|
+
advanceExternalOverlay(1);
|
|
1635
|
+
const remaining = remainingHoldMs();
|
|
1636
|
+
if (remaining <= 0)
|
|
1637
|
+
return Promise.resolve();
|
|
1638
|
+
return new Promise((resolve) => {
|
|
1639
|
+
const finish = () => {
|
|
1640
|
+
if (holdTimer !== null)
|
|
1641
|
+
clearTimeout(holdTimer);
|
|
1642
|
+
holdTimer = null;
|
|
1643
|
+
holdResolve = null;
|
|
1644
|
+
resolve();
|
|
1645
|
+
};
|
|
1646
|
+
holdResolve = finish;
|
|
1647
|
+
holdTimer = setTimeout(finish, remaining);
|
|
1648
|
+
});
|
|
1649
|
+
}
|
|
1650
|
+
/**
|
|
1651
|
+
* Dismiss the overlay and forget it. Idempotent — this is reached both on the
|
|
1652
|
+
* normal hand-over and defensively from the failure paths, and a second
|
|
1653
|
+
* `hideLoader()` must not fire.
|
|
1654
|
+
*
|
|
1655
|
+
* @returns whether this call is the one that dismissed it. Callers that can run
|
|
1656
|
+
* BEFORE adoption (`createSlotGame`'s `fatal`, which refuses some launches
|
|
1657
|
+
* before `GameApplication` exists) use the `false` to fall back to hiding the
|
|
1658
|
+
* game's overlay directly.
|
|
1659
|
+
*/
|
|
1660
|
+
function releaseExternalOverlay() {
|
|
1661
|
+
// Settle a hand-over that is still waiting out the floor, even if there is
|
|
1662
|
+
// nothing to release. This is the failure path: a boot that threw must not
|
|
1663
|
+
// leave a timer holding the process open (Node-side tests) or a promise that
|
|
1664
|
+
// resolves into a teardown that already happened.
|
|
1665
|
+
if (holdResolve)
|
|
1666
|
+
holdResolve();
|
|
1667
|
+
const overlay = adopted;
|
|
1668
|
+
if (!overlay)
|
|
1669
|
+
return false;
|
|
1670
|
+
adopted = null;
|
|
1671
|
+
lastProgress = 0;
|
|
1672
|
+
crossfadeStartedAt = 0;
|
|
1673
|
+
guard('hideLoader', () => overlay.hideLoader());
|
|
1674
|
+
return true;
|
|
1675
|
+
}
|
|
1676
|
+
|
|
1677
|
+
export { DEFAULT_EXTERNAL_MIN_DISPLAY_MS, DevBridge, EventEmitter, LOADER_BAR_MAX_WIDTH$2 as LOADER_BAR_MAX_WIDTH, PlatformSession, adoptExternalOverlay, advanceExternalOverlay, buildLogoSVG, createCSSPreloader, createPlatformSession, externalOverlayHold, hasExternalOverlay, releaseExternalOverlay, removeCSSPreloader, setCSSPreloaderProgress, waitCSSPreloaderTap };
|
|
1467
1678
|
//# sourceMappingURL=index.esm.js.map
|