@playcraft/build 0.0.4 → 0.0.9
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/analyzers/scene-asset-collector.js +210 -1
- package/dist/base-builder.d.ts +15 -0
- package/dist/base-builder.js +192 -16
- package/dist/generators/config-generator.js +29 -3
- package/dist/loaders/playcanvas-loader.d.ts +7 -0
- package/dist/loaders/playcanvas-loader.js +53 -3
- package/dist/platforms/adikteev.d.ts +10 -0
- package/dist/platforms/adikteev.js +72 -0
- package/dist/platforms/base.d.ts +12 -0
- package/dist/platforms/base.js +208 -0
- package/dist/platforms/facebook.js +5 -2
- package/dist/platforms/index.d.ts +4 -0
- package/dist/platforms/index.js +16 -0
- package/dist/platforms/inmobi.d.ts +10 -0
- package/dist/platforms/inmobi.js +68 -0
- package/dist/platforms/ironsource.js +5 -2
- package/dist/platforms/moloco.js +5 -2
- package/dist/platforms/playcraft.d.ts +33 -0
- package/dist/platforms/playcraft.js +44 -0
- package/dist/platforms/remerge.d.ts +10 -0
- package/dist/platforms/remerge.js +56 -0
- package/dist/templates/__loading__.js +100 -0
- package/dist/templates/__modules__.js +47 -0
- package/dist/templates/__settings__.template.js +20 -0
- package/dist/templates/__start__.js +332 -0
- package/dist/templates/index.html +18 -0
- package/dist/templates/logo.png +0 -0
- package/dist/templates/manifest.json +1 -0
- package/dist/templates/patches/cannon.min.js +28 -0
- package/dist/templates/patches/lz4.js +10 -0
- package/dist/templates/patches/one-page-http-get.js +20 -0
- package/dist/templates/patches/one-page-inline-game-scripts.js +52 -0
- package/dist/templates/patches/one-page-mraid-resize-canvas.js +46 -0
- package/dist/templates/patches/p2.min.js +27 -0
- package/dist/templates/patches/playcraft-no-xhr.js +76 -0
- package/dist/templates/playcanvas-stable.min.js +16363 -0
- package/dist/templates/styles.css +43 -0
- package/dist/types.d.ts +14 -1
- package/dist/utils/build-mode-detector.d.ts +9 -0
- package/dist/utils/build-mode-detector.js +42 -0
- package/dist/vite/config-builder.d.ts +29 -1
- package/dist/vite/config-builder.js +169 -25
- package/dist/vite/platform-configs.d.ts +4 -0
- package/dist/vite/platform-configs.js +97 -13
- package/dist/vite/plugin-esm-html-generator.d.ts +22 -0
- package/dist/vite/plugin-esm-html-generator.js +1061 -0
- package/dist/vite/plugin-platform.js +56 -17
- package/dist/vite/plugin-playcanvas.d.ts +2 -0
- package/dist/vite/plugin-playcanvas.js +497 -40
- package/dist/vite/plugin-source-builder.d.ts +3 -0
- package/dist/vite/plugin-source-builder.js +886 -19
- package/dist/vite-builder.d.ts +19 -2
- package/dist/vite-builder.js +162 -12
- package/package.json +2 -1
- package/physics/cannon-es-bundle.js +13092 -0
- package/physics/cannon-rigidbody-adapter.js +375 -0
- package/physics/connon-integration.js +411 -0
- package/templates/__start__.js +8 -3
- package/templates/index.esm.html +20 -0
- package/templates/index.esm.mjs +502 -0
- package/templates/patches/one-page-inline-game-scripts.js +25 -1
- package/templates/patches/playcraft-cta-adapter.js +297 -0
- package/templates/patches/playcraft-no-xhr.js +25 -1
- package/templates/playcanvas-esm-wrapper.mjs +827 -0
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
// Import PlayCanvas Engine
|
|
2
|
+
import * as pc from './playcanvas-engine.mjs';
|
|
3
|
+
|
|
4
|
+
// Export for global access - create a mutable wrapper object
|
|
5
|
+
// ESM namespace objects are frozen, so we spread into a new object
|
|
6
|
+
window.pc = { ...pc };
|
|
7
|
+
|
|
8
|
+
// Configuration Constants
|
|
9
|
+
const ASSET_PREFIX = "{{ASSET_PREFIX}}";
|
|
10
|
+
const SCRIPT_PREFIX = "{{SCRIPT_PREFIX}}";
|
|
11
|
+
const SCENE_PATH = "{{SCENE_PATH}}";
|
|
12
|
+
const CONTEXT_OPTIONS = {{CONTEXT_OPTIONS}};
|
|
13
|
+
const SCRIPTS = {{SCRIPTS}};
|
|
14
|
+
const CONFIG_FILENAME = "config.json";
|
|
15
|
+
const INPUT_SETTINGS = {{INPUT_SETTINGS}};
|
|
16
|
+
const PRELOAD_MODULES = {{PRELOAD_MODULES}};
|
|
17
|
+
if (pc.script) {
|
|
18
|
+
pc.script.legacy = {{LEGACY_SCRIPTS}};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Export configuration constants to window for compatibility with __start__.js
|
|
22
|
+
// This is needed when ESM code is bundled as IIFE for playable ads
|
|
23
|
+
window.ASSET_PREFIX = ASSET_PREFIX;
|
|
24
|
+
window.SCRIPT_PREFIX = SCRIPT_PREFIX;
|
|
25
|
+
window.SCENE_PATH = SCENE_PATH;
|
|
26
|
+
window.CONTEXT_OPTIONS = CONTEXT_OPTIONS;
|
|
27
|
+
window.SCRIPTS = SCRIPTS;
|
|
28
|
+
window.INPUT_SETTINGS = INPUT_SETTINGS;
|
|
29
|
+
window.PRELOAD_MODULES = PRELOAD_MODULES;
|
|
30
|
+
|
|
31
|
+
// Script imports will be loaded after app initialization
|
|
32
|
+
const SCRIPT_IMPORT_PATHS = {{SCRIPT_IMPORT_PATHS}};
|
|
33
|
+
|
|
34
|
+
// GameRule path from import map (resolved at build time for bundling)
|
|
35
|
+
const GAME_RULE_PATH = "{{GAME_RULE_PATH}}";
|
|
36
|
+
|
|
37
|
+
// WASM Module Loader
|
|
38
|
+
const loadModules = function(modules, urlPrefix) {
|
|
39
|
+
return new Promise(resolve => {
|
|
40
|
+
if (typeof modules === 'undefined' || modules.length === 0) {
|
|
41
|
+
resolve();
|
|
42
|
+
} else {
|
|
43
|
+
let remaining = modules.length;
|
|
44
|
+
const moduleLoaded = () => {
|
|
45
|
+
if (--remaining === 0) {
|
|
46
|
+
resolve();
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
modules.forEach(function(m) {
|
|
50
|
+
pc.WasmModule.setConfig(m.moduleName, {
|
|
51
|
+
glueUrl: urlPrefix + m.glueUrl,
|
|
52
|
+
wasmUrl: urlPrefix + m.wasmUrl,
|
|
53
|
+
fallbackUrl: urlPrefix + m.fallbackUrl
|
|
54
|
+
});
|
|
55
|
+
if (!m.hasOwnProperty('preload') || m.preload) {
|
|
56
|
+
if (m.moduleName === 'BASIS') {
|
|
57
|
+
pc.basisInitialize();
|
|
58
|
+
moduleLoaded();
|
|
59
|
+
} else if (m.moduleName === 'DracoDecoderModule') {
|
|
60
|
+
if (pc.dracoInitialize) {
|
|
61
|
+
pc.dracoInitialize();
|
|
62
|
+
moduleLoaded();
|
|
63
|
+
} else {
|
|
64
|
+
pc.WasmModule.getInstance(m.moduleName, moduleLoaded);
|
|
65
|
+
}
|
|
66
|
+
} else {
|
|
67
|
+
pc.WasmModule.getInstance(m.moduleName, moduleLoaded);
|
|
68
|
+
}
|
|
69
|
+
} else {
|
|
70
|
+
moduleLoaded();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Store loaded modules globally for later GameRule initialization
|
|
78
|
+
let loadedScriptModules = new Map();
|
|
79
|
+
let scriptBaseUrl = '';
|
|
80
|
+
|
|
81
|
+
// Dynamic script loader - loads scripts after app is initialized
|
|
82
|
+
async function loadUserScripts(scriptPaths) {
|
|
83
|
+
if (!scriptPaths || scriptPaths.length === 0) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
console.log(`[ESM] Loading ${scriptPaths.length} user scripts...`);
|
|
87
|
+
|
|
88
|
+
// Get the base URL of the current module
|
|
89
|
+
scriptBaseUrl = new URL('.', import.meta.url).href;
|
|
90
|
+
console.log(`[ESM] Base URL: ${scriptBaseUrl}`);
|
|
91
|
+
|
|
92
|
+
for (const scriptPath of scriptPaths) {
|
|
93
|
+
try {
|
|
94
|
+
// Resolve the script path relative to this module's location
|
|
95
|
+
const absoluteUrl = new URL(scriptPath, scriptBaseUrl).href;
|
|
96
|
+
console.log(`[ESM] Loading: ${scriptPath} -> ${absoluteUrl}`);
|
|
97
|
+
const module = await import(absoluteUrl);
|
|
98
|
+
loadedScriptModules.set(scriptPath, module);
|
|
99
|
+
console.log(`[ESM] Loaded: ${scriptPath}`);
|
|
100
|
+
} catch (e) {
|
|
101
|
+
console.error(`[ESM] Failed to load script: ${scriptPath}`, e);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// NOTE: Do NOT call initializeGameRule here!
|
|
106
|
+
// GameRule initialization must happen AFTER app.configure() completes,
|
|
107
|
+
// because app.root is not available until then.
|
|
108
|
+
// initializeGameRule is called in configure() after loadScene().
|
|
109
|
+
console.log('[ESM] Scripts loaded. GameRule will be initialized after scene loads.');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Auto-initialize GameRule from loaded modules
|
|
113
|
+
// IMPORTANT: This must be called AFTER app.root is available (after loadScene)
|
|
114
|
+
async function initializeGameRule() {
|
|
115
|
+
try {
|
|
116
|
+
// Find GameplaySystem module
|
|
117
|
+
let GameplaySystem = null;
|
|
118
|
+
for (const [path, module] of loadedScriptModules) {
|
|
119
|
+
if (module.GameplaySystem) {
|
|
120
|
+
GameplaySystem = module.GameplaySystem;
|
|
121
|
+
console.log(`[ESM] Found GameplaySystem in: ${path}`);
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (!GameplaySystem) {
|
|
127
|
+
console.log('[ESM] GameplaySystem not found in loaded modules');
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Check if gameRule is already set
|
|
132
|
+
if (GameplaySystem.gameRule) {
|
|
133
|
+
console.log('[ESM] GameplaySystem.gameRule already initialized');
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Try to load GameRule using resolved path (not bare specifier for bundling compatibility)
|
|
138
|
+
// The path is resolved from import map at build time
|
|
139
|
+
if (!GAME_RULE_PATH) {
|
|
140
|
+
console.log('[ESM] No gameRule path configured');
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
// Use relative path for dynamic import (bundler-friendly)
|
|
146
|
+
const absoluteUrl = new URL(GAME_RULE_PATH, scriptBaseUrl).href;
|
|
147
|
+
console.log(`[ESM] Loading GameRule from: ${absoluteUrl}`);
|
|
148
|
+
const gameRuleModule = await import(absoluteUrl);
|
|
149
|
+
|
|
150
|
+
if (gameRuleModule.GameRule) {
|
|
151
|
+
GameplaySystem.gameRule = new gameRuleModule.GameRule();
|
|
152
|
+
console.log(`[ESM] Auto-initialized GameplaySystem.gameRule: ${GameplaySystem.gameRule.ruleName || 'GameRule'}`);
|
|
153
|
+
}
|
|
154
|
+
} catch (e) {
|
|
155
|
+
// GameRule module not found or doesn't export GameRule class
|
|
156
|
+
console.log('[ESM] Could not auto-initialize GameRule:', e.message);
|
|
157
|
+
}
|
|
158
|
+
} catch (e) {
|
|
159
|
+
console.error('[ESM] Error in initializeGameRule:', e);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Canvas Setup
|
|
164
|
+
const CANVAS_ID = 'application-canvas';
|
|
165
|
+
|
|
166
|
+
const getIosVersion = () => {
|
|
167
|
+
if (/iP(hone|od|ad)/.test(navigator.platform)) {
|
|
168
|
+
const v = navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/);
|
|
169
|
+
return [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)];
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
let lastWindowHeight = window.innerHeight;
|
|
175
|
+
let lastWindowWidth = window.innerWidth;
|
|
176
|
+
let windowSizeChangeIntervalHandler = null;
|
|
177
|
+
|
|
178
|
+
const pcBootstrap = {
|
|
179
|
+
reflowHandler: null,
|
|
180
|
+
iosVersion: getIosVersion(),
|
|
181
|
+
createCanvas: function() {
|
|
182
|
+
const canvas = document.createElement('canvas');
|
|
183
|
+
canvas.setAttribute('id', CANVAS_ID);
|
|
184
|
+
canvas.setAttribute('tabindex', 0);
|
|
185
|
+
canvas.onselectstart = function() { return false; };
|
|
186
|
+
canvas.style['-webkit-user-select'] = 'none';
|
|
187
|
+
document.body.appendChild(canvas);
|
|
188
|
+
return canvas;
|
|
189
|
+
},
|
|
190
|
+
resizeCanvas: function(app, canvas) {
|
|
191
|
+
canvas.style.width = '';
|
|
192
|
+
canvas.style.height = '';
|
|
193
|
+
app.resizeCanvas(canvas.width, canvas.height);
|
|
194
|
+
const fillMode = app._fillMode;
|
|
195
|
+
if (fillMode === pc.FILLMODE_NONE || fillMode === pc.FILLMODE_KEEP_ASPECT) {
|
|
196
|
+
if ((fillMode === pc.FILLMODE_NONE && canvas.clientHeight < window.innerHeight) ||
|
|
197
|
+
canvas.clientWidth / canvas.clientHeight >= window.innerWidth / window.innerHeight) {
|
|
198
|
+
canvas.style.marginTop = Math.floor((window.innerHeight - canvas.clientHeight) / 2) + 'px';
|
|
199
|
+
} else {
|
|
200
|
+
canvas.style.marginTop = '';
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
lastWindowHeight = window.innerHeight;
|
|
204
|
+
lastWindowWidth = window.innerWidth;
|
|
205
|
+
if (this.iosVersion && this.iosVersion[0] <= 12) {
|
|
206
|
+
window.scrollTo(0, 0);
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
reflow: function(app, canvas) {
|
|
210
|
+
this.resizeCanvas(app, canvas);
|
|
211
|
+
if (windowSizeChangeIntervalHandler === null) {
|
|
212
|
+
windowSizeChangeIntervalHandler = setInterval(() => {
|
|
213
|
+
if (lastWindowHeight !== window.innerHeight || lastWindowWidth !== window.innerWidth) {
|
|
214
|
+
this.resizeCanvas(app, canvas);
|
|
215
|
+
}
|
|
216
|
+
}, 100);
|
|
217
|
+
setTimeout(function() {
|
|
218
|
+
if (!!windowSizeChangeIntervalHandler) {
|
|
219
|
+
clearInterval(windowSizeChangeIntervalHandler);
|
|
220
|
+
windowSizeChangeIntervalHandler = null;
|
|
221
|
+
}
|
|
222
|
+
}, 2000);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
window.pcBootstrap = pcBootstrap;
|
|
228
|
+
|
|
229
|
+
const canvas = pcBootstrap.createCanvas();
|
|
230
|
+
const app = new pc.AppBase(canvas);
|
|
231
|
+
|
|
232
|
+
// Make app accessible via pc.app for scripts that use it
|
|
233
|
+
// Note: We use window.pc (the original mutable object) instead of the imported pc module
|
|
234
|
+
// because ES module namespace objects are frozen and cannot have properties added
|
|
235
|
+
if (window.pc) {
|
|
236
|
+
window.pc.app = app;
|
|
237
|
+
console.log('[ESM] window.pc.app has been set:', !!window.pc.app);
|
|
238
|
+
} else {
|
|
239
|
+
console.error('[ESM] window.pc is not available!');
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function initCSS() {
|
|
243
|
+
if (document.head.querySelector) {
|
|
244
|
+
var css = `@media screen and (min-aspect-ratio: ${app._width}/${app._height}) {
|
|
245
|
+
#application-canvas.fill-mode-KEEP_ASPECT {
|
|
246
|
+
width: auto;
|
|
247
|
+
height: 100%;
|
|
248
|
+
margin: 0 auto;
|
|
249
|
+
}
|
|
250
|
+
}`;
|
|
251
|
+
document.head.querySelector('style').innerHTML += css;
|
|
252
|
+
}
|
|
253
|
+
if (canvas.classList) {
|
|
254
|
+
canvas.classList.add(`fill-mode-${app.fillMode}`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function displayError(html) {
|
|
259
|
+
const div = document.createElement('div');
|
|
260
|
+
div.innerHTML = `<table style="background-color: #8CE; width: 100%; height: 100%;">
|
|
261
|
+
<tr>
|
|
262
|
+
<td align="center">
|
|
263
|
+
<div style="display: table-cell; vertical-align: middle;">
|
|
264
|
+
<div style="">${html}</div>
|
|
265
|
+
</div>
|
|
266
|
+
</td>
|
|
267
|
+
</tr>
|
|
268
|
+
</table>`;
|
|
269
|
+
document.body.appendChild(div);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function createLocalGraphicsDevice() {
|
|
273
|
+
const deviceOptions = CONTEXT_OPTIONS ?? {};
|
|
274
|
+
var LEGACY_WEBGL = 'webgl';
|
|
275
|
+
// 确保 deviceTypes 存在,如果不存在则使用默认值
|
|
276
|
+
var deviceTypes = deviceOptions.deviceTypes
|
|
277
|
+
? [...deviceOptions.deviceTypes, LEGACY_WEBGL]
|
|
278
|
+
: ['webgl2', 'webgl1', LEGACY_WEBGL];
|
|
279
|
+
const gpuLibPath = '';
|
|
280
|
+
const gfxOptions = {
|
|
281
|
+
deviceTypes: deviceTypes,
|
|
282
|
+
glslangUrl: gpuLibPath + 'glslang.js',
|
|
283
|
+
twgslUrl: gpuLibPath + 'twgsl.js',
|
|
284
|
+
powerPreference: deviceOptions.powerPreference,
|
|
285
|
+
antialias: deviceOptions.antialias !== false,
|
|
286
|
+
alpha: deviceOptions.alpha === true,
|
|
287
|
+
preserveDrawingBuffer: !!deviceOptions.preserveDrawingBuffer
|
|
288
|
+
};
|
|
289
|
+
return pc.createGraphicsDevice(canvas, gfxOptions);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function initApp(device) {
|
|
293
|
+
try {
|
|
294
|
+
var createOptions = new pc.AppOptions();
|
|
295
|
+
createOptions.graphicsDevice = device;
|
|
296
|
+
createOptions.componentSystems = [
|
|
297
|
+
pc.RigidBodyComponentSystem, pc.CollisionComponentSystem, pc.JointComponentSystem,
|
|
298
|
+
pc.AnimationComponentSystem, pc.AnimComponentSystem, pc.ModelComponentSystem,
|
|
299
|
+
pc.RenderComponentSystem, pc.CameraComponentSystem, pc.LightComponentSystem,
|
|
300
|
+
pc.ScriptComponentSystem, pc.SoundComponentSystem, pc.AudioListenerComponentSystem,
|
|
301
|
+
pc.ParticleSystemComponentSystem, pc.ScreenComponentSystem, pc.ElementComponentSystem,
|
|
302
|
+
pc.ButtonComponentSystem, pc.ScrollViewComponentSystem, pc.ScrollbarComponentSystem,
|
|
303
|
+
pc.SpriteComponentSystem, pc.LayoutGroupComponentSystem, pc.LayoutChildComponentSystem,
|
|
304
|
+
pc.ZoneComponentSystem, pc.GSplatComponentSystem
|
|
305
|
+
];
|
|
306
|
+
createOptions.resourceHandlers = [
|
|
307
|
+
pc.RenderHandler, pc.AnimationHandler, pc.AnimClipHandler, pc.AnimStateGraphHandler,
|
|
308
|
+
pc.ModelHandler, pc.MaterialHandler, pc.TextureHandler, pc.TextHandler,
|
|
309
|
+
pc.JsonHandler, pc.AudioHandler, pc.ScriptHandler, pc.SceneHandler,
|
|
310
|
+
pc.CubemapHandler, pc.HtmlHandler, pc.CssHandler, pc.ShaderHandler,
|
|
311
|
+
pc.HierarchyHandler, pc.FolderHandler, pc.FontHandler, pc.BinaryHandler,
|
|
312
|
+
pc.TextureAtlasHandler, pc.SpriteHandler, pc.TemplateHandler, pc.ContainerHandler,
|
|
313
|
+
pc.GSplatHandler
|
|
314
|
+
];
|
|
315
|
+
createOptions.elementInput = new pc.ElementInput(canvas, {
|
|
316
|
+
useMouse: INPUT_SETTINGS.useMouse,
|
|
317
|
+
useTouch: INPUT_SETTINGS.useTouch
|
|
318
|
+
});
|
|
319
|
+
createOptions.keyboard = INPUT_SETTINGS.useKeyboard ? new pc.Keyboard(window) : null;
|
|
320
|
+
createOptions.mouse = INPUT_SETTINGS.useMouse ? new pc.Mouse(canvas) : null;
|
|
321
|
+
createOptions.gamepads = INPUT_SETTINGS.useGamepads ? new pc.GamePads() : null;
|
|
322
|
+
createOptions.touch = INPUT_SETTINGS.useTouch && pc.platform.touch ? new pc.TouchDevice(canvas) : null;
|
|
323
|
+
createOptions.assetPrefix = ASSET_PREFIX ?? '';
|
|
324
|
+
createOptions.scriptPrefix = SCRIPT_PREFIX ?? '';
|
|
325
|
+
createOptions.scriptsOrder = SCRIPTS ?? [];
|
|
326
|
+
createOptions.soundManager = new pc.SoundManager();
|
|
327
|
+
createOptions.lightmapper = pc.Lightmapper;
|
|
328
|
+
createOptions.batchManager = pc.BatchManager;
|
|
329
|
+
createOptions.xr = pc.XrManager;
|
|
330
|
+
app.init(createOptions);
|
|
331
|
+
|
|
332
|
+
// Execute deferred ESM Script registrations (if any)
|
|
333
|
+
// This happens after app.init() so pc.registerScript can work properly
|
|
334
|
+
if (window.__deferredESMScripts) {
|
|
335
|
+
console.log('[ESM] Executing ' + window.__deferredESMScripts.length + ' deferred ESM script registrations...');
|
|
336
|
+
window.__deferredESMScripts.forEach(function(fn) {
|
|
337
|
+
try {
|
|
338
|
+
fn();
|
|
339
|
+
} catch (e) {
|
|
340
|
+
console.error('[ESM] Deferred script registration error:', e);
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
window.__deferredESMScripts = null;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return true;
|
|
347
|
+
} catch (e) {
|
|
348
|
+
displayError('Could not initialize application. Error: ' + e);
|
|
349
|
+
console.error(e);
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function configure() {
|
|
355
|
+
app.configure(CONFIG_FILENAME, err => {
|
|
356
|
+
if (err) {
|
|
357
|
+
console.error(err);
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
initCSS();
|
|
361
|
+
setTimeout(() => {
|
|
362
|
+
pcBootstrap.reflow(app, canvas);
|
|
363
|
+
pcBootstrap.reflowHandler = function() {
|
|
364
|
+
pcBootstrap.reflow(app, canvas);
|
|
365
|
+
};
|
|
366
|
+
window.addEventListener('resize', pcBootstrap.reflowHandler, false);
|
|
367
|
+
window.addEventListener('orientationchange', pcBootstrap.reflowHandler, false);
|
|
368
|
+
app.preload(() => {
|
|
369
|
+
app.scenes.loadScene(SCENE_PATH, async err => {
|
|
370
|
+
if (err) {
|
|
371
|
+
console.error(err);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Initialize GameRule AFTER scene is loaded (app.root is now available)
|
|
376
|
+
// This fixes: "Cannot read properties of undefined (reading 'root')"
|
|
377
|
+
console.log('[ESM] Scene loaded, app.root is now available:', !!app.root);
|
|
378
|
+
await initializeGameRule();
|
|
379
|
+
|
|
380
|
+
app.start();
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
async function main() {
|
|
388
|
+
try {
|
|
389
|
+
const device = await createLocalGraphicsDevice();
|
|
390
|
+
if (initApp(device)) {
|
|
391
|
+
// Load WASM modules
|
|
392
|
+
await loadModules(PRELOAD_MODULES, ASSET_PREFIX);
|
|
393
|
+
|
|
394
|
+
// Load user scripts AFTER app.init() - this is critical for createScript to work
|
|
395
|
+
await loadUserScripts(SCRIPT_IMPORT_PATHS);
|
|
396
|
+
|
|
397
|
+
// Configure and start
|
|
398
|
+
configure();
|
|
399
|
+
}
|
|
400
|
+
} catch (e) {
|
|
401
|
+
console.error('Device creation error:', e);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// Loading Screen
|
|
406
|
+
pc.script.createLoadingScreen(app => {
|
|
407
|
+
const createCss = () => {
|
|
408
|
+
const css = `
|
|
409
|
+
body {
|
|
410
|
+
background-color: #283538;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
#application-splash-wrapper {
|
|
414
|
+
position: absolute;
|
|
415
|
+
top: 0;
|
|
416
|
+
left: 0;
|
|
417
|
+
height: 100%;
|
|
418
|
+
width: 100%;
|
|
419
|
+
background-color: #283538;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
#application-splash {
|
|
423
|
+
position: absolute;
|
|
424
|
+
top: calc(50% - 28px);
|
|
425
|
+
width: 264px;
|
|
426
|
+
left: calc(50% - 132px);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
#application-splash img {
|
|
430
|
+
width: 100%;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
#progress-bar-container {
|
|
434
|
+
margin: 20px auto 0 auto;
|
|
435
|
+
height: 2px;
|
|
436
|
+
width: 100%;
|
|
437
|
+
background-color: #1d292c;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
#progress-bar {
|
|
441
|
+
width: 0%;
|
|
442
|
+
height: 100%;
|
|
443
|
+
background-color: #f60;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
@media (max-width: 480px) {
|
|
447
|
+
#application-splash {
|
|
448
|
+
width: 170px;
|
|
449
|
+
left: calc(50% - 85px);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
`;
|
|
453
|
+
const style = document.createElement('style');
|
|
454
|
+
style.textContent = css;
|
|
455
|
+
document.head.appendChild(style);
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
const showSplash = () => {
|
|
459
|
+
const wrapper = document.createElement('div');
|
|
460
|
+
wrapper.id = 'application-splash-wrapper';
|
|
461
|
+
document.body.appendChild(wrapper);
|
|
462
|
+
const splash = document.createElement('div');
|
|
463
|
+
splash.id = 'application-splash';
|
|
464
|
+
wrapper.appendChild(splash);
|
|
465
|
+
splash.style.display = 'none';
|
|
466
|
+
const logo = document.createElement('img');
|
|
467
|
+
logo.src = `${ASSET_PREFIX}logo.png`;
|
|
468
|
+
splash.appendChild(logo);
|
|
469
|
+
logo.onload = () => {
|
|
470
|
+
splash.style.display = 'block';
|
|
471
|
+
};
|
|
472
|
+
const container = document.createElement('div');
|
|
473
|
+
container.id = 'progress-bar-container';
|
|
474
|
+
splash.appendChild(container);
|
|
475
|
+
const bar = document.createElement('div');
|
|
476
|
+
bar.id = 'progress-bar';
|
|
477
|
+
container.appendChild(bar);
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
const setProgress = value => {
|
|
481
|
+
const bar = document.getElementById('progress-bar');
|
|
482
|
+
if (bar) {
|
|
483
|
+
value = Math.min(1, Math.max(0, value));
|
|
484
|
+
bar.style.width = `${value * 100}%`;
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
const hideSplash = () => {
|
|
489
|
+
document.getElementById('application-splash-wrapper').remove();
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
createCss();
|
|
493
|
+
showSplash();
|
|
494
|
+
app.on('preload:end', () => {
|
|
495
|
+
app.off('preload:progress');
|
|
496
|
+
});
|
|
497
|
+
app.on('preload:progress', setProgress);
|
|
498
|
+
app.on('start', hideSplash);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
// Start the application
|
|
502
|
+
main();
|
|
@@ -18,7 +18,31 @@
|
|
|
18
18
|
// Decode the url from base64 to text
|
|
19
19
|
var index = url.indexOf(',');
|
|
20
20
|
var base64 = url.slice(index + 1);
|
|
21
|
-
|
|
21
|
+
|
|
22
|
+
// 移除查询参数(如 ?t=hash 用于缓存破坏)
|
|
23
|
+
var queryIndex = base64.indexOf('?');
|
|
24
|
+
if (queryIndex !== -1) {
|
|
25
|
+
base64 = base64.substring(0, queryIndex);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 防御性检查:清理 base64 字符串
|
|
29
|
+
base64 = base64.trim().replace(/\s/g, '');
|
|
30
|
+
|
|
31
|
+
// 如果 base64 为空,跳过加载
|
|
32
|
+
if (!base64) {
|
|
33
|
+
console.warn('警告: 脚本 base64 数据为空,URL:', url.substring(0, 100));
|
|
34
|
+
callback('base64 数据为空');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
var data;
|
|
39
|
+
try {
|
|
40
|
+
data = window.atob(base64);
|
|
41
|
+
} catch (e) {
|
|
42
|
+
console.error('脚本 atob 解码失败:', e.message, 'URL:', url.substring(0, 100));
|
|
43
|
+
callback('atob 解码失败: ' + e.message);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
22
46
|
|
|
23
47
|
element.innerText = data;
|
|
24
48
|
head.appendChild(element);
|