@energy8platform/game-engine 0.15.2 → 0.16.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 +22 -12
- package/dist/assets.d.ts +2 -14
- package/dist/core.cjs.js +16 -201
- package/dist/core.cjs.js.map +1 -1
- package/dist/core.d.ts +4 -35
- package/dist/core.esm.js +12 -197
- package/dist/core.esm.js.map +1 -1
- package/dist/debug.cjs.js +5 -233
- package/dist/debug.cjs.js.map +1 -1
- package/dist/debug.d.ts +2 -140
- package/dist/debug.esm.js +2 -233
- package/dist/debug.esm.js.map +1 -1
- package/dist/index.cjs.js +21 -432
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +10 -195
- package/dist/index.esm.js +14 -429
- package/dist/index.esm.js.map +1 -1
- package/dist/lua.cjs.js +7 -1493
- package/dist/lua.cjs.js.map +1 -1
- package/dist/lua.d.ts +1 -403
- package/dist/lua.esm.js +1 -1483
- package/dist/lua.esm.js.map +1 -1
- package/dist/react.cjs.js.map +1 -1
- package/dist/react.d.ts +4 -35
- package/dist/react.esm.js.map +1 -1
- package/dist/vite.cjs.js +19 -175
- package/dist/vite.cjs.js.map +1 -1
- package/dist/vite.d.ts +8 -3
- package/dist/vite.esm.js +10 -173
- package/dist/vite.esm.js.map +1 -1
- package/package.json +6 -16
- package/src/core/GameApplication.ts +14 -18
- package/src/debug/index.ts +4 -2
- package/src/index.ts +3 -3
- package/src/loading/LoadingScene.ts +1 -1
- package/src/loading/index.ts +9 -2
- package/src/lua/index.ts +3 -31
- package/src/types.ts +16 -41
- package/src/vite/index.ts +13 -196
- package/bin/simulate.ts +0 -139
- package/scripts/install-simulate.mjs +0 -101
- package/src/debug/DevBridge.ts +0 -305
- package/src/loading/CSSPreloader.ts +0 -129
- package/src/loading/logo.ts +0 -95
- package/src/lua/ActionRouter.ts +0 -132
- package/src/lua/LuaEngine.ts +0 -412
- package/src/lua/LuaEngineAPI.ts +0 -314
- package/src/lua/NativeSimulationRunner.ts +0 -367
- package/src/lua/ParallelSimulationRunner.ts +0 -156
- package/src/lua/PersistentState.ts +0 -80
- package/src/lua/SessionManager.ts +0 -227
- package/src/lua/SimulationRunner.ts +0 -192
- package/src/lua/SimulationWorker.ts +0 -44
- package/src/lua/fengari.d.ts +0 -10
- package/src/lua/types.ts +0 -149
|
@@ -3,7 +3,7 @@ import { Scene } from '../core/Scene';
|
|
|
3
3
|
import { Tween } from '../animation/Tween';
|
|
4
4
|
import { Easing } from '../animation/Easing';
|
|
5
5
|
import type { LoadingScreenConfig } from '../types';
|
|
6
|
-
import { buildLogoSVG, LOADER_BAR_MAX_WIDTH } from '
|
|
6
|
+
import { buildLogoSVG, LOADER_BAR_MAX_WIDTH } from '@energy8platform/platform-core/loading';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Build the loading scene variant of the logo SVG.
|
package/src/loading/index.ts
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
// LoadingScene is pixi-specific and stays in game-engine.
|
|
2
|
+
// CSSPreloader and the Energy8 SVG logo live in platform-core so any
|
|
3
|
+
// renderer (Pixi, Phaser, Three.js) shows the same brand frame.
|
|
1
4
|
export { LoadingScene } from './LoadingScene';
|
|
2
|
-
export {
|
|
3
|
-
|
|
5
|
+
export {
|
|
6
|
+
createCSSPreloader,
|
|
7
|
+
removeCSSPreloader,
|
|
8
|
+
buildLogoSVG,
|
|
9
|
+
LOADER_BAR_MAX_WIDTH,
|
|
10
|
+
} from '@energy8platform/platform-core/loading';
|
package/src/lua/index.ts
CHANGED
|
@@ -1,31 +1,3 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
export
|
|
4
|
-
export { SessionManager } from './SessionManager';
|
|
5
|
-
export { PersistentState } from './PersistentState';
|
|
6
|
-
export { SimulationRunner, formatSimulationResult } from './SimulationRunner';
|
|
7
|
-
export { ParallelSimulationRunner } from './ParallelSimulationRunner';
|
|
8
|
-
export { NativeSimulationRunner, findNativeBinary, formatNativeResult } from './NativeSimulationRunner';
|
|
9
|
-
export type {
|
|
10
|
-
NativeSimulationConfig,
|
|
11
|
-
NativeSimulationResult,
|
|
12
|
-
StageStats,
|
|
13
|
-
DistributionBucket,
|
|
14
|
-
} from './NativeSimulationRunner';
|
|
15
|
-
export type {
|
|
16
|
-
GameDefinition,
|
|
17
|
-
ActionDefinition,
|
|
18
|
-
TransitionRule,
|
|
19
|
-
SessionConfig,
|
|
20
|
-
LuaEngineConfig,
|
|
21
|
-
LuaPlayResult,
|
|
22
|
-
MaxWinConfig,
|
|
23
|
-
BuyBonusConfig,
|
|
24
|
-
BuyBonusMode,
|
|
25
|
-
AnteBetConfig,
|
|
26
|
-
PersistentStateConfig,
|
|
27
|
-
BetLevelsConfig,
|
|
28
|
-
SimulationConfig,
|
|
29
|
-
SimulationResult,
|
|
30
|
-
SimulationRawAccumulators,
|
|
31
|
-
} from './types';
|
|
1
|
+
// Re-export from @energy8platform/platform-core to keep the
|
|
2
|
+
// `@energy8platform/game-engine/lua` sub-path import path stable.
|
|
3
|
+
export * from '@energy8platform/platform-core/lua';
|
package/src/types.ts
CHANGED
|
@@ -31,48 +31,23 @@ export enum Orientation {
|
|
|
31
31
|
ANY = 'any',
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
// ─── Loading Screen Config
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
logoScale?: number;
|
|
45
|
-
/** Show percentage text below the loader bar */
|
|
46
|
-
showPercentage?: boolean;
|
|
47
|
-
/** Custom progress text formatter */
|
|
48
|
-
progressTextFormatter?: (progress: number) => string;
|
|
49
|
-
/** Show "Tap to start" after loading (needed for mobile audio unlock) */
|
|
50
|
-
tapToStart?: boolean;
|
|
51
|
-
/** "Tap to start" label text */
|
|
52
|
-
tapToStartText?: string;
|
|
53
|
-
/** Minimum display time in ms (so the user sees the brand, even if loading is fast) */
|
|
54
|
-
minDisplayTime?: number;
|
|
55
|
-
/** CSS preloader custom HTML (shown before PixiJS is ready) */
|
|
56
|
-
cssPreloaderHTML?: string;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// ─── Asset Manifest ────────────────────────────────────────
|
|
60
|
-
|
|
61
|
-
export interface AssetEntry {
|
|
62
|
-
alias: string;
|
|
63
|
-
src: string | string[];
|
|
64
|
-
/** Optional data to pass to the loader */
|
|
65
|
-
data?: Record<string, unknown>;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export interface AssetBundle {
|
|
69
|
-
name: string;
|
|
70
|
-
assets: AssetEntry[];
|
|
71
|
-
}
|
|
34
|
+
// ─── Loading Screen Config + Asset Manifest ───────────────
|
|
35
|
+
// Both are renderer-agnostic and live in @energy8platform/platform-core.
|
|
36
|
+
// Imported for local use inside GameApplicationConfig and re-exported so
|
|
37
|
+
// existing game-engine consumers keep their imports.
|
|
38
|
+
import type {
|
|
39
|
+
LoadingScreenConfig,
|
|
40
|
+
AssetManifest,
|
|
41
|
+
AssetBundle,
|
|
42
|
+
AssetEntry,
|
|
43
|
+
} from '@energy8platform/platform-core';
|
|
72
44
|
|
|
73
|
-
export
|
|
74
|
-
|
|
75
|
-
|
|
45
|
+
export type {
|
|
46
|
+
LoadingScreenConfig,
|
|
47
|
+
AssetManifest,
|
|
48
|
+
AssetBundle,
|
|
49
|
+
AssetEntry,
|
|
50
|
+
};
|
|
76
51
|
|
|
77
52
|
// ─── Audio Config ──────────────────────────────────────────
|
|
78
53
|
|
package/src/vite/index.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { UserConfig, Plugin } from 'vite';
|
|
2
|
+
import { devBridgePlugin, luaPlugin } from '@energy8platform/platform-core/vite';
|
|
3
|
+
|
|
4
|
+
// Re-export so users importing from `@energy8platform/game-engine/vite`
|
|
5
|
+
// can still grab the plugins directly without a separate platform-core import.
|
|
6
|
+
export { devBridgePlugin, luaPlugin } from '@energy8platform/platform-core/vite';
|
|
2
7
|
|
|
3
8
|
// ─── Types ───────────────────────────────────────────────
|
|
4
9
|
|
|
@@ -16,208 +21,20 @@ export interface GameConfig {
|
|
|
16
21
|
vite?: UserConfig;
|
|
17
22
|
}
|
|
18
23
|
|
|
19
|
-
// ─── DevBridge Plugin ────────────────────────────────────
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Vite plugin that auto-injects the DevBridge script into the
|
|
23
|
-
* HTML during development, so the game can communicate with a
|
|
24
|
-
* mock casino host without manual setup.
|
|
25
|
-
*/
|
|
26
|
-
const VIRTUAL_ID = '/@dev-bridge-entry.js';
|
|
27
|
-
|
|
28
|
-
function devBridgePlugin(configPath: string): Plugin {
|
|
29
|
-
let entrySrc = '';
|
|
30
|
-
let viteRoot = '';
|
|
31
|
-
let resolvedConfigPath = configPath;
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
name: 'game-engine:dev-bridge',
|
|
35
|
-
apply: 'serve', // dev only
|
|
36
|
-
enforce: 'pre',
|
|
37
|
-
|
|
38
|
-
configResolved(config) {
|
|
39
|
-
viteRoot = config.root;
|
|
40
|
-
// Resolve relative config path against Vite root so the virtual
|
|
41
|
-
// module can import it with an absolute path.
|
|
42
|
-
if (configPath.startsWith('.')) {
|
|
43
|
-
resolvedConfigPath = config.root + '/' + configPath.replace(/^\.\//, '');
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
resolveId(id) {
|
|
48
|
-
if (id === VIRTUAL_ID) return id;
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
load(id) {
|
|
52
|
-
if (id === VIRTUAL_ID) {
|
|
53
|
-
// This goes through Vite's pipeline so bare imports are resolved
|
|
54
|
-
return `
|
|
55
|
-
import { DevBridge } from '@energy8platform/game-engine/debug';
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
const mod = await import('${resolvedConfigPath}');
|
|
59
|
-
const config = mod.default ?? mod.config ?? mod;
|
|
60
|
-
new DevBridge(config).start();
|
|
61
|
-
} catch (e) {
|
|
62
|
-
console.warn('[DevBridge] Failed to load config:', e);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
await import('${entrySrc}');
|
|
66
|
-
`;
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
|
|
70
|
-
transformIndexHtml(html) {
|
|
71
|
-
// Find the app's entry module script (skip Vite internal /@... scripts)
|
|
72
|
-
const scriptRegex = /<script\s+type="module"\s+src="((?!\/@)[^"]+)"\s*>\s*<\/script>/;
|
|
73
|
-
const match = html.match(scriptRegex);
|
|
74
|
-
|
|
75
|
-
if (!match) {
|
|
76
|
-
console.warn('[DevBridge] Could not find entry module script in index.html');
|
|
77
|
-
return html;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
entrySrc = match[1];
|
|
81
|
-
if (entrySrc.startsWith('.')) {
|
|
82
|
-
entrySrc = viteRoot + '/' + entrySrc.replace(/^\.\//, '');
|
|
83
|
-
} else if (entrySrc.startsWith('/')) {
|
|
84
|
-
entrySrc = viteRoot + entrySrc;
|
|
85
|
-
}
|
|
86
|
-
return html.replace(match[0], `<script type="module" src="${VIRTUAL_ID}"></script>`);
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// ─── Lua Plugin ─────────────────────────────────────────
|
|
92
|
-
|
|
93
|
-
/**
|
|
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`
|
|
97
|
-
*
|
|
98
|
-
* fengari runs server-side only — no browser shims needed.
|
|
99
|
-
*/
|
|
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
|
-
}
|
|
144
|
-
|
|
145
|
-
return {
|
|
146
|
-
name: 'game-engine:lua',
|
|
147
|
-
apply: 'serve',
|
|
148
|
-
|
|
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
|
-
});
|
|
185
|
-
},
|
|
186
|
-
|
|
187
|
-
transform(code: string, id: string) {
|
|
188
|
-
if (id.endsWith('.lua')) {
|
|
189
|
-
return {
|
|
190
|
-
code: `export default ${JSON.stringify(code)};`,
|
|
191
|
-
map: null,
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
|
|
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
|
-
|
|
200
|
-
// Invalidate all SSR modules so ssrLoadModule picks up fresh code
|
|
201
|
-
server.moduleGraph.invalidateAll();
|
|
202
|
-
|
|
203
|
-
await initEngine();
|
|
204
|
-
server.ws.send({ type: 'full-reload' });
|
|
205
|
-
return [];
|
|
206
|
-
}
|
|
207
|
-
},
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
|
|
211
24
|
// ─── defineGameConfig ────────────────────────────────────
|
|
212
25
|
|
|
213
26
|
/**
|
|
214
|
-
* Define a Vite configuration tailored for Energy8 casino games
|
|
27
|
+
* Define a Vite configuration tailored for Energy8 casino games on
|
|
28
|
+
* @energy8platform/game-engine (PixiJS).
|
|
215
29
|
*
|
|
216
30
|
* Merges sensible defaults for iGaming projects:
|
|
217
31
|
* - Build target: ESNext
|
|
218
32
|
* - Asset inlining threshold: 8KB
|
|
219
|
-
* -
|
|
220
|
-
* - Optional DevBridge auto-injection in dev mode
|
|
33
|
+
* - PixiJS-specific dedupe / chunk splitting / prebundle hints
|
|
34
|
+
* - Optional DevBridge auto-injection in dev mode (with Lua engine)
|
|
35
|
+
*
|
|
36
|
+
* For Phaser/Three/custom engines, import the bare plugins directly
|
|
37
|
+
* from `@energy8platform/platform-core/vite` and write your own config.
|
|
221
38
|
*
|
|
222
39
|
* @example
|
|
223
40
|
* ```ts
|
|
@@ -242,7 +59,7 @@ export function defineGameConfig(config: GameConfig = {}): UserConfig {
|
|
|
242
59
|
const userVite = config.vite ?? {};
|
|
243
60
|
|
|
244
61
|
return {
|
|
245
|
-
base: config.base ?? '
|
|
62
|
+
base: config.base ?? './',
|
|
246
63
|
|
|
247
64
|
plugins: [
|
|
248
65
|
...plugins,
|
package/bin/simulate.ts
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env npx tsx
|
|
2
|
-
import { SimulationRunner, formatSimulationResult } from '../src/lua/SimulationRunner';
|
|
3
|
-
import { ParallelSimulationRunner } from '../src/lua/ParallelSimulationRunner';
|
|
4
|
-
import { NativeSimulationRunner, findNativeBinary, formatNativeResult } from '../src/lua/NativeSimulationRunner';
|
|
5
|
-
import { cpus } from 'os';
|
|
6
|
-
import { resolve, dirname } from 'path';
|
|
7
|
-
import { fileURLToPath } from 'url';
|
|
8
|
-
|
|
9
|
-
// ─── Argument Parsing ───────────────────────────────────
|
|
10
|
-
|
|
11
|
-
function parseArgs(argv: string[]): Record<string, string> {
|
|
12
|
-
const args: Record<string, string> = {};
|
|
13
|
-
for (let i = 2; i < argv.length; i++) {
|
|
14
|
-
const arg = argv[i];
|
|
15
|
-
if (arg.startsWith('--')) {
|
|
16
|
-
const key = arg.slice(2);
|
|
17
|
-
// Boolean flags (no value)
|
|
18
|
-
if (key === 'native' || key === 'js') {
|
|
19
|
-
args[key] = 'true';
|
|
20
|
-
} else if (i + 1 < argv.length) {
|
|
21
|
-
args[key] = argv[++i];
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return args;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async function main() {
|
|
29
|
-
const args = parseArgs(process.argv);
|
|
30
|
-
|
|
31
|
-
const configPath = resolve(process.cwd(), args.config ?? './dev.config.ts');
|
|
32
|
-
const iterations = parseInt(args.iterations ?? '1000000', 10);
|
|
33
|
-
const bet = parseFloat(args.bet ?? '1');
|
|
34
|
-
const seed = args.seed ? parseInt(args.seed, 10) : undefined;
|
|
35
|
-
const action = args.action ?? 'spin';
|
|
36
|
-
const params = args.params ? JSON.parse(args.params) : undefined;
|
|
37
|
-
const workers = args.workers ? parseInt(args.workers, 10) : cpus().length;
|
|
38
|
-
const useNative = args.native === 'true';
|
|
39
|
-
const useJs = args.js === 'true';
|
|
40
|
-
|
|
41
|
-
// Load dev config
|
|
42
|
-
let config: any;
|
|
43
|
-
try {
|
|
44
|
-
const mod = await import(configPath);
|
|
45
|
-
config = mod.default ?? mod.config ?? mod;
|
|
46
|
-
} catch (e: any) {
|
|
47
|
-
console.error(`Failed to load config from ${configPath}:`);
|
|
48
|
-
console.error(e.message);
|
|
49
|
-
process.exit(1);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (!config.luaScript) {
|
|
53
|
-
console.error('Config must contain `luaScript` (Lua source code string).');
|
|
54
|
-
console.error('Make sure your dev.config.ts exports luaScript and gameDefinition.');
|
|
55
|
-
process.exit(1);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (!config.gameDefinition) {
|
|
59
|
-
console.error('Config must contain `gameDefinition` (GameDefinition object).');
|
|
60
|
-
process.exit(1);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const gameId = config.gameDefinition.id ?? 'unknown';
|
|
64
|
-
|
|
65
|
-
// ─── Native binary detection ────────────────────────────
|
|
66
|
-
// Search in config dir first, then in the game-engine package root
|
|
67
|
-
const engineRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
68
|
-
const binaryPath = args.binary ?? (useJs ? null : (findNativeBinary(dirname(configPath)) ?? findNativeBinary(engineRoot)));
|
|
69
|
-
|
|
70
|
-
if (useNative && !binaryPath) {
|
|
71
|
-
console.error('Native simulation binary not found.');
|
|
72
|
-
console.error('Use --binary <path> or set SIMULATE_BINARY environment variable.');
|
|
73
|
-
process.exit(1);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const onProgress = (completed: number, total: number) => {
|
|
77
|
-
const pct = Math.round((completed / total) * 100);
|
|
78
|
-
console.log(`Progress: ${completed.toLocaleString()}/${total.toLocaleString()} (${pct}%)`);
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
// ─── Native binary path ─────────────────────────────────
|
|
82
|
-
if (binaryPath) {
|
|
83
|
-
console.log(`Using native binary: ${binaryPath}`);
|
|
84
|
-
console.log(`Starting simulation for ${gameId} (${iterations.toLocaleString()} iterations, action: ${action})...`);
|
|
85
|
-
|
|
86
|
-
const runner = new NativeSimulationRunner({
|
|
87
|
-
binaryPath,
|
|
88
|
-
script: config.luaScript,
|
|
89
|
-
gameDefinition: config.gameDefinition,
|
|
90
|
-
iterations,
|
|
91
|
-
bet,
|
|
92
|
-
action,
|
|
93
|
-
params,
|
|
94
|
-
});
|
|
95
|
-
const result = await runner.run();
|
|
96
|
-
console.log(formatNativeResult(result));
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// ─── JS simulation path ─────────────────────────────────
|
|
101
|
-
const useParallel = workers > 1;
|
|
102
|
-
console.log(`Starting simulation for ${gameId} (${iterations.toLocaleString()} iterations, action: ${action}, workers: ${useParallel ? workers : 1})...`);
|
|
103
|
-
|
|
104
|
-
let result;
|
|
105
|
-
|
|
106
|
-
if (useParallel) {
|
|
107
|
-
const runner = new ParallelSimulationRunner({
|
|
108
|
-
script: config.luaScript,
|
|
109
|
-
gameDefinition: config.gameDefinition,
|
|
110
|
-
iterations,
|
|
111
|
-
bet,
|
|
112
|
-
seed,
|
|
113
|
-
action,
|
|
114
|
-
params,
|
|
115
|
-
workerCount: workers,
|
|
116
|
-
onProgress,
|
|
117
|
-
});
|
|
118
|
-
result = await runner.run();
|
|
119
|
-
} else {
|
|
120
|
-
const runner = new SimulationRunner({
|
|
121
|
-
script: config.luaScript,
|
|
122
|
-
gameDefinition: config.gameDefinition,
|
|
123
|
-
iterations,
|
|
124
|
-
bet,
|
|
125
|
-
seed,
|
|
126
|
-
action,
|
|
127
|
-
params,
|
|
128
|
-
onProgress,
|
|
129
|
-
});
|
|
130
|
-
result = runner.run();
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
console.log(formatSimulationResult(result));
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
main().catch((err) => {
|
|
137
|
-
console.error(err);
|
|
138
|
-
process.exit(1);
|
|
139
|
-
});
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Postinstall script: downloads the native simulation binary for the current
|
|
5
|
-
* platform from GitHub Releases. Falls back silently — the JS simulation
|
|
6
|
-
* will be used if the binary is unavailable.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { createWriteStream, chmodSync, existsSync, mkdirSync } from 'fs';
|
|
10
|
-
import { join, dirname } from 'path';
|
|
11
|
-
import { fileURLToPath } from 'url';
|
|
12
|
-
import { get } from 'https';
|
|
13
|
-
|
|
14
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
|
-
const BIN_DIR = join(__dirname, '..', 'bin');
|
|
16
|
-
|
|
17
|
-
// ─── Config ─────────────────────────────────────────────
|
|
18
|
-
|
|
19
|
-
const REPO = 'energy8platform/game-engine';
|
|
20
|
-
// Binary version — update this when new Go binaries are built and uploaded to GitHub Releases.
|
|
21
|
-
// The binary is backwards-compatible: it runs any Lua script, so it doesn't need to match
|
|
22
|
-
// the engine version exactly. Only bump when the Go simulate CLI itself changes.
|
|
23
|
-
const BINARY_VERSION = '0.13.0';
|
|
24
|
-
|
|
25
|
-
const PLATFORM_MAP = {
|
|
26
|
-
'darwin-arm64': 'simulate-darwin-arm64',
|
|
27
|
-
'darwin-x64': 'simulate-darwin-amd64',
|
|
28
|
-
'linux-x64': 'simulate-linux-amd64',
|
|
29
|
-
'linux-arm64': 'simulate-linux-arm64',
|
|
30
|
-
'win32-x64': 'simulate-windows-amd64.exe',
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
// ─── Main ───────────────────────────────────────────────
|
|
34
|
-
|
|
35
|
-
async function main() {
|
|
36
|
-
const key = `${process.platform}-${process.arch}`;
|
|
37
|
-
const binaryName = PLATFORM_MAP[key];
|
|
38
|
-
|
|
39
|
-
if (!binaryName) {
|
|
40
|
-
console.log(`[simulate] No native binary available for ${key}, will use JS simulation.`);
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const dest = join(BIN_DIR, binaryName);
|
|
45
|
-
|
|
46
|
-
// Skip if already downloaded
|
|
47
|
-
if (existsSync(dest)) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const tag = `v${BINARY_VERSION}`;
|
|
52
|
-
|
|
53
|
-
const url = `https://github.com/${REPO}/releases/download/${tag}/${binaryName}`;
|
|
54
|
-
|
|
55
|
-
console.log(`[simulate] Downloading native binary for ${key}...`);
|
|
56
|
-
|
|
57
|
-
try {
|
|
58
|
-
if (!existsSync(BIN_DIR)) {
|
|
59
|
-
mkdirSync(BIN_DIR, { recursive: true });
|
|
60
|
-
}
|
|
61
|
-
await download(url, dest);
|
|
62
|
-
chmodSync(dest, 0o755);
|
|
63
|
-
console.log(`[simulate] Installed ${binaryName}`);
|
|
64
|
-
} catch (err) {
|
|
65
|
-
// Non-fatal — JS simulation is the fallback
|
|
66
|
-
console.log(`[simulate] Could not download native binary: ${err.message}`);
|
|
67
|
-
console.log(`[simulate] Will use JS simulation instead.`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// ─── Download with redirect following ───────────────────
|
|
72
|
-
|
|
73
|
-
function download(url, dest, redirects = 5) {
|
|
74
|
-
return new Promise((resolve, reject) => {
|
|
75
|
-
if (redirects <= 0) {
|
|
76
|
-
return reject(new Error('Too many redirects'));
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
get(url, { headers: { 'User-Agent': 'game-engine-postinstall' } }, (res) => {
|
|
80
|
-
// Follow redirects (GitHub sends 302 to S3)
|
|
81
|
-
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
82
|
-
res.resume();
|
|
83
|
-
return download(res.headers.location, dest, redirects - 1).then(resolve, reject);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (res.statusCode !== 200) {
|
|
87
|
-
res.resume();
|
|
88
|
-
return reject(new Error(`HTTP ${res.statusCode} for ${url}`));
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const file = createWriteStream(dest);
|
|
92
|
-
res.pipe(file);
|
|
93
|
-
file.on('finish', () => file.close(resolve));
|
|
94
|
-
file.on('error', reject);
|
|
95
|
-
}).on('error', reject);
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
main().catch(() => {
|
|
100
|
-
// Never fail the install
|
|
101
|
-
});
|