@auraindustry/aurajs 0.1.0 → 0.1.1
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 +3 -0
- package/src/cli.mjs +17 -2
- package/src/commands/project-authoring.mjs +20 -0
- package/src/config.mjs +17 -0
- package/src/scaffold/project-docs.mjs +2 -1
- package/src/web-conformance.mjs +4 -4
- 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 +30 -1
- package/templates/create/local-multiplayer/ui/hud.screen.js +12 -7
- package/templates/create-bin/play.js +7 -2
package/package.json
CHANGED
package/src/asset-pack.mjs
CHANGED
|
@@ -276,6 +276,9 @@ function collectAssets(assetsRoot) {
|
|
|
276
276
|
function walkDir(dir, out) {
|
|
277
277
|
const items = readdirSync(dir).sort((a, b) => a.localeCompare(b));
|
|
278
278
|
for (const item of items) {
|
|
279
|
+
if (item === '.gitkeep' || item === '.DS_Store') {
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
279
282
|
const full = join(dir, item);
|
|
280
283
|
const stat = statSync(full);
|
|
281
284
|
if (stat.isDirectory()) {
|
package/src/cli.mjs
CHANGED
|
@@ -4553,6 +4553,11 @@ function printForegroundSessionReady(commandLabel, record, {
|
|
|
4553
4553
|
reattached = false,
|
|
4554
4554
|
includeWorkflowHints = false,
|
|
4555
4555
|
} = {}) {
|
|
4556
|
+
if (commandLabel === 'aura run') {
|
|
4557
|
+
printRunLaunchBanner();
|
|
4558
|
+
return;
|
|
4559
|
+
}
|
|
4560
|
+
|
|
4556
4561
|
const readiness = reattached ? 'dev session reattached' : 'dev session ready';
|
|
4557
4562
|
console.log(
|
|
4558
4563
|
` ${commandLabel}: ${readiness} `
|
|
@@ -4570,6 +4575,18 @@ function printForegroundSessionReady(commandLabel, record, {
|
|
|
4570
4575
|
console.log('');
|
|
4571
4576
|
}
|
|
4572
4577
|
|
|
4578
|
+
function printRunLaunchBanner() {
|
|
4579
|
+
console.log('');
|
|
4580
|
+
console.log(' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ');
|
|
4581
|
+
console.log(' [ AURAJS ]');
|
|
4582
|
+
console.log(' Docs: https://www.aurajs.gg/docs');
|
|
4583
|
+
console.log('');
|
|
4584
|
+
console.log(' To create your own game:');
|
|
4585
|
+
console.log(' npm install -g auramaxx');
|
|
4586
|
+
console.log(' auramaxx create my-game');
|
|
4587
|
+
console.log('');
|
|
4588
|
+
}
|
|
4589
|
+
|
|
4573
4590
|
function logForegroundSessionRegistryHygiene(commandLabel, report) {
|
|
4574
4591
|
if (!report || typeof report !== 'object') {
|
|
4575
4592
|
return;
|
|
@@ -4689,8 +4706,6 @@ async function cmdRun(args) {
|
|
|
4689
4706
|
const launchContract = built.buildManifestPath;
|
|
4690
4707
|
const launchExecutablePath = built.launchExecutablePath || built.executablePath;
|
|
4691
4708
|
|
|
4692
|
-
console.log('\n aura run: launching native host.\n');
|
|
4693
|
-
|
|
4694
4709
|
const launchEnv = {
|
|
4695
4710
|
...process.env,
|
|
4696
4711
|
AURA_MODE: 'release',
|
|
@@ -221,6 +221,8 @@ export async function cmdInit(args, { error } = {}) {
|
|
|
221
221
|
const dest = resolve(process.cwd(), name);
|
|
222
222
|
|
|
223
223
|
try {
|
|
224
|
+
printBanner('CREATE');
|
|
225
|
+
printSection('AuraJS Create', `Scaffolding ${name}...`);
|
|
224
226
|
const result = scaffold(name, dest);
|
|
225
227
|
console.log(`\n Created "${name}" at ${dest}`);
|
|
226
228
|
console.log('');
|
|
@@ -232,6 +234,13 @@ export async function cmdInit(args, { error } = {}) {
|
|
|
232
234
|
console.log(` cd ${name}`);
|
|
233
235
|
console.log(' aura dev');
|
|
234
236
|
console.log('');
|
|
237
|
+
console.log(' Docs:');
|
|
238
|
+
console.log(' https://www.aurajs.gg/docs');
|
|
239
|
+
console.log('');
|
|
240
|
+
console.log(' Agent skills:');
|
|
241
|
+
console.log(' cd <your-codebase>');
|
|
242
|
+
console.log(' npx -y skills add Aura-Industry/auramaxx');
|
|
243
|
+
console.log('');
|
|
235
244
|
} catch (err) {
|
|
236
245
|
handleProjectAuthoringError(error, err);
|
|
237
246
|
}
|
|
@@ -251,6 +260,8 @@ export async function cmdCreate(args, { error } = {}) {
|
|
|
251
260
|
const dest = resolve(process.cwd(), name);
|
|
252
261
|
|
|
253
262
|
try {
|
|
263
|
+
printBanner('CREATE');
|
|
264
|
+
printSection('AuraJS Create', `Scaffolding ${name}...`);
|
|
254
265
|
const result = scaffoldGame({
|
|
255
266
|
name,
|
|
256
267
|
dest,
|
|
@@ -287,9 +298,18 @@ export async function cmdCreate(args, { error } = {}) {
|
|
|
287
298
|
console.log(' npm run build:gbc');
|
|
288
299
|
} else {
|
|
289
300
|
console.log(' npm run dev');
|
|
301
|
+
console.log('');
|
|
302
|
+
console.log(' Optional packaged local run:');
|
|
290
303
|
console.log(' npm run play');
|
|
291
304
|
}
|
|
292
305
|
console.log('');
|
|
306
|
+
console.log(' Docs:');
|
|
307
|
+
console.log(' https://www.aurajs.gg/docs');
|
|
308
|
+
console.log('');
|
|
309
|
+
console.log(' Agent skills:');
|
|
310
|
+
console.log(' cd <your-codebase>');
|
|
311
|
+
console.log(' npx -y skills add Aura-Industry/auramaxx');
|
|
312
|
+
console.log('');
|
|
293
313
|
} catch (err) {
|
|
294
314
|
handleProjectAuthoringError(error, err);
|
|
295
315
|
}
|
package/src/config.mjs
CHANGED
|
@@ -175,6 +175,7 @@ const SCHEMA = {
|
|
|
175
175
|
relay: { type: 'string', default: null, validate: nonEmptyString },
|
|
176
176
|
coordinatorUrl: { type: 'string', default: null, validate: nonEmptyString },
|
|
177
177
|
relayUrl: { type: 'string', default: null, validate: nonEmptyString },
|
|
178
|
+
launcherBaseUrl: { type: 'string', default: null, validate: absoluteHttpUrl },
|
|
178
179
|
showDiagnostics: { type: 'boolean', default: false },
|
|
179
180
|
chatEnabled: { type: 'boolean', default: false },
|
|
180
181
|
chatHistoryLimit: { type: 'number', default: 6, validate: positiveInteger },
|
|
@@ -234,6 +235,22 @@ function nonEmptyString(value, path) {
|
|
|
234
235
|
return null;
|
|
235
236
|
}
|
|
236
237
|
|
|
238
|
+
function absoluteHttpUrl(value, path) {
|
|
239
|
+
const normalized = String(value || '').trim();
|
|
240
|
+
if (normalized.length === 0) {
|
|
241
|
+
return `${path} must be a non-empty string`;
|
|
242
|
+
}
|
|
243
|
+
try {
|
|
244
|
+
const parsed = new URL(normalized);
|
|
245
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
246
|
+
return `${path} must use http:// or https://, got: ${JSON.stringify(value)}`;
|
|
247
|
+
}
|
|
248
|
+
return null;
|
|
249
|
+
} catch {
|
|
250
|
+
return `${path} must be an absolute http:// or https:// URL, got: ${JSON.stringify(value)}`;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
237
254
|
function multiplayerRoomCode(value, path) {
|
|
238
255
|
const normalized = String(value || '').trim();
|
|
239
256
|
if (!/^[A-Za-z0-9]{4,8}$/.test(normalized)) {
|
|
@@ -13,6 +13,7 @@ export function renderProjectReadme({ name, projectTitle, template, templateMeta
|
|
|
13
13
|
|
|
14
14
|
const optionalCommands = [
|
|
15
15
|
'npm run build',
|
|
16
|
+
'# packaged local launch sanity check',
|
|
16
17
|
'npm run play',
|
|
17
18
|
templateMetadata?.optionalModules?.multiplayer === true ? 'npm run join -- AURA2P' : null,
|
|
18
19
|
'npm run publish',
|
|
@@ -109,7 +110,7 @@ If any are unavailable at runtime, fail fast and capture the reason code before
|
|
|
109
110
|
## Build + Share
|
|
110
111
|
|
|
111
112
|
1. \`npm run build\` to emit native/web artifacts.
|
|
112
|
-
1. \`npm run play\` to
|
|
113
|
+
1. \`npm run play\` to sanity-check the packaged local wrapper path.
|
|
113
114
|
${templateMetadata?.optionalModules?.multiplayer === true ? '1. `npm run join -- AURA2P` to join a room-code multiplayer session through the generated wrapper.\n' : ''}1. \`npm run session -- start\` to open a persistent local developer session.
|
|
114
115
|
1. \`npm run publish\` once metadata and binaries are ready.
|
|
115
116
|
1. If you are not publishing under \`@aurajs\`, update \`package.json -> name\` first.
|
package/src/web-conformance.mjs
CHANGED
|
@@ -26,13 +26,13 @@ export const DEFAULT_WEB_SMOKE_CASES = Object.freeze([
|
|
|
26
26
|
examples: ['examples/web-render-2d-proof'],
|
|
27
27
|
}),
|
|
28
28
|
Object.freeze({
|
|
29
|
-
id: 'web-partial-
|
|
29
|
+
id: 'web-partial-auramon-example',
|
|
30
30
|
bucket: 'partial',
|
|
31
31
|
expectation: 'partial',
|
|
32
|
-
title: '
|
|
32
|
+
title: 'auramon runs on the current browser-backed subset',
|
|
33
33
|
file: DEFAULT_TEST_FILE,
|
|
34
|
-
testName: 'web loader browser smoke:
|
|
35
|
-
examples: ['examples/
|
|
34
|
+
testName: 'web loader browser smoke: auramon runs on the supported 2d subset with input and resize evidence',
|
|
35
|
+
examples: ['examples/auramon'],
|
|
36
36
|
}),
|
|
37
37
|
Object.freeze({
|
|
38
38
|
id: 'web-supported-starter-3d-example',
|
|
@@ -11,4 +11,6 @@ multiplayer. The default template keeps hosting local-first, while the authored
|
|
|
11
11
|
project config can promote the same `npm run dev` + `npm run join -- CODE`
|
|
12
12
|
flow to internet-backed hosting by setting `aura.config.json -> multiplayer.relay`
|
|
13
13
|
or the `AURA_MULTIPLAYER_RELAY_HOST` env var. Direct host/port joins and LAN
|
|
14
|
-
discovery still stay outside this starter.
|
|
14
|
+
discovery still stay outside this starter. If you also set
|
|
15
|
+
`aura.config.json -> multiplayer.launcherBaseUrl`, the host HUD can surface one
|
|
16
|
+
shareable launcher join page instead of only room-code shell instructions.
|
|
@@ -52,6 +52,11 @@ function readAuraEnvBoolean(name) {
|
|
|
52
52
|
return null;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
function normalizeLauncherBaseUrl(value) {
|
|
56
|
+
const normalized = String(value || '').trim().replace(/\/+$/, '');
|
|
57
|
+
return normalized || null;
|
|
58
|
+
}
|
|
59
|
+
|
|
55
60
|
function isKnownConnectivityMode(value) {
|
|
56
61
|
const normalized = String(value || '').trim().toLowerCase();
|
|
57
62
|
return normalized === 'local'
|
|
@@ -134,9 +139,16 @@ function resolveDiagnosticsConfig() {
|
|
|
134
139
|
};
|
|
135
140
|
}
|
|
136
141
|
|
|
142
|
+
function resolveLauncherConfig() {
|
|
143
|
+
return {
|
|
144
|
+
baseUrl: normalizeLauncherBaseUrl(readAuraEnv('AURA_MULTIPLAYER_LAUNCHER_BASE_URL')),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
137
148
|
const ROOM_CONFIG = resolveRoomConfig();
|
|
138
149
|
const CONNECTIVITY_CONFIG = resolveConnectivityConfig();
|
|
139
150
|
const DIAGNOSTICS_CONFIG = resolveDiagnosticsConfig();
|
|
151
|
+
const LAUNCHER_CONFIG = resolveLauncherConfig();
|
|
140
152
|
const LAUNCH_JOIN_CODE = (() => {
|
|
141
153
|
const value = readAuraEnv('AURA_MULTIPLAYER_JOIN_CODE');
|
|
142
154
|
return value ? value.toUpperCase() : null;
|
|
@@ -233,6 +245,7 @@ function readInput() {
|
|
|
233
245
|
}
|
|
234
246
|
|
|
235
247
|
function currentStatusLine(sceneState, roomCode) {
|
|
248
|
+
const shareLink = currentLauncherJoinLink(sceneState);
|
|
236
249
|
if (sceneState.role === 'pending') {
|
|
237
250
|
if (launchedViaExplicitJoin()) {
|
|
238
251
|
return `Joining room code ${roomCode}...`;
|
|
@@ -240,6 +253,9 @@ function currentStatusLine(sceneState, roomCode) {
|
|
|
240
253
|
return 'Checking for a room-code match before hosting this room...';
|
|
241
254
|
}
|
|
242
255
|
if (sceneState.role === 'host') {
|
|
256
|
+
if (shareLink) {
|
|
257
|
+
return `Share launcher link: ${displayShareLink(shareLink)}`;
|
|
258
|
+
}
|
|
243
259
|
if (usesInternetBackedHosting()) {
|
|
244
260
|
return `Share this room code anywhere: npm run join -- ${roomCode}`;
|
|
245
261
|
}
|
|
@@ -251,6 +267,17 @@ function currentStatusLine(sceneState, roomCode) {
|
|
|
251
267
|
return 'Joined through the local-first room-code multiplayer flow.';
|
|
252
268
|
}
|
|
253
269
|
|
|
270
|
+
function currentLauncherJoinLink(sceneState) {
|
|
271
|
+
if (!usesInternetBackedHosting() || !LAUNCHER_CONFIG.baseUrl) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
return `${LAUNCHER_CONFIG.baseUrl}/join/${encodeURIComponent(currentRoomCode(sceneState))}`;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function displayShareLink(link) {
|
|
278
|
+
return String(link || '').replace(/^https?:\/\//, '');
|
|
279
|
+
}
|
|
280
|
+
|
|
254
281
|
export function createGameplayScene(context = {}) {
|
|
255
282
|
const appState = context.appState && typeof context.appState === 'object'
|
|
256
283
|
? context.appState
|
|
@@ -272,6 +299,7 @@ export function createGameplayScene(context = {}) {
|
|
|
272
299
|
function syncHud() {
|
|
273
300
|
const roomInfo = currentRoomInfo(sceneState);
|
|
274
301
|
const roomCode = currentRoomCode(sceneState);
|
|
302
|
+
const shareLink = currentLauncherJoinLink(sceneState);
|
|
275
303
|
const diagnostics = DIAGNOSTICS_CONFIG.enabled
|
|
276
304
|
? {
|
|
277
305
|
mode: roomInfo?.requestedMode || CONNECTIVITY_CONFIG.mode,
|
|
@@ -290,6 +318,7 @@ export function createGameplayScene(context = {}) {
|
|
|
290
318
|
playerCount: Number(aura.multiplayer.getPlayerCount?.() || 0),
|
|
291
319
|
connected: aura.multiplayer.isConnected() === true,
|
|
292
320
|
showControlsHint: multiplayerUi.showControlsHint !== false,
|
|
321
|
+
shareLink: shareLink ? displayShareLink(shareLink) : null,
|
|
293
322
|
statusLine: currentStatusLine(sceneState, roomCode),
|
|
294
323
|
diagnostics,
|
|
295
324
|
});
|
|
@@ -434,7 +463,7 @@ export function createGameplayScene(context = {}) {
|
|
|
434
463
|
notes: [
|
|
435
464
|
'Keep the room-code multiplayer dev loop starter-owned here instead of splitting it between src/main.js and layout-generated files.',
|
|
436
465
|
'Use appState.session for durable room/session summary, appState.ui for HUD presentation state, and keep connection-side details scene-local.',
|
|
437
|
-
'aura.config.json -> multiplayer is the project-level source of truth for room code, room name, diagnostics, and
|
|
466
|
+
'aura.config.json -> multiplayer is the project-level source of truth for room code, room name, diagnostics, relay defaults, and optional launcher join pages.',
|
|
438
467
|
'config/gameplay/local-multiplayer.config.js stays focused on local gameplay tuning like port, player speed, bounds, and fallback timing.',
|
|
439
468
|
],
|
|
440
469
|
};
|
|
@@ -23,6 +23,7 @@ export function drawLocalMultiplayerHud({
|
|
|
23
23
|
playerCount = 0,
|
|
24
24
|
connected = false,
|
|
25
25
|
showControlsHint = true,
|
|
26
|
+
shareLink = null,
|
|
26
27
|
statusLine = '',
|
|
27
28
|
diagnostics = null,
|
|
28
29
|
} = {}) {
|
|
@@ -47,13 +48,17 @@ export function drawLocalMultiplayerHud({
|
|
|
47
48
|
aura.draw2d.text(`Reason: ${diagnostics.lastReasonCode || '-'}`, LOCAL_MULTIPLAYER_CONFIG.width - 248, 122, 11, aura.rgb(0.82, 0.9, 0.86));
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
if (shareLink) {
|
|
52
|
+
aura.draw2d.text(`Join link: ${shareLink}`, 18, LOCAL_MULTIPLAYER_CONFIG.height - 28, 10, aura.rgb(0.76, 0.82, 0.92));
|
|
53
|
+
} else {
|
|
54
|
+
aura.draw2d.text(
|
|
55
|
+
'Room-code multiplayer. Put room + relay defaults in aura.config.json -> multiplayer.',
|
|
56
|
+
18,
|
|
57
|
+
LOCAL_MULTIPLAYER_CONFIG.height - 28,
|
|
58
|
+
10,
|
|
59
|
+
aura.rgb(0.76, 0.82, 0.92),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
57
62
|
aura.draw2d.text(`Connected: ${connected}`, LOCAL_MULTIPLAYER_CONFIG.width - 166, 16, 11, aura.rgb(0.82, 0.9, 0.86));
|
|
58
63
|
}
|
|
59
64
|
|
|
@@ -604,6 +604,7 @@ function resolveProjectMultiplayerConfig() {
|
|
|
604
604
|
relayHost: null,
|
|
605
605
|
coordinatorUrl: null,
|
|
606
606
|
relayUrl: null,
|
|
607
|
+
launcherBaseUrl: null,
|
|
607
608
|
showDiagnostics: null,
|
|
608
609
|
chatEnabled: null,
|
|
609
610
|
chatHistoryLimit: null,
|
|
@@ -619,6 +620,7 @@ function resolveProjectMultiplayerConfig() {
|
|
|
619
620
|
relayHost: readOptionalCliText(raw.relay || raw.relayHost),
|
|
620
621
|
coordinatorUrl: readOptionalCliText(raw.coordinatorUrl),
|
|
621
622
|
relayUrl: readOptionalCliText(raw.relayUrl),
|
|
623
|
+
launcherBaseUrl: readOptionalCliText(raw.launcherBaseUrl || raw.launcherUrl),
|
|
622
624
|
showDiagnostics: typeof raw.showDiagnostics === 'boolean' ? raw.showDiagnostics : null,
|
|
623
625
|
chatEnabled: typeof raw.chatEnabled === 'boolean' ? raw.chatEnabled : null,
|
|
624
626
|
chatHistoryLimit: historyLimit,
|
|
@@ -679,6 +681,9 @@ async function resolveLocalMultiplayerCommandEnv() {
|
|
|
679
681
|
if (relayUrl) {
|
|
680
682
|
env.AURA_RELAY_URL = relayUrl;
|
|
681
683
|
}
|
|
684
|
+
if (projectMultiplayer.launcherBaseUrl) {
|
|
685
|
+
env.AURA_MULTIPLAYER_LAUNCHER_BASE_URL = projectMultiplayer.launcherBaseUrl;
|
|
686
|
+
}
|
|
682
687
|
if (typeof projectMultiplayer.showDiagnostics === 'boolean') {
|
|
683
688
|
env.AURA_MULTIPLAYER_SHOW_DIAGNOSTICS = projectMultiplayer.showDiagnostics ? '1' : '0';
|
|
684
689
|
}
|
|
@@ -900,7 +905,7 @@ function assertJoinSupported() {
|
|
|
900
905
|
: 'project capability declarations';
|
|
901
906
|
throw new Error(
|
|
902
907
|
`join unavailable: multiplayer is disabled in ${sourceDetail}. `
|
|
903
|
-
+ `Use \`npm run play\` for a
|
|
908
|
+
+ `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
909
|
);
|
|
905
910
|
}
|
|
906
911
|
}
|
|
@@ -1043,7 +1048,7 @@ async function main() {
|
|
|
1043
1048
|
|
|
1044
1049
|
if (command === 'play') {
|
|
1045
1050
|
printBanner('PLAY');
|
|
1046
|
-
printSection(toDisplayTitle(packageName), 'Starting
|
|
1051
|
+
printSection(toDisplayTitle(packageName), 'Starting packaged local game session...');
|
|
1047
1052
|
const externalAssets = await maybePrepareExternalAssets('play');
|
|
1048
1053
|
const multiplayerEnv = await resolveLocalMultiplayerCommandEnv();
|
|
1049
1054
|
await runCommand(
|