@energy8platform/game-engine 0.7.1 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@energy8platform/game-engine",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "Universal casino game engine built on PixiJS v8 and @energy8platform/game-sdk",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs.js",
@@ -25,12 +25,14 @@ interface SceneManagerEvents {
25
25
  * ```
26
26
  */
27
27
  export class SceneManager extends EventEmitter<SceneManagerEvents> {
28
+ private static MAX_TRANSITION_DEPTH = 10;
29
+
28
30
  /** Root container that scenes are added to */
29
31
  public root!: Container;
30
32
 
31
33
  private registry = new Map<string, SceneConstructor>();
32
34
  private stack: SceneEntry[] = [];
33
- private _transitioning = false;
35
+ private _transitionDepth = 0;
34
36
 
35
37
  /** Current viewport dimensions — set by ViewportManager */
36
38
  private _width = 0;
@@ -64,7 +66,7 @@ export class SceneManager extends EventEmitter<SceneManagerEvents> {
64
66
 
65
67
  /** Whether a scene transition is in progress */
66
68
  get isTransitioning(): boolean {
67
- return this._transitioning;
69
+ return this._transitionDepth > 0;
68
70
  }
69
71
 
70
72
  /**
@@ -75,6 +77,9 @@ export class SceneManager extends EventEmitter<SceneManagerEvents> {
75
77
  data?: unknown,
76
78
  transition?: TransitionConfig,
77
79
  ): Promise<void> {
80
+ if (this._transitionDepth >= SceneManager.MAX_TRANSITION_DEPTH) {
81
+ throw new Error('[SceneManager] Max transition depth exceeded — possible infinite loop');
82
+ }
78
83
  const prevKey = this.currentKey;
79
84
 
80
85
  // Exit all current scenes
@@ -96,6 +101,9 @@ export class SceneManager extends EventEmitter<SceneManagerEvents> {
96
101
  data?: unknown,
97
102
  transition?: TransitionConfig,
98
103
  ): Promise<void> {
104
+ if (this._transitionDepth >= SceneManager.MAX_TRANSITION_DEPTH) {
105
+ throw new Error('[SceneManager] Max transition depth exceeded — possible infinite loop');
106
+ }
99
107
  const prevKey = this.currentKey;
100
108
  await this.pushInternal(key, data, transition);
101
109
  this.emit('change', { from: prevKey, to: key });
@@ -109,6 +117,9 @@ export class SceneManager extends EventEmitter<SceneManagerEvents> {
109
117
  console.warn('[SceneManager] Cannot pop the last scene');
110
118
  return;
111
119
  }
120
+ if (this._transitionDepth >= SceneManager.MAX_TRANSITION_DEPTH) {
121
+ throw new Error('[SceneManager] Max transition depth exceeded — possible infinite loop');
122
+ }
112
123
  const prevKey = this.currentKey;
113
124
  await this.popInternal(true, transition);
114
125
  this.emit('change', { from: prevKey, to: this.currentKey! });
@@ -122,6 +133,9 @@ export class SceneManager extends EventEmitter<SceneManagerEvents> {
122
133
  data?: unknown,
123
134
  transition?: TransitionConfig,
124
135
  ): Promise<void> {
136
+ if (this._transitionDepth >= SceneManager.MAX_TRANSITION_DEPTH) {
137
+ throw new Error('[SceneManager] Max transition depth exceeded — possible infinite loop');
138
+ }
125
139
  const prevKey = this.currentKey;
126
140
  await this.popInternal(false);
127
141
  await this.pushInternal(key, data, transition);
@@ -177,7 +191,7 @@ export class SceneManager extends EventEmitter<SceneManagerEvents> {
177
191
  data?: unknown,
178
192
  transition?: TransitionConfig,
179
193
  ): Promise<void> {
180
- this._transitioning = true;
194
+ this._transitionDepth++;
181
195
 
182
196
  const scene = this.createScene(key);
183
197
  this.root.addChild(scene.container);
@@ -195,7 +209,7 @@ export class SceneManager extends EventEmitter<SceneManagerEvents> {
195
209
 
196
210
  await scene.onEnter?.(data);
197
211
 
198
- this._transitioning = false;
212
+ this._transitionDepth--;
199
213
  }
200
214
 
201
215
  private async popInternal(
@@ -205,7 +219,7 @@ export class SceneManager extends EventEmitter<SceneManagerEvents> {
205
219
  const entry = this.stack.pop();
206
220
  if (!entry) return;
207
221
 
208
- this._transitioning = true;
222
+ this._transitionDepth++;
209
223
 
210
224
  await entry.scene.onExit?.();
211
225
 
@@ -216,7 +230,7 @@ export class SceneManager extends EventEmitter<SceneManagerEvents> {
216
230
  entry.scene.onDestroy?.();
217
231
  entry.scene.container.destroy({ children: true });
218
232
 
219
- this._transitioning = false;
233
+ this._transitionDepth--;
220
234
  }
221
235
 
222
236
  private async transitionIn(
@@ -58,10 +58,12 @@ interface StateMachineEvents {
58
58
  * ```
59
59
  */
60
60
  export class StateMachine<TContext = Record<string, unknown>> extends EventEmitter<StateMachineEvents> {
61
+ private static MAX_TRANSITION_DEPTH = 10;
62
+
61
63
  private _states = new Map<string, StateConfig<TContext>>();
62
64
  private _guards = new Map<string, (ctx: TContext) => boolean>();
63
65
  private _current: string | null = null;
64
- private _transitioning = false;
66
+ private _transitionDepth = 0;
65
67
  private _context: TContext;
66
68
 
67
69
  constructor(context: TContext) {
@@ -76,7 +78,7 @@ export class StateMachine<TContext = Record<string, unknown>> extends EventEmitt
76
78
 
77
79
  /** Whether a transition is in progress */
78
80
  get isTransitioning(): boolean {
79
- return this._transitioning;
81
+ return this._transitionDepth > 0;
80
82
  }
81
83
 
82
84
  /** State machine context (shared data) */
@@ -131,9 +133,10 @@ export class StateMachine<TContext = Record<string, unknown>> extends EventEmitt
131
133
  * @returns true if the transition succeeded, false if blocked by a guard
132
134
  */
133
135
  async transition(to: string, data?: unknown): Promise<boolean> {
134
- if (this._transitioning) {
135
- console.warn('[StateMachine] Transition already in progress');
136
- return false;
136
+ if (this._transitionDepth >= StateMachine.MAX_TRANSITION_DEPTH) {
137
+ throw new Error(
138
+ '[StateMachine] Max transition depth exceeded — possible infinite loop',
139
+ );
137
140
  }
138
141
 
139
142
  const from = this._current;
@@ -152,7 +155,7 @@ export class StateMachine<TContext = Record<string, unknown>> extends EventEmitt
152
155
  throw new Error(`[StateMachine] State "${to}" not registered.`);
153
156
  }
154
157
 
155
- this._transitioning = true;
158
+ this._transitionDepth++;
156
159
 
157
160
  try {
158
161
  // Exit current state
@@ -170,7 +173,7 @@ export class StateMachine<TContext = Record<string, unknown>> extends EventEmitt
170
173
  this.emit('error', err instanceof Error ? err : new Error(String(err)));
171
174
  throw err;
172
175
  } finally {
173
- this._transitioning = false;
176
+ this._transitionDepth--;
174
177
  }
175
178
 
176
179
  return true;
@@ -213,7 +216,7 @@ export class StateMachine<TContext = Record<string, unknown>> extends EventEmitt
213
216
  await state?.exit?.(this._context);
214
217
  }
215
218
  this._current = null;
216
- this._transitioning = false;
219
+ this._transitionDepth = 0;
217
220
  }
218
221
 
219
222
  /**