@auraindustry/aurajs 0.1.1 → 0.1.5
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 +7 -0
- package/benchmarks/perf-thresholds.json +27 -0
- package/package.json +6 -1
- package/src/ai-guidance.mjs +302 -0
- package/src/asset-pack.mjs +2 -1
- package/src/authored-project.mjs +498 -2
- package/src/authored-runtime.mjs +14 -0
- package/src/bin-integrity.mjs +33 -26
- package/src/build-contract/capabilities.mjs +87 -1
- package/src/build-contract/constants.mjs +1 -0
- package/src/build-contract.mjs +2 -0
- package/src/bundler.mjs +143 -13
- package/src/cli.mjs +681 -13
- package/src/commands/packs.mjs +741 -0
- package/src/commands/project-authoring.mjs +128 -1
- package/src/conformance/cases/app-and-ui-runtime-cases.mjs +1 -2
- package/src/conformance/cases/core-runtime-cases.mjs +6 -2
- package/src/conformance/cases/scene3d-and-media-cases.mjs +238 -0
- package/src/conformance/cases/systems-and-gameplay-cases.mjs +1126 -10
- package/src/conformance-mobile.mjs +166 -0
- package/src/conformance.mjs +89 -30
- package/src/evidence-bundle.mjs +242 -0
- package/src/external-package-surface.mjs +1 -1
- package/src/headless-test/runtime-coordinator.mjs +186 -33
- package/src/headless-test.mjs +2 -0
- package/src/helpers/2d/index.mjs +183 -0
- package/src/helpers/index.mjs +26 -0
- package/src/helpers/starter-utils/adventure-objectives.js +102 -0
- package/src/helpers/starter-utils/adventure-world-2d.js +221 -0
- package/src/helpers/starter-utils/animation-2d.js +337 -0
- package/src/helpers/starter-utils/animation-packaging-2d.js +203 -0
- package/src/helpers/starter-utils/atlas-assets-2d.js +111 -0
- package/src/helpers/starter-utils/autoplay-debug-2d.js +215 -0
- package/src/helpers/starter-utils/avatar-3d.js +404 -0
- package/src/helpers/starter-utils/combat-feedback-2d.js +320 -0
- package/src/helpers/starter-utils/combat-runtime-2d.js +290 -0
- package/src/helpers/starter-utils/core.js +150 -0
- package/src/helpers/starter-utils/dialogue-2d.js +351 -0
- package/src/helpers/starter-utils/enemy-archetypes-2d.js +68 -0
- package/src/helpers/starter-utils/index.js +26 -0
- package/src/helpers/starter-utils/inventory-2d.js +268 -0
- package/src/helpers/starter-utils/journal-2d.js +267 -0
- package/src/helpers/starter-utils/platformer-3d.js +132 -0
- package/src/helpers/starter-utils/scene-audio-2d.js +236 -0
- package/src/helpers/starter-utils/streamed-world-2d.js +378 -0
- package/src/helpers/starter-utils/tilemap-nav-2d.js +499 -0
- package/src/helpers/starter-utils/tilemap-world-2d.js +205 -0
- package/src/helpers/starter-utils/triggers.js +662 -0
- package/src/helpers/starter-utils/tween-2d.js +615 -0
- package/src/helpers/starter-utils/wave-director.js +101 -0
- package/src/helpers/starter-utils/world-compositor-2d.js +253 -0
- package/src/helpers/starter-utils/world-persistence-2d.js +180 -0
- package/src/mobile/android/build.mjs +606 -0
- package/src/mobile/android/host-artifact.mjs +280 -0
- package/src/mobile/ios/build.mjs +1323 -0
- package/src/mobile/ios/host-artifact.mjs +819 -0
- package/src/mobile/shared/capabilities.mjs +174 -0
- package/src/package-integrity.mjs +18 -4
- package/src/packs/catalog.mjs +259 -0
- package/src/perf-benchmark-runner.mjs +17 -12
- package/src/perf-benchmark.mjs +408 -4
- package/src/publish-command.mjs +434 -17
- package/src/publish-validation.mjs +22 -11
- package/src/replay-runtime.mjs +257 -0
- package/src/scaffold/config.mjs +2 -0
- package/src/scaffold/fs.mjs +8 -1
- package/src/scaffold/project-docs.mjs +101 -41
- package/src/scaffold.mjs +4 -0
- package/src/session-runtime.mjs +4 -3
- package/src/web-conformance.mjs +0 -36
- package/templates/create/2d/src/runtime/app.js +4 -0
- package/templates/create/2d-adventure/config/gameplay/adventure.config.js +9 -6
- package/templates/create/2d-adventure/content/gameplay/dialogue.js +85 -0
- package/templates/create/2d-adventure/content/gameplay/world.js +32 -36
- package/templates/create/2d-adventure/content/gameplay/world.tilemap.json +273 -0
- package/templates/create/2d-adventure/docs/design/loop.md +4 -3
- package/templates/create/2d-adventure/prefabs/relic.prefab.js +10 -10
- package/templates/create/2d-adventure/prefabs/world.prefab.js +127 -74
- package/templates/create/2d-adventure/scenes/gameplay.scene.js +603 -112
- package/templates/create/2d-adventure/src/runtime/capabilities.js +16 -0
- package/templates/create/2d-adventure/ui/hud.screen.js +187 -4
- package/templates/create/2d-adventure/ui/journal.screen.js +183 -0
- package/templates/create/2d-survivor/src/runtime/app.js +4 -0
- package/templates/create/3d/scenes/gameplay.scene.js +30 -3
- package/templates/create/3d/src/runtime/app.js +4 -0
- package/templates/create/3d/src/runtime/capabilities.js +5 -0
- package/templates/create/3d/src/runtime/materials.js +10 -0
- package/templates/create/3d-adventure/scenes/gameplay.scene.js +30 -3
- package/templates/create/3d-adventure/src/runtime/capabilities.js +5 -0
- package/templates/create/3d-adventure/src/runtime/materials.js +11 -0
- package/templates/create/3d-collectathon/scenes/gameplay.scene.js +30 -3
- package/templates/create/3d-collectathon/src/runtime/app.js +4 -0
- package/templates/create/3d-collectathon/src/runtime/capabilities.js +5 -0
- package/templates/create/3d-collectathon/src/runtime/materials.js +10 -0
- package/templates/create/blank/assets/splash/aurajs-gg-wordmark.webp +0 -0
- package/templates/create/blank/assets/splash/bg.webp +0 -0
- package/templates/create/blank/assets/splash/boot-loop.wav +0 -0
- package/templates/create/blank/assets/splash/boot-sting.wav +0 -0
- package/templates/create/blank/assets/splash/logo-mascot-sheet.webp +0 -0
- package/templates/create/blank/assets/splash/logoholo.webp +0 -0
- package/templates/create/blank/src/main.js +5 -1
- package/templates/create/blank/src/runtime/splash.js +305 -0
- package/templates/create/local-multiplayer/scenes/gameplay.scene.js +186 -12
- package/templates/create/local-multiplayer/src/runtime/capabilities.js +8 -1
- package/templates/create/shared/assets/splash/aurajs-gg-wordmark.webp +0 -0
- package/templates/create/shared/assets/splash/bg.webp +0 -0
- package/templates/create/shared/assets/splash/boot-loop.wav +0 -0
- package/templates/create/shared/assets/splash/boot-sting.wav +0 -0
- package/templates/create/shared/assets/splash/logo-mascot-sheet.webp +0 -0
- package/templates/create/shared/assets/splash/logoholo.webp +0 -0
- package/templates/create/shared/src/runtime/splash.js +305 -0
- package/templates/create/shared/src/runtime/ui-forms.js +552 -0
- package/templates/create/shared/src/starter-utils/adventure-world-2d.js +221 -0
- package/templates/create/shared/src/starter-utils/animation-packaging-2d.js +203 -0
- package/templates/create/shared/src/starter-utils/atlas-assets-2d.js +111 -0
- package/templates/create/shared/src/starter-utils/autoplay-debug-2d.js +215 -0
- package/templates/create/shared/src/starter-utils/combat-runtime-2d.js +290 -0
- package/templates/create/shared/src/starter-utils/dialogue-2d.js +351 -0
- package/templates/create/shared/src/starter-utils/index.js +15 -1
- package/templates/create/shared/src/starter-utils/inventory-2d.js +268 -0
- package/templates/create/shared/src/starter-utils/journal-2d.js +267 -0
- package/templates/create/shared/src/starter-utils/scene-audio-2d.js +236 -0
- package/templates/create/shared/src/starter-utils/streamed-world-2d.js +378 -0
- package/templates/create/shared/src/starter-utils/tilemap-nav-2d.js +499 -0
- package/templates/create/shared/src/starter-utils/tilemap-world-2d.js +205 -0
- package/templates/create/shared/src/starter-utils/world-compositor-2d.js +253 -0
- package/templates/create/shared/src/starter-utils/world-persistence-2d.js +180 -0
- package/templates/create/video-cutscene/src/runtime/app.js +4 -0
- package/templates/create-bin/play.js +148 -7
- package/templates/skills/auramaxx/SKILL.md +46 -0
- package/templates/skills/auramaxx/project-requirements.md +68 -0
- package/templates/skills/auramaxx/starter-recipes.md +104 -0
- package/templates/skills/auramaxx/validation-checklist.md +49 -0
- package/templates/starter/assets/splash/aurajs-gg-wordmark.webp +0 -0
- package/templates/starter/assets/splash/bg.webp +0 -0
- package/templates/starter/assets/splash/boot-loop.wav +0 -0
- package/templates/starter/assets/splash/boot-sting.wav +0 -0
- package/templates/starter/assets/splash/logo-mascot-sheet.webp +0 -0
- package/templates/starter/assets/splash/logoholo.webp +0 -0
- package/templates/starter/src/main.js +4 -0
- package/templates/starter/src/runtime/splash.js +305 -0
- package/templates/skills/aurajs/SKILL.md +0 -96
- package/templates/skills/aurajs/api-contract-3d.md +0 -7
- package/templates/skills/aurajs/api-contract.md +0 -7
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
function finite(value, fallback = 0) {
|
|
2
|
+
const numeric = Number(value);
|
|
3
|
+
return Number.isFinite(numeric) ? numeric : fallback;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
function positiveInteger(value, fallback = null) {
|
|
7
|
+
const numeric = Math.floor(finite(value, Number.NaN));
|
|
8
|
+
if (numeric >= 1) return numeric;
|
|
9
|
+
return fallback;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function normalizeText(value, fallback) {
|
|
13
|
+
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : fallback;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function resolveAura(auraRef = globalThis.aura) {
|
|
17
|
+
return auraRef && typeof auraRef === 'object' ? auraRef : null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function normalizeStageIds(value) {
|
|
21
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
22
|
+
return ['scene', 'light', 'final'];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const stageIds = [];
|
|
26
|
+
const seen = new Set();
|
|
27
|
+
for (const entry of value) {
|
|
28
|
+
const id = normalizeText(entry, null);
|
|
29
|
+
if (!id || seen.has(id)) continue;
|
|
30
|
+
seen.add(id);
|
|
31
|
+
stageIds.push(id);
|
|
32
|
+
}
|
|
33
|
+
return stageIds.length > 0 ? stageIds : ['scene', 'light', 'final'];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function createTarget(aura, width, height) {
|
|
37
|
+
if (!aura?.draw2d || typeof aura.draw2d.createRenderTarget !== 'function') return null;
|
|
38
|
+
const target = aura.draw2d.createRenderTarget(width, height);
|
|
39
|
+
return target && typeof target === 'object' && target.ok === true ? target : null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function destroyTarget(aura, target) {
|
|
43
|
+
if (!target || typeof target !== 'object' || target.ok !== true) return false;
|
|
44
|
+
if (!aura?.draw2d || typeof aura.draw2d.destroyRenderTarget !== 'function') return false;
|
|
45
|
+
aura.draw2d.destroyRenderTarget(target);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function resolveDimensions(compositor, options = {}) {
|
|
50
|
+
const width = positiveInteger(
|
|
51
|
+
options.width ?? compositor.fixedWidth,
|
|
52
|
+
null,
|
|
53
|
+
);
|
|
54
|
+
const height = positiveInteger(
|
|
55
|
+
options.height ?? compositor.fixedHeight,
|
|
56
|
+
null,
|
|
57
|
+
);
|
|
58
|
+
if (width == null || height == null) return null;
|
|
59
|
+
return { width, height };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getStageRecord(compositor, stageId) {
|
|
63
|
+
return Array.isArray(compositor?.stages)
|
|
64
|
+
? compositor.stages.find((entry) => entry.id === stageId) || null
|
|
65
|
+
: null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function normalizeStageDefinitions(compositor, stageDefinitions) {
|
|
69
|
+
if (Array.isArray(stageDefinitions)) {
|
|
70
|
+
return compositor.stageIds.map((stageId) => (
|
|
71
|
+
stageDefinitions.find((entry) => entry?.id === stageId) || { id: stageId }
|
|
72
|
+
));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (stageDefinitions && typeof stageDefinitions === 'object') {
|
|
76
|
+
return compositor.stageIds.map((stageId) => {
|
|
77
|
+
const entry = stageDefinitions[stageId];
|
|
78
|
+
if (typeof entry === 'function') {
|
|
79
|
+
return { id: stageId, draw: entry };
|
|
80
|
+
}
|
|
81
|
+
if (entry && typeof entry === 'object') {
|
|
82
|
+
return { id: stageId, ...entry };
|
|
83
|
+
}
|
|
84
|
+
return { id: stageId };
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return compositor.stageIds.map((stageId) => ({ id: stageId }));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function createWorldCompositor2D(options = {}) {
|
|
92
|
+
const stageIds = normalizeStageIds(options.stageIds);
|
|
93
|
+
const finalStageId = stageIds.includes(options.finalStageId)
|
|
94
|
+
? options.finalStageId
|
|
95
|
+
: stageIds[stageIds.length - 1];
|
|
96
|
+
return {
|
|
97
|
+
graphName: normalizeText(options.graphName, 'world-compositor-2d'),
|
|
98
|
+
stageIds,
|
|
99
|
+
finalStageId,
|
|
100
|
+
fixedWidth: positiveInteger(options.width ?? options.fixedWidth, null),
|
|
101
|
+
fixedHeight: positiveInteger(options.height ?? options.fixedHeight, null),
|
|
102
|
+
width: 0,
|
|
103
|
+
height: 0,
|
|
104
|
+
targets: null,
|
|
105
|
+
stages: null,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function destroyWorldCompositor2D(compositor, auraRef = globalThis.aura) {
|
|
110
|
+
const aura = resolveAura(auraRef);
|
|
111
|
+
if (!compositor || typeof compositor !== 'object') return false;
|
|
112
|
+
if (compositor.targets && typeof compositor.targets === 'object') {
|
|
113
|
+
for (const stageId of compositor.stageIds || []) {
|
|
114
|
+
destroyTarget(aura, compositor.targets[stageId]);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
compositor.width = 0;
|
|
118
|
+
compositor.height = 0;
|
|
119
|
+
compositor.targets = null;
|
|
120
|
+
compositor.stages = null;
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function ensureWorldCompositorTargets2D(compositor, options = {}, auraRef = globalThis.aura) {
|
|
125
|
+
const aura = resolveAura(auraRef);
|
|
126
|
+
if (!compositor || typeof compositor !== 'object') {
|
|
127
|
+
throw new Error('world compositor state is required.');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const dimensions = resolveDimensions(compositor, options);
|
|
131
|
+
if (!dimensions) return null;
|
|
132
|
+
if (
|
|
133
|
+
compositor.targets
|
|
134
|
+
&& compositor.width === dimensions.width
|
|
135
|
+
&& compositor.height === dimensions.height
|
|
136
|
+
) {
|
|
137
|
+
return compositor.targets;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
destroyWorldCompositor2D(compositor, aura);
|
|
141
|
+
|
|
142
|
+
const targets = {};
|
|
143
|
+
for (const stageId of compositor.stageIds) {
|
|
144
|
+
const target = createTarget(aura, dimensions.width, dimensions.height);
|
|
145
|
+
if (!target) {
|
|
146
|
+
for (const created of Object.values(targets)) {
|
|
147
|
+
destroyTarget(aura, created);
|
|
148
|
+
}
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
targets[stageId] = target;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
compositor.width = dimensions.width;
|
|
155
|
+
compositor.height = dimensions.height;
|
|
156
|
+
compositor.targets = targets;
|
|
157
|
+
return targets;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function runWorldCompositor2D(
|
|
161
|
+
compositor,
|
|
162
|
+
stageDefinitions,
|
|
163
|
+
options = {},
|
|
164
|
+
auraRef = globalThis.aura,
|
|
165
|
+
) {
|
|
166
|
+
const aura = resolveAura(auraRef);
|
|
167
|
+
if (!compositor || typeof compositor !== 'object') {
|
|
168
|
+
throw new Error('world compositor state is required.');
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const dimensions = resolveDimensions(compositor, options);
|
|
172
|
+
const fallbackDraw = typeof options.fallbackDraw === 'function' ? options.fallbackDraw : null;
|
|
173
|
+
if (!dimensions) {
|
|
174
|
+
if (fallbackDraw) fallbackDraw({ reasonCode: 'invalid_world_compositor_size' });
|
|
175
|
+
return {
|
|
176
|
+
ok: false,
|
|
177
|
+
reasonCode: 'invalid_world_compositor_size',
|
|
178
|
+
graph: null,
|
|
179
|
+
targets: null,
|
|
180
|
+
finalStage: null,
|
|
181
|
+
width: 0,
|
|
182
|
+
height: 0,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const targets = ensureWorldCompositorTargets2D(compositor, dimensions, aura);
|
|
187
|
+
if (!targets) {
|
|
188
|
+
if (fallbackDraw) fallbackDraw({ reasonCode: 'draw2d_render_target_unavailable' });
|
|
189
|
+
return {
|
|
190
|
+
ok: false,
|
|
191
|
+
reasonCode: 'draw2d_render_target_unavailable',
|
|
192
|
+
graph: null,
|
|
193
|
+
targets: null,
|
|
194
|
+
finalStage: null,
|
|
195
|
+
width: dimensions.width,
|
|
196
|
+
height: dimensions.height,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const definitions = normalizeStageDefinitions(compositor, stageDefinitions);
|
|
201
|
+
compositor.stages = definitions;
|
|
202
|
+
if (!aura?.draw2d || typeof aura.draw2d.runCompositorGraph !== 'function') {
|
|
203
|
+
if (fallbackDraw) {
|
|
204
|
+
fallbackDraw({
|
|
205
|
+
reasonCode: 'draw2d_compositor_unavailable',
|
|
206
|
+
width: dimensions.width,
|
|
207
|
+
height: dimensions.height,
|
|
208
|
+
targets,
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
ok: false,
|
|
213
|
+
reasonCode: 'draw2d_compositor_unavailable',
|
|
214
|
+
graph: null,
|
|
215
|
+
targets,
|
|
216
|
+
finalStage: targets[compositor.finalStageId] || null,
|
|
217
|
+
width: dimensions.width,
|
|
218
|
+
height: dimensions.height,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const graph = aura.draw2d.runCompositorGraph(
|
|
223
|
+
compositor.graphName,
|
|
224
|
+
definitions.map((entry) => ({
|
|
225
|
+
id: entry.id,
|
|
226
|
+
target: targets[entry.id],
|
|
227
|
+
draw: () => {
|
|
228
|
+
if (typeof entry.draw !== 'function') return;
|
|
229
|
+
entry.draw({
|
|
230
|
+
id: entry.id,
|
|
231
|
+
width: dimensions.width,
|
|
232
|
+
height: dimensions.height,
|
|
233
|
+
graphName: compositor.graphName,
|
|
234
|
+
targets,
|
|
235
|
+
aura,
|
|
236
|
+
stage: getStageRecord(compositor, entry.id),
|
|
237
|
+
finalStageId: compositor.finalStageId,
|
|
238
|
+
});
|
|
239
|
+
},
|
|
240
|
+
})),
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
const finalStageIndex = compositor.stageIds.indexOf(compositor.finalStageId);
|
|
244
|
+
return {
|
|
245
|
+
ok: graph?.ok === true,
|
|
246
|
+
reasonCode: graph?.reasonCode || null,
|
|
247
|
+
graph,
|
|
248
|
+
targets,
|
|
249
|
+
finalStage: graph?.stages?.[finalStageIndex] || targets[compositor.finalStageId] || null,
|
|
250
|
+
width: dimensions.width,
|
|
251
|
+
height: dimensions.height,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { syncStreamedWorldFocus2D } from './streamed-world-2d.js';
|
|
2
|
+
|
|
3
|
+
function cloneSnapshot(value) {
|
|
4
|
+
if (value == null) return value;
|
|
5
|
+
return JSON.parse(JSON.stringify(value));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function resolveStorage(auraRef = globalThis.aura) {
|
|
9
|
+
const storage = auraRef?.storage;
|
|
10
|
+
if (!storage || typeof storage !== 'object') return null;
|
|
11
|
+
return storage;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function storageSave(storage, key, value) {
|
|
15
|
+
if (!storage || typeof key !== 'string' || key.length === 0) return false;
|
|
16
|
+
if (typeof storage.save === 'function') {
|
|
17
|
+
storage.save(key, value);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
if (typeof storage.set === 'function') {
|
|
21
|
+
storage.set(key, value);
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function storageLoad(storage, key, fallback = null) {
|
|
28
|
+
if (!storage || typeof key !== 'string' || key.length === 0) return fallback;
|
|
29
|
+
if (typeof storage.load === 'function') {
|
|
30
|
+
return storage.load(key, fallback);
|
|
31
|
+
}
|
|
32
|
+
if (typeof storage.get === 'function') {
|
|
33
|
+
return storage.get(key, fallback);
|
|
34
|
+
}
|
|
35
|
+
return fallback;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function storageDelete(storage, key) {
|
|
39
|
+
if (!storage || typeof key !== 'string' || key.length === 0) return false;
|
|
40
|
+
if (typeof storage.delete === 'function') {
|
|
41
|
+
storage.delete(key);
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function normalizeSnapshot(snapshot) {
|
|
48
|
+
if (!snapshot || typeof snapshot !== 'object' || Array.isArray(snapshot)) return null;
|
|
49
|
+
return {
|
|
50
|
+
version: Number.isFinite(Number(snapshot.version)) ? Number(snapshot.version) : 1,
|
|
51
|
+
scope: typeof snapshot.scope === 'string' ? snapshot.scope : 'streamed-world-2d',
|
|
52
|
+
activeRegionId: typeof snapshot.activeRegionId === 'string' ? snapshot.activeRegionId : null,
|
|
53
|
+
focus: snapshot.focus && typeof snapshot.focus === 'object'
|
|
54
|
+
? {
|
|
55
|
+
x: Number(snapshot.focus.x) || 0,
|
|
56
|
+
y: Number(snapshot.focus.y) || 0,
|
|
57
|
+
}
|
|
58
|
+
: null,
|
|
59
|
+
world: snapshot.world == null ? null : cloneSnapshot(snapshot.world),
|
|
60
|
+
regions: Array.isArray(snapshot.regions)
|
|
61
|
+
? snapshot.regions
|
|
62
|
+
.filter((entry) => entry && typeof entry === 'object' && typeof entry.id === 'string')
|
|
63
|
+
.map((entry) => ({
|
|
64
|
+
id: entry.id,
|
|
65
|
+
data: cloneSnapshot(entry.data),
|
|
66
|
+
}))
|
|
67
|
+
: [],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function captureStreamedWorldSnapshot2D(stream, options = {}) {
|
|
72
|
+
if (!stream || !(stream.regionById instanceof Map)) {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const captureRegion = typeof options.captureRegion === 'function' ? options.captureRegion : null;
|
|
76
|
+
const captureWorld = typeof options.captureWorld === 'function' ? options.captureWorld : null;
|
|
77
|
+
const regionIds = Array.isArray(options.regionIds)
|
|
78
|
+
? options.regionIds.filter((entry) => typeof entry === 'string')
|
|
79
|
+
: stream.regions.map((entry) => entry.id);
|
|
80
|
+
const regions = [];
|
|
81
|
+
|
|
82
|
+
regionIds.forEach((regionId) => {
|
|
83
|
+
const descriptor = stream.regionById.get(regionId) || null;
|
|
84
|
+
if (!descriptor || !captureRegion) return;
|
|
85
|
+
const record = stream.loaded?.get(regionId) || null;
|
|
86
|
+
const data = captureRegion(descriptor, record, stream, options);
|
|
87
|
+
if (data == null) return;
|
|
88
|
+
regions.push({
|
|
89
|
+
id: regionId,
|
|
90
|
+
data: cloneSnapshot(data),
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
version: 1,
|
|
96
|
+
scope: options.scope ?? 'streamed-world-2d',
|
|
97
|
+
activeRegionId: stream.activeRegionId ?? null,
|
|
98
|
+
focus: stream.focus ? { x: Number(stream.focus.x) || 0, y: Number(stream.focus.y) || 0 } : null,
|
|
99
|
+
world: captureWorld ? cloneSnapshot(captureWorld(stream, options)) : null,
|
|
100
|
+
regions,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function applyStreamedWorldSnapshot2D(stream, snapshot, options = {}) {
|
|
105
|
+
if (!stream || !(stream.regionById instanceof Map)) {
|
|
106
|
+
return {
|
|
107
|
+
ok: false,
|
|
108
|
+
reasonCode: 'invalid_stream',
|
|
109
|
+
appliedRegionIds: [],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
const normalized = normalizeSnapshot(snapshot);
|
|
113
|
+
if (!normalized) {
|
|
114
|
+
return {
|
|
115
|
+
ok: false,
|
|
116
|
+
reasonCode: 'invalid_snapshot',
|
|
117
|
+
appliedRegionIds: [],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const applyRegion = typeof options.applyRegion === 'function' ? options.applyRegion : null;
|
|
121
|
+
const applyWorld = typeof options.applyWorld === 'function' ? options.applyWorld : null;
|
|
122
|
+
const appliedRegionIds = [];
|
|
123
|
+
|
|
124
|
+
if (applyWorld && normalized.world != null) {
|
|
125
|
+
applyWorld(cloneSnapshot(normalized.world), stream, options);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
normalized.regions.forEach((entry) => {
|
|
129
|
+
const descriptor = stream.regionById.get(entry.id) || null;
|
|
130
|
+
if (!descriptor || !applyRegion) return;
|
|
131
|
+
const record = stream.loaded?.get(entry.id) || null;
|
|
132
|
+
applyRegion(descriptor, record, cloneSnapshot(entry.data), stream, options);
|
|
133
|
+
appliedRegionIds.push(entry.id);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (options.restoreFocus === true && normalized.focus) {
|
|
137
|
+
syncStreamedWorldFocus2D(stream, options.auraRef, normalized.focus);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
ok: true,
|
|
142
|
+
reasonCode: null,
|
|
143
|
+
appliedRegionIds,
|
|
144
|
+
restoredFocus: options.restoreFocus === true && normalized.focus != null,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function saveStreamedWorldSnapshot2D(key, stream, auraRef = globalThis.aura, options = {}) {
|
|
149
|
+
const storage = resolveStorage(auraRef);
|
|
150
|
+
const snapshot = captureStreamedWorldSnapshot2D(stream, options);
|
|
151
|
+
if (!snapshot) {
|
|
152
|
+
return {
|
|
153
|
+
ok: false,
|
|
154
|
+
reasonCode: 'invalid_stream',
|
|
155
|
+
snapshot: null,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
if (!storageSave(storage, key, snapshot)) {
|
|
159
|
+
return {
|
|
160
|
+
ok: false,
|
|
161
|
+
reasonCode: 'storage_unavailable',
|
|
162
|
+
snapshot,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
ok: true,
|
|
167
|
+
reasonCode: null,
|
|
168
|
+
snapshot,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function loadStreamedWorldSnapshot2D(key, auraRef = globalThis.aura, fallback = null) {
|
|
173
|
+
const storage = resolveStorage(auraRef);
|
|
174
|
+
const snapshot = storageLoad(storage, key, fallback);
|
|
175
|
+
return normalizeSnapshot(snapshot);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function deleteStreamedWorldSnapshot2D(key, auraRef = globalThis.aura) {
|
|
179
|
+
return storageDelete(resolveStorage(auraRef), key);
|
|
180
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createSceneRegistry } from './scene-registry.js';
|
|
2
2
|
import { createProjectInspector } from './project-inspector.js';
|
|
3
3
|
import { assertRuntimeCapabilities } from './capabilities.js';
|
|
4
|
+
import { initSplash, updateSplash, drawSplash, isSplashActive } from './splash.js';
|
|
4
5
|
|
|
5
6
|
export function createApp() {
|
|
6
7
|
const sceneRegistry = createSceneRegistry({
|
|
@@ -26,13 +27,16 @@ export function createApp() {
|
|
|
26
27
|
},
|
|
27
28
|
setup() {
|
|
28
29
|
assertRuntimeCapabilities();
|
|
30
|
+
initSplash();
|
|
29
31
|
activeScene()?.setup?.();
|
|
30
32
|
},
|
|
31
33
|
update(dt) {
|
|
34
|
+
if (isSplashActive()) { updateSplash(dt); return; }
|
|
32
35
|
projectInspector.syncInput(globalThis.aura?.input || null);
|
|
33
36
|
activeScene()?.update?.(dt);
|
|
34
37
|
},
|
|
35
38
|
draw() {
|
|
39
|
+
if (isSplashActive()) { drawSplash(); return; }
|
|
36
40
|
activeScene()?.draw?.();
|
|
37
41
|
projectInspector.draw({ activeSceneId });
|
|
38
42
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { spawn } from 'node:child_process';
|
|
3
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
4
4
|
import { cpSync, createWriteStream, existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
5
5
|
import { dirname, isAbsolute, join, relative, resolve } from 'node:path';
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
@@ -58,9 +58,79 @@ const ALL_COMMANDS = ['dev', 'join', 'play', 'fork', 'publish', 'session', 'stat
|
|
|
58
58
|
const ROOM_CODE_PATTERN = /^[A-Z0-9]{4,8}$/;
|
|
59
59
|
const FORK_EXCLUDED_TOP_LEVEL = new Set(['.aura', '.git', '.logs', 'build', 'dist', 'node_modules']);
|
|
60
60
|
|
|
61
|
+
function resolveAuraMaxxBinary() {
|
|
62
|
+
return process.platform === 'win32' ? 'auramaxx.cmd' : 'auramaxx';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function resolveAuraAliasBinary() {
|
|
66
|
+
return process.platform === 'win32' ? 'aura.cmd' : 'aura';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function resolveNpmBinary() {
|
|
70
|
+
return process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function resolveInvocationCwd() {
|
|
74
|
+
const forwardedCwd = process.env.AURA_INVOKE_CWD;
|
|
75
|
+
if (forwardedCwd && isAbsolute(forwardedCwd)) {
|
|
76
|
+
return resolve(forwardedCwd);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const shellPwd = process.env.PWD;
|
|
80
|
+
if (shellPwd && isAbsolute(shellPwd)) {
|
|
81
|
+
return resolve(shellPwd);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return resolve(process.cwd());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function canProbeBinary(binary) {
|
|
88
|
+
const probe = spawnSync(binary, ['--help'], {
|
|
89
|
+
stdio: 'ignore',
|
|
90
|
+
env: process.env,
|
|
91
|
+
});
|
|
92
|
+
return !probe.error && probe.status === 0;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function isAuraMaxxInstalled() {
|
|
96
|
+
if (process.env.AURAMAXX_CLI_AVAILABLE === '1') {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
if (localAuraCli) {
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
const candidates = [resolveAuraMaxxBinary(), resolveAuraAliasBinary()];
|
|
103
|
+
return candidates.some((binary, index) => candidates.indexOf(binary) === index && canProbeBinary(binary));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function installAuraMaxxGlobally() {
|
|
107
|
+
return new Promise((resolveInstall) => {
|
|
108
|
+
const child = spawn(
|
|
109
|
+
resolveNpmBinary(),
|
|
110
|
+
['install', '-g', 'auramaxx', '--foreground-scripts'],
|
|
111
|
+
{
|
|
112
|
+
cwd: process.cwd(),
|
|
113
|
+
stdio: 'inherit',
|
|
114
|
+
env: process.env,
|
|
115
|
+
},
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
child.on('error', () => resolveInstall(false));
|
|
119
|
+
child.on('close', (code) => resolveInstall((code ?? 1) === 0));
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
61
123
|
function resolveLocalAuraCli(startRoot) {
|
|
62
124
|
let current = resolve(startRoot);
|
|
63
125
|
while (true) {
|
|
126
|
+
const monorepoCandidate = resolve(current, 'packages', 'aurascript', 'src', 'cli', 'src', 'cli.mjs');
|
|
127
|
+
if (existsSync(monorepoCandidate)) {
|
|
128
|
+
return monorepoCandidate;
|
|
129
|
+
}
|
|
130
|
+
const packageSourceCandidate = resolve(current, 'src', 'cli', 'src', 'cli.mjs');
|
|
131
|
+
if (existsSync(packageSourceCandidate)) {
|
|
132
|
+
return packageSourceCandidate;
|
|
133
|
+
}
|
|
64
134
|
const candidate = resolve(current, 'node_modules', '@auraindustry', 'aurajs', 'src', 'cli.mjs');
|
|
65
135
|
if (existsSync(candidate)) {
|
|
66
136
|
return candidate;
|
|
@@ -332,7 +402,7 @@ async function promptSelect(message, options, defaultValue) {
|
|
|
332
402
|
|
|
333
403
|
function normalizeDisplayPath(targetPath) {
|
|
334
404
|
const resolvedPath = resolve(targetPath);
|
|
335
|
-
const relativePath = relative(
|
|
405
|
+
const relativePath = relative(resolveInvocationCwd(), resolvedPath).replaceAll('\\', '/');
|
|
336
406
|
if (!relativePath) return '.';
|
|
337
407
|
if (!relativePath.startsWith('.') && !relativePath.startsWith('/')) {
|
|
338
408
|
return `./${relativePath}`;
|
|
@@ -361,10 +431,10 @@ function resolveUniqueDestination(preferredPath) {
|
|
|
361
431
|
|
|
362
432
|
function resolveDefaultForkDestination() {
|
|
363
433
|
const defaultBaseName = `${toPackageShortName(packageName)}-fork`;
|
|
364
|
-
const
|
|
365
|
-
const baseDir = isSubpath(projectRoot,
|
|
434
|
+
const invocationCwd = resolveInvocationCwd();
|
|
435
|
+
const baseDir = isSubpath(projectRoot, invocationCwd)
|
|
366
436
|
? dirname(projectRoot)
|
|
367
|
-
:
|
|
437
|
+
: invocationCwd;
|
|
368
438
|
return resolveUniqueDestination(resolve(baseDir, defaultBaseName));
|
|
369
439
|
}
|
|
370
440
|
|
|
@@ -413,7 +483,7 @@ function parseForkArgs(args) {
|
|
|
413
483
|
async function resolveForkDestination(args) {
|
|
414
484
|
const parsed = parseForkArgs(args);
|
|
415
485
|
if (parsed.destination) {
|
|
416
|
-
return resolve(
|
|
486
|
+
return resolve(resolveInvocationCwd(), parsed.destination);
|
|
417
487
|
}
|
|
418
488
|
|
|
419
489
|
const suggestedDestination = resolveDefaultForkDestination();
|
|
@@ -495,6 +565,41 @@ function parseArgs(argv) {
|
|
|
495
565
|
};
|
|
496
566
|
}
|
|
497
567
|
|
|
568
|
+
function parsePublishArgs(args) {
|
|
569
|
+
const passthroughArgs = [];
|
|
570
|
+
let npmToken = null;
|
|
571
|
+
|
|
572
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
573
|
+
const token = String(args[index] || '');
|
|
574
|
+
if (token === '--token') {
|
|
575
|
+
if ((index + 1) >= args.length) {
|
|
576
|
+
throw createCliError('publish requires a token value after --token.');
|
|
577
|
+
}
|
|
578
|
+
npmToken = String(args[index + 1] || '').trim();
|
|
579
|
+
index += 1;
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
if (token.startsWith('--token=')) {
|
|
583
|
+
npmToken = token.slice('--token='.length).trim();
|
|
584
|
+
continue;
|
|
585
|
+
}
|
|
586
|
+
passthroughArgs.push(token);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (npmToken !== null && npmToken.length === 0) {
|
|
590
|
+
throw createCliError('publish requires a non-empty token value after --token.');
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
return {
|
|
594
|
+
commandArgs: passthroughArgs,
|
|
595
|
+
env: npmToken
|
|
596
|
+
? {
|
|
597
|
+
NODE_AUTH_TOKEN: npmToken,
|
|
598
|
+
}
|
|
599
|
+
: null,
|
|
600
|
+
};
|
|
601
|
+
}
|
|
602
|
+
|
|
498
603
|
function readJsonIfExists(filePath) {
|
|
499
604
|
if (!existsSync(filePath)) {
|
|
500
605
|
return null;
|
|
@@ -1020,6 +1125,38 @@ async function chooseDefaultCommand() {
|
|
|
1020
1125
|
return choice;
|
|
1021
1126
|
}
|
|
1022
1127
|
|
|
1128
|
+
async function maybeOfferAuraMaxxInstall() {
|
|
1129
|
+
if (isAuraMaxxInstalled()) {
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
printSection('AuraMaxx', 'AuraMaxx is not installed globally.');
|
|
1138
|
+
const choice = await promptSelect(
|
|
1139
|
+
' Install AuraMaxx now?',
|
|
1140
|
+
[
|
|
1141
|
+
{ value: 'yes', label: 'Yes, install AuraMaxx' },
|
|
1142
|
+
{ value: 'no', label: 'No, continue playing' },
|
|
1143
|
+
],
|
|
1144
|
+
'yes',
|
|
1145
|
+
);
|
|
1146
|
+
|
|
1147
|
+
if (choice !== 'yes') {
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
printSection('AuraMaxx', 'Installing global CLI...');
|
|
1152
|
+
const installed = await installAuraMaxxGlobally();
|
|
1153
|
+
if (installed) {
|
|
1154
|
+
printSection('AuraMaxx', 'Installed. Continuing play...');
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
printSection('AuraMaxx', 'Install failed. Continuing play...');
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1023
1160
|
async function main() {
|
|
1024
1161
|
const parsed = parseArgs(process.argv.slice(2));
|
|
1025
1162
|
|
|
@@ -1049,6 +1186,7 @@ async function main() {
|
|
|
1049
1186
|
if (command === 'play') {
|
|
1050
1187
|
printBanner('PLAY');
|
|
1051
1188
|
printSection(toDisplayTitle(packageName), 'Starting packaged local game session...');
|
|
1189
|
+
await maybeOfferAuraMaxxInstall();
|
|
1052
1190
|
const externalAssets = await maybePrepareExternalAssets('play');
|
|
1053
1191
|
const multiplayerEnv = await resolveLocalMultiplayerCommandEnv();
|
|
1054
1192
|
await runCommand(
|
|
@@ -1145,8 +1283,11 @@ async function main() {
|
|
|
1145
1283
|
printSection(toDisplayTitle(packageName), 'Publish lifecycle detected, skipping wrapper recursion.');
|
|
1146
1284
|
return;
|
|
1147
1285
|
}
|
|
1286
|
+
const publish = parsePublishArgs(commandArgs);
|
|
1148
1287
|
printSection(toDisplayTitle(packageName), 'Publishing npm package (source + assets)...');
|
|
1149
|
-
await runCommand('publish', resolveAuraCliInvocation(['publish', ...commandArgs])
|
|
1288
|
+
await runCommand('publish', resolveAuraCliInvocation(['publish', ...publish.commandArgs]), {
|
|
1289
|
+
env: publish.env,
|
|
1290
|
+
});
|
|
1150
1291
|
return;
|
|
1151
1292
|
}
|
|
1152
1293
|
|