@energy8platform/game-engine 0.10.2 → 0.10.4

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/vite.esm.js CHANGED
@@ -64,31 +64,89 @@ await import('${entrySrc}');
64
64
  }
65
65
  // ─── Lua Plugin ─────────────────────────────────────────
66
66
  /**
67
- * Vite plugin that enables importing `.lua` files as raw strings
68
- * and triggers a full page reload on `.lua` file changes.
67
+ * Vite plugin that:
68
+ * 1. Enables importing `.lua` files as raw strings with HMR
69
+ * 2. Runs a LuaEngine on the Vite dev server (Node.js) via `POST /__lua-play`
69
70
  *
70
- * Also shims Node.js built-in modules (`os`, `child_process`, etc.) that
71
- * `fengari` references at the top level so the library can be pre-bundled
72
- * by esbuild and run in the browser without errors.
71
+ * fengari runs server-side only no browser shims needed.
73
72
  */
74
- const FENGARI_NODE_SHIMS = {
75
- os: 'export function platform() { return "browser"; } export default { platform };',
76
- child_process: 'export default {};',
77
- fs: 'export default {};',
78
- };
79
- function luaPlugin() {
73
+ function luaPlugin(configPath) {
74
+ let luaEngine = null;
75
+ let viteServer = null;
76
+ async function initEngine() {
77
+ if (!viteServer)
78
+ return;
79
+ try {
80
+ // Invalidate cached modules so HMR picks up changes
81
+ const root = viteServer.config.root;
82
+ const fullConfigPath = configPath.startsWith('.')
83
+ ? root + '/' + configPath.replace(/^\.\//, '')
84
+ : configPath;
85
+ // Invalidate the config module and its dependencies
86
+ const configMod = viteServer.moduleGraph.getModuleById(fullConfigPath);
87
+ if (configMod)
88
+ viteServer.moduleGraph.invalidateModule(configMod);
89
+ // ssrLoadModule handles TS transpilation and resolves all imports
90
+ const mod = await viteServer.ssrLoadModule(fullConfigPath);
91
+ const config = mod.default ?? mod.config ?? mod;
92
+ if (!config.luaScript || !config.gameDefinition) {
93
+ console.log('[LuaPlugin] No luaScript/gameDefinition in config — Lua server disabled');
94
+ luaEngine = null;
95
+ return;
96
+ }
97
+ // Load LuaEngine via SSR (fengari runs natively in Node.js)
98
+ const luaMod = await viteServer.ssrLoadModule('@energy8platform/game-engine/lua');
99
+ const { LuaEngine } = luaMod;
100
+ if (luaEngine)
101
+ luaEngine.destroy();
102
+ luaEngine = new LuaEngine({
103
+ script: config.luaScript,
104
+ gameDefinition: config.gameDefinition,
105
+ seed: config.luaSeed,
106
+ });
107
+ console.log('[LuaPlugin] LuaEngine initialized (server-side)');
108
+ }
109
+ catch (e) {
110
+ console.warn('[LuaPlugin] Failed to initialize LuaEngine:', e.message);
111
+ luaEngine = null;
112
+ }
113
+ }
80
114
  return {
81
115
  name: 'game-engine:lua',
82
116
  apply: 'serve',
83
- resolveId(id) {
84
- if (id in FENGARI_NODE_SHIMS)
85
- return `\0fengari-shim:${id}`;
86
- },
87
- load(id) {
88
- if (id.startsWith('\0fengari-shim:')) {
89
- const mod = id.slice('\0fengari-shim:'.length);
90
- return FENGARI_NODE_SHIMS[mod];
91
- }
117
+ async configureServer(server) {
118
+ viteServer = server;
119
+ await initEngine();
120
+ // POST /__lua-play — execute Lua on the server
121
+ server.middlewares.use('/__lua-play', (req, res) => {
122
+ if (req.method !== 'POST') {
123
+ res.statusCode = 405;
124
+ res.end('Method Not Allowed');
125
+ return;
126
+ }
127
+ let body = '';
128
+ req.on('data', (chunk) => { body += chunk; });
129
+ req.on('end', () => {
130
+ try {
131
+ if (!luaEngine) {
132
+ res.statusCode = 503;
133
+ res.setHeader('Content-Type', 'application/json');
134
+ res.end(JSON.stringify({ error: 'LuaEngine not initialized' }));
135
+ return;
136
+ }
137
+ const params = JSON.parse(body);
138
+ const result = luaEngine.execute(params);
139
+ res.statusCode = 200;
140
+ res.setHeader('Content-Type', 'application/json');
141
+ res.end(JSON.stringify(result));
142
+ }
143
+ catch (e) {
144
+ res.statusCode = 500;
145
+ res.setHeader('Content-Type', 'application/json');
146
+ res.end(JSON.stringify({ error: e.message }));
147
+ }
148
+ });
149
+ });
92
150
  },
93
151
  transform(code, id) {
94
152
  if (id.endsWith('.lua')) {
@@ -98,8 +156,10 @@ function luaPlugin() {
98
156
  };
99
157
  }
100
158
  },
101
- handleHotUpdate({ file, server }) {
102
- if (file.endsWith('.lua')) {
159
+ async handleHotUpdate({ file, server }) {
160
+ if (file.endsWith('.lua') || file.includes('dev.config')) {
161
+ console.log('[LuaPlugin] Reloading LuaEngine...');
162
+ await initEngine();
103
163
  server.ws.send({ type: 'full-reload' });
104
164
  return [];
105
165
  }
@@ -132,26 +192,15 @@ function defineGameConfig(config = {}) {
132
192
  if (config.devBridge) {
133
193
  const configPath = config.devBridgeConfig ?? './dev.config';
134
194
  plugins.push(devBridgePlugin(configPath));
135
- plugins.push(luaPlugin());
195
+ plugins.push(luaPlugin(configPath));
136
196
  }
137
197
  const userVite = config.vite ?? {};
138
- // fengari (Lua 5.3 in JS) is a CJS Node.js package that references `process`
139
- // and `require('os')` at module level. Provide shims so it works in the browser.
140
- const fengariShims = config.devBridge ? {
141
- 'process.versions.node': 'undefined',
142
- 'process.env': '({})',
143
- 'process.platform': '"browser"',
144
- } : {};
145
198
  return {
146
199
  base: config.base ?? '/',
147
200
  plugins: [
148
201
  ...plugins,
149
202
  ...(userVite.plugins ?? []),
150
203
  ],
151
- define: {
152
- ...fengariShims,
153
- ...userVite.define,
154
- },
155
204
  build: {
156
205
  target: 'esnext',
157
206
  assetsInlineLimit: 8192,
@@ -193,10 +242,10 @@ function defineGameConfig(config = {}) {
193
242
  'yoga-layout/load',
194
243
  'react',
195
244
  'react-dom',
196
- ...(config.devBridge ? ['fengari'] : []),
197
245
  ],
198
246
  exclude: [
199
247
  'yoga-layout',
248
+ 'fengari',
200
249
  ],
201
250
  esbuildOptions: {
202
251
  target: 'esnext',
@@ -1 +1 @@
1
- {"version":3,"file":"vite.esm.js","sources":["../src/vite/index.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"vite.esm.js","sources":["../src/vite/index.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@energy8platform/game-engine",
3
- "version": "0.10.2",
3
+ "version": "0.10.4",
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",
@@ -9,7 +9,6 @@ import {
9
9
  type PlayParams,
10
10
  } from '@energy8platform/game-sdk';
11
11
  import type { GameDefinition } from '../lua/types';
12
- import { LuaEngine } from '../lua/LuaEngine';
13
12
 
14
13
  export interface DevBridgeConfig {
15
14
  /** Mock initial balance */
@@ -28,7 +27,7 @@ export interface DevBridgeConfig {
28
27
  networkDelay?: number;
29
28
  /** Enable debug logging */
30
29
  debug?: boolean;
31
- /** Lua script source code. When set, overrides onPlay with LuaEngine. */
30
+ /** Lua script source code. When set, play requests are routed to the Vite dev server's LuaEngine. */
32
31
  luaScript?: string;
33
32
  /** Game definition for Lua engine (actions, transitions, etc.) */
34
33
  gameDefinition?: GameDefinition;
@@ -60,11 +59,11 @@ const DEFAULT_CONFIG: Omit<Required<DevBridgeConfig>, 'luaScript' | 'gameDefinit
60
59
  * `CasinoGameSDK` via a shared in-memory `MemoryChannel`, removing
61
60
  * the need for postMessage and iframes.
62
61
  *
63
- * This allows games to be developed and tested without a real backend.
62
+ * When `luaScript` is set, play requests are sent to the Vite dev server
63
+ * which runs LuaEngine in Node.js — no fengari in the browser.
64
64
  *
65
65
  * @example
66
66
  * ```ts
67
- * // In your dev entry point or vite plugin
68
67
  * import { DevBridge } from '@energy8platform/game-engine/debug';
69
68
  *
70
69
  * const devBridge = new DevBridge({
@@ -73,10 +72,7 @@ const DEFAULT_CONFIG: Omit<Required<DevBridgeConfig>, 'luaScript' | 'gameDefinit
73
72
  * gameConfig: { id: 'my-slot', type: 'slot', betLevels: [0.2, 0.5, 1, 2] },
74
73
  * onPlay: ({ action, bet }) => ({
75
74
  * totalWin: Math.random() > 0.5 ? bet * (Math.random() * 20) : 0,
76
- * data: {
77
- * matrix: generateRandomMatrix(5, 3, 10),
78
- * win_lines: [],
79
- * },
75
+ * data: { matrix: [[1,2,3],[4,5,6],[7,8,9]] },
80
76
  * }),
81
77
  * });
82
78
  * devBridge.start();
@@ -87,12 +83,12 @@ export class DevBridge {
87
83
  private _balance: number;
88
84
  private _roundCounter = 0;
89
85
  private _bridge: Bridge | null = null;
90
- private _luaEngine: LuaEngine | null = null;
86
+ private _useLuaServer: boolean;
91
87
 
92
88
  constructor(config: DevBridgeConfig = {}) {
93
89
  this._config = { ...DEFAULT_CONFIG, ...config };
94
90
  this._balance = this._config.balance;
95
- this.initLuaEngine();
91
+ this._useLuaServer = !!(this._config.luaScript && this._config.gameDefinition);
96
92
  }
97
93
 
98
94
  /** Current mock balance */
@@ -133,7 +129,8 @@ export class DevBridge {
133
129
  });
134
130
 
135
131
  if (this._config.debug) {
136
- console.log('[DevBridge] Started listening via Bridge (devMode)');
132
+ const mode = this._useLuaServer ? 'Lua (server-side)' : 'onPlay callback';
133
+ console.log(`[DevBridge] Started — mode: ${mode}`);
137
134
  }
138
135
  }
139
136
 
@@ -158,24 +155,6 @@ export class DevBridge {
158
155
  /** Destroy the dev bridge */
159
156
  destroy(): void {
160
157
  this.stop();
161
- if (this._luaEngine) {
162
- this._luaEngine.destroy();
163
- this._luaEngine = null;
164
- }
165
- }
166
-
167
- private initLuaEngine(): void {
168
- if (!this._config.luaScript || !this._config.gameDefinition) return;
169
-
170
- this._luaEngine = new LuaEngine({
171
- script: this._config.luaScript,
172
- gameDefinition: this._config.gameDefinition,
173
- seed: this._config.luaSeed,
174
- });
175
-
176
- if (this._config.debug) {
177
- console.log('[DevBridge] LuaEngine initialized');
178
- }
179
158
  }
180
159
 
181
160
  // ─── Message Handling ──────────────────────────────────
@@ -202,28 +181,18 @@ export class DevBridge {
202
181
  this._balance -= bet;
203
182
  this._roundCounter++;
204
183
 
205
- let result: PlayResultData;
206
-
207
- if (this._luaEngine) {
208
- // Use LuaEngine for real Lua execution
209
- const luaResult = this._luaEngine.execute({ action, bet, roundId, params });
210
- const totalWin = luaResult.creditDeferred ? 0 : luaResult.totalWin;
211
-
212
- this._balance += totalWin;
213
-
214
- result = {
215
- roundId: roundId ?? `dev-round-${this._roundCounter}`,
216
- action,
217
- balanceAfter: this._balance,
218
- totalWin: Math.round(luaResult.totalWin * 100) / 100,
219
- data: luaResult.data,
220
- nextActions: luaResult.nextActions,
221
- session: luaResult.session,
222
- creditPending: luaResult.creditDeferred,
223
- bonusFreeSpin: null,
224
- currency: this._config.currency,
225
- gameId: this._config.gameConfig?.id ?? 'dev-game',
226
- };
184
+ if (this._useLuaServer) {
185
+ // Send to Vite dev server for Lua execution
186
+ this.executeLuaOnServer({ action, bet, roundId, params })
187
+ .then((result) => {
188
+ this._bridge?.send('PLAY_RESULT', result, id);
189
+ })
190
+ .catch((err) => {
191
+ console.error('[DevBridge] Lua server error:', err);
192
+ // Refund bet on error
193
+ this._balance += bet;
194
+ this._bridge?.send('PLAY_RESULT', this.buildFallbackResult(action, bet, roundId), id);
195
+ });
227
196
  } else {
228
197
  // Fallback to onPlay callback
229
198
  const customResult = this._config.onPlay({ action, bet, roundId, params });
@@ -231,7 +200,7 @@ export class DevBridge {
231
200
 
232
201
  this._balance += totalWin;
233
202
 
234
- result = {
203
+ const result: PlayResultData = {
235
204
  roundId: roundId ?? `dev-round-${this._roundCounter}`,
236
205
  action,
237
206
  balanceAfter: this._balance,
@@ -244,9 +213,57 @@ export class DevBridge {
244
213
  currency: this._config.currency,
245
214
  gameId: this._config.gameConfig?.id ?? 'dev-game',
246
215
  };
216
+
217
+ this.delayedSend('PLAY_RESULT', result, id);
247
218
  }
219
+ }
220
+
221
+ private async executeLuaOnServer(params: PlayParams): Promise<PlayResultData> {
222
+ const response = await fetch('/__lua-play', {
223
+ method: 'POST',
224
+ headers: { 'Content-Type': 'application/json' },
225
+ body: JSON.stringify(params),
226
+ });
248
227
 
249
- this.delayedSend('PLAY_RESULT', result, id);
228
+ if (!response.ok) {
229
+ const err = await response.json().catch(() => ({ error: 'Unknown error' }));
230
+ throw new Error(err.error ?? `HTTP ${response.status}`);
231
+ }
232
+
233
+ const luaResult = await response.json();
234
+
235
+ const totalWin = luaResult.creditDeferred ? 0 : luaResult.totalWin;
236
+ this._balance += totalWin;
237
+
238
+ return {
239
+ roundId: params.roundId ?? `dev-round-${this._roundCounter}`,
240
+ action: params.action,
241
+ balanceAfter: this._balance,
242
+ totalWin: Math.round(luaResult.totalWin * 100) / 100,
243
+ data: luaResult.data,
244
+ nextActions: luaResult.nextActions,
245
+ session: luaResult.session,
246
+ creditPending: luaResult.creditDeferred,
247
+ bonusFreeSpin: null,
248
+ currency: this._config.currency,
249
+ gameId: this._config.gameConfig?.id ?? 'dev-game',
250
+ };
251
+ }
252
+
253
+ private buildFallbackResult(action: string, bet: number, roundId?: string): PlayResultData {
254
+ return {
255
+ roundId: roundId ?? `dev-round-${this._roundCounter}`,
256
+ action,
257
+ balanceAfter: this._balance,
258
+ totalWin: 0,
259
+ data: { error: 'Lua execution failed' },
260
+ nextActions: ['spin'],
261
+ session: null,
262
+ creditPending: false,
263
+ bonusFreeSpin: null,
264
+ currency: this._config.currency,
265
+ gameId: this._config.gameConfig?.id ?? 'dev-game',
266
+ };
250
267
  }
251
268
 
252
269
  private handlePlayAck(_payload: PlayResultAckPayload): void {
@@ -265,7 +282,7 @@ export class DevBridge {
265
282
 
266
283
  private handleOpenDeposit(): void {
267
284
  if (this._config.debug) {
268
- console.log('[DevBridge] 💰 Open deposit requested (mock: adding 1000)');
285
+ console.log('[DevBridge] Open deposit requested (mock: adding 1000)');
269
286
  }
270
287
  this._balance += 1000;
271
288
  this._bridge?.send('BALANCE_UPDATE', { balance: this._balance });
package/src/index.ts CHANGED
@@ -101,14 +101,9 @@ export type { DevBridgeConfig } from './debug/DevBridge';
101
101
  export { FPSOverlay } from './debug/FPSOverlay';
102
102
 
103
103
  // ─── Lua ────────────────────────────────────────────────
104
- // NOTE: fengari is an optional peer dependency.
105
- // Import Lua components from '@energy8platform/game-engine/lua' sub-path
106
- // to avoid pulling in fengari when it isn't installed.
107
- export { LuaEngine } from './lua/LuaEngine';
108
- export { LuaEngineAPI, createSeededRng } from './lua/LuaEngineAPI';
109
- export { ActionRouter, evaluateCondition } from './lua/ActionRouter';
110
- export { SessionManager } from './lua/SessionManager';
111
- export { PersistentState } from './lua/PersistentState';
104
+ // Lua module is Node.js only (fengari). Use the sub-path import:
105
+ // import { LuaEngine } from '@energy8platform/game-engine/lua'
106
+ // Re-export only types (zero runtime cost, no fengari in browser bundle).
112
107
  export type {
113
108
  GameDefinition,
114
109
  ActionDefinition,
@@ -6,9 +6,8 @@ import { SessionManager } from './SessionManager';
6
6
  import { PersistentState } from './PersistentState';
7
7
 
8
8
  // fengari — Lua 5.3 in pure JavaScript
9
- // eslint-disable-next-line @typescript-eslint/no-var-requires
10
- declare const require: (module: string) => any;
11
- const fengari = require('fengari');
9
+ import fengari from 'fengari';
10
+
12
11
  const { lua, lauxlib, lualib } = fengari;
13
12
  const { to_luastring, to_jsstring } = fengari;
14
13
 
@@ -1,9 +1,6 @@
1
1
  import type { GameDefinition } from './types';
2
+ import fengari from 'fengari';
2
3
 
3
- // fengari is a CJS module — use dynamic import workaround for ESM compatibility
4
- // eslint-disable-next-line @typescript-eslint/no-var-requires
5
- declare const require: (module: string) => any;
6
- const fengari = require('fengari');
7
4
  const { lua, lauxlib } = fengari;
8
5
  const { to_luastring, to_jsstring } = fengari;
9
6
 
@@ -0,0 +1,10 @@
1
+ declare module 'fengari' {
2
+ const fengari: {
3
+ lua: any;
4
+ lauxlib: any;
5
+ lualib: any;
6
+ to_luastring: (str: string) => Uint8Array;
7
+ to_jsstring: (str: Uint8Array) => string;
8
+ };
9
+ export default fengari;
10
+ }
package/src/vite/index.ts CHANGED
@@ -91,33 +91,97 @@ await import('${entrySrc}');
91
91
  // ─── Lua Plugin ─────────────────────────────────────────
92
92
 
93
93
  /**
94
- * Vite plugin that enables importing `.lua` files as raw strings
95
- * and triggers a full page reload on `.lua` file changes.
94
+ * Vite plugin that:
95
+ * 1. Enables importing `.lua` files as raw strings with HMR
96
+ * 2. Runs a LuaEngine on the Vite dev server (Node.js) via `POST /__lua-play`
96
97
  *
97
- * Also shims Node.js built-in modules (`os`, `child_process`, etc.) that
98
- * `fengari` references at the top level so the library can be pre-bundled
99
- * by esbuild and run in the browser without errors.
98
+ * fengari runs server-side only no browser shims needed.
100
99
  */
101
- const FENGARI_NODE_SHIMS: Record<string, string> = {
102
- os: 'export function platform() { return "browser"; } export default { platform };',
103
- child_process: 'export default {};',
104
- fs: 'export default {};',
105
- };
100
+ function luaPlugin(configPath: string): Plugin {
101
+ let luaEngine: any = null;
102
+ let viteServer: any = null;
103
+
104
+ async function initEngine() {
105
+ if (!viteServer) return;
106
+
107
+ try {
108
+ // Invalidate cached modules so HMR picks up changes
109
+ const root = viteServer.config.root;
110
+ const fullConfigPath = configPath.startsWith('.')
111
+ ? root + '/' + configPath.replace(/^\.\//, '')
112
+ : configPath;
113
+
114
+ // Invalidate the config module and its dependencies
115
+ const configMod = viteServer.moduleGraph.getModuleById(fullConfigPath);
116
+ if (configMod) viteServer.moduleGraph.invalidateModule(configMod);
117
+
118
+ // ssrLoadModule handles TS transpilation and resolves all imports
119
+ const mod = await viteServer.ssrLoadModule(fullConfigPath);
120
+ const config = mod.default ?? mod.config ?? mod;
121
+
122
+ if (!config.luaScript || !config.gameDefinition) {
123
+ console.log('[LuaPlugin] No luaScript/gameDefinition in config — Lua server disabled');
124
+ luaEngine = null;
125
+ return;
126
+ }
127
+
128
+ // Load LuaEngine via SSR (fengari runs natively in Node.js)
129
+ const luaMod = await viteServer.ssrLoadModule('@energy8platform/game-engine/lua');
130
+ const { LuaEngine } = luaMod;
131
+
132
+ if (luaEngine) luaEngine.destroy();
133
+ luaEngine = new LuaEngine({
134
+ script: config.luaScript,
135
+ gameDefinition: config.gameDefinition,
136
+ seed: config.luaSeed,
137
+ });
138
+ console.log('[LuaPlugin] LuaEngine initialized (server-side)');
139
+ } catch (e: any) {
140
+ console.warn('[LuaPlugin] Failed to initialize LuaEngine:', e.message);
141
+ luaEngine = null;
142
+ }
143
+ }
106
144
 
107
- function luaPlugin(): Plugin {
108
145
  return {
109
146
  name: 'game-engine:lua',
110
147
  apply: 'serve',
111
148
 
112
- resolveId(id: string) {
113
- if (id in FENGARI_NODE_SHIMS) return `\0fengari-shim:${id}`;
114
- },
115
-
116
- load(id: string) {
117
- if (id.startsWith('\0fengari-shim:')) {
118
- const mod = id.slice('\0fengari-shim:'.length);
119
- return FENGARI_NODE_SHIMS[mod];
120
- }
149
+ async configureServer(server) {
150
+ viteServer = server;
151
+ await initEngine();
152
+
153
+ // POST /__lua-play — execute Lua on the server
154
+ server.middlewares.use('/__lua-play', (req: any, res: any) => {
155
+ if (req.method !== 'POST') {
156
+ res.statusCode = 405;
157
+ res.end('Method Not Allowed');
158
+ return;
159
+ }
160
+
161
+ let body = '';
162
+ req.on('data', (chunk: string) => { body += chunk; });
163
+ req.on('end', () => {
164
+ try {
165
+ if (!luaEngine) {
166
+ res.statusCode = 503;
167
+ res.setHeader('Content-Type', 'application/json');
168
+ res.end(JSON.stringify({ error: 'LuaEngine not initialized' }));
169
+ return;
170
+ }
171
+
172
+ const params = JSON.parse(body);
173
+ const result = luaEngine.execute(params);
174
+
175
+ res.statusCode = 200;
176
+ res.setHeader('Content-Type', 'application/json');
177
+ res.end(JSON.stringify(result));
178
+ } catch (e: any) {
179
+ res.statusCode = 500;
180
+ res.setHeader('Content-Type', 'application/json');
181
+ res.end(JSON.stringify({ error: e.message }));
182
+ }
183
+ });
184
+ });
121
185
  },
122
186
 
123
187
  transform(code: string, id: string) {
@@ -129,8 +193,10 @@ function luaPlugin(): Plugin {
129
193
  }
130
194
  },
131
195
 
132
- handleHotUpdate({ file, server }: { file: string; server: any }) {
133
- if (file.endsWith('.lua')) {
196
+ async handleHotUpdate({ file, server }: { file: string; server: any }) {
197
+ if (file.endsWith('.lua') || file.includes('dev.config')) {
198
+ console.log('[LuaPlugin] Reloading LuaEngine...');
199
+ await initEngine();
134
200
  server.ws.send({ type: 'full-reload' });
135
201
  return [];
136
202
  }
@@ -166,19 +232,11 @@ export function defineGameConfig(config: GameConfig = {}): UserConfig {
166
232
  if (config.devBridge) {
167
233
  const configPath = config.devBridgeConfig ?? './dev.config';
168
234
  plugins.push(devBridgePlugin(configPath));
169
- plugins.push(luaPlugin());
235
+ plugins.push(luaPlugin(configPath));
170
236
  }
171
237
 
172
238
  const userVite = config.vite ?? {};
173
239
 
174
- // fengari (Lua 5.3 in JS) is a CJS Node.js package that references `process`
175
- // and `require('os')` at module level. Provide shims so it works in the browser.
176
- const fengariShims: Record<string, string> = config.devBridge ? {
177
- 'process.versions.node': 'undefined',
178
- 'process.env': '({})',
179
- 'process.platform': '"browser"',
180
- } : {};
181
-
182
240
  return {
183
241
  base: config.base ?? '/',
184
242
 
@@ -187,11 +245,6 @@ export function defineGameConfig(config: GameConfig = {}): UserConfig {
187
245
  ...((userVite.plugins as Plugin[]) ?? []),
188
246
  ],
189
247
 
190
- define: {
191
- ...fengariShims,
192
- ...(userVite as Record<string, any>).define,
193
- },
194
-
195
248
  build: {
196
249
  target: 'esnext',
197
250
  assetsInlineLimit: 8192,
@@ -236,10 +289,10 @@ export function defineGameConfig(config: GameConfig = {}): UserConfig {
236
289
  'yoga-layout/load',
237
290
  'react',
238
291
  'react-dom',
239
- ...(config.devBridge ? ['fengari'] : []),
240
292
  ],
241
293
  exclude: [
242
294
  'yoga-layout',
295
+ 'fengari',
243
296
  ],
244
297
  esbuildOptions: {
245
298
  target: 'esnext',