@energy8platform/game-engine 0.7.0 → 0.8.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/dist/index.esm.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Ticker, Assets, Container, Text, Application, AnimatedSprite, Texture, Graphics, NineSliceSprite } from 'pixi.js';
2
2
  import { CasinoGameSDK, Bridge } from '@energy8platform/game-sdk';
3
+ export { BridgeDestroyedError, BridgeNotReadyError, SDKError, TimeoutError } from '@energy8platform/game-sdk';
3
4
  import { FancyButton, ProgressBar as ProgressBar$1, ScrollBox } from '@pixi/ui';
4
5
  import { LayoutContainer } from '@pixi/layout/components';
5
6
 
@@ -362,11 +363,12 @@ class Tween {
362
363
  * ```
363
364
  */
364
365
  class SceneManager extends EventEmitter {
366
+ static MAX_TRANSITION_DEPTH = 10;
365
367
  /** Root container that scenes are added to */
366
368
  root;
367
369
  registry = new Map();
368
370
  stack = [];
369
- _transitioning = false;
371
+ _transitionDepth = 0;
370
372
  /** Current viewport dimensions — set by ViewportManager */
371
373
  _width = 0;
372
374
  _height = 0;
@@ -394,12 +396,15 @@ class SceneManager extends EventEmitter {
394
396
  }
395
397
  /** Whether a scene transition is in progress */
396
398
  get isTransitioning() {
397
- return this._transitioning;
399
+ return this._transitionDepth > 0;
398
400
  }
399
401
  /**
400
402
  * Navigate to a scene, replacing the entire stack.
401
403
  */
402
404
  async goto(key, data, transition) {
405
+ if (this._transitionDepth >= SceneManager.MAX_TRANSITION_DEPTH) {
406
+ throw new Error('[SceneManager] Max transition depth exceeded — possible infinite loop');
407
+ }
403
408
  const prevKey = this.currentKey;
404
409
  // Exit all current scenes
405
410
  while (this.stack.length > 0) {
@@ -414,6 +419,9 @@ class SceneManager extends EventEmitter {
414
419
  * Useful for overlays, modals, pause screens.
415
420
  */
416
421
  async push(key, data, transition) {
422
+ if (this._transitionDepth >= SceneManager.MAX_TRANSITION_DEPTH) {
423
+ throw new Error('[SceneManager] Max transition depth exceeded — possible infinite loop');
424
+ }
417
425
  const prevKey = this.currentKey;
418
426
  await this.pushInternal(key, data, transition);
419
427
  this.emit('change', { from: prevKey, to: key });
@@ -426,6 +434,9 @@ class SceneManager extends EventEmitter {
426
434
  console.warn('[SceneManager] Cannot pop the last scene');
427
435
  return;
428
436
  }
437
+ if (this._transitionDepth >= SceneManager.MAX_TRANSITION_DEPTH) {
438
+ throw new Error('[SceneManager] Max transition depth exceeded — possible infinite loop');
439
+ }
429
440
  const prevKey = this.currentKey;
430
441
  await this.popInternal(true, transition);
431
442
  this.emit('change', { from: prevKey, to: this.currentKey });
@@ -434,6 +445,9 @@ class SceneManager extends EventEmitter {
434
445
  * Replace the top scene with a new one.
435
446
  */
436
447
  async replace(key, data, transition) {
448
+ if (this._transitionDepth >= SceneManager.MAX_TRANSITION_DEPTH) {
449
+ throw new Error('[SceneManager] Max transition depth exceeded — possible infinite loop');
450
+ }
437
451
  const prevKey = this.currentKey;
438
452
  await this.popInternal(false);
439
453
  await this.pushInternal(key, data, transition);
@@ -478,7 +492,7 @@ class SceneManager extends EventEmitter {
478
492
  return new Ctor();
479
493
  }
480
494
  async pushInternal(key, data, transition) {
481
- this._transitioning = true;
495
+ this._transitionDepth++;
482
496
  const scene = this.createScene(key);
483
497
  this.root.addChild(scene.container);
484
498
  // Set initial size
@@ -490,20 +504,20 @@ class SceneManager extends EventEmitter {
490
504
  // Push to stack BEFORE onEnter so currentKey is correct during initialization
491
505
  this.stack.push({ scene, key });
492
506
  await scene.onEnter?.(data);
493
- this._transitioning = false;
507
+ this._transitionDepth--;
494
508
  }
495
509
  async popInternal(showTransition, transition) {
496
510
  const entry = this.stack.pop();
497
511
  if (!entry)
498
512
  return;
499
- this._transitioning = true;
513
+ this._transitionDepth++;
500
514
  await entry.scene.onExit?.();
501
515
  if (showTransition) {
502
516
  await this.transitionOut(entry.scene.container, transition);
503
517
  }
504
518
  entry.scene.onDestroy?.();
505
519
  entry.scene.container.destroy({ children: true });
506
- this._transitioning = false;
520
+ this._transitionDepth--;
507
521
  }
508
522
  async transitionIn(container, config) {
509
523
  const type = config?.type ?? TransitionType.NONE;
@@ -2205,6 +2219,9 @@ class GameApplication extends EventEmitter {
2205
2219
  this.sdk.on('error', (err) => {
2206
2220
  this.emit('error', err);
2207
2221
  });
2222
+ this.sdk.on('balanceUpdate', (data) => {
2223
+ this.emit('balanceUpdate', data);
2224
+ });
2208
2225
  }
2209
2226
  applySDKConfig() {
2210
2227
  // If SDK provides viewport dimensions, use them as design reference
@@ -2318,10 +2335,11 @@ class GameApplication extends EventEmitter {
2318
2335
  * ```
2319
2336
  */
2320
2337
  class StateMachine extends EventEmitter {
2338
+ static MAX_TRANSITION_DEPTH = 10;
2321
2339
  _states = new Map();
2322
2340
  _guards = new Map();
2323
2341
  _current = null;
2324
- _transitioning = false;
2342
+ _transitionDepth = 0;
2325
2343
  _context;
2326
2344
  constructor(context) {
2327
2345
  super();
@@ -2333,7 +2351,7 @@ class StateMachine extends EventEmitter {
2333
2351
  }
2334
2352
  /** Whether a transition is in progress */
2335
2353
  get isTransitioning() {
2336
- return this._transitioning;
2354
+ return this._transitionDepth > 0;
2337
2355
  }
2338
2356
  /** State machine context (shared data) */
2339
2357
  get context() {
@@ -2381,9 +2399,8 @@ class StateMachine extends EventEmitter {
2381
2399
  * @returns true if the transition succeeded, false if blocked by a guard
2382
2400
  */
2383
2401
  async transition(to, data) {
2384
- if (this._transitioning) {
2385
- console.warn('[StateMachine] Transition already in progress');
2386
- return false;
2402
+ if (this._transitionDepth >= StateMachine.MAX_TRANSITION_DEPTH) {
2403
+ throw new Error('[StateMachine] Max transition depth exceeded — possible infinite loop');
2387
2404
  }
2388
2405
  const from = this._current;
2389
2406
  // Check guard
@@ -2398,7 +2415,7 @@ class StateMachine extends EventEmitter {
2398
2415
  if (!toState) {
2399
2416
  throw new Error(`[StateMachine] State "${to}" not registered.`);
2400
2417
  }
2401
- this._transitioning = true;
2418
+ this._transitionDepth++;
2402
2419
  try {
2403
2420
  // Exit current state
2404
2421
  if (from !== null) {
@@ -2415,7 +2432,7 @@ class StateMachine extends EventEmitter {
2415
2432
  throw err;
2416
2433
  }
2417
2434
  finally {
2418
- this._transitioning = false;
2435
+ this._transitionDepth--;
2419
2436
  }
2420
2437
  return true;
2421
2438
  }
@@ -2456,7 +2473,7 @@ class StateMachine extends EventEmitter {
2456
2473
  await state?.exit?.(this._context);
2457
2474
  }
2458
2475
  this._current = null;
2459
- this._transitioning = false;
2476
+ this._transitionDepth = 0;
2460
2477
  }
2461
2478
  /**
2462
2479
  * Destroy the state machine.
@@ -4071,7 +4088,8 @@ class DevBridge {
4071
4088
  nextActions: customResult.nextActions ?? ['spin'],
4072
4089
  session: customResult.session ?? null,
4073
4090
  creditPending: false,
4074
- currency: 'USD',
4091
+ bonusFreeSpin: customResult.bonusFreeSpin ?? null,
4092
+ currency: this._config.currency,
4075
4093
  gameId: this._config.gameConfig?.id ?? 'dev-game',
4076
4094
  };
4077
4095
  this.delayedSend('PLAY_RESULT', result, id);
@@ -4085,7 +4103,7 @@ class DevBridge {
4085
4103
  this.delayedSend('BALANCE_UPDATE', { balance: this._balance }, id);
4086
4104
  }
4087
4105
  handleGetState(id) {
4088
- this.delayedSend('STATE_RESPONSE', this._config.session, id);
4106
+ this.delayedSend('STATE_RESPONSE', { session: this._config.session ?? null }, id);
4089
4107
  }
4090
4108
  handleOpenDeposit() {
4091
4109
  if (this._config.debug) {