@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
|
@@ -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;
|
|
@@ -166,13 +178,20 @@ function assertLocalMultiplayerCapabilities() {
|
|
|
166
178
|
if (!hasMethod(aura.draw2d, 'text')) missing.push('aura.draw2d.text');
|
|
167
179
|
if (typeof aura.rgb !== 'function') missing.push('aura.rgb');
|
|
168
180
|
if (!hasMethod(aura.multiplayer, 'configure')) missing.push('aura.multiplayer.configure');
|
|
181
|
+
if (!hasMethod(aura.multiplayer, 'configureRollbackLane')) missing.push('aura.multiplayer.configureRollbackLane');
|
|
182
|
+
if (!hasMethod(aura.multiplayer, 'clearRollbackLane')) missing.push('aura.multiplayer.clearRollbackLane');
|
|
169
183
|
if (!hasMethod(aura.multiplayer, 'getAllPlayerInputs')) missing.push('aura.multiplayer.getAllPlayerInputs');
|
|
170
|
-
if (!hasMethod(aura.multiplayer, '
|
|
184
|
+
if (!hasMethod(aura.multiplayer, 'getInterpolatedAllState')) missing.push('aura.multiplayer.getInterpolatedAllState');
|
|
185
|
+
if (!hasMethod(aura.multiplayer, 'getLocalId')) missing.push('aura.multiplayer.getLocalId');
|
|
186
|
+
if (!hasMethod(aura.multiplayer, 'getNetworkDiagnostics')) missing.push('aura.multiplayer.getNetworkDiagnostics');
|
|
171
187
|
if (!hasMethod(aura.multiplayer, 'getPlayerCount')) missing.push('aura.multiplayer.getPlayerCount');
|
|
188
|
+
if (!hasMethod(aura.multiplayer, 'getRollbackDiagnostics')) missing.push('aura.multiplayer.getRollbackDiagnostics');
|
|
189
|
+
if (!hasMethod(aura.multiplayer, 'getRollbackState')) missing.push('aura.multiplayer.getRollbackState');
|
|
172
190
|
if (!hasMethod(aura.multiplayer, 'getRoomInfo')) missing.push('aura.multiplayer.getRoomInfo');
|
|
173
191
|
if (!hasMethod(aura.multiplayer, 'getState')) missing.push('aura.multiplayer.getState');
|
|
174
192
|
if (!hasMethod(aura.multiplayer, 'host')) missing.push('aura.multiplayer.host');
|
|
175
193
|
if (!hasMethod(aura.multiplayer, 'isConnected')) missing.push('aura.multiplayer.isConnected');
|
|
194
|
+
if (!hasMethod(aura.multiplayer, 'onStateUpdate')) missing.push('aura.multiplayer.onStateUpdate');
|
|
176
195
|
if (!hasMethod(aura.multiplayer, 'onPlayerJoin')) missing.push('aura.multiplayer.onPlayerJoin');
|
|
177
196
|
if (!hasMethod(aura.multiplayer, 'onPlayerLeave')) missing.push('aura.multiplayer.onPlayerLeave');
|
|
178
197
|
if (!hasMethod(aura.multiplayer, 'sendInput')) missing.push('aura.multiplayer.sendInput');
|
|
@@ -207,6 +226,24 @@ function createSceneState() {
|
|
|
207
226
|
hostedRoom: null,
|
|
208
227
|
pendingElapsed: 0,
|
|
209
228
|
registeredHostCallbacks: false,
|
|
229
|
+
registeredRuntimeCallbacks: false,
|
|
230
|
+
rollbackKey: null,
|
|
231
|
+
stateUpdateMeta: null,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function resolveDrawStates(sceneState) {
|
|
236
|
+
const drawState = aura.multiplayer.getInterpolatedAllState() || {};
|
|
237
|
+
if (sceneState.role !== 'client' || !sceneState.rollbackKey) {
|
|
238
|
+
return drawState;
|
|
239
|
+
}
|
|
240
|
+
const rollbackState = aura.multiplayer.getRollbackState?.(sceneState.rollbackKey);
|
|
241
|
+
if (!rollbackState || typeof rollbackState !== 'object') {
|
|
242
|
+
return drawState;
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
...drawState,
|
|
246
|
+
[sceneState.rollbackKey]: rollbackState,
|
|
210
247
|
};
|
|
211
248
|
}
|
|
212
249
|
|
|
@@ -232,7 +269,57 @@ function readInput() {
|
|
|
232
269
|
};
|
|
233
270
|
}
|
|
234
271
|
|
|
272
|
+
function readFiniteNumber(value) {
|
|
273
|
+
const numeric = Number(value);
|
|
274
|
+
return Number.isFinite(numeric) ? numeric : null;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function formatDiagnosticNumber(value, suffix = '') {
|
|
278
|
+
const numeric = readFiniteNumber(value);
|
|
279
|
+
return numeric === null ? '-' : `${Math.round(numeric)}${suffix}`;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function readLocalPlayerId(fallback = null) {
|
|
283
|
+
const localId = Number(aura.multiplayer.getLocalId?.());
|
|
284
|
+
return Number.isInteger(localId) && localId >= 0 ? localId : fallback;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function localPlayerStateKey(playerId) {
|
|
288
|
+
return Number.isInteger(playerId) && playerId >= 0 ? `player_${playerId}` : null;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function defaultLocalPlayerLabel(playerId, role = 'client') {
|
|
292
|
+
if (!Number.isInteger(playerId) || playerId < 0) {
|
|
293
|
+
return 'player';
|
|
294
|
+
}
|
|
295
|
+
if (role === 'host' && playerId === 0) {
|
|
296
|
+
return 'host';
|
|
297
|
+
}
|
|
298
|
+
return `p${playerId}`;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function createFallbackPlayerState(playerId, role = 'client') {
|
|
302
|
+
return createLocalPlayerState(playerId, defaultLocalPlayerLabel(playerId, role));
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function normalizeStateUpdateMetadata(metadata) {
|
|
306
|
+
if (!metadata || typeof metadata !== 'object') {
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
source: typeof metadata.source === 'string' && metadata.source.trim() ? metadata.source.trim() : null,
|
|
311
|
+
sequence: readFiniteNumber(metadata.sequence),
|
|
312
|
+
serverTimeMs: readFiniteNumber(metadata.serverTimeMs),
|
|
313
|
+
tickIntervalMs: readFiniteNumber(metadata.tickIntervalMs),
|
|
314
|
+
jitterMs: readFiniteNumber(metadata.jitterMs),
|
|
315
|
+
bufferDelayMs: readFiniteNumber(metadata.bufferDelayMs),
|
|
316
|
+
historyDepth: readFiniteNumber(metadata.historyDepth),
|
|
317
|
+
bufferedServerTimeMs: readFiniteNumber(metadata.bufferedServerTimeMs),
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
235
321
|
function currentStatusLine(sceneState, roomCode) {
|
|
322
|
+
const shareLink = currentLauncherJoinLink(sceneState);
|
|
236
323
|
if (sceneState.role === 'pending') {
|
|
237
324
|
if (launchedViaExplicitJoin()) {
|
|
238
325
|
return `Joining room code ${roomCode}...`;
|
|
@@ -240,6 +327,9 @@ function currentStatusLine(sceneState, roomCode) {
|
|
|
240
327
|
return 'Checking for a room-code match before hosting this room...';
|
|
241
328
|
}
|
|
242
329
|
if (sceneState.role === 'host') {
|
|
330
|
+
if (shareLink) {
|
|
331
|
+
return `Share launcher link: ${displayShareLink(shareLink)}`;
|
|
332
|
+
}
|
|
243
333
|
if (usesInternetBackedHosting()) {
|
|
244
334
|
return `Share this room code anywhere: npm run join -- ${roomCode}`;
|
|
245
335
|
}
|
|
@@ -251,6 +341,17 @@ function currentStatusLine(sceneState, roomCode) {
|
|
|
251
341
|
return 'Joined through the local-first room-code multiplayer flow.';
|
|
252
342
|
}
|
|
253
343
|
|
|
344
|
+
function currentLauncherJoinLink(sceneState) {
|
|
345
|
+
if (!usesInternetBackedHosting() || !LAUNCHER_CONFIG.baseUrl) {
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
return `${LAUNCHER_CONFIG.baseUrl}/join/${encodeURIComponent(currentRoomCode(sceneState))}`;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function displayShareLink(link) {
|
|
352
|
+
return String(link || '').replace(/^https?:\/\//, '');
|
|
353
|
+
}
|
|
354
|
+
|
|
254
355
|
export function createGameplayScene(context = {}) {
|
|
255
356
|
const appState = context.appState && typeof context.appState === 'object'
|
|
256
357
|
? context.appState
|
|
@@ -272,15 +373,39 @@ export function createGameplayScene(context = {}) {
|
|
|
272
373
|
function syncHud() {
|
|
273
374
|
const roomInfo = currentRoomInfo(sceneState);
|
|
274
375
|
const roomCode = currentRoomCode(sceneState);
|
|
376
|
+
const shareLink = currentLauncherJoinLink(sceneState);
|
|
377
|
+
const network = aura.multiplayer.getNetworkDiagnostics?.() || {};
|
|
378
|
+
const rollback = aura.multiplayer.getRollbackDiagnostics?.(sceneState.rollbackKey || undefined) || {};
|
|
379
|
+
const stateUpdateMeta = sceneState.stateUpdateMeta || {};
|
|
380
|
+
const transportPath = network.transportPath || roomInfo?.transportPath || (usesInternetBackedHosting() ? 'internet_pending' : 'local_room');
|
|
381
|
+
const transportStatus = network.transportStatus || roomInfo?.transportStatus || (aura.multiplayer.isConnected() ? 'connected' : 'waiting');
|
|
382
|
+
const joinPath = roomInfo?.joinPath || (usesInternetBackedHosting() ? 'internet_fallback' : 'local');
|
|
383
|
+
const roomReason = roomInfo?.lastReasonCode || network.lastReasonCode || null;
|
|
275
384
|
const diagnostics = DIAGNOSTICS_CONFIG.enabled
|
|
276
385
|
? {
|
|
277
|
-
mode: roomInfo?.requestedMode || CONNECTIVITY_CONFIG.mode
|
|
278
|
-
scope: roomInfo?.scope || (usesInternetBackedHosting() ? 'internet' : 'local'),
|
|
279
|
-
transportPath:
|
|
280
|
-
transportStatus:
|
|
281
|
-
joinPath:
|
|
282
|
-
lastReasonCode:
|
|
386
|
+
mode: `${network.requestedMode || roomInfo?.requestedMode || CONNECTIVITY_CONFIG.mode}${rollback.enabled ? ' / rollback on' : ' / rollback off'}`,
|
|
387
|
+
scope: network.scope || roomInfo?.scope || (usesInternetBackedHosting() ? 'internet' : 'local'),
|
|
388
|
+
transportPath: `${transportPath}${stateUpdateMeta.source ? ` / ${stateUpdateMeta.source}` : ''}`,
|
|
389
|
+
transportStatus: `${transportStatus} / seq ${formatDiagnosticNumber(network.lastSequence ?? stateUpdateMeta.sequence)} / gap ${formatDiagnosticNumber(network.sequenceGapCount)}`,
|
|
390
|
+
joinPath: `${joinPath} / buf ${formatDiagnosticNumber(network.bufferDelayMs ?? stateUpdateMeta.bufferDelayMs, 'ms')} / jit ${formatDiagnosticNumber(network.jitterMs ?? stateUpdateMeta.jitterMs, 'ms')} / q ${formatDiagnosticNumber(network.queuedEventCount)}`,
|
|
391
|
+
lastReasonCode: `${roomReason || rollback.lastReasonCode || '-'} / rb ${formatDiagnosticNumber(rollback.rollbackCount)} rp ${formatDiagnosticNumber(rollback.replayCount)}`,
|
|
283
392
|
pingMs: Number(aura.multiplayer.getPing?.()),
|
|
393
|
+
source: stateUpdateMeta.source || null,
|
|
394
|
+
sequence: network.lastSequence ?? stateUpdateMeta.sequence,
|
|
395
|
+
serverTimeMs: network.lastSnapshotServerTimeMs ?? stateUpdateMeta.serverTimeMs,
|
|
396
|
+
tickIntervalMs: network.lastSnapshotTickIntervalMs ?? stateUpdateMeta.tickIntervalMs,
|
|
397
|
+
jitterMs: network.jitterMs ?? stateUpdateMeta.jitterMs,
|
|
398
|
+
bufferDelayMs: network.bufferDelayMs ?? stateUpdateMeta.bufferDelayMs,
|
|
399
|
+
historyDepth: network.historyDepth ?? stateUpdateMeta.historyDepth,
|
|
400
|
+
bufferedServerTimeMs: network.bufferedServerTimeMs ?? stateUpdateMeta.bufferedServerTimeMs,
|
|
401
|
+
rollbackEnabled: rollback.enabled === true,
|
|
402
|
+
rollbackKey: rollback.key || sceneState.rollbackKey || null,
|
|
403
|
+
rollbackCount: readFiniteNumber(rollback.rollbackCount),
|
|
404
|
+
replayCount: readFiniteNumber(rollback.replayCount),
|
|
405
|
+
continuityResetCount: readFiniteNumber(rollback.continuityResetCount),
|
|
406
|
+
lastCorrectionMagnitude: readFiniteNumber(rollback.lastCorrectionMagnitude),
|
|
407
|
+
lastAuthoritativeServerTick: readFiniteNumber(rollback.lastAuthoritativeServerTick),
|
|
408
|
+
lastAckInputTick: readFiniteNumber(rollback.lastAckInputTick),
|
|
284
409
|
}
|
|
285
410
|
: null;
|
|
286
411
|
context.setHudScreen?.('hud', {
|
|
@@ -290,6 +415,7 @@ export function createGameplayScene(context = {}) {
|
|
|
290
415
|
playerCount: Number(aura.multiplayer.getPlayerCount?.() || 0),
|
|
291
416
|
connected: aura.multiplayer.isConnected() === true,
|
|
292
417
|
showControlsHint: multiplayerUi.showControlsHint !== false,
|
|
418
|
+
shareLink: shareLink ? displayShareLink(shareLink) : null,
|
|
293
419
|
statusLine: currentStatusLine(sceneState, roomCode),
|
|
294
420
|
diagnostics,
|
|
295
421
|
});
|
|
@@ -312,6 +438,68 @@ export function createGameplayScene(context = {}) {
|
|
|
312
438
|
});
|
|
313
439
|
}
|
|
314
440
|
|
|
441
|
+
function ensureRuntimeCallbacks() {
|
|
442
|
+
if (sceneState.registeredRuntimeCallbacks) return;
|
|
443
|
+
sceneState.registeredRuntimeCallbacks = true;
|
|
444
|
+
aura.multiplayer.onStateUpdate((_snapshot, metadata) => {
|
|
445
|
+
sceneState.stateUpdateMeta = normalizeStateUpdateMetadata(metadata);
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function clearClientRollback() {
|
|
450
|
+
if (sceneState.rollbackKey) {
|
|
451
|
+
aura.multiplayer.clearRollbackLane(sceneState.rollbackKey);
|
|
452
|
+
} else if (aura.multiplayer.getRollbackDiagnostics?.()?.enabled) {
|
|
453
|
+
aura.multiplayer.clearRollbackLane();
|
|
454
|
+
}
|
|
455
|
+
sceneState.rollbackKey = null;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function syncRoleFromRuntime() {
|
|
459
|
+
const runtimeRole = aura.multiplayer.getRoomInfo?.()?.role;
|
|
460
|
+
if (runtimeRole === 'host' && sceneState.role !== 'host') {
|
|
461
|
+
sceneState.role = 'host';
|
|
462
|
+
ensureHostCallbacks();
|
|
463
|
+
} else if (runtimeRole === 'client' && sceneState.role !== 'client') {
|
|
464
|
+
sceneState.role = 'client';
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
function ensureClientRollback() {
|
|
469
|
+
const connected = aura.multiplayer.isConnected() === true;
|
|
470
|
+
const roomRole = aura.multiplayer.getRoomInfo?.()?.role || sceneState.role;
|
|
471
|
+
if (!connected || roomRole !== 'client') {
|
|
472
|
+
clearClientRollback();
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
const localId = readLocalPlayerId();
|
|
476
|
+
const rollbackKey = localPlayerStateKey(localId);
|
|
477
|
+
if (!rollbackKey) {
|
|
478
|
+
clearClientRollback();
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
const diagnostics = aura.multiplayer.getRollbackDiagnostics?.(rollbackKey) || {};
|
|
482
|
+
if (sceneState.rollbackKey !== rollbackKey || diagnostics?.enabled !== true || diagnostics?.key !== rollbackKey) {
|
|
483
|
+
if (sceneState.rollbackKey && sceneState.rollbackKey !== rollbackKey) {
|
|
484
|
+
aura.multiplayer.clearRollbackLane(sceneState.rollbackKey);
|
|
485
|
+
}
|
|
486
|
+
aura.multiplayer.configureRollbackLane({
|
|
487
|
+
key: rollbackKey,
|
|
488
|
+
playerId: localId,
|
|
489
|
+
speed: LOCAL_MULTIPLAYER_CONFIG.playerSpeed,
|
|
490
|
+
historyLimit: 12,
|
|
491
|
+
bounds: {
|
|
492
|
+
minX: 24,
|
|
493
|
+
maxX: LOCAL_MULTIPLAYER_CONFIG.width - LOCAL_MULTIPLAYER_CONFIG.playerWidth - 24,
|
|
494
|
+
minY: 96,
|
|
495
|
+
maxY: LOCAL_MULTIPLAYER_CONFIG.height - LOCAL_MULTIPLAYER_CONFIG.playerHeight - 28,
|
|
496
|
+
},
|
|
497
|
+
});
|
|
498
|
+
sceneState.rollbackKey = rollbackKey;
|
|
499
|
+
}
|
|
500
|
+
return { key: rollbackKey, playerId: localId };
|
|
501
|
+
}
|
|
502
|
+
|
|
315
503
|
function activateHost() {
|
|
316
504
|
if (sceneState.role === 'host') return;
|
|
317
505
|
const hostOptions = {
|
|
@@ -332,15 +520,20 @@ export function createGameplayScene(context = {}) {
|
|
|
332
520
|
}
|
|
333
521
|
sceneState.hostedRoom = aura.multiplayer.host(hostOptions);
|
|
334
522
|
sceneState.role = 'host';
|
|
523
|
+
clearClientRollback();
|
|
335
524
|
ensureHostCallbacks();
|
|
336
|
-
|
|
525
|
+
const hostPlayerId = readLocalPlayerId(0);
|
|
526
|
+
const hostKey = localPlayerStateKey(hostPlayerId) || 'player_0';
|
|
527
|
+
aura.multiplayer.setState(hostKey, createFallbackPlayerState(hostPlayerId, 'host'));
|
|
337
528
|
}
|
|
338
529
|
|
|
339
530
|
function resetRun() {
|
|
531
|
+
clearClientRollback();
|
|
340
532
|
sceneState.role = 'pending';
|
|
341
533
|
sceneState.hostedRoom = null;
|
|
342
534
|
sceneState.pendingElapsed = 0;
|
|
343
535
|
sceneState.registeredHostCallbacks = false;
|
|
536
|
+
sceneState.stateUpdateMeta = null;
|
|
344
537
|
multiplayerSession.runsStarted += 1;
|
|
345
538
|
multiplayerUi.showControlsHint = true;
|
|
346
539
|
syncSessionState();
|
|
@@ -356,10 +549,11 @@ export function createGameplayScene(context = {}) {
|
|
|
356
549
|
'config/gameplay/local-multiplayer.config.js',
|
|
357
550
|
'content/gameplay/room-layout.js',
|
|
358
551
|
],
|
|
359
|
-
summary: 'Room-code multiplayer starter. `npm run dev` hosts, `npm run join -- CODE` joins, `aura.config.json -> multiplayer` owns room/connectivity defaults, and the HUD surfaces live transport diagnostics.',
|
|
552
|
+
summary: 'Room-code multiplayer starter. `npm run dev` hosts, `npm run join -- CODE` joins, `aura.config.json -> multiplayer` owns room/connectivity defaults, and the HUD surfaces live transport plus rollback diagnostics.',
|
|
360
553
|
|
|
361
554
|
setup() {
|
|
362
555
|
assertLocalMultiplayerCapabilities();
|
|
556
|
+
ensureRuntimeCallbacks();
|
|
363
557
|
aura.multiplayer.configure({
|
|
364
558
|
maxPlayers: LOCAL_MULTIPLAYER_CONFIG.maxPlayers,
|
|
365
559
|
tickRate: LOCAL_MULTIPLAYER_CONFIG.tickRate,
|
|
@@ -385,9 +579,14 @@ export function createGameplayScene(context = {}) {
|
|
|
385
579
|
}
|
|
386
580
|
}
|
|
387
581
|
|
|
582
|
+
syncRoleFromRuntime();
|
|
583
|
+
|
|
388
584
|
if (sceneState.role === 'host') {
|
|
389
|
-
|
|
390
|
-
|
|
585
|
+
clearClientRollback();
|
|
586
|
+
const hostPlayerId = readLocalPlayerId(0);
|
|
587
|
+
const hostKey = localPlayerStateKey(hostPlayerId) || 'player_0';
|
|
588
|
+
const hostState = aura.multiplayer.getState(hostKey) || createFallbackPlayerState(hostPlayerId, 'host');
|
|
589
|
+
aura.multiplayer.setState(hostKey, applyLocalPlayerMovement(hostState, input, frameDt));
|
|
391
590
|
|
|
392
591
|
const inputs = aura.multiplayer.getAllPlayerInputs() || {};
|
|
393
592
|
for (const [playerId, playerInput] of Object.entries(inputs)) {
|
|
@@ -397,7 +596,10 @@ export function createGameplayScene(context = {}) {
|
|
|
397
596
|
aura.multiplayer.setState(key, applyLocalPlayerMovement(current, playerInput, frameDt));
|
|
398
597
|
}
|
|
399
598
|
} else if (sceneState.role === 'client' && aura.multiplayer.isConnected()) {
|
|
599
|
+
ensureClientRollback();
|
|
400
600
|
aura.multiplayer.sendInput(input);
|
|
601
|
+
} else {
|
|
602
|
+
clearClientRollback();
|
|
401
603
|
}
|
|
402
604
|
|
|
403
605
|
syncSessionState();
|
|
@@ -405,13 +607,14 @@ export function createGameplayScene(context = {}) {
|
|
|
405
607
|
},
|
|
406
608
|
|
|
407
609
|
onExit() {
|
|
610
|
+
clearClientRollback();
|
|
408
611
|
context.clearHudScreen?.();
|
|
409
612
|
},
|
|
410
613
|
|
|
411
614
|
draw() {
|
|
412
615
|
aura.draw2d.clear(aura.rgb(0.07, 0.09, 0.13));
|
|
413
616
|
|
|
414
|
-
const allState =
|
|
617
|
+
const allState = resolveDrawStates(sceneState);
|
|
415
618
|
for (const key of Object.keys(allState).sort()) {
|
|
416
619
|
drawLocalPlayerState(allState[key], key);
|
|
417
620
|
}
|
|
@@ -434,7 +637,7 @@ export function createGameplayScene(context = {}) {
|
|
|
434
637
|
notes: [
|
|
435
638
|
'Keep the room-code multiplayer dev loop starter-owned here instead of splitting it between src/main.js and layout-generated files.',
|
|
436
639
|
'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
|
|
640
|
+
'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
641
|
'config/gameplay/local-multiplayer.config.js stays focused on local gameplay tuning like port, player speed, bounds, and fallback timing.',
|
|
439
642
|
],
|
|
440
643
|
};
|
|
@@ -10,13 +10,20 @@ export function assertRuntimeCapabilities() {
|
|
|
10
10
|
if (!hasMethod(aura.draw2d, 'text')) missing.push('aura.draw2d.text');
|
|
11
11
|
if (typeof aura.rgb !== 'function') missing.push('aura.rgb');
|
|
12
12
|
if (!hasMethod(aura.multiplayer, 'configure')) missing.push('aura.multiplayer.configure');
|
|
13
|
+
if (!hasMethod(aura.multiplayer, 'configureRollbackLane')) missing.push('aura.multiplayer.configureRollbackLane');
|
|
14
|
+
if (!hasMethod(aura.multiplayer, 'clearRollbackLane')) missing.push('aura.multiplayer.clearRollbackLane');
|
|
13
15
|
if (!hasMethod(aura.multiplayer, 'getAllPlayerInputs')) missing.push('aura.multiplayer.getAllPlayerInputs');
|
|
14
|
-
if (!hasMethod(aura.multiplayer, '
|
|
16
|
+
if (!hasMethod(aura.multiplayer, 'getInterpolatedAllState')) missing.push('aura.multiplayer.getInterpolatedAllState');
|
|
17
|
+
if (!hasMethod(aura.multiplayer, 'getLocalId')) missing.push('aura.multiplayer.getLocalId');
|
|
18
|
+
if (!hasMethod(aura.multiplayer, 'getNetworkDiagnostics')) missing.push('aura.multiplayer.getNetworkDiagnostics');
|
|
15
19
|
if (!hasMethod(aura.multiplayer, 'getPlayerCount')) missing.push('aura.multiplayer.getPlayerCount');
|
|
20
|
+
if (!hasMethod(aura.multiplayer, 'getRollbackDiagnostics')) missing.push('aura.multiplayer.getRollbackDiagnostics');
|
|
21
|
+
if (!hasMethod(aura.multiplayer, 'getRollbackState')) missing.push('aura.multiplayer.getRollbackState');
|
|
16
22
|
if (!hasMethod(aura.multiplayer, 'getRoomInfo')) missing.push('aura.multiplayer.getRoomInfo');
|
|
17
23
|
if (!hasMethod(aura.multiplayer, 'getState')) missing.push('aura.multiplayer.getState');
|
|
18
24
|
if (!hasMethod(aura.multiplayer, 'host')) missing.push('aura.multiplayer.host');
|
|
19
25
|
if (!hasMethod(aura.multiplayer, 'isConnected')) missing.push('aura.multiplayer.isConnected');
|
|
26
|
+
if (!hasMethod(aura.multiplayer, 'onStateUpdate')) missing.push('aura.multiplayer.onStateUpdate');
|
|
20
27
|
if (!hasMethod(aura.multiplayer, 'onPlayerJoin')) missing.push('aura.multiplayer.onPlayerJoin');
|
|
21
28
|
if (!hasMethod(aura.multiplayer, 'onPlayerLeave')) missing.push('aura.multiplayer.onPlayerLeave');
|
|
22
29
|
if (!hasMethod(aura.multiplayer, 'sendInput')) missing.push('aura.multiplayer.sendInput');
|
|
@@ -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
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|