@auraindustry/aurajs 0.1.0 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/asset-pack.mjs +5 -1
- package/src/authored-runtime.mjs +14 -0
- package/src/bin-integrity.mjs +33 -26
- package/src/cli.mjs +17 -2
- package/src/commands/project-authoring.mjs +20 -0
- package/src/config.mjs +17 -0
- package/src/conformance/cases/systems-and-gameplay-cases.mjs +861 -6
- package/src/external-package-surface.mjs +1 -1
- package/src/package-integrity.mjs +18 -4
- package/src/publish-command.mjs +133 -13
- package/src/publish-validation.mjs +22 -11
- package/src/scaffold/project-docs.mjs +60 -41
- package/src/web-conformance.mjs +4 -4
- package/templates/create/2d/src/runtime/app.js +4 -0
- package/templates/create/2d-survivor/src/runtime/app.js +4 -0
- package/templates/create/3d/src/runtime/app.js +4 -0
- package/templates/create/3d-collectathon/src/runtime/app.js +4 -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/aura.config.json +1 -0
- package/templates/create/local-multiplayer/docs/design/loop.md +3 -1
- package/templates/create/local-multiplayer/scenes/gameplay.scene.js +216 -13
- package/templates/create/local-multiplayer/src/runtime/capabilities.js +8 -1
- package/templates/create/local-multiplayer/ui/hud.screen.js +12 -7
- 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/video-cutscene/src/runtime/app.js +4 -0
- package/templates/create-bin/play.js +121 -4
- 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
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
// AuraJS Splash — unified retro paper-card boot screen.
|
|
2
|
+
// Auto-wired by createApp(). Shows once on launch, then hands off to the game.
|
|
3
|
+
|
|
4
|
+
const FADE_IN = 0.7;
|
|
5
|
+
const HOLD = 1.8;
|
|
6
|
+
const FADE_OUT = 0.7;
|
|
7
|
+
const TOTAL = FADE_IN + HOLD + FADE_OUT;
|
|
8
|
+
|
|
9
|
+
const PAPER = [0.89, 0.89, 0.89];
|
|
10
|
+
const PAPER_SHADOW = [0.80, 0.80, 0.80];
|
|
11
|
+
const PAPER_EDGE = [0.41, 0.41, 0.41];
|
|
12
|
+
const INK = [0.1, 0.1, 0.1];
|
|
13
|
+
const INK_MUTED = [0.42, 0.42, 0.42];
|
|
14
|
+
const MASCOT_FRAME_W = 64;
|
|
15
|
+
const MASCOT_FRAME_H = 84;
|
|
16
|
+
const MASCOT_SEQUENCE = Object.freeze([0, 1, 2, 1]);
|
|
17
|
+
const SPLASH_STING_PATH = 'splash/boot-sting.wav';
|
|
18
|
+
const SPLASH_LOOP_PATH = 'splash/boot-loop.wav';
|
|
19
|
+
const SPLASH_BUS = 'splash';
|
|
20
|
+
const QUIET_BUSES = Object.freeze(['default', 'music', 'sfx']);
|
|
21
|
+
|
|
22
|
+
let state = null;
|
|
23
|
+
|
|
24
|
+
function has(obj, method) {
|
|
25
|
+
return Boolean(obj) && typeof obj[method] === 'function';
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function color(rgb, alpha = 1) {
|
|
29
|
+
return aura.rgba(rgb[0], rgb[1], rgb[2], alpha);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function drawShadowText(text, x, y, options = {}) {
|
|
33
|
+
const {
|
|
34
|
+
shadowOffset = 2,
|
|
35
|
+
shadowColor = color(INK_MUTED, 0.3),
|
|
36
|
+
...rest
|
|
37
|
+
} = options;
|
|
38
|
+
aura.draw2d.text(text, x + shadowOffset, y + shadowOffset, {
|
|
39
|
+
...rest,
|
|
40
|
+
color: shadowColor,
|
|
41
|
+
});
|
|
42
|
+
aura.draw2d.text(text, x, y, rest);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function initSplash() {
|
|
46
|
+
state = {
|
|
47
|
+
t: 0,
|
|
48
|
+
logo: null,
|
|
49
|
+
mascot: null,
|
|
50
|
+
wordmark: null,
|
|
51
|
+
font: null,
|
|
52
|
+
stingHandle: null,
|
|
53
|
+
loopHandle: null,
|
|
54
|
+
busVolumes: null,
|
|
55
|
+
pausedHandles: [],
|
|
56
|
+
};
|
|
57
|
+
try { if (has(aura.assets, 'load')) state.logo = aura.assets.load('splash/logoholo.webp'); } catch (_) {}
|
|
58
|
+
try { if (has(aura.assets, 'load')) state.mascot = aura.assets.load('splash/logo-mascot-sheet.webp'); } catch (_) {}
|
|
59
|
+
try { if (has(aura.assets, 'load')) state.wordmark = aura.assets.load('splash/aurajs-gg-wordmark.webp'); } catch (_) {}
|
|
60
|
+
try {
|
|
61
|
+
if (has(aura.assets, 'loadBitmapFont')) {
|
|
62
|
+
const result = aura.assets.loadBitmapFont();
|
|
63
|
+
if (result && result.ok && result.font) state.font = result.font;
|
|
64
|
+
}
|
|
65
|
+
} catch (_) {}
|
|
66
|
+
captureBusVolumes();
|
|
67
|
+
applySplashBusIsolation();
|
|
68
|
+
try {
|
|
69
|
+
if (aura.audio && aura.audio.supported !== false && typeof aura.audio.play === 'function') {
|
|
70
|
+
state.loopHandle = aura.audio.play(SPLASH_LOOP_PATH, {
|
|
71
|
+
loop: true,
|
|
72
|
+
volume: 0.22,
|
|
73
|
+
bus: SPLASH_BUS,
|
|
74
|
+
});
|
|
75
|
+
state.stingHandle = aura.audio.play(SPLASH_STING_PATH, {
|
|
76
|
+
loop: false,
|
|
77
|
+
volume: 0.54,
|
|
78
|
+
bus: SPLASH_BUS,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
} catch (_) {}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function isSplashActive() {
|
|
85
|
+
return state !== null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function updateSplash(dt) {
|
|
89
|
+
if (!state) return;
|
|
90
|
+
syncPausedTracks();
|
|
91
|
+
try {
|
|
92
|
+
if (aura.audio && typeof aura.audio.update === 'function') {
|
|
93
|
+
aura.audio.update(Number(dt) > 0 ? Number(dt) : (1 / 60));
|
|
94
|
+
}
|
|
95
|
+
} catch (_) {}
|
|
96
|
+
state.t += dt;
|
|
97
|
+
if (state.t >= TOTAL) {
|
|
98
|
+
stopSplashAudio();
|
|
99
|
+
state = null;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function drawSplash() {
|
|
104
|
+
if (!state) return;
|
|
105
|
+
const { width: w, height: h } = has(aura.window, 'getSize')
|
|
106
|
+
? aura.window.getSize()
|
|
107
|
+
: { width: 640, height: 480 };
|
|
108
|
+
const t = state.t;
|
|
109
|
+
const cx = w / 2;
|
|
110
|
+
const cy = h / 2;
|
|
111
|
+
|
|
112
|
+
let a = 1;
|
|
113
|
+
if (t < FADE_IN) a = easeOut(t / FADE_IN);
|
|
114
|
+
else if (t > FADE_IN + HOLD) a = 1 - easeIn((t - FADE_IN - HOLD) / FADE_OUT);
|
|
115
|
+
|
|
116
|
+
aura.draw2d.clear(color(PAPER));
|
|
117
|
+
|
|
118
|
+
const panelW = Math.min(Math.floor(w * 0.48), 580);
|
|
119
|
+
const panelH = Math.min(Math.floor(h * 0.72), 620);
|
|
120
|
+
const panelX = Math.floor(cx - (panelW * 0.5));
|
|
121
|
+
const panelY = Math.floor(cy - (panelH * 0.5));
|
|
122
|
+
|
|
123
|
+
aura.draw2d.rectFill(panelX + 6, panelY + 6, panelW, panelH, color(INK, 0.08 * a));
|
|
124
|
+
aura.draw2d.rectFill(panelX, panelY, panelW, panelH, color(PAPER_EDGE, a));
|
|
125
|
+
aura.draw2d.rectFill(panelX + 6, panelY + 6, panelW - 12, panelH - 12, color(PAPER, a));
|
|
126
|
+
aura.draw2d.rectFill(panelX + 12, panelY + 12, panelW - 24, panelH - 24, color(PAPER, a * 0.96));
|
|
127
|
+
|
|
128
|
+
const floatY = Math.sin(t * 1.4 * Math.PI * 2) * 2;
|
|
129
|
+
const breathe = 0.985 + 0.015 * (0.5 + 0.5 * Math.sin(t * Math.PI * 2));
|
|
130
|
+
const mascotScale = Math.max(1, Math.min(Math.floor(Math.min(w, h) / 260), 2));
|
|
131
|
+
const mascotW = Math.floor(MASCOT_FRAME_W * mascotScale * breathe);
|
|
132
|
+
const mascotH = Math.floor(MASCOT_FRAME_H * mascotScale * breathe);
|
|
133
|
+
const mascotX = Math.floor(cx - (mascotW * 0.5));
|
|
134
|
+
const mascotY = Math.floor(panelY + panelH * 0.40 + floatY);
|
|
135
|
+
const mascotFrame = MASCOT_SEQUENCE[Math.floor(t * 7.5) % MASCOT_SEQUENCE.length] || 0;
|
|
136
|
+
|
|
137
|
+
aura.draw2d.rectFill(
|
|
138
|
+
mascotX + Math.floor(mascotW * 0.18),
|
|
139
|
+
mascotY + mascotH - 8,
|
|
140
|
+
Math.floor(mascotW * 0.64),
|
|
141
|
+
6,
|
|
142
|
+
color(INK, a * 0.08),
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
if (state.mascot) {
|
|
146
|
+
aura.draw2d.sprite(state.mascot, mascotX, mascotY, {
|
|
147
|
+
width: mascotW,
|
|
148
|
+
height: mascotH,
|
|
149
|
+
frameX: mascotFrame * MASCOT_FRAME_W,
|
|
150
|
+
frameY: 0,
|
|
151
|
+
frameW: MASCOT_FRAME_W,
|
|
152
|
+
frameH: MASCOT_FRAME_H,
|
|
153
|
+
alpha: a,
|
|
154
|
+
});
|
|
155
|
+
} else if (state.logo) {
|
|
156
|
+
const sz = Math.min(Math.floor(panelW * 0.24), Math.floor(h * 0.14)) * breathe;
|
|
157
|
+
aura.draw2d.sprite(state.logo, Math.floor(cx - sz / 2), Math.floor(mascotY + 6), {
|
|
158
|
+
width: sz,
|
|
159
|
+
height: sz,
|
|
160
|
+
alpha: a,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const ta = clamp(a * easeOut(clamp((t - 0.2) / (FADE_IN * 0.6))));
|
|
165
|
+
const sa = clamp(a * easeOut(clamp((t - 0.4) / (FADE_IN * 0.6))));
|
|
166
|
+
const scale = Math.min(w, h) / 480;
|
|
167
|
+
const tsz = Math.max(24, Math.round(scale * 28));
|
|
168
|
+
const ssz = Math.max(11, Math.round(scale * 12));
|
|
169
|
+
const wordmarkW = Math.min(Math.floor(panelW * 0.52), 290);
|
|
170
|
+
const wordmarkH = Math.floor(wordmarkW * (768 / 1408));
|
|
171
|
+
const wordmarkX = Math.floor(cx - (wordmarkW * 0.5));
|
|
172
|
+
const wordmarkY = Math.floor(panelY + 36);
|
|
173
|
+
const sY = Math.floor(panelY + panelH - 108);
|
|
174
|
+
const fo = state.font ? { font: state.font } : {};
|
|
175
|
+
|
|
176
|
+
if (state.wordmark) {
|
|
177
|
+
aura.draw2d.sprite(state.wordmark, wordmarkX, wordmarkY, {
|
|
178
|
+
width: wordmarkW,
|
|
179
|
+
height: wordmarkH,
|
|
180
|
+
alpha: ta,
|
|
181
|
+
tint: color(INK, ta),
|
|
182
|
+
});
|
|
183
|
+
} else {
|
|
184
|
+
drawShadowText('AuraJS.gg', cx, wordmarkY + Math.floor(wordmarkH * 0.55), {
|
|
185
|
+
...fo,
|
|
186
|
+
size: tsz,
|
|
187
|
+
color: color(INK, ta),
|
|
188
|
+
shadowColor: color(INK_MUTED, ta * 0.28),
|
|
189
|
+
align: 'center',
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
drawShadowText('Open-Source. MIT.', cx, sY, {
|
|
194
|
+
...fo,
|
|
195
|
+
size: ssz,
|
|
196
|
+
color: color(INK_MUTED, sa),
|
|
197
|
+
shadowColor: color(PAPER_EDGE, sa * 0.18),
|
|
198
|
+
shadowOffset: 1,
|
|
199
|
+
align: 'center',
|
|
200
|
+
});
|
|
201
|
+
drawShadowText('Who needs publishers?', cx, sY + Math.max(16, Math.round(ssz * 1.45)), {
|
|
202
|
+
...fo,
|
|
203
|
+
size: ssz,
|
|
204
|
+
color: color(INK_MUTED, sa),
|
|
205
|
+
shadowColor: color(PAPER_EDGE, sa * 0.18),
|
|
206
|
+
shadowOffset: 1,
|
|
207
|
+
align: 'center',
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const rw = Math.min(panelW - 140, 240);
|
|
211
|
+
aura.draw2d.rectFill(Math.floor(cx - rw / 2), Math.floor(sY + Math.max(34, ssz * 3.2)), rw, 2, color(PAPER_EDGE, sa * 0.38));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function easeOut(t) {
|
|
215
|
+
const u = 1 - clamp(t);
|
|
216
|
+
return 1 - (u * u * u);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function easeIn(t) {
|
|
220
|
+
const c = clamp(t);
|
|
221
|
+
return c * c * c;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function clamp(v) {
|
|
225
|
+
return v < 0 ? 0 : v > 1 ? 1 : v;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function stopSplashAudio() {
|
|
229
|
+
if (!state || !aura.audio || typeof aura.audio.stop !== 'function') return;
|
|
230
|
+
try {
|
|
231
|
+
if (state.stingHandle != null) aura.audio.stop(state.stingHandle);
|
|
232
|
+
} catch (_) {}
|
|
233
|
+
try {
|
|
234
|
+
if (state.loopHandle != null) aura.audio.stop(state.loopHandle);
|
|
235
|
+
} catch (_) {}
|
|
236
|
+
restoreAudioState();
|
|
237
|
+
state.stingHandle = null;
|
|
238
|
+
state.loopHandle = null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function captureBusVolumes() {
|
|
242
|
+
if (!state || !aura.audio || typeof aura.audio.getMixerState !== 'function') return;
|
|
243
|
+
try {
|
|
244
|
+
const mixer = aura.audio.getMixerState();
|
|
245
|
+
const buses = Array.isArray(mixer?.buses) ? mixer.buses : [];
|
|
246
|
+
state.busVolumes = buses.reduce((acc, entry) => {
|
|
247
|
+
const bus = String(entry?.bus || '').trim();
|
|
248
|
+
if (!bus) return acc;
|
|
249
|
+
acc[bus] = Number(entry?.volume);
|
|
250
|
+
return acc;
|
|
251
|
+
}, {});
|
|
252
|
+
} catch (_) {}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function applySplashBusIsolation() {
|
|
256
|
+
if (!state || !aura.audio || typeof aura.audio.setBusVolume !== 'function') return;
|
|
257
|
+
try { aura.audio.setBusVolume(SPLASH_BUS, 1); } catch (_) {}
|
|
258
|
+
for (const bus of QUIET_BUSES) {
|
|
259
|
+
try { aura.audio.setBusVolume(bus, 0); } catch (_) {}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function syncPausedTracks() {
|
|
264
|
+
if (!state || !aura.audio || typeof aura.audio.getMixerState !== 'function' || typeof aura.audio.pause !== 'function') return;
|
|
265
|
+
try {
|
|
266
|
+
const mixer = aura.audio.getMixerState();
|
|
267
|
+
const tracks = Array.isArray(mixer?.tracks) ? mixer.tracks : [];
|
|
268
|
+
const splashHandles = [state.stingHandle, state.loopHandle].filter((handle) => handle != null);
|
|
269
|
+
const pausedHandles = new Set(Array.isArray(state.pausedHandles) ? state.pausedHandles : []);
|
|
270
|
+
for (const track of tracks) {
|
|
271
|
+
const handle = Number(track?.handle);
|
|
272
|
+
if (!Number.isInteger(handle) || handle <= 0) continue;
|
|
273
|
+
if (splashHandles.includes(handle)) continue;
|
|
274
|
+
if (track?.paused === true || pausedHandles.has(handle)) continue;
|
|
275
|
+
try {
|
|
276
|
+
aura.audio.pause(handle);
|
|
277
|
+
pausedHandles.add(handle);
|
|
278
|
+
} catch (_) {}
|
|
279
|
+
}
|
|
280
|
+
state.pausedHandles = Array.from(pausedHandles);
|
|
281
|
+
} catch (_) {}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function restoreAudioState() {
|
|
285
|
+
if (!state || !aura.audio) return;
|
|
286
|
+
if (typeof aura.audio.setBusVolume === 'function') {
|
|
287
|
+
const snapshot = state.busVolumes && typeof state.busVolumes === 'object' ? state.busVolumes : null;
|
|
288
|
+
if (snapshot) {
|
|
289
|
+
for (const [bus, volume] of Object.entries(snapshot)) {
|
|
290
|
+
try { aura.audio.setBusVolume(bus, Number(volume)); } catch (_) {}
|
|
291
|
+
}
|
|
292
|
+
} else {
|
|
293
|
+
for (const bus of QUIET_BUSES) {
|
|
294
|
+
try { aura.audio.setBusVolume(bus, 1); } catch (_) {}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
try { aura.audio.setBusVolume(SPLASH_BUS, 1); } catch (_) {}
|
|
298
|
+
}
|
|
299
|
+
if (typeof aura.audio.resume === 'function') {
|
|
300
|
+
for (const handle of Array.isArray(state.pausedHandles) ? state.pausedHandles : []) {
|
|
301
|
+
try { aura.audio.resume(handle); } catch (_) {}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
state.pausedHandles = [];
|
|
305
|
+
}
|
|
@@ -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,50 @@ 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 resolveNpmBinary() {
|
|
66
|
+
return process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function isAuraMaxxInstalled() {
|
|
70
|
+
const probe = spawnSync(resolveAuraMaxxBinary(), ['--version'], {
|
|
71
|
+
stdio: 'ignore',
|
|
72
|
+
env: process.env,
|
|
73
|
+
});
|
|
74
|
+
return !probe.error && probe.status === 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function installAuraMaxxGlobally() {
|
|
78
|
+
return new Promise((resolveInstall) => {
|
|
79
|
+
const child = spawn(
|
|
80
|
+
resolveNpmBinary(),
|
|
81
|
+
['install', '-g', 'auramaxx', '--foreground-scripts'],
|
|
82
|
+
{
|
|
83
|
+
cwd: process.cwd(),
|
|
84
|
+
stdio: 'inherit',
|
|
85
|
+
env: process.env,
|
|
86
|
+
},
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
child.on('error', () => resolveInstall(false));
|
|
90
|
+
child.on('close', (code) => resolveInstall((code ?? 1) === 0));
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
61
94
|
function resolveLocalAuraCli(startRoot) {
|
|
62
95
|
let current = resolve(startRoot);
|
|
63
96
|
while (true) {
|
|
97
|
+
const monorepoCandidate = resolve(current, 'packages', 'aurascript', 'src', 'cli', 'src', 'cli.mjs');
|
|
98
|
+
if (existsSync(monorepoCandidate)) {
|
|
99
|
+
return monorepoCandidate;
|
|
100
|
+
}
|
|
101
|
+
const packageSourceCandidate = resolve(current, 'src', 'cli', 'src', 'cli.mjs');
|
|
102
|
+
if (existsSync(packageSourceCandidate)) {
|
|
103
|
+
return packageSourceCandidate;
|
|
104
|
+
}
|
|
64
105
|
const candidate = resolve(current, 'node_modules', '@auraindustry', 'aurajs', 'src', 'cli.mjs');
|
|
65
106
|
if (existsSync(candidate)) {
|
|
66
107
|
return candidate;
|
|
@@ -495,6 +536,41 @@ function parseArgs(argv) {
|
|
|
495
536
|
};
|
|
496
537
|
}
|
|
497
538
|
|
|
539
|
+
function parsePublishArgs(args) {
|
|
540
|
+
const passthroughArgs = [];
|
|
541
|
+
let npmToken = null;
|
|
542
|
+
|
|
543
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
544
|
+
const token = String(args[index] || '');
|
|
545
|
+
if (token === '--token') {
|
|
546
|
+
if ((index + 1) >= args.length) {
|
|
547
|
+
throw createCliError('publish requires a token value after --token.');
|
|
548
|
+
}
|
|
549
|
+
npmToken = String(args[index + 1] || '').trim();
|
|
550
|
+
index += 1;
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
if (token.startsWith('--token=')) {
|
|
554
|
+
npmToken = token.slice('--token='.length).trim();
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
passthroughArgs.push(token);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if (npmToken !== null && npmToken.length === 0) {
|
|
561
|
+
throw createCliError('publish requires a non-empty token value after --token.');
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
return {
|
|
565
|
+
commandArgs: passthroughArgs,
|
|
566
|
+
env: npmToken
|
|
567
|
+
? {
|
|
568
|
+
NODE_AUTH_TOKEN: npmToken,
|
|
569
|
+
}
|
|
570
|
+
: null,
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
|
|
498
574
|
function readJsonIfExists(filePath) {
|
|
499
575
|
if (!existsSync(filePath)) {
|
|
500
576
|
return null;
|
|
@@ -604,6 +680,7 @@ function resolveProjectMultiplayerConfig() {
|
|
|
604
680
|
relayHost: null,
|
|
605
681
|
coordinatorUrl: null,
|
|
606
682
|
relayUrl: null,
|
|
683
|
+
launcherBaseUrl: null,
|
|
607
684
|
showDiagnostics: null,
|
|
608
685
|
chatEnabled: null,
|
|
609
686
|
chatHistoryLimit: null,
|
|
@@ -619,6 +696,7 @@ function resolveProjectMultiplayerConfig() {
|
|
|
619
696
|
relayHost: readOptionalCliText(raw.relay || raw.relayHost),
|
|
620
697
|
coordinatorUrl: readOptionalCliText(raw.coordinatorUrl),
|
|
621
698
|
relayUrl: readOptionalCliText(raw.relayUrl),
|
|
699
|
+
launcherBaseUrl: readOptionalCliText(raw.launcherBaseUrl || raw.launcherUrl),
|
|
622
700
|
showDiagnostics: typeof raw.showDiagnostics === 'boolean' ? raw.showDiagnostics : null,
|
|
623
701
|
chatEnabled: typeof raw.chatEnabled === 'boolean' ? raw.chatEnabled : null,
|
|
624
702
|
chatHistoryLimit: historyLimit,
|
|
@@ -679,6 +757,9 @@ async function resolveLocalMultiplayerCommandEnv() {
|
|
|
679
757
|
if (relayUrl) {
|
|
680
758
|
env.AURA_RELAY_URL = relayUrl;
|
|
681
759
|
}
|
|
760
|
+
if (projectMultiplayer.launcherBaseUrl) {
|
|
761
|
+
env.AURA_MULTIPLAYER_LAUNCHER_BASE_URL = projectMultiplayer.launcherBaseUrl;
|
|
762
|
+
}
|
|
682
763
|
if (typeof projectMultiplayer.showDiagnostics === 'boolean') {
|
|
683
764
|
env.AURA_MULTIPLAYER_SHOW_DIAGNOSTICS = projectMultiplayer.showDiagnostics ? '1' : '0';
|
|
684
765
|
}
|
|
@@ -900,7 +981,7 @@ function assertJoinSupported() {
|
|
|
900
981
|
: 'project capability declarations';
|
|
901
982
|
throw new Error(
|
|
902
983
|
`join unavailable: multiplayer is disabled in ${sourceDetail}. `
|
|
903
|
-
+ `Use \`npm run play\` for a
|
|
984
|
+
+ `Use \`npm run dev\` for the local authoring loop, \`npm run play\` for a packaged local launch, or \`${packageBin} play\` for an installed package launch.`,
|
|
904
985
|
);
|
|
905
986
|
}
|
|
906
987
|
}
|
|
@@ -1015,6 +1096,38 @@ async function chooseDefaultCommand() {
|
|
|
1015
1096
|
return choice;
|
|
1016
1097
|
}
|
|
1017
1098
|
|
|
1099
|
+
async function maybeOfferAuraMaxxInstall() {
|
|
1100
|
+
if (isAuraMaxxInstalled()) {
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
printSection('AuraMaxx', 'AuraMaxx is not installed globally.');
|
|
1109
|
+
const choice = await promptSelect(
|
|
1110
|
+
' Install AuraMaxx now?',
|
|
1111
|
+
[
|
|
1112
|
+
{ value: 'yes', label: 'Yes, install AuraMaxx' },
|
|
1113
|
+
{ value: 'no', label: 'No, continue playing' },
|
|
1114
|
+
],
|
|
1115
|
+
'yes',
|
|
1116
|
+
);
|
|
1117
|
+
|
|
1118
|
+
if (choice !== 'yes') {
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
printSection('AuraMaxx', 'Installing global CLI...');
|
|
1123
|
+
const installed = await installAuraMaxxGlobally();
|
|
1124
|
+
if (installed) {
|
|
1125
|
+
printSection('AuraMaxx', 'Installed. Continuing play...');
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
printSection('AuraMaxx', 'Install failed. Continuing play...');
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1018
1131
|
async function main() {
|
|
1019
1132
|
const parsed = parseArgs(process.argv.slice(2));
|
|
1020
1133
|
|
|
@@ -1043,7 +1156,8 @@ async function main() {
|
|
|
1043
1156
|
|
|
1044
1157
|
if (command === 'play') {
|
|
1045
1158
|
printBanner('PLAY');
|
|
1046
|
-
printSection(toDisplayTitle(packageName), 'Starting
|
|
1159
|
+
printSection(toDisplayTitle(packageName), 'Starting packaged local game session...');
|
|
1160
|
+
await maybeOfferAuraMaxxInstall();
|
|
1047
1161
|
const externalAssets = await maybePrepareExternalAssets('play');
|
|
1048
1162
|
const multiplayerEnv = await resolveLocalMultiplayerCommandEnv();
|
|
1049
1163
|
await runCommand(
|
|
@@ -1140,8 +1254,11 @@ async function main() {
|
|
|
1140
1254
|
printSection(toDisplayTitle(packageName), 'Publish lifecycle detected, skipping wrapper recursion.');
|
|
1141
1255
|
return;
|
|
1142
1256
|
}
|
|
1257
|
+
const publish = parsePublishArgs(commandArgs);
|
|
1143
1258
|
printSection(toDisplayTitle(packageName), 'Publishing npm package (source + assets)...');
|
|
1144
|
-
await runCommand('publish', resolveAuraCliInvocation(['publish', ...commandArgs])
|
|
1259
|
+
await runCommand('publish', resolveAuraCliInvocation(['publish', ...publish.commandArgs]), {
|
|
1260
|
+
env: publish.env,
|
|
1261
|
+
});
|
|
1145
1262
|
return;
|
|
1146
1263
|
}
|
|
1147
1264
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// - modules.physics: enables aura.physics.*
|
|
4
4
|
// - modules.network: enables aura.net.*
|
|
5
5
|
// - modules.steam: enables aura.steam.*
|
|
6
|
+
import { initSplash, updateSplash, drawSplash, isSplashActive } from './runtime/splash.js';
|
|
6
7
|
|
|
7
8
|
let x = 400;
|
|
8
9
|
let y = 300;
|
|
@@ -153,6 +154,7 @@ function clearScreen(color) {
|
|
|
153
154
|
|
|
154
155
|
aura.setup = function () {
|
|
155
156
|
assertRuntimeCapabilities();
|
|
157
|
+
initSplash();
|
|
156
158
|
console.log("{{PROJECT_NAME}} started");
|
|
157
159
|
if (!hasAsset(PLAYER_SPRITE_PATH)) {
|
|
158
160
|
console.log(`Tip: add assets/${PLAYER_SPRITE_PATH} to preview sprite draw2d options.`);
|
|
@@ -163,6 +165,7 @@ aura.setup = function () {
|
|
|
163
165
|
};
|
|
164
166
|
|
|
165
167
|
aura.update = function (dt) {
|
|
168
|
+
if (isSplashActive()) { updateSplash(dt); return; }
|
|
166
169
|
if (keyDown("arrowright")) x += speed * dt;
|
|
167
170
|
if (keyDown("arrowleft")) x -= speed * dt;
|
|
168
171
|
if (keyDown("arrowup")) y -= speed * dt;
|
|
@@ -186,6 +189,7 @@ aura.update = function (dt) {
|
|
|
186
189
|
};
|
|
187
190
|
|
|
188
191
|
aura.draw = function () {
|
|
192
|
+
if (isSplashActive()) { drawSplash(); return; }
|
|
189
193
|
const bg = rgba(0.08, 0.08, 0.12, 1.0);
|
|
190
194
|
clearScreen(bg);
|
|
191
195
|
|