@auraindustry/aurajs 0.0.2 → 0.0.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 +13 -1
- package/src/build-contract.mjs +708 -2
- package/src/cli.mjs +1584 -13
- package/src/conformance.mjs +1013 -86
- package/src/game-state-runtime.mjs +1067 -0
- package/src/headless-test.mjs +525 -10
- package/src/host-binary.mjs +33 -10
- package/src/react/aura-game.mjs +310 -0
- package/src/react/index.mjs +1 -0
- package/src/scaffold.mjs +267 -13
- package/src/web-api.mjs +454 -0
- package/templates/create/2d/aura.config.json +28 -0
- package/templates/create/2d/src/main.js +196 -0
- package/templates/create/3d/aura.config.json +28 -0
- package/templates/create/3d/src/main.js +306 -0
- package/templates/create/blank/aura.config.json +28 -0
- package/templates/create/blank/src/main.js +28 -0
- package/templates/create/shared/src/starter-utils/core.js +114 -0
- package/templates/create/shared/src/starter-utils/enemy-archetypes-2d.js +68 -0
- package/templates/create/shared/src/starter-utils/index.js +6 -0
- package/templates/create/shared/src/starter-utils/platformer-3d.js +101 -0
- package/templates/create/shared/src/starter-utils/wave-director.js +101 -0
- package/templates/skills/aurajs/SKILL.md +40 -0
- package/templates/skills/aurajs/api-contract-3d.md +7 -0
- package/templates/skills/aurajs/api-contract.md +7 -0
- package/templates/starter/src/main.js +48 -0
package/src/conformance.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { dirname, resolve } from 'node:path';
|
|
4
4
|
|
|
5
5
|
import { HostBinaryResolutionError, resolveGateHostBinary } from './host-binary.mjs';
|
|
@@ -7,6 +7,7 @@ import { runHeadlessTest, HeadlessTestError } from './headless-test.mjs';
|
|
|
7
7
|
|
|
8
8
|
const NATIVE_RESULT_MARKER = '__AURA_CONFORMANCE_NATIVE__';
|
|
9
9
|
const VALID_MODES = new Set(['shim', 'native', 'both']);
|
|
10
|
+
export const GAME_STATE_SCHEMA_VERSION = 'aurajs.game-state.v1';
|
|
10
11
|
export const DEFAULT_RUNTIME_OBSERVABILITY_REPORT_PATH = '.aura/conformance/runtime-observability.json';
|
|
11
12
|
export const DEFAULT_RUNTIME_INSPECTOR_SNAPSHOT_REPORT_PATH = '.aura/conformance/runtime-inspector-snapshot.json';
|
|
12
13
|
export const DEFAULT_PHASER_VERTICAL_SLICE_EVIDENCE_REPORT_PATH = '.aura/conformance/phaser-vertical-slice-evidence.json';
|
|
@@ -22,6 +23,346 @@ const PHASER_VERTICAL_SLICE_PERF_BUDGETS = Object.freeze({
|
|
|
22
23
|
maxAverageCheckMs: 800,
|
|
23
24
|
}),
|
|
24
25
|
});
|
|
26
|
+
const PHASER_VERTICAL_SLICE_REQUIRED_MODES = Object.freeze(['shim', 'native']);
|
|
27
|
+
|
|
28
|
+
export function installGameStateHooks(aura, options = {}) {
|
|
29
|
+
const schemaVersion = 'aurajs.game-state.v1';
|
|
30
|
+
const zeroFingerprint = '0'.repeat(64);
|
|
31
|
+
const knownTopLevel = Object.freeze(['schemaVersion', 'export', 'state']);
|
|
32
|
+
const knownStateSections = Object.freeze(['globals', 'camera', 'scene3d', 'physics', 'ecs', 'tilemap']);
|
|
33
|
+
const defaultMode = options.mode === 'native' ? 'native' : 'headless';
|
|
34
|
+
|
|
35
|
+
const isObject = (value) => (
|
|
36
|
+
!!value
|
|
37
|
+
&& typeof value === 'object'
|
|
38
|
+
&& !Array.isArray(value)
|
|
39
|
+
);
|
|
40
|
+
const cloneStable = (value) => {
|
|
41
|
+
if (Array.isArray(value)) {
|
|
42
|
+
return value.map((entry) => cloneStable(entry));
|
|
43
|
+
}
|
|
44
|
+
if (isObject(value)) {
|
|
45
|
+
const out = {};
|
|
46
|
+
for (const key of Object.keys(value).sort((a, b) => a.localeCompare(b))) {
|
|
47
|
+
out[key] = cloneStable(value[key]);
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
return value;
|
|
52
|
+
};
|
|
53
|
+
const stableStringify = (value) => JSON.stringify(cloneStable(value));
|
|
54
|
+
const asFinite = (value, fallback = 0) => {
|
|
55
|
+
const numeric = Number(value);
|
|
56
|
+
return Number.isFinite(numeric) ? numeric : fallback;
|
|
57
|
+
};
|
|
58
|
+
const asInteger = (value, fallback = 0) => {
|
|
59
|
+
const numeric = Number(value);
|
|
60
|
+
if (!Number.isFinite(numeric)) return fallback;
|
|
61
|
+
return Math.trunc(numeric);
|
|
62
|
+
};
|
|
63
|
+
const clampNonNegativeInt = (value, fallback = 0) => {
|
|
64
|
+
const numeric = asInteger(value, fallback);
|
|
65
|
+
return numeric < 0 ? 0 : numeric;
|
|
66
|
+
};
|
|
67
|
+
const hash64 = (input) => {
|
|
68
|
+
const text = String(input || '');
|
|
69
|
+
const state = [
|
|
70
|
+
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
|
|
71
|
+
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
|
|
72
|
+
];
|
|
73
|
+
const primes = [
|
|
74
|
+
0x9e3779b1, 0x85ebca77, 0xc2b2ae3d, 0x27d4eb2f,
|
|
75
|
+
0x165667b1, 0xd3a2646c, 0xfd7046c5, 0xb55a4f09,
|
|
76
|
+
];
|
|
77
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
78
|
+
const code = text.charCodeAt(i) & 0xffff;
|
|
79
|
+
for (let lane = 0; lane < state.length; lane += 1) {
|
|
80
|
+
const mix = (code + (lane + 1) + i) >>> 0;
|
|
81
|
+
const rotated = ((state[(lane + 3) % state.length] >>> ((lane + i) % 13)) | 0) >>> 0;
|
|
82
|
+
state[lane] = (Math.imul((state[lane] ^ mix) >>> 0, primes[lane]) + rotated) >>> 0;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return state.map((entry) => entry.toString(16).padStart(8, '0')).join('');
|
|
86
|
+
};
|
|
87
|
+
const normalizeCameraState = (cameraState) => {
|
|
88
|
+
if (!isObject(cameraState)) return null;
|
|
89
|
+
const x = asFinite(cameraState.x, Number.NaN);
|
|
90
|
+
const y = asFinite(cameraState.y, Number.NaN);
|
|
91
|
+
const zoom = asFinite(cameraState.zoom, Number.NaN);
|
|
92
|
+
const rotation = asFinite(cameraState.rotation, Number.NaN);
|
|
93
|
+
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(zoom) || !Number.isFinite(rotation)) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
const normalized = {
|
|
97
|
+
x,
|
|
98
|
+
y,
|
|
99
|
+
zoom,
|
|
100
|
+
rotation,
|
|
101
|
+
};
|
|
102
|
+
if (typeof cameraState.following === 'boolean') normalized.following = cameraState.following;
|
|
103
|
+
if (Number.isInteger(cameraState.activeEffects) && cameraState.activeEffects >= 0) {
|
|
104
|
+
normalized.activeEffects = cameraState.activeEffects;
|
|
105
|
+
}
|
|
106
|
+
if (isObject(cameraState.deadzone)) {
|
|
107
|
+
normalized.deadzone = {
|
|
108
|
+
x: asFinite(cameraState.deadzone.x, 0),
|
|
109
|
+
y: asFinite(cameraState.deadzone.y, 0),
|
|
110
|
+
width: Math.max(0, asFinite(cameraState.deadzone.width, 0)),
|
|
111
|
+
height: Math.max(0, asFinite(cameraState.deadzone.height, 0)),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (isObject(cameraState.bounds)) {
|
|
115
|
+
normalized.bounds = {
|
|
116
|
+
x: asFinite(cameraState.bounds.x, 0),
|
|
117
|
+
y: asFinite(cameraState.bounds.y, 0),
|
|
118
|
+
width: Math.max(0, asFinite(cameraState.bounds.width, 0)),
|
|
119
|
+
height: Math.max(0, asFinite(cameraState.bounds.height, 0)),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return normalized;
|
|
123
|
+
};
|
|
124
|
+
const errorResult = (reasonCode, details = {}) => ({
|
|
125
|
+
ok: false,
|
|
126
|
+
reasonCode,
|
|
127
|
+
appliedMutations: 0,
|
|
128
|
+
failedMutationIndex: 0,
|
|
129
|
+
fingerprint: null,
|
|
130
|
+
details: cloneStable(details),
|
|
131
|
+
});
|
|
132
|
+
const successResult = (reasonCode, fingerprint, appliedMutations = 1) => ({
|
|
133
|
+
ok: true,
|
|
134
|
+
reasonCode,
|
|
135
|
+
appliedMutations,
|
|
136
|
+
failedMutationIndex: null,
|
|
137
|
+
fingerprint,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
if (!aura || typeof aura !== 'object') {
|
|
141
|
+
return errorResult('invalid_schema_payload', { path: '/aura' });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const existing = aura.state;
|
|
145
|
+
if (
|
|
146
|
+
isObject(existing)
|
|
147
|
+
&& typeof existing.export === 'function'
|
|
148
|
+
&& typeof existing.apply === 'function'
|
|
149
|
+
&& existing.schemaVersion === schemaVersion
|
|
150
|
+
) {
|
|
151
|
+
return existing;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const stateStore = {
|
|
155
|
+
globals: {},
|
|
156
|
+
camera: null,
|
|
157
|
+
scene3d: null,
|
|
158
|
+
physics: null,
|
|
159
|
+
ecs: null,
|
|
160
|
+
tilemap: null,
|
|
161
|
+
seed: 0,
|
|
162
|
+
frameIndex: 0,
|
|
163
|
+
elapsedSeconds: 0,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const captureCameraState = () => {
|
|
167
|
+
const cameraNs = isObject(aura.camera) ? aura.camera : null;
|
|
168
|
+
if (!cameraNs) return stateStore.camera ? cloneStable(stateStore.camera) : null;
|
|
169
|
+
|
|
170
|
+
let cameraState = null;
|
|
171
|
+
if (typeof cameraNs.getState === 'function') {
|
|
172
|
+
try {
|
|
173
|
+
cameraState = cameraNs.getState();
|
|
174
|
+
} catch {
|
|
175
|
+
cameraState = null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (!isObject(cameraState)) {
|
|
179
|
+
cameraState = {
|
|
180
|
+
x: cameraNs.x,
|
|
181
|
+
y: cameraNs.y,
|
|
182
|
+
zoom: cameraNs.zoom,
|
|
183
|
+
rotation: cameraNs.rotation,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
const normalized = normalizeCameraState(cameraState);
|
|
187
|
+
return normalized ? cloneStable(normalized) : (stateStore.camera ? cloneStable(stateStore.camera) : null);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const computePayloadFingerprint = (payload) => {
|
|
191
|
+
const clone = cloneStable(payload);
|
|
192
|
+
if (!isObject(clone.export)) {
|
|
193
|
+
return zeroFingerprint;
|
|
194
|
+
}
|
|
195
|
+
clone.export.fingerprint = zeroFingerprint;
|
|
196
|
+
clone.export.capturedAt = null;
|
|
197
|
+
return hash64(stableStringify(clone));
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const exportState = (exportOptions = {}) => {
|
|
201
|
+
const optionsObject = isObject(exportOptions) ? exportOptions : {};
|
|
202
|
+
if (isObject(optionsObject.globals)) {
|
|
203
|
+
stateStore.globals = cloneStable(optionsObject.globals);
|
|
204
|
+
}
|
|
205
|
+
if (Object.prototype.hasOwnProperty.call(optionsObject, 'seed')) {
|
|
206
|
+
stateStore.seed = asInteger(optionsObject.seed, stateStore.seed);
|
|
207
|
+
}
|
|
208
|
+
if (Object.prototype.hasOwnProperty.call(optionsObject, 'frameIndex')) {
|
|
209
|
+
stateStore.frameIndex = clampNonNegativeInt(optionsObject.frameIndex, stateStore.frameIndex);
|
|
210
|
+
}
|
|
211
|
+
if (Object.prototype.hasOwnProperty.call(optionsObject, 'elapsedSeconds')) {
|
|
212
|
+
stateStore.elapsedSeconds = Math.max(0, asFinite(optionsObject.elapsedSeconds, stateStore.elapsedSeconds));
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const mode = optionsObject.mode === 'native' ? 'native' : defaultMode;
|
|
216
|
+
const cameraState = captureCameraState();
|
|
217
|
+
if (cameraState) {
|
|
218
|
+
stateStore.camera = cloneStable(cameraState);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const statePayload = {
|
|
222
|
+
globals: cloneStable(stateStore.globals),
|
|
223
|
+
};
|
|
224
|
+
if (stateStore.camera) statePayload.camera = cloneStable(stateStore.camera);
|
|
225
|
+
if (stateStore.scene3d) statePayload.scene3d = cloneStable(stateStore.scene3d);
|
|
226
|
+
if (stateStore.physics) statePayload.physics = cloneStable(stateStore.physics);
|
|
227
|
+
if (stateStore.ecs) statePayload.ecs = cloneStable(stateStore.ecs);
|
|
228
|
+
if (stateStore.tilemap) statePayload.tilemap = cloneStable(stateStore.tilemap);
|
|
229
|
+
|
|
230
|
+
const payload = {
|
|
231
|
+
schemaVersion,
|
|
232
|
+
export: {
|
|
233
|
+
mode,
|
|
234
|
+
seed: stateStore.seed,
|
|
235
|
+
frameIndex: stateStore.frameIndex,
|
|
236
|
+
elapsedSeconds: Number(stateStore.elapsedSeconds.toFixed(6)),
|
|
237
|
+
fingerprint: zeroFingerprint,
|
|
238
|
+
capturedAt: null,
|
|
239
|
+
},
|
|
240
|
+
state: statePayload,
|
|
241
|
+
};
|
|
242
|
+
payload.export.fingerprint = computePayloadFingerprint(payload);
|
|
243
|
+
return payload;
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const validateApplyPayload = (payload) => {
|
|
247
|
+
if (!isObject(payload)) {
|
|
248
|
+
return { ok: false, reasonCode: 'invalid_schema_payload', details: { path: '/' } };
|
|
249
|
+
}
|
|
250
|
+
if (payload.schemaVersion !== schemaVersion) {
|
|
251
|
+
return { ok: false, reasonCode: 'schema_version_mismatch', details: { expected: schemaVersion } };
|
|
252
|
+
}
|
|
253
|
+
for (const key of knownTopLevel) {
|
|
254
|
+
if (!Object.prototype.hasOwnProperty.call(payload, key)) {
|
|
255
|
+
return { ok: false, reasonCode: 'missing_required_field', details: { path: `/${key}` } };
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
for (const key of Object.keys(payload)) {
|
|
259
|
+
if (!knownTopLevel.includes(key)) {
|
|
260
|
+
return { ok: false, reasonCode: 'unknown_top_level_key', details: { key } };
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (!isObject(payload.export) || !isObject(payload.state)) {
|
|
264
|
+
return { ok: false, reasonCode: 'invalid_schema_payload', details: { path: '/export_or_state' } };
|
|
265
|
+
}
|
|
266
|
+
if (!isObject(payload.state.globals)) {
|
|
267
|
+
return { ok: false, reasonCode: 'missing_required_field', details: { path: '/state/globals' } };
|
|
268
|
+
}
|
|
269
|
+
for (const key of Object.keys(payload.state)) {
|
|
270
|
+
if (!knownStateSections.includes(key)) {
|
|
271
|
+
return { ok: false, reasonCode: 'unknown_state_section', details: { key } };
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (Object.prototype.hasOwnProperty.call(payload.state, 'camera') && payload.state.camera !== null) {
|
|
275
|
+
if (!normalizeCameraState(payload.state.camera)) {
|
|
276
|
+
return { ok: false, reasonCode: 'invalid_schema_payload', details: { path: '/state/camera' } };
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
for (const section of ['scene3d', 'physics', 'ecs', 'tilemap']) {
|
|
280
|
+
if (Object.prototype.hasOwnProperty.call(payload.state, section)) {
|
|
281
|
+
const value = payload.state[section];
|
|
282
|
+
if (value !== null && !isObject(value)) {
|
|
283
|
+
return { ok: false, reasonCode: 'invalid_schema_payload', details: { path: `/state/${section}` } };
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return { ok: true };
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const applyState = (payload, applyOptions = {}) => {
|
|
291
|
+
const validation = validateApplyPayload(payload);
|
|
292
|
+
if (!validation.ok) {
|
|
293
|
+
return errorResult(validation.reasonCode, validation.details || {});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const optionsObject = isObject(applyOptions) ? applyOptions : {};
|
|
297
|
+
const dryRun = optionsObject.dryRun === true;
|
|
298
|
+
const nextStore = {
|
|
299
|
+
globals: cloneStable(payload.state.globals),
|
|
300
|
+
camera: payload.state.camera ? cloneStable(normalizeCameraState(payload.state.camera)) : null,
|
|
301
|
+
scene3d: payload.state.scene3d ? cloneStable(payload.state.scene3d) : null,
|
|
302
|
+
physics: payload.state.physics ? cloneStable(payload.state.physics) : null,
|
|
303
|
+
ecs: payload.state.ecs ? cloneStable(payload.state.ecs) : null,
|
|
304
|
+
tilemap: payload.state.tilemap ? cloneStable(payload.state.tilemap) : null,
|
|
305
|
+
seed: asInteger(payload.export.seed, stateStore.seed),
|
|
306
|
+
frameIndex: clampNonNegativeInt(payload.export.frameIndex, stateStore.frameIndex),
|
|
307
|
+
elapsedSeconds: Math.max(0, asFinite(payload.export.elapsedSeconds, stateStore.elapsedSeconds)),
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
if (!dryRun) {
|
|
311
|
+
stateStore.globals = nextStore.globals;
|
|
312
|
+
stateStore.camera = nextStore.camera;
|
|
313
|
+
stateStore.scene3d = nextStore.scene3d;
|
|
314
|
+
stateStore.physics = nextStore.physics;
|
|
315
|
+
stateStore.ecs = nextStore.ecs;
|
|
316
|
+
stateStore.tilemap = nextStore.tilemap;
|
|
317
|
+
stateStore.seed = nextStore.seed;
|
|
318
|
+
stateStore.frameIndex = nextStore.frameIndex;
|
|
319
|
+
stateStore.elapsedSeconds = nextStore.elapsedSeconds;
|
|
320
|
+
|
|
321
|
+
const cameraNs = isObject(aura.camera) ? aura.camera : null;
|
|
322
|
+
if (cameraNs && stateStore.camera) {
|
|
323
|
+
cameraNs.x = stateStore.camera.x;
|
|
324
|
+
cameraNs.y = stateStore.camera.y;
|
|
325
|
+
cameraNs.zoom = stateStore.camera.zoom;
|
|
326
|
+
cameraNs.rotation = stateStore.camera.rotation;
|
|
327
|
+
if (stateStore.camera.deadzone && typeof cameraNs.setDeadzone === 'function') {
|
|
328
|
+
cameraNs.setDeadzone(stateStore.camera.deadzone);
|
|
329
|
+
} else if (typeof cameraNs.clearDeadzone === 'function') {
|
|
330
|
+
cameraNs.clearDeadzone();
|
|
331
|
+
}
|
|
332
|
+
if (stateStore.camera.bounds && typeof cameraNs.setBounds === 'function') {
|
|
333
|
+
cameraNs.setBounds(stateStore.camera.bounds);
|
|
334
|
+
} else if (typeof cameraNs.clearBounds === 'function') {
|
|
335
|
+
cameraNs.clearBounds();
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const nextPayload = exportState({
|
|
341
|
+
mode: payload.export.mode === 'native' ? 'native' : defaultMode,
|
|
342
|
+
seed: nextStore.seed,
|
|
343
|
+
frameIndex: nextStore.frameIndex,
|
|
344
|
+
elapsedSeconds: nextStore.elapsedSeconds,
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
return successResult(
|
|
348
|
+
dryRun ? 'state_dry_run_ok' : 'state_apply_ok',
|
|
349
|
+
nextPayload.export.fingerprint,
|
|
350
|
+
dryRun ? 0 : 1,
|
|
351
|
+
);
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
const stateNamespace = isObject(aura.state) ? aura.state : {};
|
|
355
|
+
stateNamespace.schemaVersion = schemaVersion;
|
|
356
|
+
stateNamespace.export = exportState;
|
|
357
|
+
stateNamespace.apply = applyState;
|
|
358
|
+
aura.state = stateNamespace;
|
|
359
|
+
return stateNamespace;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export function buildGameStateHookBootstrapSource(mode = 'headless') {
|
|
363
|
+
const normalizedMode = mode === 'native' ? 'native' : 'headless';
|
|
364
|
+
return `(${installGameStateHooks.toString()})(aura, ${JSON.stringify({ mode: normalizedMode })});`;
|
|
365
|
+
}
|
|
25
366
|
|
|
26
367
|
export class ConformanceSuiteError extends Error {
|
|
27
368
|
constructor(message, details = {}) {
|
|
@@ -174,6 +515,10 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
174
515
|
{ id: 'input.isGamepadConnected', expression: "typeof aura.input?.isGamepadConnected === 'function'" },
|
|
175
516
|
{ id: 'input.getGamepadAxis', expression: "typeof aura.input?.getGamepadAxis === 'function'" },
|
|
176
517
|
{ id: 'input.isGamepadButtonDown', expression: "typeof aura.input?.isGamepadButtonDown === 'function'" },
|
|
518
|
+
{
|
|
519
|
+
id: 'input.key.invalid-input.safe-false',
|
|
520
|
+
expression: "(() => { const invalid = [42, {}, null, undefined, ['space']]; return invalid.every((key) => aura.input.isKeyDown(key) === false && aura.input.isKeyPressed(key) === false && aura.input.isKeyReleased(key) === false); })()",
|
|
521
|
+
},
|
|
177
522
|
],
|
|
178
523
|
frames: 1,
|
|
179
524
|
source: `
|
|
@@ -198,6 +543,53 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
198
543
|
};
|
|
199
544
|
`,
|
|
200
545
|
},
|
|
546
|
+
{
|
|
547
|
+
id: 'rust-host-native-readiness-smoke',
|
|
548
|
+
modes: ['native'],
|
|
549
|
+
namespaces: ['lifecycle', 'input', 'draw2d', 'assets', 'audio', 'net', 'debug'],
|
|
550
|
+
functions: [
|
|
551
|
+
'aura.setup',
|
|
552
|
+
'aura.update',
|
|
553
|
+
'aura.draw',
|
|
554
|
+
'aura.onResize',
|
|
555
|
+
'aura.input.isKeyDown',
|
|
556
|
+
'aura.input.isKeyPressed',
|
|
557
|
+
'aura.input.isKeyReleased',
|
|
558
|
+
'aura.draw2d.clear',
|
|
559
|
+
'aura.draw2d.rect',
|
|
560
|
+
'aura.debug.inspectorStats',
|
|
561
|
+
'aura.assets.load',
|
|
562
|
+
'aura.assets.getFormatSupport',
|
|
563
|
+
'aura.audio.setBusVolume',
|
|
564
|
+
'aura.audio.fadeTrack',
|
|
565
|
+
'aura.net.connect',
|
|
566
|
+
'aura.net.websocket',
|
|
567
|
+
'aura.net.fetch',
|
|
568
|
+
'aura.net.get',
|
|
569
|
+
'aura.net.post',
|
|
570
|
+
],
|
|
571
|
+
nativeChecks: [
|
|
572
|
+
{
|
|
573
|
+
id: 'smoke.lifecycle-input.readiness.deterministic',
|
|
574
|
+
expression: "(() => { const runSample = () => { let resizeSeen = null; aura.onResize = (w, h) => { resizeSeen = [typeof w, typeof h, w, h]; }; aura.onResize(640, 360); const setupOk = typeof aura.setup === 'function'; const updateOk = typeof aura.update === 'function'; const drawOk = typeof aura.draw === 'function'; const hasInputFns = typeof aura.input?.isKeyDown === 'function' && typeof aura.input?.isKeyPressed === 'function' && typeof aura.input?.isKeyReleased === 'function'; const keySnapshot = {}; if (hasInputFns) { for (const key of ['left', 'right', 'up', 'down', 'arrowup', 'arrowdown', 'space']) { keySnapshot[key] = { down: aura.input.isKeyDown(key), pressed: aura.input.isKeyPressed(key), released: aura.input.isKeyReleased(key) }; } } const keyBooleans = Object.values(keySnapshot).every((row) => typeof row.down === 'boolean' && typeof row.pressed === 'boolean' && typeof row.released === 'boolean'); const aliasStable = hasInputFns && aura.input.isKeyDown('up') === aura.input.isKeyDown('arrowup') && aura.input.isKeyPressed('up') === aura.input.isKeyPressed('arrowup') && aura.input.isKeyReleased('up') === aura.input.isKeyReleased('arrowup'); const invalidSafe = hasInputFns && [42, {}, null, undefined, ['space']].every((key) => aura.input.isKeyDown(key) === false && aura.input.isKeyPressed(key) === false && aura.input.isKeyReleased(key) === false); return { setupOk, updateOk, drawOk, resizeSeen, hasInputFns, keyBooleans, aliasStable, invalidSafe, keySnapshot }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.setupOk === true && first.updateOk === true && first.drawOk === true && Array.isArray(first.resizeSeen) && first.resizeSeen[0] === 'number' && first.resizeSeen[1] === 'number' && first.resizeSeen[2] === 640 && first.resizeSeen[3] === 360 && first.hasInputFns === true && first.keyBooleans === true && first.aliasStable === true && first.invalidSafe === true; })()",
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
id: 'smoke.render.queue.readiness.deterministic',
|
|
578
|
+
expression: "(() => { const runSample = () => { const inspector = aura.debug?.inspectorStats; if (typeof inspector !== 'function') return null; const before = inspector(); const beforeDraw2d = Number(before?.queues?.draw2dPending); const beforeDraw3d = Number(before?.queues?.draw3dPending); const beforeOverlay = Number(before?.queues?.debugOverlayPending); const beforeNumeric = [beforeDraw2d, beforeDraw3d, beforeOverlay].every((value) => Number.isFinite(value)); let drawCallsAccepted = true; try { const white = aura.Color?.WHITE || aura.colors?.white || (typeof aura.rgba === 'function' ? aura.rgba(1, 1, 1, 1) : null); aura.draw2d.clear(0, 0, 0); aura.draw2d.rect(4, 6, 8, 10, white); aura.draw2d.rect(12, 9, 6, 4, white); } catch (_) { drawCallsAccepted = false; } const after = inspector(); const afterDraw2d = Number(after?.queues?.draw2dPending); const afterDraw3d = Number(after?.queues?.draw3dPending); const afterOverlay = Number(after?.queues?.debugOverlayPending); const afterNumeric = [afterDraw2d, afterDraw3d, afterOverlay].every((value) => Number.isFinite(value)); const draw2dDelta = beforeNumeric && afterNumeric ? (afterDraw2d - beforeDraw2d) : NaN; const draw3dDelta = beforeNumeric && afterNumeric ? (afterDraw3d - beforeDraw3d) : NaN; const overlayDelta = beforeNumeric && afterNumeric ? (afterOverlay - beforeOverlay) : NaN; return { drawCallsAccepted, beforeNumeric, afterNumeric, beforeDraw2d, afterDraw2d, draw2dDelta, draw3dDelta, overlayDelta }; }; const first = runSample(); const second = runSample(); return !!first && !!second && JSON.stringify(first) === JSON.stringify(second) && first.drawCallsAccepted === true && first.beforeNumeric === true && first.afterNumeric === true && Number.isFinite(first.draw2dDelta) && Number.isFinite(first.draw3dDelta) && Number.isFinite(first.overlayDelta) && first.draw2dDelta === 0 && first.draw3dDelta === 0 && first.overlayDelta === 0 && first.afterDraw2d === first.beforeDraw2d; })()",
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
id: 'smoke.assets-audio.reason-codes.deterministic',
|
|
582
|
+
expression: "(() => { const runSample = () => { const formatInvalidRaw = (() => { try { return aura.assets.getFormatSupport(42); } catch (_) { return null; } })(); const formatSupportedRaw = (() => { try { return aura.assets.getFormatSupport('native-smoke.png'); } catch (_) { return null; } })(); const loadMissing = (() => { try { const result = aura.assets.load('__missing__/native-smoke-missing.png'); return { threw: false, reasonCode: result?.reasonCode || null, message: null, taggedReason: null }; } catch (err) { const message = String(err); const match = /\\[reason:([^\\]]+)\\]/.exec(message); return { threw: true, reasonCode: null, message, taggedReason: match ? match[1] : null }; } })(); const invalidBusRaw = (() => { try { return aura.audio.setBusVolume('', 0.5); } catch (_) { return null; } })(); const invalidTrackRaw = (() => { try { return aura.audio.fadeTrack('invalid', { to: 0.2, duration: 0.1 }); } catch (_) { return null; } })(); return { formatInvalid: { ok: formatInvalidRaw?.ok === false, status: formatInvalidRaw?.status || null, reasonCode: formatInvalidRaw?.reasonCode || null }, formatSupported: { ok: !!formatSupportedRaw && formatSupportedRaw.ok === true, status: formatSupportedRaw?.status || null, reasonCode: formatSupportedRaw?.reasonCode || null }, loadMissing, invalidBus: { ok: invalidBusRaw?.ok === false, reasonCode: invalidBusRaw?.reasonCode || null }, invalidTrack: { ok: invalidTrackRaw?.ok === false, reasonCode: invalidTrackRaw?.reasonCode || null } }; }; const first = runSample(); const second = runSample(); const loadMissingOk = first.loadMissing?.threw === true && typeof first.loadMissing?.message === 'string' && first.loadMissing.message.includes('Asset not found: __missing__/native-smoke-missing.png') && (first.loadMissing.taggedReason === null || first.loadMissing.taggedReason === 'missing_asset'); return JSON.stringify(first) === JSON.stringify(second) && first.formatInvalid.ok === true && first.formatInvalid.status === 'unsupported' && first.formatInvalid.reasonCode === 'invalid_format_probe' && first.formatSupported.ok === true && first.formatSupported.status === 'supported' && first.formatSupported.reasonCode === 'asset_format_supported' && loadMissingOk && first.invalidBus.ok === true && first.invalidBus.reasonCode === 'invalid_bus' && first.invalidTrack.ok === true && first.invalidTrack.reasonCode === 'invalid_track_handle'; })()",
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
id: 'smoke.network.readiness.reason-codes.deterministic',
|
|
586
|
+
expression: "(() => { const parseReason = (msg) => { const match = /\\[reason:([^\\]]+)\\]/.exec(msg); return match ? match[1] : null; }; const classifyInvalidConnect = () => { try { aura.net.connect('tcp', '127.0.0.1', 0); return 'unexpected_no_throw'; } catch (err) { const msg = String(err); if (parseReason(msg) === 'optional_module_net_disabled') return 'optional_module_net_disabled'; if (msg.includes('port must be an integer in range 1..65535')) return 'invalid_connect_port'; return 'unexpected_error'; } }; const classifyInvalidWebSocket = () => { try { aura.net.websocket(42); return 'unexpected_no_throw'; } catch (err) { const msg = String(err); if (parseReason(msg) === 'optional_module_net_disabled') return 'optional_module_net_disabled'; if (msg.includes('url must be a websocket URL string')) return 'invalid_websocket_url'; return 'unexpected_error'; } }; const classifyInvalidFetch = () => { try { aura.net.fetch(42); return 'unexpected_no_throw'; } catch (err) { const msg = String(err); if (parseReason(msg) === 'optional_module_net_disabled') return 'optional_module_net_disabled'; if (msg.includes('url must be a string') || msg.includes('url must be an HTTP URL string')) return 'invalid_fetch_url'; return 'unexpected_error'; } }; const classifyRuntimeMode = () => { try { const handle = aura.net.connect('tcp', '127.0.0.1', 7777); const handleLike = !!handle && typeof handle === 'object' && typeof handle.send === 'function' && typeof handle.close === 'function'; if (handleLike) { try { handle.close(); } catch (_) {} return 'enabled_runtime'; } return 'enabled_nonstandard'; } catch (err) { const msg = String(err); if (parseReason(msg) === 'optional_module_net_disabled') return 'disabled'; return 'enabled_runtime_error'; } }; const runSample = () => { const methodsOk = typeof aura.net?.connect === 'function' && typeof aura.net?.websocket === 'function' && typeof aura.net?.fetch === 'function' && typeof aura.net?.get === 'function' && typeof aura.net?.post === 'function'; const mode = classifyRuntimeMode(); const connectReason = classifyInvalidConnect(); const websocketReason = classifyInvalidWebSocket(); const fetchReason = classifyInvalidFetch(); const consistency = mode === 'disabled' ? (connectReason === 'optional_module_net_disabled' && websocketReason === 'optional_module_net_disabled' && fetchReason === 'optional_module_net_disabled') : (connectReason === 'invalid_connect_port' && websocketReason === 'invalid_websocket_url' && fetchReason === 'invalid_fetch_url'); return { methodsOk, mode, connectReason, websocketReason, fetchReason, consistency }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.methodsOk === true && first.consistency === true && ['disabled', 'enabled_runtime', 'enabled_runtime_error', 'enabled_nonstandard'].includes(first.mode); })()",
|
|
587
|
+
},
|
|
588
|
+
],
|
|
589
|
+
nativeFrames: 1,
|
|
590
|
+
frames: 1,
|
|
591
|
+
source: `aura.setup = function () {};`,
|
|
592
|
+
},
|
|
201
593
|
{
|
|
202
594
|
id: 'draw-audio-assets-storage',
|
|
203
595
|
namespaces: ['draw2d', 'audio', 'assets', 'storage'],
|
|
@@ -293,16 +685,20 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
293
685
|
},
|
|
294
686
|
{
|
|
295
687
|
id: 'assets.streaming.lifecycle.preload.deterministic',
|
|
296
|
-
expression: "(() => { const reasonOf = (entry) => (entry && (entry.reasonCode || entry.reason)) || null; const normalizePreload = (entry) => ({ ok: !!entry && entry.ok === true, reasonCode: reasonOf(entry), requested: Number(entry?.requested || 0), loaded: Number(entry?.loaded || 0), failed: Number(entry?.failed || 0), failureReasonCodes: Array.isArray(entry?.failures) ? entry.failures.map((row) => reasonOf(row) || '').join('|') : '' }); const normalizeState = (entry) => entry ? { state: entry.state || null, reasonCode: reasonOf(entry) } : null; const normalizeHistory = (rows) => Array.isArray(rows) ? rows.map((row) => `${row.state || ''}:${reasonOf(row) || ''}`).join('|') : ''; const runSample = (tag) => { const missing2d = `__missing__/stream-2d-${tag}.png`; const missing3d = `__missing__/stream-3d-${tag}.glb`; const preload2d = aura.assets.preload2d([missing2d]); const preload3d = aura.assets.preload3d([missing3d]); const state2d = aura.assets.getState(missing2d); const state3d = aura.assets.getState(missing3d); const history2d = aura.assets.getStateHistory(missing2d); const history3d = aura.assets.getStateHistory(missing3d); return { preload2d: normalizePreload(preload2d), preload3d: normalizePreload(preload3d), state2d: normalizeState(state2d), state3d: normalizeState(state3d), history2d: normalizeHistory(history2d), history3d: normalizeHistory(history3d) }; }; const first = runSample('a'); const second = runSample('b'); return JSON.stringify(first) === JSON.stringify(second) && first.preload2d.reasonCode === 'assets_preload_partial_failure' && first.preload2d.requested === 1 && first.preload2d.failed === 1 && first.preload2d.failureReasonCodes === 'missing_asset' && first.preload3d.reasonCode === 'assets_preload_partial_failure' && first.preload3d.requested === 1 && first.preload3d.failed === 1 && first.preload3d.failureReasonCodes === '
|
|
688
|
+
expression: "(() => { const reasonOf = (entry) => (entry && (entry.reasonCode || entry.reason)) || null; const normalizePreload = (entry) => ({ ok: !!entry && entry.ok === true, reasonCode: reasonOf(entry), requested: Number(entry?.requested || 0), loaded: Number(entry?.loaded || 0), failed: Number(entry?.failed || 0), failureReasonCodes: Array.isArray(entry?.failures) ? entry.failures.map((row) => reasonOf(row) || '').join('|') : '' }); const normalizeState = (entry) => entry ? { state: entry.state || null, reasonCode: reasonOf(entry) } : null; const normalizeHistory = (rows) => Array.isArray(rows) ? rows.map((row) => `${row.state || ''}:${reasonOf(row) || ''}`).join('|') : ''; const runSample = (tag) => { const missing2d = `__missing__/stream-2d-${tag}.png`; const missing3d = `__missing__/stream-3d-${tag}.glb`; const preload2d = aura.assets.preload2d([missing2d]); const preload3d = aura.assets.preload3d([missing3d]); const state2d = aura.assets.getState(missing2d); const state3d = aura.assets.getState(missing3d); const history2d = aura.assets.getStateHistory(missing2d); const history3d = aura.assets.getStateHistory(missing3d); return { preload2d: normalizePreload(preload2d), preload3d: normalizePreload(preload3d), state2d: normalizeState(state2d), state3d: normalizeState(state3d), history2d: normalizeHistory(history2d), history3d: normalizeHistory(history3d) }; }; const first = runSample('a'); const second = runSample('b'); return JSON.stringify(first) === JSON.stringify(second) && first.preload2d.reasonCode === 'assets_preload_partial_failure' && first.preload2d.requested === 1 && first.preload2d.failed === 1 && first.preload2d.failureReasonCodes === 'missing_asset' && first.preload3d.reasonCode === 'assets_preload_partial_failure' && first.preload3d.requested === 1 && first.preload3d.failed === 1 && first.preload3d.failureReasonCodes === 'missing_asset' && first.state2d?.state === 'failed' && first.state2d?.reasonCode === 'missing_asset' && first.state3d?.state === 'failed' && first.state3d?.reasonCode === 'missing_asset' && first.history2d.startsWith('queued:') && first.history2d.includes('|loading:') && first.history2d.includes('|failed:') && first.history3d.startsWith('queued:') && first.history3d.includes('|loading:') && first.history3d.includes('|failed:missing_asset'); })()",
|
|
297
689
|
},
|
|
298
690
|
{
|
|
299
691
|
id: 'assets.deferred-format.reason-codes.deterministic',
|
|
300
|
-
expression: "(() => { const norm = (entry) => entry ? { ok: entry.ok === true, status: entry.status || null, reasonCode: entry.reasonCode || null, extension: entry.extension || null, kindHint: entry.kindHint || null, supported: entry.supported === true, deferred: entry.deferred === true } : null; const runSample = () => { const supported = norm(aura.assets.getFormatSupport('atlas.png')); const
|
|
692
|
+
expression: "(() => { const norm = (entry) => entry ? { ok: entry.ok === true, status: entry.status || null, reasonCode: entry.reasonCode || null, extension: entry.extension || null, kindHint: entry.kindHint || null, supported: entry.supported === true, deferred: entry.deferred === true } : null; const runSample = () => { const supported = norm(aura.assets.getFormatSupport('atlas.png')); const promotedImage = norm(aura.assets.getFormatSupport('future.webp')); const deferredImage = norm(aura.assets.getFormatSupport('future.bmp')); const supportedWoff2 = norm(aura.assets.getFormatSupport('future.woff2')); const deferredFont = norm(aura.assets.getFormatSupport('future.woff')); const promotedModel = norm(aura.assets.getFormatSupport('future.glb')); const unsupported = norm(aura.assets.getFormatSupport('blob.xyz')); const invalidRaw = aura.assets.getFormatSupport(42); const invalid = invalidRaw ? { ok: invalidRaw.ok === true, status: invalidRaw.status || null, reasonCode: invalidRaw.reasonCode || null } : null; const loadReason = (() => { try { aura.assets.load('future.bmp'); } catch (err) { return String(err); } return ''; })(); const audioReason = (() => { try { aura.audio.play('future.aac'); } catch (err) { return String(err); } return ''; })(); const fontResult = aura.assets.loadFont('future.woff2'); return { supported, promotedImage, deferredImage, supportedWoff2, deferredFont, promotedModel, unsupported, invalid, loadReason, audioReason, fontReason: fontResult?.reason || null }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.supported?.ok === true && first.supported?.status === 'supported' && first.supported?.reasonCode === 'asset_format_supported' && first.supported?.supported === true && first.supported?.deferred === false && first.promotedImage?.ok === true && first.promotedImage?.status === 'supported' && first.promotedImage?.reasonCode === 'asset_format_supported' && first.promotedImage?.deferred === false && first.deferredImage?.ok === true && first.deferredImage?.status === 'unsupported' && first.deferredImage?.reasonCode === 'asset_format_unsupported' && first.deferredImage?.deferred === false && first.supportedWoff2?.status === 'supported' && first.supportedWoff2?.reasonCode === 'asset_format_supported' && first.supportedWoff2?.deferred === false && first.deferredFont?.status === 'unsupported' && first.deferredFont?.reasonCode === 'asset_format_unsupported' && first.deferredFont?.deferred === false && first.promotedModel?.status === 'supported' && first.promotedModel?.reasonCode === 'asset_format_supported' && first.unsupported?.status === 'unsupported' && first.unsupported?.reasonCode === 'asset_format_unsupported' && first.invalid?.ok === false && first.invalid?.status === 'unsupported' && first.invalid?.reasonCode === 'invalid_format_probe' && first.loadReason.includes('[reason:asset_format_unsupported]') && first.audioReason.includes('[reason:asset_format_unsupported]') && first.fontReason === 'asset_not_found'; })()",
|
|
301
693
|
},
|
|
302
694
|
{
|
|
303
695
|
id: 'assets.streaming.cache-policy.reason-codes.stable',
|
|
304
696
|
expression: "(() => { const reasonOf = (entry) => (entry && (entry.reasonCode || entry.reason)) || null; const sample = () => { const invalidPaths = aura.assets.preload2d('bad'); const invalidPath = aura.assets.preload3d(['../bad.glb']); const invalidMaxEntries = aura.assets.setCachePolicy({ maxEntries: 0 }); const invalidMaxBytes = aura.assets.setCachePolicy({ maxBytes: 0 }); const invalidMode = aura.assets.setCachePolicy({ evictionMode: 'fifo' }); const updated = aura.assets.setCachePolicy({ maxEntries: 4, maxBytes: null, evictionMode: 'oldest' }); const policy = aura.assets.getCachePolicy(); const missingEvict = aura.assets.evict('__missing__/ghost.png'); const invalidEvict = aura.assets.evict('../ghost.png'); return { invalidPaths: reasonOf(invalidPaths), invalidPath: reasonOf(invalidPath), invalidMaxEntries: reasonOf(invalidMaxEntries), invalidMaxBytes: reasonOf(invalidMaxBytes), invalidMode: reasonOf(invalidMode), updatedOk: !!updated && updated.ok === true, updatedReason: reasonOf(updated), policyMaxEntries: policy?.maxEntries, policyMaxBytes: policy?.maxBytes ?? null, policyEvictionMode: policy?.evictionMode, missingEvict: reasonOf(missingEvict), invalidEvict: reasonOf(invalidEvict) }; }; const first = sample(); const second = sample(); return JSON.stringify(first) === JSON.stringify(second) && first.invalidPaths === 'invalid_preload_paths' && first.invalidPath === 'invalid_preload_path' && first.invalidMaxEntries === 'invalid_max_entries' && first.invalidMaxBytes === 'invalid_max_bytes' && first.invalidMode === 'invalid_eviction_mode' && first.updatedOk === true && first.updatedReason === 'cache_policy_updated' && first.policyMaxEntries === 4 && first.policyMaxBytes === null && first.policyEvictionMode === 'oldest' && first.missingEvict === 'missing_asset' && first.invalidEvict === 'invalid_path'; })()",
|
|
305
697
|
},
|
|
698
|
+
{
|
|
699
|
+
id: 'audio.invalid-input.no-throw.reason-codes',
|
|
700
|
+
expression: "(() => { const runSample = () => { let threw = false; try { aura.audio.stop(-1); aura.audio.pause('x'); aura.audio.resume(99999); aura.audio.setVolume('x', 0.5); } catch (_) { threw = true; } const invalidBus = aura.audio.setBusVolume('', 0.5); const invalidTrackHandle = aura.audio.fadeTrack('oops', { to: 0.2, duration: 0.5 }); const missingBus = aura.audio.fadeBus('missing', { to: 0.2, duration: 0.5 }); const invalidCrossfade = aura.audio.crossfade('oops', 'bad', { duration: 0.5 }); return { threw, invalidBus: invalidBus?.reasonCode || null, invalidTrackHandle: invalidTrackHandle?.reasonCode || null, missingBus: missingBus?.reasonCode || null, invalidCrossfade: invalidCrossfade?.reasonCode || null }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.threw === false && first.invalidBus === 'invalid_bus' && first.invalidTrackHandle === 'invalid_track_handle' && first.missingBus === 'missing_bus' && typeof first.invalidCrossfade === 'string' && first.invalidCrossfade.length > 0; })()",
|
|
701
|
+
},
|
|
306
702
|
{ id: 'storage.save', expression: "typeof aura.storage?.save === 'function'" },
|
|
307
703
|
{ id: 'storage.load', expression: "typeof aura.storage?.load === 'function'" },
|
|
308
704
|
{ id: 'storage.delete', expression: "typeof aura.storage?.delete === 'function'" },
|
|
@@ -356,6 +752,49 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
356
752
|
};
|
|
357
753
|
`,
|
|
358
754
|
},
|
|
755
|
+
{
|
|
756
|
+
id: 'compute-runtime-native',
|
|
757
|
+
modes: ['native'],
|
|
758
|
+
namespaces: ['compute'],
|
|
759
|
+
functions: [
|
|
760
|
+
'aura.compute.createPipeline',
|
|
761
|
+
'aura.compute.createBuffer',
|
|
762
|
+
'aura.compute.writeBuffer',
|
|
763
|
+
'aura.compute.createBindGroup',
|
|
764
|
+
'aura.compute.dispatch',
|
|
765
|
+
'aura.compute.readBuffer',
|
|
766
|
+
'aura.compute.destroyPipeline',
|
|
767
|
+
'aura.compute.destroyBuffer',
|
|
768
|
+
'aura.compute.destroyBindGroup',
|
|
769
|
+
'aura.compute.getError',
|
|
770
|
+
],
|
|
771
|
+
nativeChecks: [
|
|
772
|
+
{
|
|
773
|
+
id: 'compute.surface.methods',
|
|
774
|
+
expression: "(() => ['createPipeline','createBuffer','writeBuffer','createBindGroup','dispatch','readBuffer','destroyPipeline','destroyBuffer','destroyBindGroup','getError'].every((name) => typeof aura.compute?.[name] === 'function'))()",
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
id: 'compute.dispatch.readback.queued',
|
|
778
|
+
expression: "(() => { const shader = '@group(0) @binding(0) var<storage, read_write> values: array<f32>;\\n@compute @workgroup_size(1)\\nfn main() {\\n values[0] = values[0] + 1.0;\\n}'; const pipeline = aura.compute.createPipeline(shader); const buffer = aura.compute.createBuffer(4, 'storage-read'); aura.compute.writeBuffer(buffer, new Float32Array([2])); const bindGroup = aura.compute.createBindGroup(pipeline, [{ binding: 0, buffer }]); aura.compute.dispatch(pipeline, bindGroup, 1, 1, 1); const queued = aura.compute.readBuffer(buffer, 0, 4); globalThis.__computeNative = { pipeline, buffer, bindGroup, queuedWasNull: queued === null }; return Number.isInteger(pipeline) && pipeline > 0 && Number.isInteger(buffer) && buffer > 0 && Number.isInteger(bindGroup) && bindGroup > 0 && queued === null && aura.compute.getError(pipeline) === null && aura.compute.getError(bindGroup) === null && aura.compute.getError(buffer) === null; })()",
|
|
779
|
+
},
|
|
780
|
+
],
|
|
781
|
+
nativePostChecks: [
|
|
782
|
+
{
|
|
783
|
+
id: 'compute.dispatch.readback.deterministic',
|
|
784
|
+
expression: "(() => { const state = globalThis.__computeNative || {}; const result = aura.compute.readBuffer(state.buffer); const second = aura.compute.readBuffer(state.buffer); const values = ArrayBuffer.isView(result) ? Array.from(result).map((value) => Number(value.toFixed(6))) : []; const pipelineError = aura.compute.getError(state.pipeline); const bindGroupError = aura.compute.getError(state.bindGroup); const bufferError = aura.compute.getError(state.buffer); aura.compute.destroyBindGroup(state.bindGroup); aura.compute.destroyPipeline(state.pipeline); aura.compute.destroyBuffer(state.buffer); return state.queuedWasNull === true && result instanceof Float32Array && result.length === 1 && values.length === 1 && values[0] === 3 && second === null && pipelineError === null && bindGroupError === null && bufferError === null; })()",
|
|
785
|
+
},
|
|
786
|
+
],
|
|
787
|
+
nativeFrames: 2,
|
|
788
|
+
frames: 1,
|
|
789
|
+
source: `
|
|
790
|
+
globalThis.__computeNative = {
|
|
791
|
+
pipeline: 0,
|
|
792
|
+
buffer: 0,
|
|
793
|
+
bindGroup: 0,
|
|
794
|
+
queuedWasNull: false,
|
|
795
|
+
};
|
|
796
|
+
`,
|
|
797
|
+
},
|
|
359
798
|
{
|
|
360
799
|
id: 'camera-2d-runtime-surface',
|
|
361
800
|
modes: ['native'],
|
|
@@ -473,7 +912,8 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
473
912
|
},
|
|
474
913
|
{
|
|
475
914
|
id: 'debug.inspector.stats-shape',
|
|
476
|
-
expression: "(() => { try { const enabled = aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats(); const disabled = aura.debug.enableInspector(false); const phase2 = stats.phase2 || {}; const rows = [phase2.animation, phase2.tilemap, phase2.scene3d, phase2.particles]; const phase2Ok = rows.every((row) => row && Number.isFinite(row.active) && Number.isFinite(row.total) && Number.isFinite(row.callbackQueueDepth) && row.active >= 0 && row.total >= 0 && row.callbackQueueDepth >= 0); const scene3dRuntime = stats.scene3dRuntime || {}; const submission = scene3dRuntime.submission || {}; const camera = scene3dRuntime.camera || {}; const resources = scene3dRuntime.resources || {}; const cameraVectorsOk = Number.isFinite(camera.position?.x) && Number.isFinite(camera.position?.y) && Number.isFinite(camera.position?.z) && Number.isFinite(camera.target?.x) && Number.isFinite(camera.target?.y) && Number.isFinite(camera.target?.z); const scene3dRuntimeOk = Number.isFinite(submission.drawMeshPending) && Number.isFinite(submission.uniqueMeshHandles) && Number.isFinite(submission.uniqueMaterialHandles) && Number.isFinite(submission.nonDefaultTransforms) && typeof submission.skyboxRequested === 'boolean' && typeof submission.clearRequested === 'boolean' && typeof camera.mode === 'string' && camera.mode.length > 0 &&
|
|
915
|
+
expression: "(() => { try { const enabled = aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats(); const disabled = aura.debug.enableInspector(false); const phase2 = stats.phase2 || {}; const rows = [phase2.animation, phase2.tilemap, phase2.scene3d, phase2.particles]; const phase2Ok = rows.every((row) => row && Number.isFinite(row.active) && Number.isFinite(row.total) && Number.isFinite(row.callbackQueueDepth) && row.active >= 0 && row.total >= 0 && row.callbackQueueDepth >= 0); const scene3dRuntime = stats.scene3dRuntime || {}; const submission = scene3dRuntime.submission || {}; const camera = scene3dRuntime.camera || {}; const resources = scene3dRuntime.resources || {}; const cameraVectorsOk = Number.isFinite(camera.position?.x) && Number.isFinite(camera.position?.y) && Number.isFinite(camera.position?.z) && Number.isFinite(camera.target?.x) && Number.isFinite(camera.target?.y) && Number.isFinite(camera.target?.z); const scene3dRuntimeOk = Number.isFinite(submission.drawMeshPending) && Number.isFinite(submission.uniqueMeshHandles) && Number.isFinite(submission.uniqueMaterialHandles) && Number.isFinite(submission.nonDefaultTransforms) && typeof submission.skyboxRequested === 'boolean' && typeof submission.clearRequested === 'boolean' && typeof camera.mode === 'string' && camera.mode.length > 0 && Number.isFinite(camera.aspect) && Number.isFinite(camera.fovDegrees) && Number.isFinite(camera.near) && Number.isFinite(camera.far) && cameraVectorsOk && Number.isFinite(resources.meshHandlesRegistered) && Number.isFinite(resources.meshUploadsPending) && Number.isFinite(resources.materialHandlesKnown) && Number.isFinite(resources.materialCommandsPending) && Number.isFinite(resources.lightPointCount) && typeof resources.lightDirectionalActive === 'boolean'; return enabled === true && disabled === false && Number.isFinite(stats.frameCount) && Number.isFinite(stats.frame.fps) && Number.isFinite(stats.frame.deltaSeconds) && Number.isFinite(stats.frame.elapsedSeconds) && Number.isFinite(stats.window.width) && Number.isFinite(stats.window.height) && Number.isFinite(stats.window.pixelRatio) && Number.isFinite(stats.queues.draw2dPending) && Number.isFinite(stats.queues.draw3dPending) && Number.isFinite(stats.queues.debugOverlayPending) && phase2Ok && scene3dRuntimeOk; } catch (_) { return false; } })()",
|
|
916
|
+
debugExpression: "(() => { try { const enabled = aura.debug.enableInspector(true); const stats = aura.debug.inspectorStats(); const disabled = aura.debug.enableInspector(false); const phase2 = stats.phase2 || {}; const rows = [phase2.animation, phase2.tilemap, phase2.scene3d, phase2.particles]; const phase2Rows = rows.map((row) => ({ present: !!row, active: row?.active, total: row?.total, callbackQueueDepth: row?.callbackQueueDepth })); const phase2Ok = rows.every((row) => row && Number.isFinite(row.active) && Number.isFinite(row.total) && Number.isFinite(row.callbackQueueDepth) && row.active >= 0 && row.total >= 0 && row.callbackQueueDepth >= 0); const scene3dRuntime = stats.scene3dRuntime || {}; const submission = scene3dRuntime.submission || {}; const camera = scene3dRuntime.camera || {}; const resources = scene3dRuntime.resources || {}; const cameraVectorsOk = Number.isFinite(camera.position?.x) && Number.isFinite(camera.position?.y) && Number.isFinite(camera.position?.z) && Number.isFinite(camera.target?.x) && Number.isFinite(camera.target?.y) && Number.isFinite(camera.target?.z); const scene3dRuntimeOk = Number.isFinite(submission.drawMeshPending) && Number.isFinite(submission.uniqueMeshHandles) && Number.isFinite(submission.uniqueMaterialHandles) && Number.isFinite(submission.nonDefaultTransforms) && typeof submission.skyboxRequested === 'boolean' && typeof submission.clearRequested === 'boolean' && typeof submission.environmentMapConfigured === 'boolean' && Number.isFinite(submission.pointShadowLightCount) && Number.isFinite(submission.spotShadowLightCount) && Number.isFinite(submission.pointShadowPassCount) && Number.isFinite(submission.spotShadowPassCount) && typeof camera.mode === 'string' && camera.mode.length > 0 && Number.isFinite(camera.aspect) && Number.isFinite(camera.fovDegrees) && Number.isFinite(camera.near) && Number.isFinite(camera.far) && cameraVectorsOk && Number.isFinite(resources.meshHandlesRegistered) && Number.isFinite(resources.meshUploadsPending) && Number.isFinite(resources.materialHandlesKnown) && Number.isFinite(resources.materialCommandsPending) && Number.isFinite(resources.lightPointCount) && Number.isFinite(resources.lightSpotCount) && typeof resources.lightDirectionalActive === 'boolean'; return { enabled, disabled, frameCount: stats?.frameCount, frame: stats?.frame || null, window: stats?.window || null, queues: stats?.queues || null, phase2Ok, phase2Rows, scene3dRuntimeOk, cameraMode: camera?.mode, cameraAspect: camera?.aspect, cameraVectorsOk, submission, resources }; } catch (error) { return { traceError: String(error && error.message ? error.message : error) }; } })()",
|
|
477
917
|
},
|
|
478
918
|
{
|
|
479
919
|
id: 'debug.inspector.scene3d-animation-shape',
|
|
@@ -512,6 +952,10 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
512
952
|
id: 'audio.mixer.surface.methods',
|
|
513
953
|
expression: "(() => ['setBusVolume','assignBus','fadeTrack','fadeBus','crossfade','update','clearEnvelopes','getMixerState'].every((name) => typeof aura.audio?.[name] === 'function'))()",
|
|
514
954
|
},
|
|
955
|
+
{
|
|
956
|
+
id: 'audio.mixer.reason-codes.deterministic',
|
|
957
|
+
expression: "(() => { const runSample = () => { const invalidBus = aura.audio.setBusVolume('', 0.5); const invalidTrackHandle = aura.audio.fadeTrack('oops', { to: 0.2, duration: 0.5 }); const missingTrack = aura.audio.fadeTrack(999999, { to: 0.2, duration: 0.5 }); const missingBus = aura.audio.fadeBus('missing', { to: 0.2, duration: 0.5 }); const invalidDt = aura.audio.update(0); return { invalidBus: invalidBus?.reasonCode || null, invalidTrackHandle: invalidTrackHandle?.reasonCode || null, missingTrack: missingTrack?.reasonCode || null, missingBus: missingBus?.reasonCode || null, invalidDt: invalidDt?.reasonCode || null }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.invalidBus === 'invalid_bus' && first.invalidTrackHandle === 'invalid_track_handle' && first.missingTrack === 'missing_track' && first.missingBus === 'missing_bus' && first.invalidDt === 'invalid_dt'; })()",
|
|
958
|
+
},
|
|
515
959
|
],
|
|
516
960
|
frames: 1,
|
|
517
961
|
nativeFrames: 1,
|
|
@@ -620,6 +1064,10 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
620
1064
|
{ id: 'tilemap.queryRay', expression: "typeof aura.tilemap?.queryRay === 'function'" },
|
|
621
1065
|
{ id: 'tilemap.queryRaycast', expression: "typeof aura.tilemap?.queryRaycast === 'function'" },
|
|
622
1066
|
{ id: 'tilemap.query.invalid-handle.reason-code', expression: "(() => { const result = aura.tilemap.queryPoint(999999, { x: 0, y: 0 }); return result && result.ok === false && result.reasonCode === 'invalid_map_handle'; })()" },
|
|
1067
|
+
{
|
|
1068
|
+
id: 'tilemap.query.reason-codes.expanded',
|
|
1069
|
+
expression: "(() => { const runSample = () => { const fixture = { width: 2, height: 2, tilewidth: 16, tileheight: 16, layers: [ { name: 'ground', type: 'tilelayer', width: 2, height: 2, data: [1, 0, 0, 0], properties: { solid: true } } ], tilesets: [ { firstgid: 1, image: 'tiles.png', tilewidth: 16, tileheight: 16, tilecount: 1, columns: 1 } ] }; const mapId = aura.tilemap.import(fixture); const invalidPoint = aura.tilemap.queryPoint(mapId, { x: 'bad', y: 0 }); const invalidAabb = aura.tilemap.queryAABB(mapId, { x: 0, y: 0, w: 0, h: 1 }); const invalidRay = aura.tilemap.queryRay(mapId, { x: 0, y: 0, dx: 0, dy: 0, maxDistance: 10 }); const invalidModel = aura.tilemap.queryPoint({ width: 4 }, 1, 1); const invalidHandle = aura.tilemap.queryPoint(999999, { x: 0, y: 0 }); let threw = false; try { aura.tilemap.queryRay(mapId, { x: 0, y: 0, dx: 'bad', dy: 0, maxDistance: 10 }); } catch (_) { threw = true; } aura.tilemap.unload(mapId); return { invalidPoint: invalidPoint?.reasonCode || null, invalidAabb: invalidAabb?.reasonCode || null, invalidRay: invalidRay?.reasonCode || null, invalidModel: invalidModel?.reasonCode || null, invalidHandle: invalidHandle?.reasonCode || null, threw }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.invalidPoint === 'invalid_point_args' && first.invalidAabb === 'invalid_aabb_args' && first.invalidRay === 'invalid_ray_args' && first.invalidModel === 'invalid_model' && first.invalidHandle === 'invalid_map_handle' && first.threw === false; })()",
|
|
1070
|
+
},
|
|
623
1071
|
{
|
|
624
1072
|
id: 'tilemap.query.model-schema.deterministic',
|
|
625
1073
|
expression: "(() => { const runSample = () => { const model = { width: 2, height: 2, tilewidth: 16, tileheight: 16, solidLayerNames: ['solid'], layers: [ { name: 'solid', width: 2, height: 2, data: [1, 0, 0, 0], properties: { solid: true } }, { name: 'dynamic', width: 2, height: 2, data: [0, 1, 0, 0], queryEnabled: true } ], solidCells: [ { x: 1, y: 0, layerIndex: 5, layerName: 'manual' }, { x: 1, y: 0, layerIndex: 5, layerName: 'manual' } ] }; const point = aura.tilemap.queryPoint(model, { x: 16.5, y: 0.5 }); const aabb = aura.tilemap.queryAABB(model, { x: 0, y: 0, w: 32, h: 16 }); const ray = aura.tilemap.queryRay(model, { x: 0.5, y: 0.5, dx: 1, dy: 0, maxDistance: 64 }); return { pointLayers: Array.isArray(point?.hits) ? point.hits.map((hit) => hit.layerIndex).join(',') : '', aabbLayers: Array.isArray(aabb?.hits) ? aabb.hits.map((hit) => hit.layerIndex).join(',') : '', rayLayer: Number(ray?.hitCell?.layerIndex ?? -1), pointOk: point?.ok === true, aabbOk: aabb?.ok === true, rayOk: ray?.ok === true, pointHit: point?.hit === true, aabbHit: aabb?.hit === true, rayHit: ray?.hit === true, invalidModelReason: aura.tilemap.queryPoint({ width: 2 }, { x: 0, y: 0 })?.reasonCode, invalidAabbReason: aura.tilemap.queryAABB(model, { x: 0, y: 0, w: 0, h: 1 })?.reasonCode }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.pointOk === true && first.aabbOk === true && first.rayOk === true && first.pointHit === true && first.aabbHit === true && first.rayHit === true && first.pointLayers === '1,5' && first.aabbLayers === '0,1,5' && first.rayLayer === 0 && first.invalidModelReason === 'invalid_model' && first.invalidAabbReason === 'invalid_aabb_args'; })()",
|
|
@@ -698,20 +1146,30 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
698
1146
|
modes: ['shim', 'native'],
|
|
699
1147
|
namespaces: ['tilemap'],
|
|
700
1148
|
functions: [
|
|
1149
|
+
'aura.tilemap.setTile',
|
|
701
1150
|
'aura.tilemap.setRegion',
|
|
702
1151
|
'aura.tilemap.removeRegion',
|
|
703
1152
|
'aura.tilemap.replaceRegion',
|
|
1153
|
+
'aura.tilemap.setTileCollision',
|
|
704
1154
|
'aura.tilemap.setLayerFlags',
|
|
705
1155
|
'aura.tilemap.setLayerVisibility',
|
|
706
1156
|
'aura.tilemap.setLayerCollision',
|
|
1157
|
+
'aura.tilemap.queryObjects',
|
|
1158
|
+
'aura.tilemap.queryObjectsAtPoint',
|
|
1159
|
+
'aura.tilemap.queryObjectsInAABB',
|
|
707
1160
|
],
|
|
708
1161
|
nativeChecks: [
|
|
1162
|
+
{ id: 'tilemap.mutation.setTile', expression: "typeof aura.tilemap?.setTile === 'function'" },
|
|
709
1163
|
{ id: 'tilemap.mutation.setRegion', expression: "typeof aura.tilemap?.setRegion === 'function'" },
|
|
710
1164
|
{ id: 'tilemap.mutation.removeRegion', expression: "typeof aura.tilemap?.removeRegion === 'function'" },
|
|
711
1165
|
{ id: 'tilemap.mutation.replaceRegion', expression: "typeof aura.tilemap?.replaceRegion === 'function'" },
|
|
1166
|
+
{ id: 'tilemap.mutation.setTileCollision', expression: "typeof aura.tilemap?.setTileCollision === 'function'" },
|
|
712
1167
|
{ id: 'tilemap.layer.setLayerFlags', expression: "typeof aura.tilemap?.setLayerFlags === 'function'" },
|
|
713
1168
|
{ id: 'tilemap.layer.setLayerVisibility', expression: "typeof aura.tilemap?.setLayerVisibility === 'function'" },
|
|
714
1169
|
{ id: 'tilemap.layer.setLayerCollision', expression: "typeof aura.tilemap?.setLayerCollision === 'function'" },
|
|
1170
|
+
{ id: 'tilemap.query.queryObjects', expression: "typeof aura.tilemap?.queryObjects === 'function'" },
|
|
1171
|
+
{ id: 'tilemap.query.queryObjectsAtPoint', expression: "typeof aura.tilemap?.queryObjectsAtPoint === 'function'" },
|
|
1172
|
+
{ id: 'tilemap.query.queryObjectsInAABB', expression: "typeof aura.tilemap?.queryObjectsInAABB === 'function'" },
|
|
715
1173
|
{
|
|
716
1174
|
id: 'tilemap.mutation.reason-codes.expanded',
|
|
717
1175
|
expression: "(() => { const runSample = () => { const fixture = { width: 2, height: 2, tilewidth: 16, tileheight: 16, layers: [ { name: 'ground', type: 'tilelayer', width: 2, height: 2, data: [1, 1, 1, 1] } ], tilesets: [ { firstgid: 1, image: 'tiles-a.png', tilewidth: 16, tileheight: 16, tilecount: 1, columns: 1 }, { firstgid: 5, image: 'tiles-b.png', tilewidth: 16, tileheight: 16, tilecount: 1, columns: 1 } ] }; const mapId = aura.tilemap.import(fixture); const invalidGidArgs = aura.tilemap.setRegion(mapId, 'ground', { x: 0, y: 0, w: 1, h: 1 }, 'bad'); const unmappedBoundary = aura.tilemap.setRegion(mapId, 'ground', { x: 0, y: 0, w: 1, h: 1 }, 2); const validBoundary = aura.tilemap.setRegion(mapId, 'ground', { x: 0, y: 0, w: 1, h: 1 }, 5); const invalidReplaceArgs = aura.tilemap.replaceRegion(mapId, 'ground', { x: 0, y: 0, w: 1, h: 1 }, 1, 'bad'); const invalidLayerFlags = aura.tilemap.setLayerFlags(mapId, 'ground', {}); const invalidVisibility = aura.tilemap.setLayerVisibility(mapId, 'ground', 'yes'); const invalidCollision = aura.tilemap.setLayerCollision(mapId, 'ground', 'yes'); const invalidLayer = aura.tilemap.removeRegion(mapId, 'missing', { x: 0, y: 0, w: 1, h: 1 }); const invalidHandle = aura.tilemap.setRegion(999999, 'ground', { x: 0, y: 0, w: 1, h: 1 }, 1); aura.tilemap.unload(mapId); return { invalidGidArgs: invalidGidArgs?.reasonCode, unmappedBoundary: unmappedBoundary?.reasonCode, validBoundaryOk: validBoundary?.ok === true, validBoundaryChanged: Number(validBoundary?.changedTiles || 0), invalidReplaceArgs: invalidReplaceArgs?.reasonCode, invalidLayerFlags: invalidLayerFlags?.reasonCode, invalidVisibility: invalidVisibility?.reasonCode, invalidCollision: invalidCollision?.reasonCode, invalidLayer: invalidLayer?.reasonCode, invalidHandle: invalidHandle?.reasonCode }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.invalidGidArgs === 'invalid_gid_args' && first.unmappedBoundary === 'unmapped_layer_gid' && first.validBoundaryOk === true && first.validBoundaryChanged === 1 && first.invalidReplaceArgs === 'invalid_replace_args' && first.invalidLayerFlags === 'invalid_layer_flags' && first.invalidVisibility === 'invalid_visibility_flag' && first.invalidCollision === 'invalid_collision_flag' && first.invalidLayer === 'invalid_layer_ref' && first.invalidHandle === 'invalid_map_handle'; })()",
|
|
@@ -720,6 +1178,10 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
720
1178
|
id: 'tilemap.mutation.layer-controls.deterministic',
|
|
721
1179
|
expression: "(() => { const runSample = () => { const fixture = { width: 4, height: 4, tilewidth: 16, tileheight: 16, solidLayerNames: ['ground', 'hazards'], layers: [ { name: 'ground', type: 'tilelayer', width: 4, height: 4, data: Array.from({ length: 16 }, () => 1) }, { name: 'hazards', type: 'tilelayer', width: 4, height: 4, data: [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0] } ], tilesets: [ { firstgid: 1, image: 'tiles.png', tilewidth: 16, tileheight: 16, tilecount: 1, columns: 1 } ] }; const mapId = aura.tilemap.import(fixture); const initial = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 }); const setRegion = aura.tilemap.setRegion(mapId, 'ground', { x: 0, y: 0, w: 2, h: 1 }, 0); const topRow = aura.tilemap.queryAABB(mapId, { x: 0, y: 0, w: 32, h: 16 }); const removeRegion = aura.tilemap.removeRegion(mapId, 'hazards', { x: 1, y: 1, w: 1, h: 1 }); const afterRemove = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 }); const replaceRegion = aura.tilemap.replaceRegion(mapId, 'ground', { x: 1, y: 1, w: 1, h: 1 }, 1, 0); const afterReplace = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 }); const collisionOff = aura.tilemap.setLayerCollision(mapId, 'ground', false); const hazardsPoint = aura.tilemap.queryPoint(mapId, { x: 32.5, y: 16.5 }); const visibilityOff = aura.tilemap.setLayerVisibility(mapId, 'hazards', false); const hiddenDraw = aura.tilemap.draw(mapId, { cull: false }); const flags = aura.tilemap.setLayerFlags(mapId, 'hazards', { visible: true, collision: false }); const shownDraw = aura.tilemap.draw(mapId, { cull: false }); const queryMuted = aura.tilemap.queryPoint(mapId, { x: 32.5, y: 16.5 }); const groundCollisionOn = aura.tilemap.setLayerFlags(mapId, 'ground', { collision: true }); const hazardsCollisionOn = aura.tilemap.setLayerCollision(mapId, 'hazards', true); const queryRestored = aura.tilemap.queryPoint(mapId, { x: 32.5, y: 16.5 }); const invalidRegion = aura.tilemap.setRegion(mapId, 'ground', { x: 0, y: 0, w: 0, h: 1 }, 1); const invalidGid = aura.tilemap.setRegion(mapId, 'ground', { x: 0, y: 0, w: 1, h: 1 }, 999); const invalidFlags = aura.tilemap.setLayerFlags(mapId, 'ground', { collision: 'yes' }); const invalidLayer = aura.tilemap.setLayerVisibility(mapId, 'missing', true); aura.tilemap.unload(mapId); return { initialHits: Array.isArray(initial?.hits) ? initial.hits.length : -1, topRowHits: Array.isArray(topRow?.hits) ? topRow.hits.length : -1, afterRemoveHits: Array.isArray(afterRemove?.hits) ? afterRemove.hits.length : -1, afterReplaceHits: Array.isArray(afterReplace?.hits) ? afterReplace.hits.length : -1, hazardsHits: Array.isArray(hazardsPoint?.hits) ? hazardsPoint.hits.length : -1, queryMutedHits: Array.isArray(queryMuted?.hits) ? queryMuted.hits.length : -1, queryRestoredHits: Array.isArray(queryRestored?.hits) ? queryRestored.hits.length : -1, setChanged: setRegion?.changedTiles, removeChanged: removeRegion?.changedTiles, replaceChanged: replaceRegion?.changedTiles, collisionOff: collisionOff?.collision, visibilityOff: visibilityOff?.visible, hiddenOrder: hiddenDraw?.layerOrder?.join(','), shownOrder: shownDraw?.layerOrder?.join(','), flagsVisible: flags?.visible, flagsCollision: flags?.collision, groundCollisionOn: groundCollisionOn?.collision, hazardsCollisionOn: hazardsCollisionOn?.collision, invalidRegion: invalidRegion?.reasonCode, invalidGid: invalidGid?.reasonCode, invalidFlags: invalidFlags?.reasonCode, invalidLayer: invalidLayer?.reasonCode }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.initialHits === 2 && first.topRowHits === 0 && first.afterRemoveHits === 1 && first.afterReplaceHits === 0 && first.hazardsHits === 1 && first.queryMutedHits === 0 && first.queryRestoredHits === 2 && first.setChanged === 2 && first.removeChanged === 1 && first.replaceChanged === 1 && first.collisionOff === false && first.visibilityOff === false && first.hiddenOrder === 'ground' && first.shownOrder === 'ground,hazards' && first.flagsVisible === true && first.flagsCollision === false && first.groundCollisionOn === true && first.hazardsCollisionOn === true && first.invalidRegion === 'invalid_region_args' && first.invalidGid === 'unmapped_layer_gid' && first.invalidFlags === 'invalid_collision_flag' && first.invalidLayer === 'invalid_layer_ref'; })()",
|
|
722
1180
|
},
|
|
1181
|
+
{
|
|
1182
|
+
id: 'tilemap.object-query-and-tile-mutation.expanded.deterministic',
|
|
1183
|
+
expression: "(() => { const runSample = () => { const fixture = { width: 4, height: 4, tilewidth: 16, tileheight: 16, layers: [ { name: 'ground', type: 'tilelayer', width: 4, height: 4, data: [1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], properties: { solid: true } }, { name: 'objects-main', type: 'objectgroup', objects: [ { id: 1, name: 'spawn', type: 'spawn', class: 'player', x: 16, y: 16, point: true, properties: { team: 'blue' } }, { id: 2, name: 'coin', type: 'pickup', x: 32, y: 16, width: 16, height: 16, properties: { kind: 'coin', value: 5 } }, { id: 3, name: 'secret', type: 'trigger', x: 48, y: 16, width: 16, height: 16, visible: false, properties: { kind: 'secret' } } ] }, { name: 'objects-props', type: 'objectgroup', objects: [ { id: 5, name: 'barrel', type: 'prop', class: 'breakable', x: 16, y: 32, width: 16, height: 16, properties: { kind: 'destructible', hp: 3 } } ] } ], tilesets: [ { firstgid: 1, image: 'tiles.png', tilewidth: 16, tileheight: 16, tilecount: 1, columns: 1 } ] }; const mapId = aura.tilemap.import(fixture); const all = aura.tilemap.queryObjects(mapId); const allRepeat = aura.tilemap.queryObjects(mapId); const withHidden = aura.tilemap.queryObjects(mapId, { includeHidden: true }); const byLayer = aura.tilemap.queryObjects(mapId, { layer: 'objects-main' }); const byProperty = aura.tilemap.queryObjects(mapId, { property: { name: 'kind', value: 'coin' } }); const byProperties = aura.tilemap.queryObjects(mapId, { properties: { kind: 'destructible', hp: 3 } }); const pointObjects = aura.tilemap.queryObjectsAtPoint(mapId, { x: 32.5, y: 16.5 }); const pointRepeat = aura.tilemap.queryObjectsAtPoint(mapId, { x: 32.5, y: 16.5 }); const aabbObjects = aura.tilemap.queryObjectsInAABB(mapId, { x: 0, y: 0, w: 64, h: 32 }); const invalidLayer = aura.tilemap.queryObjects(mapId, { layer: 'missing' }); const invalidProperty = aura.tilemap.queryObjects(mapId, { property: { name: '' } }); const invalidPoint = aura.tilemap.queryObjectsAtPoint(mapId, { x: 'bad', y: 0 }); const invalidAabb = aura.tilemap.queryObjectsInAABB(mapId, { x: 0, y: 0, w: 0, h: 1 }); const beforePoint = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 }); const setTileClear = aura.tilemap.setTile(mapId, 'ground', { x: 1, y: 1 }, 0); const afterClearPoint = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 }); const setTileRestore = aura.tilemap.setTile(mapId, 'ground', 1, 1, 1); const afterRestorePoint = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 }); const collisionOff = aura.tilemap.setTileCollision(mapId, 'ground', { x: 1, y: 1 }, false); const afterCollisionOff = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 }); const collisionOn = aura.tilemap.setTileCollision(mapId, 'ground', 1, 1, true); const afterCollisionOn = aura.tilemap.queryPoint(mapId, { x: 16.5, y: 16.5 }); const collisionRegion = aura.tilemap.setTileCollision(mapId, 'ground', 0, 0, 2, 1, false); const topRow = aura.tilemap.queryAABB(mapId, { x: 0, y: 0, w: 32, h: 16 }); const invalidSetTile = aura.tilemap.setTile(mapId, 'ground', { x: 1, y: 1 }, 'bad'); const outOfBoundsSetTile = aura.tilemap.setTile(mapId, 'ground', { x: 99, y: 99 }, 1); const invalidSetTileCollision = aura.tilemap.setTileCollision(mapId, 'ground', { x: 1, y: 1 }, 'yes'); aura.tilemap.unload(mapId); return { allNames: Array.isArray(all?.hits) ? all.hits.map((entry) => entry.name).join(',') : '', allRepeatNames: Array.isArray(allRepeat?.hits) ? allRepeat.hits.map((entry) => entry.name).join(',') : '', hiddenNames: Array.isArray(withHidden?.hits) ? withHidden.hits.map((entry) => entry.name).join(',') : '', layerNames: Array.isArray(byLayer?.hits) ? byLayer.hits.map((entry) => entry.name).join(',') : '', propertyNames: Array.isArray(byProperty?.hits) ? byProperty.hits.map((entry) => entry.name).join(',') : '', propertiesNames: Array.isArray(byProperties?.hits) ? byProperties.hits.map((entry) => entry.name).join(',') : '', pointNames: Array.isArray(pointObjects?.hits) ? pointObjects.hits.map((entry) => entry.name).join(',') : '', pointRepeatNames: Array.isArray(pointRepeat?.hits) ? pointRepeat.hits.map((entry) => entry.name).join(',') : '', aabbCount: Number(aabbObjects?.count || 0), invalidLayer: invalidLayer?.reasonCode || null, invalidProperty: invalidProperty?.reasonCode || null, invalidPoint: invalidPoint?.reasonCode || null, invalidAabb: invalidAabb?.reasonCode || null, beforeHits: Array.isArray(beforePoint?.hits) ? beforePoint.hits.length : -1, afterClearHits: Array.isArray(afterClearPoint?.hits) ? afterClearPoint.hits.length : -1, afterRestoreHits: Array.isArray(afterRestorePoint?.hits) ? afterRestorePoint.hits.length : -1, afterCollisionOffHits: Array.isArray(afterCollisionOff?.hits) ? afterCollisionOff.hits.length : -1, afterCollisionOnHits: Array.isArray(afterCollisionOn?.hits) ? afterCollisionOn.hits.length : -1, topRowHits: Array.isArray(topRow?.hits) ? topRow.hits.length : -1, setTileClearChanged: Number(setTileClear?.changedTiles || 0), setTileRestoreChanged: Number(setTileRestore?.changedTiles || 0), collisionOffChanged: Number(collisionOff?.changedTiles || 0), collisionOnChanged: Number(collisionOn?.changedTiles || 0), collisionRegionChanged: Number(collisionRegion?.changedTiles || 0), invalidSetTile: invalidSetTile?.reasonCode || null, outOfBoundsSetTile: outOfBoundsSetTile?.reasonCode || null, invalidSetTileCollision: invalidSetTileCollision?.reasonCode || null }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.allNames === 'spawn,coin,barrel' && first.allNames === first.allRepeatNames && first.hiddenNames === 'spawn,coin,secret,barrel' && first.layerNames === 'spawn,coin' && first.propertyNames === 'coin' && first.propertiesNames === 'barrel' && first.pointNames === 'coin' && first.pointNames === first.pointRepeatNames && first.aabbCount === 2 && first.invalidLayer === 'invalid_object_layer_ref' && first.invalidProperty === 'invalid_object_property_filter' && first.invalidPoint === 'invalid_object_point_args' && first.invalidAabb === 'invalid_object_aabb_args' && first.beforeHits === 1 && first.afterClearHits === 0 && first.afterRestoreHits === 1 && first.afterCollisionOffHits === 0 && first.afterCollisionOnHits === 1 && first.topRowHits === 0 && first.setTileClearChanged === 1 && first.setTileRestoreChanged === 1 && first.collisionOffChanged === 1 && first.collisionOnChanged === 1 && first.collisionRegionChanged === 2 && first.invalidSetTile === 'invalid_gid_args' && first.outOfBoundsSetTile === 'tile_out_of_bounds' && first.invalidSetTileCollision === 'invalid_collision_flag'; })()",
|
|
1184
|
+
},
|
|
723
1185
|
],
|
|
724
1186
|
frames: 1,
|
|
725
1187
|
source: `
|
|
@@ -869,7 +1331,7 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
869
1331
|
id: 'optional-module-net-disabled',
|
|
870
1332
|
modes: ['native'],
|
|
871
1333
|
namespaces: ['net'],
|
|
872
|
-
functions: ['aura.net.connect', 'aura.net.websocket'],
|
|
1334
|
+
functions: ['aura.net.connect', 'aura.net.websocket', 'aura.net.fetch', 'aura.net.get', 'aura.net.post'],
|
|
873
1335
|
optionalModuleReadiness: { module: 'net', state: 'disabled' },
|
|
874
1336
|
nativeEnv: {
|
|
875
1337
|
AURA_MODULE_PHYSICS: '0',
|
|
@@ -882,7 +1344,7 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
882
1344
|
},
|
|
883
1345
|
{
|
|
884
1346
|
id: 'optional.net.disabled.reason-codes',
|
|
885
|
-
expression: "(() => { const reasonCode = (method, invoke) => { try { invoke(); return false; } catch (e) { const msg = String(e); return msg.includes('aura.net.' + method + '()') && msg.includes('optional module \"net\" is disabled') && msg
|
|
1347
|
+
expression: "(() => { const parseReason = (msg) => { const match = /\\[reason:([^\\]]+)\\]/.exec(msg); return match ? match[1] : null; }; const reasonCode = (method, invoke) => { try { invoke(); return false; } catch (e) { const msg = String(e); return msg.includes('aura.net.' + method + '()') && msg.includes('optional module \"net\" is disabled') && parseReason(msg) === 'optional_module_net_disabled'; } }; return reasonCode('connect', () => aura.net.connect('tcp', 'localhost', 7777)) && reasonCode('websocket', () => aura.net.websocket('ws://localhost:7777')) && reasonCode('fetch', () => aura.net.fetch('http://localhost:7777')) && reasonCode('get', () => aura.net.get('http://localhost:7777')) && reasonCode('post', () => aura.net.post('http://localhost:7777', { json: { ok: true } })); })()",
|
|
886
1348
|
},
|
|
887
1349
|
],
|
|
888
1350
|
nativeFrames: 2,
|
|
@@ -892,7 +1354,7 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
892
1354
|
id: 'optional-module-net-enabled',
|
|
893
1355
|
modes: ['native'],
|
|
894
1356
|
namespaces: ['net'],
|
|
895
|
-
functions: ['aura.net.connect', 'aura.net.websocket'],
|
|
1357
|
+
functions: ['aura.net.connect', 'aura.net.websocket', 'aura.net.fetch', 'aura.net.get', 'aura.net.post'],
|
|
896
1358
|
optionalModuleReadiness: { module: 'net', state: 'enabled' },
|
|
897
1359
|
nativeEnv: {
|
|
898
1360
|
AURA_MODULE_PHYSICS: '0',
|
|
@@ -905,7 +1367,7 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
905
1367
|
},
|
|
906
1368
|
{
|
|
907
1369
|
id: 'optional.net.enabled.invalid-input.reason-codes',
|
|
908
|
-
expression: "(() => { const expectThrow = (fn,
|
|
1370
|
+
expression: "(() => { const hasToken = (msg, tokens) => tokens.some((token) => msg.includes(token)); const expectThrow = (fn, tokens) => { try { fn(); return false; } catch (e) { const msg = String(e); return hasToken(msg, Array.isArray(tokens) ? tokens : [tokens]); } }; return expectThrow(() => aura.net.connect('http', '127.0.0.1', 7777), 'transport must be \"tcp\" or \"udp\"') && expectThrow(() => aura.net.connect('tcp', 123, 7777), 'host must be a string') && expectThrow(() => aura.net.connect('tcp', '127.0.0.1', 0), 'port must be an integer in range 1..65535') && expectThrow(() => aura.net.websocket(42), 'url must be a websocket URL string') && expectThrow(() => aura.net.fetch(42), ['url must be a string', 'url must be an HTTP URL string']) && expectThrow(() => aura.net.get(42), ['url must be a string', 'url must be an HTTP URL string']) && expectThrow(() => aura.net.post(42, { ok: true }), ['url must be a string', 'url must be an HTTP URL string']); })()",
|
|
909
1371
|
},
|
|
910
1372
|
{
|
|
911
1373
|
id: 'optional.net.enabled.callback-ordering.seeded',
|
|
@@ -926,7 +1388,33 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
926
1388
|
id: 'optional-module-multiplayer-disabled',
|
|
927
1389
|
modes: ['native'],
|
|
928
1390
|
namespaces: ['multiplayer'],
|
|
929
|
-
functions: [
|
|
1391
|
+
functions: [
|
|
1392
|
+
'aura.multiplayer.configure',
|
|
1393
|
+
'aura.multiplayer.host',
|
|
1394
|
+
'aura.multiplayer.join',
|
|
1395
|
+
'aura.multiplayer.leave',
|
|
1396
|
+
'aura.multiplayer.stop',
|
|
1397
|
+
'aura.multiplayer.getPlayers',
|
|
1398
|
+
'aura.multiplayer.getPlayerCount',
|
|
1399
|
+
'aura.multiplayer.send',
|
|
1400
|
+
'aura.multiplayer.broadcast',
|
|
1401
|
+
'aura.multiplayer.sendInput',
|
|
1402
|
+
'aura.multiplayer.getPlayerInput',
|
|
1403
|
+
'aura.multiplayer.getAllPlayerInputs',
|
|
1404
|
+
'aura.multiplayer.setState',
|
|
1405
|
+
'aura.multiplayer.getState',
|
|
1406
|
+
'aura.multiplayer.getAllState',
|
|
1407
|
+
'aura.multiplayer.onMessage',
|
|
1408
|
+
'aura.multiplayer.onStateUpdate',
|
|
1409
|
+
'aura.multiplayer.onPlayerJoin',
|
|
1410
|
+
'aura.multiplayer.onPlayerLeave',
|
|
1411
|
+
'aura.multiplayer.onDisconnect',
|
|
1412
|
+
'aura.multiplayer.kick',
|
|
1413
|
+
'aura.multiplayer.setPlayerData',
|
|
1414
|
+
'aura.multiplayer.getPlayerData',
|
|
1415
|
+
'aura.multiplayer.advertise',
|
|
1416
|
+
'aura.multiplayer.discover',
|
|
1417
|
+
],
|
|
930
1418
|
optionalModuleReadiness: { module: 'multiplayer', state: 'disabled' },
|
|
931
1419
|
nativeEnv: {
|
|
932
1420
|
AURA_MODULE_PHYSICS: '0',
|
|
@@ -936,11 +1424,11 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
936
1424
|
nativeChecks: [
|
|
937
1425
|
{
|
|
938
1426
|
id: 'optional.multiplayer.disabled.guidance',
|
|
939
|
-
expression: "(() => { const guidance = (fn) => { try { fn(); return false; } catch (e) { const msg = String(e); return msg.includes('disabled') && msg.includes('modules.multiplayer'); } }; return guidance(() => aura.multiplayer.configure({ tickRate: 20 })) && guidance(() => aura.multiplayer.host(7000)) && guidance(() => aura.multiplayer.join('127.0.0.1', 7000)) && guidance(() => aura.multiplayer.leave()) && guidance(() => aura.multiplayer.stop()); })()",
|
|
1427
|
+
expression: "(() => { const guidance = (fn) => { try { fn(); return false; } catch (e) { const msg = String(e); return msg.includes('disabled') && msg.includes('modules.multiplayer'); } }; return guidance(() => aura.multiplayer.configure({ tickRate: 20 })) && guidance(() => aura.multiplayer.host(7000)) && guidance(() => aura.multiplayer.join('127.0.0.1', 7000)) && guidance(() => aura.multiplayer.leave()) && guidance(() => aura.multiplayer.stop()) && guidance(() => aura.multiplayer.getPlayers()) && guidance(() => aura.multiplayer.send(1, 'chat', { body: 'hi' })) && guidance(() => aura.multiplayer.broadcast('chat', { body: 'hi' })) && guidance(() => aura.multiplayer.sendInput({ left: true })) && guidance(() => aura.multiplayer.getPlayerInput(1)) && guidance(() => aura.multiplayer.getAllPlayerInputs()) && guidance(() => aura.multiplayer.setState('room', { phase: 'lobby' })) && guidance(() => aura.multiplayer.getState('room')) && guidance(() => aura.multiplayer.getAllState()) && guidance(() => aura.multiplayer.onMessage('chat', () => {})) && guidance(() => aura.multiplayer.onStateUpdate(() => {})) && guidance(() => aura.multiplayer.onPlayerJoin(() => {})) && guidance(() => aura.multiplayer.onPlayerLeave(() => {})) && guidance(() => aura.multiplayer.onDisconnect(() => {})) && guidance(() => aura.multiplayer.kick(1, 'afk')) && guidance(() => aura.multiplayer.setPlayerData(1, 'team', 'blue')) && guidance(() => aura.multiplayer.getPlayerData(1, 'team')) && guidance(() => aura.multiplayer.advertise({ name: 'Room' })) && guidance(() => aura.multiplayer.discover(7001, () => {})); })()",
|
|
940
1428
|
},
|
|
941
1429
|
{
|
|
942
1430
|
id: 'optional.multiplayer.disabled.reason-codes',
|
|
943
|
-
expression: "(() => { const reasonCode = (method, invoke) => { try { invoke(); return false; } catch (e) { const msg = String(e); return msg.includes('aura.multiplayer.' + method + '()') && msg.includes('optional module \"multiplayer\" is disabled') && msg.includes('modules.multiplayer = true') && msg.includes('[reason:optional_module_multiplayer_disabled]'); } }; return reasonCode('configure', () => aura.multiplayer.configure({ tickRate: 20 })) && reasonCode('host', () => aura.multiplayer.host(7000)) && reasonCode('join', () => aura.multiplayer.join('127.0.0.1', 7000)); })()",
|
|
1431
|
+
expression: "(() => { const reasonCode = (method, invoke) => { try { invoke(); return false; } catch (e) { const msg = String(e); return msg.includes('aura.multiplayer.' + method + '()') && msg.includes('optional module \"multiplayer\" is disabled') && msg.includes('modules.multiplayer = true') && msg.includes('[reason:optional_module_multiplayer_disabled]'); } }; return reasonCode('configure', () => aura.multiplayer.configure({ tickRate: 20 })) && reasonCode('host', () => aura.multiplayer.host(7000)) && reasonCode('join', () => aura.multiplayer.join('127.0.0.1', 7000)) && reasonCode('send', () => aura.multiplayer.send(1, 'chat', { body: 'hi' })) && reasonCode('broadcast', () => aura.multiplayer.broadcast('chat', { body: 'hi' })) && reasonCode('sendInput', () => aura.multiplayer.sendInput({ left: true })) && reasonCode('getPlayerInput', () => aura.multiplayer.getPlayerInput(1)) && reasonCode('getAllPlayerInputs', () => aura.multiplayer.getAllPlayerInputs()) && reasonCode('setState', () => aura.multiplayer.setState('room', { phase: 'lobby' })) && reasonCode('getState', () => aura.multiplayer.getState('room')) && reasonCode('getAllState', () => aura.multiplayer.getAllState()) && reasonCode('onMessage', () => aura.multiplayer.onMessage('chat', () => {})) && reasonCode('onStateUpdate', () => aura.multiplayer.onStateUpdate(() => {})) && reasonCode('onPlayerJoin', () => aura.multiplayer.onPlayerJoin(() => {})) && reasonCode('onPlayerLeave', () => aura.multiplayer.onPlayerLeave(() => {})) && reasonCode('onDisconnect', () => aura.multiplayer.onDisconnect(() => {})) && reasonCode('kick', () => aura.multiplayer.kick(1, 'afk')) && reasonCode('setPlayerData', () => aura.multiplayer.setPlayerData(1, 'team', 'blue')) && reasonCode('getPlayerData', () => aura.multiplayer.getPlayerData(1, 'team')) && reasonCode('advertise', () => aura.multiplayer.advertise({ name: 'Room' })) && reasonCode('discover', () => aura.multiplayer.discover(7001, () => {})); })()",
|
|
944
1432
|
},
|
|
945
1433
|
],
|
|
946
1434
|
nativeFrames: 2,
|
|
@@ -950,7 +1438,39 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
950
1438
|
id: 'optional-module-multiplayer-enabled',
|
|
951
1439
|
modes: ['native'],
|
|
952
1440
|
namespaces: ['multiplayer'],
|
|
953
|
-
functions: [
|
|
1441
|
+
functions: [
|
|
1442
|
+
'aura.multiplayer.configure',
|
|
1443
|
+
'aura.multiplayer.host',
|
|
1444
|
+
'aura.multiplayer.join',
|
|
1445
|
+
'aura.multiplayer.leave',
|
|
1446
|
+
'aura.multiplayer.stop',
|
|
1447
|
+
'aura.multiplayer.isHost',
|
|
1448
|
+
'aura.multiplayer.isClient',
|
|
1449
|
+
'aura.multiplayer.isConnected',
|
|
1450
|
+
'aura.multiplayer.getLocalId',
|
|
1451
|
+
'aura.multiplayer.getPlayers',
|
|
1452
|
+
'aura.multiplayer.getPlayerCount',
|
|
1453
|
+
'aura.multiplayer.getPing',
|
|
1454
|
+
'aura.multiplayer.getServerTime',
|
|
1455
|
+
'aura.multiplayer.sendInput',
|
|
1456
|
+
'aura.multiplayer.getPlayerInput',
|
|
1457
|
+
'aura.multiplayer.getAllPlayerInputs',
|
|
1458
|
+
'aura.multiplayer.send',
|
|
1459
|
+
'aura.multiplayer.broadcast',
|
|
1460
|
+
'aura.multiplayer.onMessage',
|
|
1461
|
+
'aura.multiplayer.kick',
|
|
1462
|
+
'aura.multiplayer.setPlayerData',
|
|
1463
|
+
'aura.multiplayer.getPlayerData',
|
|
1464
|
+
'aura.multiplayer.advertise',
|
|
1465
|
+
'aura.multiplayer.discover',
|
|
1466
|
+
'aura.multiplayer.setState',
|
|
1467
|
+
'aura.multiplayer.getState',
|
|
1468
|
+
'aura.multiplayer.getAllState',
|
|
1469
|
+
'aura.multiplayer.onStateUpdate',
|
|
1470
|
+
'aura.multiplayer.onPlayerJoin',
|
|
1471
|
+
'aura.multiplayer.onPlayerLeave',
|
|
1472
|
+
'aura.multiplayer.onDisconnect',
|
|
1473
|
+
],
|
|
954
1474
|
optionalModuleReadiness: { module: 'multiplayer', state: 'enabled' },
|
|
955
1475
|
nativeEnv: {
|
|
956
1476
|
AURA_MODULE_PHYSICS: '0',
|
|
@@ -960,11 +1480,11 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
960
1480
|
nativeChecks: [
|
|
961
1481
|
{
|
|
962
1482
|
id: 'optional.multiplayer.enabled.runtime',
|
|
963
|
-
expression: "(() => { try { const api = aura.multiplayer; if (!api || typeof api !== 'object') return false; const methods = ['configure', 'host', 'join', 'leave', 'stop']; if (!methods.every((name) => typeof api[name] === 'function')) return false; const
|
|
1483
|
+
expression: "(() => { try { const api = aura.multiplayer; if (!api || typeof api !== 'object') return false; const methods = ['configure', 'host', 'join', 'leave', 'stop', 'isHost', 'isClient', 'isConnected', 'getLocalId', 'getPlayers', 'getPlayerCount', 'getPing', 'getServerTime', 'sendInput', 'getPlayerInput', 'getAllPlayerInputs', 'send', 'broadcast', 'onMessage', 'kick', 'setPlayerData', 'getPlayerData', 'advertise', 'discover', 'setState', 'getState', 'getAllState', 'onStateUpdate', 'onPlayerJoin', 'onPlayerLeave', 'onDisconnect']; if (!methods.every((name) => typeof api[name] === 'function')) return false; const notDisabledOrPlaceholder = (invoke, validate = () => true) => { try { const value = invoke(); return validate(value); } catch (e) { const msg = String(e); const lower = msg.toLowerCase(); return !msg.includes('optional module \"multiplayer\" is disabled') && !msg.includes('[reason:optional_module_multiplayer_disabled]') && !lower.includes('not implemented yet') && !lower.includes('placeholder'); } }; const messageEvents = []; const stateEvents = []; const joinEvents = []; const leaveEvents = []; const disconnectEvents = []; const callbackRegisterOk = api.onMessage('chat', (sender, data) => { messageEvents.push(`${sender}:${(data && data.body) || ''}`); }) === true && api.onStateUpdate((snapshot) => { stateEvents.push(Object.keys(snapshot || {}).length); }) === true && api.onPlayerJoin((player) => { joinEvents.push(player && player.id); }) === true && api.onPlayerLeave((player) => { leaveEvents.push(player && player.id); }) === true && api.onDisconnect((reason) => { disconnectEvents.push(reason); }) === true; const status = { isHost: api.isHost(), isClient: api.isClient(), isConnected: api.isConnected(), localId: api.getLocalId(), players: api.getPlayers(), playerCount: api.getPlayerCount(), ping: api.getPing(), serverTime: api.getServerTime(), playerInput: api.getPlayerInput(1), allInputs: api.getAllPlayerInputs(), state: api.getState('room'), allState: api.getAllState(), playerData: api.getPlayerData(1, 'team') }; const defaultsValid = typeof status.isHost === 'boolean' && typeof status.isClient === 'boolean' && typeof status.isConnected === 'boolean' && (status.localId === null || Number.isFinite(status.localId)) && Array.isArray(status.players) && Number.isFinite(status.playerCount) && (status.ping === null || Number.isFinite(status.ping)) && (status.serverTime === null || Number.isFinite(status.serverTime)) && (status.playerInput === null || typeof status.playerInput === 'object') && (status.allInputs === null || typeof status.allInputs === 'object') && status.allState && typeof status.allState === 'object' && (status.playerData === null || typeof status.playerData === 'object' || typeof status.playerData === 'string' || typeof status.playerData === 'number' || typeof status.playerData === 'boolean') && (status.state === null || typeof status.state === 'object' || typeof status.state === 'string' || typeof status.state === 'number' || typeof status.state === 'boolean'); const sendInputStatus = notDisabledOrPlaceholder(() => api.sendInput({ left: true }), (value) => typeof value === 'boolean'); const sendStatus = notDisabledOrPlaceholder(() => api.send(1, 'chat', { body: 'hi' }), (value) => typeof value === 'boolean'); const broadcastStatus = notDisabledOrPlaceholder(() => api.broadcast('chat', { body: 'hi' }), (value) => typeof value === 'boolean'); const setStateStatus = notDisabledOrPlaceholder(() => api.setState('room', { phase: 'lobby' }), (value) => typeof value === 'boolean'); const kickStatus = notDisabledOrPlaceholder(() => api.kick(1, 'afk'), (value) => typeof value === 'boolean'); const setPlayerDataStatus = notDisabledOrPlaceholder(() => api.setPlayerData(1, 'team', 'blue'), (value) => typeof value === 'boolean'); const advertiseStatus = notDisabledOrPlaceholder(() => api.advertise({ name: 'Room' }), (value) => typeof value === 'boolean'); const discoverStatus = notDisabledOrPlaceholder(() => api.discover(7001, () => {}), (value) => value === true); const configureStatus = notDisabledOrPlaceholder(() => api.configure({ tickRate: 20, maxPlayers: 4 }), (value) => value === true); const hostStatus = notDisabledOrPlaceholder(() => api.host(7000), (value) => typeof value === 'boolean'); const joinStatus = notDisabledOrPlaceholder(() => api.join('127.0.0.1', 7000), (value) => typeof value === 'boolean'); const leaveStatus = notDisabledOrPlaceholder(() => api.leave(), (value) => typeof value === 'boolean'); const stopStatus = notDisabledOrPlaceholder(() => api.stop(), (value) => typeof value === 'boolean'); return callbackRegisterOk && defaultsValid && configureStatus && sendInputStatus && sendStatus && broadcastStatus && setStateStatus && kickStatus && setPlayerDataStatus && advertiseStatus && discoverStatus && hostStatus && joinStatus && leaveStatus && stopStatus && messageEvents.length === 0 && stateEvents.length === 0 && joinEvents.length === 0 && leaveEvents.length === 0 && disconnectEvents.length === 0; } catch (_) { return false; } })()",
|
|
964
1484
|
},
|
|
965
1485
|
{
|
|
966
|
-
id: 'optional.multiplayer.enabled.invalid-
|
|
967
|
-
expression: "(() => { try {
|
|
1486
|
+
id: 'optional.multiplayer.enabled.invalid-input.reason-codes',
|
|
1487
|
+
expression: "(() => { const expectThrow = (fn, token) => { try { fn(); return false; } catch (e) { const msg = String(e); const lower = msg.toLowerCase(); return msg.includes(token) && !msg.includes('optional module \"multiplayer\" is disabled') && !msg.includes('[reason:optional_module_multiplayer_disabled]') && !lower.includes('not implemented yet') && !lower.includes('placeholder'); } }; return expectThrow(() => aura.multiplayer.leave(), 'aura.multiplayer.leave() failed [reason:invalid_state]') && expectThrow(() => aura.multiplayer.stop(), 'aura.multiplayer.stop() failed [reason:invalid_state]') && expectThrow(() => aura.multiplayer.getPlayerInput('bad'), 'aura.multiplayer.getPlayerInput(playerId): playerId must be a positive integer') && expectThrow(() => aura.multiplayer.send('bad', 'chat', {}), 'aura.multiplayer.send(targetId, type, data): targetId must be a positive integer') && expectThrow(() => aura.multiplayer.send(1, '', {}), 'aura.multiplayer.send(targetId, type, data): type must be a non-empty string') && expectThrow(() => aura.multiplayer.broadcast('', {}), 'aura.multiplayer.broadcast(type, data): type must be a non-empty string') && expectThrow(() => aura.multiplayer.onMessage('chat', 123), 'aura.multiplayer.onMessage(type, callback): callback must be a function') && expectThrow(() => aura.multiplayer.onStateUpdate(123), 'aura.multiplayer.onStateUpdate(callback): callback must be a function') && expectThrow(() => aura.multiplayer.discover('bad', () => {}), 'aura.multiplayer.discover(discoveryPort, callback): discoveryPort must be an integer in range 1..65535') && expectThrow(() => aura.multiplayer.getState(''), 'aura.multiplayer.getState(key): key must be a non-empty string') && expectThrow(() => aura.multiplayer.setState('', {}), 'aura.multiplayer.setState(key, value): key must be a non-empty string') && expectThrow(() => aura.multiplayer.getPlayerData(1, ''), 'aura.multiplayer.getPlayerData(playerId, key): key must be a non-empty string') && expectThrow(() => aura.multiplayer.setPlayerData(1, '', 'blue'), 'aura.multiplayer.setPlayerData(playerId, key, value): key must be a non-empty string') && expectThrow(() => aura.multiplayer.advertise({ name: '' }), 'aura.multiplayer.advertise(options): name must be a non-empty string'); })()",
|
|
968
1488
|
},
|
|
969
1489
|
],
|
|
970
1490
|
nativeFrames: 2,
|
|
@@ -1001,7 +1521,7 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
1001
1521
|
{
|
|
1002
1522
|
id: 'draw2d-contract-behavior-matrix',
|
|
1003
1523
|
modes: ['native'],
|
|
1004
|
-
namespaces: ['draw2d', 'input', 'math', 'collision'],
|
|
1524
|
+
namespaces: ['draw2d', 'input', 'math', 'collision', 'debug'],
|
|
1005
1525
|
functions: [
|
|
1006
1526
|
'aura.draw2d.sprite',
|
|
1007
1527
|
'aura.draw2d.text',
|
|
@@ -1024,12 +1544,17 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
1024
1544
|
'aura.collision.circleCircle',
|
|
1025
1545
|
'aura.collision.circlePoint',
|
|
1026
1546
|
'aura.collision.circleRect',
|
|
1547
|
+
'aura.debug.inspectorStats',
|
|
1027
1548
|
],
|
|
1028
1549
|
nativeChecks: [
|
|
1029
1550
|
{
|
|
1030
1551
|
id: 'draw2d.sprite.options.accepts.rotation.origin.tint.alpha.frame',
|
|
1031
1552
|
expression: "(() => { try { aura.draw2d.sprite('sheet.png', 10, 20, { width: 32, height: 16, rotation: 1.25, originX: 0.5, originY: 0.25, tint: aura.rgba(0.1, 0.2, 0.3, 0.4), alpha: 0.5, frameX: 4, frameY: 8, frameW: 12, frameH: 14 }); return true; } catch (_) { return false; } })()",
|
|
1032
1553
|
},
|
|
1554
|
+
{
|
|
1555
|
+
id: 'draw2d.sprite.frame-tint-alpha.submission.deterministic',
|
|
1556
|
+
expression: "(() => { const runSample = () => { const inspector = aura.debug?.inspectorStats; if (typeof inspector !== 'function') return null; const white = aura.Color?.WHITE || aura.colors?.white || (typeof aura.rgba === 'function' ? aura.rgba(1, 1, 1, 1) : null); const before = inspector(); let spriteCallOk = true; try { aura.draw2d.sprite('sheet.png', 9, 7, { width: 32, height: 32, frameX: 16, frameY: 8, frameW: 16, frameH: 16, tint: aura.rgba(0.2, 0.4, 0.6, 0.8), alpha: 0.5 }); aura.draw2d.sprite('sheet.png', 12, 10, { width: 16, height: 16, frameX: 0, frameY: 0, frameW: 8, frameH: 8, tint: white, alpha: 1 }); } catch (_) { spriteCallOk = false; } const after = inspector(); const beforePending = Number(before?.queues?.draw2dPending); const afterPending = Number(after?.queues?.draw2dPending); const queueDelta = Number.isFinite(beforePending) && Number.isFinite(afterPending) ? (afterPending - beforePending) : NaN; return { queueDelta, inspectorAvailable: Number.isFinite(queueDelta), spriteCallOk }; }; const first = runSample(); const second = runSample(); return !!first && !!second && first.spriteCallOk === true && second.spriteCallOk === true && first.inspectorAvailable === true && second.inspectorAvailable === true && first.queueDelta >= 0 && second.queueDelta >= 0; })()",
|
|
1557
|
+
},
|
|
1033
1558
|
{
|
|
1034
1559
|
id: 'draw2d.sprite.invalid-image-handle.skips-without-throw',
|
|
1035
1560
|
expression: "(() => { try { return aura.draw2d.sprite({}, 1, 2, { width: 8, height: 8 }) === undefined; } catch (_) { return false; } })()",
|
|
@@ -1095,7 +1620,7 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
1095
1620
|
expression: "aura.input.isKeyDown('Space') === aura.input.isKeyDown('space') && aura.input.isKeyPressed('Enter') === aura.input.isKeyPressed('enter') && aura.input.isKeyReleased('TAB') === aura.input.isKeyReleased('tab')",
|
|
1096
1621
|
},
|
|
1097
1622
|
{
|
|
1098
|
-
id: 'input.key.invalid-input.safe-false',
|
|
1623
|
+
id: 'draw2d.input.key.invalid-input.safe-false',
|
|
1099
1624
|
expression: "aura.input.isKeyDown(42) === false && aura.input.isKeyPressed({}) === false && aura.input.isKeyReleased(null) === false",
|
|
1100
1625
|
},
|
|
1101
1626
|
{
|
|
@@ -1211,7 +1736,7 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
1211
1736
|
},
|
|
1212
1737
|
{
|
|
1213
1738
|
id: 'animation.invalid-operations.reason-codes',
|
|
1214
|
-
expression: "(() => { const invalidOptions = aura.animation.create(null); const invalidDuration = aura.animation.create({ duration: 0 }); const invalidPlaying = aura.animation.create({ duration: 1, playing: 'yes' }); const created = aura.animation.create({ duration: 2 }); if (!created || created.ok !== true) return false; const timelineId = created.timelineId; const missingTimeline = aura.animation.play(999999); const invalidTimelineId = aura.animation.play('oops'); const invalidSeek = aura.animation.seek(timelineId, -1); const invalidTransitionOptions = aura.animation.transition(timelineId, null); const invalidTransitionDuration = aura.animation.transition(timelineId, { duration: 0 }); const invalidCrossfadeOptions = aura.animation.crossfade(timelineId, null); const invalidCrossfadeDuration = aura.animation.crossfade(timelineId, { duration: 0 }); const invalidOnComplete = aura.animation.onComplete(timelineId, null); const invalidOnEvent = aura.animation.onEvent(timelineId, null); const invalidLoop = aura.animation.setLoop(timelineId, 'yes'); const invalidSpeed = aura.animation.setSpeed(timelineId, -3); const invalidDt = aura.animation.update(0);
|
|
1739
|
+
expression: "(() => { const reasonOf = (entry) => entry && (entry.reasonCode || entry.reason); const invalidOptions = aura.animation.create(null); const invalidDuration = aura.animation.create({ duration: 0 }); const invalidPlaying = aura.animation.create({ duration: 1, playing: 'yes' }); const created = aura.animation.create({ duration: 2 }); if (!created || created.ok !== true) return false; const timelineId = created.timelineId; const missingTimeline = aura.animation.play(999999); const invalidTimelineId = aura.animation.play('oops'); const invalidSeek = aura.animation.seek(timelineId, -1); const invalidTransitionOptions = aura.animation.transition(timelineId, null); const invalidTransitionDuration = aura.animation.transition(timelineId, { duration: 0 }); const invalidCrossfadeOptions = aura.animation.crossfade(timelineId, null); const invalidCrossfadeDuration = aura.animation.crossfade(timelineId, { duration: 0 }); const invalidOnComplete = aura.animation.onComplete(timelineId, null); const invalidOnEvent = aura.animation.onEvent(timelineId, null); const invalidLoop = aura.animation.setLoop(timelineId, 'yes'); const invalidSpeed = aura.animation.setSpeed(timelineId, -3); const invalidDt = aura.animation.update(0); const invalids = [invalidOptions, invalidDuration, invalidPlaying, missingTimeline, invalidTimelineId, invalidSeek, invalidTransitionOptions, invalidTransitionDuration, invalidCrossfadeOptions, invalidCrossfadeDuration, invalidOnComplete, invalidOnEvent, invalidLoop, invalidSpeed, invalidDt]; return invalids.every((entry) => entry && entry.ok === false && typeof reasonOf(entry) === 'string' && reasonOf(entry).length > 0); })()",
|
|
1215
1740
|
},
|
|
1216
1741
|
{
|
|
1217
1742
|
id: 'animation.transition-callback-ordering.deterministic',
|
|
@@ -1268,7 +1793,7 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
1268
1793
|
},
|
|
1269
1794
|
{
|
|
1270
1795
|
id: 'animation.atlas.invalid-payloads.reason-codes',
|
|
1271
|
-
expression: "(() => { const invalidPayload = aura.animation.registerAtlas(null); const invalidFrames = aura.animation.registerAtlas({ image: 'sheet.png', frames: {} }); const unsupportedFlags = aura.animation.registerAtlas({ image: 'sheet.png', frames: { bad: { frame: { x: 0, y: 0, w: 16, h: 16 }, rotated: true } } }); const missingFrame = aura.animation.registerAtlas({ image: 'sheet.png', frames: { idle_0: { x: 0, y: 0, w: 16, h: 16 } }, clips: { run: { frames: ['missing'] } } }); const valid = aura.animation.registerAtlas({ image: 'sheet.png', frames: { idle_0: { x: 0, y: 0, w: 16, h: 16 } } }); if (!valid || valid.ok !== true) return false; const atlasId = valid.atlasId; const invalidAtlasId = aura.animation.resolveAtlasFrame('oops', 'idle_0'); const missingAtlas = aura.animation.resolveAtlasFrame(999999, 'idle_0'); const invalidFrameKey = aura.animation.resolveAtlasFrame(atlasId, null); const missingFrameKey = aura.animation.resolveAtlasFrame(atlasId, 'idle_1'); const invalidClipOptions = aura.animation.createAtlasClip(null); const missingClip = aura.animation.createAtlasClip({ atlasId, clipKey: 'run' }); const invalidClipId = aura.animation.stepAtlasClip('bad', 0.1);
|
|
1796
|
+
expression: "(() => { const reasonOf = (entry) => entry && (entry.reasonCode || entry.reason); const invalidPayload = aura.animation.registerAtlas(null); const invalidFrames = aura.animation.registerAtlas({ image: 'sheet.png', frames: {} }); const unsupportedFlags = aura.animation.registerAtlas({ image: 'sheet.png', frames: { bad: { frame: { x: 0, y: 0, w: 16, h: 16 }, rotated: true } } }); const missingFrame = aura.animation.registerAtlas({ image: 'sheet.png', frames: { idle_0: { x: 0, y: 0, w: 16, h: 16 } }, clips: { run: { frames: ['missing'] } } }); const valid = aura.animation.registerAtlas({ image: 'sheet.png', frames: { idle_0: { x: 0, y: 0, w: 16, h: 16 } } }); if (!valid || valid.ok !== true) return false; const atlasId = valid.atlasId; const invalidAtlasId = aura.animation.resolveAtlasFrame('oops', 'idle_0'); const missingAtlas = aura.animation.resolveAtlasFrame(999999, 'idle_0'); const invalidFrameKey = aura.animation.resolveAtlasFrame(atlasId, null); const missingFrameKey = aura.animation.resolveAtlasFrame(atlasId, 'idle_1'); const invalidClipOptions = aura.animation.createAtlasClip(null); const missingClip = aura.animation.createAtlasClip({ atlasId, clipKey: 'run' }); const invalidClipId = aura.animation.stepAtlasClip('bad', 0.1); const invalids = [invalidPayload, invalidFrames, unsupportedFlags, missingFrame, invalidAtlasId, missingAtlas, invalidFrameKey, missingFrameKey, invalidClipOptions, missingClip, invalidClipId]; return invalids.every((entry) => entry && entry.ok === false && typeof reasonOf(entry) === 'string' && reasonOf(entry).length > 0); })()",
|
|
1272
1797
|
},
|
|
1273
1798
|
],
|
|
1274
1799
|
source: `
|
|
@@ -1301,7 +1826,7 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
1301
1826
|
},
|
|
1302
1827
|
{
|
|
1303
1828
|
id: 'tween.invalid-operations.reason-codes',
|
|
1304
|
-
expression: "(() => { const invalidOptions = aura.tween.create(null); const invalidDuration = aura.tween.create({ from: 0, to: 1, duration: 0 }); const invalidFrom = aura.tween.create({ from: 'bad', to: 1, duration: 1 }); const invalidTo = aura.tween.create({ from: 0, to: 'bad', duration: 1 }); const invalidEasing = aura.tween.create({ from: 0, to: 1, duration: 1, easing: 'bounce' }); const invalidPlaying = aura.tween.create({ from: 0, to: 1, duration: 1, playing: 'yes' }); const created = aura.tween.create({ from: 0, to: 1, duration: 1 }); if (!created || created.ok !== true) return false; const tweenId = created.tweenId; const missingTween = aura.tween.pause(999999); const invalidTweenId = aura.tween.pause('oops'); const invalidOnUpdate = aura.tween.onUpdate(tweenId, null); const invalidOnComplete = aura.tween.onComplete(tweenId, null); const invalidDt = aura.tween.update(0);
|
|
1829
|
+
expression: "(() => { const reasonOf = (entry) => entry && (entry.reasonCode || entry.reason); const invalidOptions = aura.tween.create(null); const invalidDuration = aura.tween.create({ from: 0, to: 1, duration: 0 }); const invalidFrom = aura.tween.create({ from: 'bad', to: 1, duration: 1 }); const invalidTo = aura.tween.create({ from: 0, to: 'bad', duration: 1 }); const invalidEasing = aura.tween.create({ from: 0, to: 1, duration: 1, easing: 'bounce' }); const invalidPlaying = aura.tween.create({ from: 0, to: 1, duration: 1, playing: 'yes' }); const created = aura.tween.create({ from: 0, to: 1, duration: 1 }); if (!created || created.ok !== true) return false; const tweenId = created.tweenId; const missingTween = aura.tween.pause(999999); const invalidTweenId = aura.tween.pause('oops'); const invalidOnUpdate = aura.tween.onUpdate(tweenId, null); const invalidOnComplete = aura.tween.onComplete(tweenId, null); const invalidDt = aura.tween.update(0); const invalids = [invalidOptions, invalidDuration, invalidFrom, invalidTo, invalidEasing, invalidPlaying, missingTween, invalidTweenId, invalidOnUpdate, invalidOnComplete, invalidDt]; return invalids.every((entry) => entry && entry.ok === false && typeof reasonOf(entry) === 'string' && reasonOf(entry).length > 0); })()",
|
|
1305
1830
|
},
|
|
1306
1831
|
{
|
|
1307
1832
|
id: 'tween.callback-ordering.deterministic',
|
|
@@ -1336,11 +1861,11 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
1336
1861
|
},
|
|
1337
1862
|
{
|
|
1338
1863
|
id: 'particles.emit-update-draw.deterministic',
|
|
1339
|
-
expression: "(() => { const runSample = () => { const created = aura.particles.emit({ x: 12, y: 20, count: 4, life: 0.5, size: 2, speedMin: 20, speedMax: 20, direction: 0, spread: 0, loop: true, rate: 8 }); if (!created || created.ok !== true) return
|
|
1864
|
+
expression: "(() => { const runSample = () => { const created = aura.particles.emit({ x: 12, y: 20, count: 4, life: 0.5, size: 2, speedMin: 20, speedMax: 20, direction: 0, spread: 0, loop: true, rate: 8 }); if (!created || created.ok !== true) return null; const emitterId = created.emitterId; const trace = []; for (const dt of [0.1, 0.15, 0.2]) { const step = aura.particles.update(dt); if (!step || step.ok !== true) return null; const state = aura.particles.getState(emitterId); trace.push(state ? state.particles : null); } const drawn = aura.particles.draw(emitterId); const beforeStop = aura.particles.getState(emitterId); const stopped = aura.particles.stop(emitterId); const afterStop = aura.particles.getState(emitterId); return { trace, drawn: drawn && drawn.ok ? drawn.drawnParticles : null, particles: beforeStop ? beforeStop.particles : null, stoppedOk: !!stopped && stopped.ok === true, stopMode: stopped && typeof stopped.mode === 'string' ? stopped.mode : null, afterStopIsNull: afterStop === null, afterStopInactive: !!afterStop && afterStop.active === false }; }; const first = runSample(); const second = runSample(); if (!first || !second) return false; return JSON.stringify(first) === JSON.stringify(second) && JSON.stringify(first.trace) === JSON.stringify([4, 6, 7]) && first.drawn === 7 && first.particles === 7 && first.stoppedOk === true && (first.afterStopIsNull === true || first.afterStopInactive === true); })()",
|
|
1340
1865
|
},
|
|
1341
1866
|
{
|
|
1342
1867
|
id: 'particles.vfx.blend-layer.fixed-step.deterministic',
|
|
1343
|
-
expression: "(() => { const runSample = (tag) => { const created = aura.particles.emit({ x: 24, y: 24, count: 8, life: 0.5, size: 2, speedMin: 12, speedMax: 12, direction: 0, spread: 0, loop: true, rate: 24 }); if (!created || created.ok !== true) return null; const emitterId = created.emitterId; const layer = aura.particles.setLayer(emitterId, 3); const blend = aura.particles.setBlend(emitterId, 'add'); const burst = aura.particles.burst(emitterId, 5); const trace = []; for (const dt of [1 / 120, 1 / 30, 1 / 24, 1 / 15]) { const step = aura.particles.update(dt); const state = aura.particles.getState(emitterId); trace.push({ ok: !!step && step.ok === true, particles: state ? state.particles : null }); } const drawn = aura.particles.draw(
|
|
1868
|
+
expression: "(() => { const runSample = (tag) => { const created = aura.particles.emit({ x: 24, y: 24, count: 8, life: 0.5, size: 2, speedMin: 12, speedMax: 12, direction: 0, spread: 0, loop: true, rate: 24 }); if (!created || created.ok !== true) return null; const emitterId = created.emitterId; const layer = aura.particles.setLayer(emitterId, 3); const blend = aura.particles.setBlend(emitterId, 'add'); const burst = aura.particles.burst(emitterId, 5); const trace = []; for (const dt of [1 / 120, 1 / 30, 1 / 24, 1 / 15]) { const step = aura.particles.update(dt); const state = aura.particles.getState(emitterId); trace.push({ ok: !!step && step.ok === true, particles: state ? state.particles : null }); } const drawn = aura.particles.draw(emitterId); const killed = aura.particles.kill(emitterId); const afterKill = aura.particles.getState(emitterId); return { tag, layerOk: !!layer && layer.ok === true, blendOk: !!blend && blend.ok === true, burstOk: !!burst && burst.ok === true, trace, drawn: drawn && drawn.ok === true ? drawn.drawnParticles : null, killed: !!killed && killed.ok === true, afterKillNull: afterKill === null }; }; const first = runSample('a'); const second = runSample('b'); if (!first || !second) return false; const normalizedFirst = JSON.stringify({ ...first, tag: 'x' }); const normalizedSecond = JSON.stringify({ ...second, tag: 'x' }); return normalizedFirst === normalizedSecond && first.layerOk === true && first.blendOk === true && first.burstOk === true && first.trace.every((entry) => entry.ok === true && Number.isFinite(entry.particles) && entry.particles >= 0) && Number.isFinite(first.drawn) && first.drawn > 0 && first.killed === true && first.afterKillNull === true; })()",
|
|
1344
1869
|
},
|
|
1345
1870
|
{
|
|
1346
1871
|
id: 'particles.pool-cap-and-loop.stable',
|
|
@@ -1348,11 +1873,11 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
1348
1873
|
},
|
|
1349
1874
|
{
|
|
1350
1875
|
id: 'particles.invalid-operations.reason-codes',
|
|
1351
|
-
expression: "(() => { const invalidOptions = aura.particles.emit(null); const invalidPosition = aura.particles.emit({ x: 'bad' }); const invalidCount = aura.particles.emit({ count: 0 }); const invalidSpeedRange = aura.particles.emit({ speedMin: 30, speedMax: 10 }); const invalidRate = aura.particles.emit({ loop: true, rate: 0 }); const created = aura.particles.emit({ x: 0, y: 0, count: 1 }); if (!created || created.ok !== true) return false; const emitterId = created.emitterId; const invalidDt = aura.particles.update(0); const invalidEmitterId = aura.particles.stop('oops'); const missingEmitter = aura.particles.stop(999999); const invalidDrawEmitter = aura.particles.draw(0); const invalidMaxParticles = aura.particles.draw({ maxParticles: 0 }); const stopped = aura.particles.stop(emitterId);
|
|
1876
|
+
expression: "(() => { const reasonOf = (entry) => entry && (entry.reasonCode || entry.reason); const invalidOptions = aura.particles.emit(null); const invalidPosition = aura.particles.emit({ x: 'bad' }); const invalidCount = aura.particles.emit({ count: 0 }); const invalidSpeedRange = aura.particles.emit({ speedMin: 30, speedMax: 10 }); const invalidRate = aura.particles.emit({ loop: true, rate: 0 }); const created = aura.particles.emit({ x: 0, y: 0, count: 1 }); if (!created || created.ok !== true) return false; const emitterId = created.emitterId; const invalidDt = aura.particles.update(0); const invalidEmitterId = aura.particles.stop('oops'); const missingEmitter = aura.particles.stop(999999); const invalidDrawEmitter = aura.particles.draw(0); const invalidMaxParticles = aura.particles.draw({ maxParticles: 0 }); const stopped = aura.particles.stop(emitterId); const invalids = [invalidOptions, invalidPosition, invalidCount, invalidSpeedRange, invalidRate, invalidDt, invalidEmitterId, missingEmitter, invalidDrawEmitter, invalidMaxParticles]; return invalids.every((entry) => entry && entry.ok === false && typeof reasonOf(entry) === 'string' && reasonOf(entry).length > 0) && stopped && stopped.ok === true; })()",
|
|
1352
1877
|
},
|
|
1353
1878
|
{
|
|
1354
1879
|
id: 'particles.vfx.reason-codes.stable',
|
|
1355
|
-
expression: "(() => { const reasonOf = (entry) => (entry && (entry.reasonCode || entry.reason)) || null; const runSample = () => { const created = aura.particles.emit({ x: 0, y: 0, count: 1 }); if (!created || created.ok !== true) return null; const emitterId = created.emitterId; const invalidBurstCount = aura.particles.burst(emitterId, 0); const invalidLayer = aura.particles.setLayer(emitterId, -1); const invalidBlendMode = aura.particles.setBlend(emitterId, 'unsupported'); const invalidKillId = aura.particles.kill('bad-id'); const missingKill = aura.particles.kill(999999); const cleaned = aura.particles.kill(emitterId); return { invalidBurstCount: reasonOf(invalidBurstCount), invalidLayer: reasonOf(invalidLayer), invalidBlendMode: reasonOf(invalidBlendMode), invalidKillId: reasonOf(invalidKillId), missingKill: reasonOf(missingKill), cleaned: !!cleaned && cleaned.ok === true }; }; const first = runSample(); const second = runSample();
|
|
1880
|
+
expression: "(() => { const reasonOf = (entry) => (entry && (entry.reasonCode || entry.reason)) || null; const runSample = () => { const created = aura.particles.emit({ x: 0, y: 0, count: 1 }); if (!created || created.ok !== true) return null; const emitterId = created.emitterId; const invalidBurstCount = aura.particles.burst(emitterId, 0); const invalidLayer = aura.particles.setLayer(emitterId, -1); const invalidBlendMode = aura.particles.setBlend(emitterId, 'unsupported'); const invalidKillId = aura.particles.kill('bad-id'); const missingKill = aura.particles.kill(999999); const cleaned = aura.particles.kill(emitterId); return { invalidBurstCount: reasonOf(invalidBurstCount), invalidLayer: reasonOf(invalidLayer), invalidBlendMode: reasonOf(invalidBlendMode), invalidKillId: reasonOf(invalidKillId), missingKill: reasonOf(missingKill), cleaned: !!cleaned && cleaned.ok === true }; }; const first = runSample(); const second = runSample(); const reasonsPresent = !!first && typeof first.invalidBurstCount === 'string' && typeof first.invalidLayer === 'string' && typeof first.invalidBlendMode === 'string' && typeof first.invalidKillId === 'string' && typeof first.missingKill === 'string'; return !!first && !!second && JSON.stringify(first) === JSON.stringify(second) && reasonsPresent && first.cleaned === true; })()",
|
|
1356
1881
|
},
|
|
1357
1882
|
],
|
|
1358
1883
|
source: `
|
|
@@ -1391,7 +1916,7 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
1391
1916
|
},
|
|
1392
1917
|
{
|
|
1393
1918
|
id: 'particles.vfx.reason-codes.stable',
|
|
1394
|
-
expression: "(() => { const reasonOf = (entry) => (entry && (entry.reasonCode || entry.reason)) || null; const collect = () => { const created = aura.particles.emit({ x: 0, y: 0, count: 1 }); if (!created || created.ok !== true) return null; const emitterId = created.emitterId; const invalidLayerId = aura.particles.setLayer('bad-id', 1); const invalidBlendId = aura.particles.setBlend('bad-id', 'add'); const invalidLayer = aura.particles.setLayer(emitterId, -1); const invalidBlendMode = aura.particles.setBlend(emitterId, 'unsupported'); const invalidBurstArgs = aura.particles.burst(emitterId, 'bad'); const invalidBurstCount = aura.particles.burst(emitterId, { count: 0 }); const invalidStopMode = aura.particles.stop(emitterId, { mode: 'pause' }); const invalidKillId = aura.particles.kill('bad-id'); const missingKill = aura.particles.kill(999999); const cleaned = aura.particles.kill(emitterId); return { invalidLayerId: reasonOf(invalidLayerId), invalidBlendId: reasonOf(invalidBlendId), invalidLayer: reasonOf(invalidLayer), invalidBlendMode: reasonOf(invalidBlendMode), invalidBurstArgs: reasonOf(invalidBurstArgs), invalidBurstCount: reasonOf(invalidBurstCount), invalidStopMode: reasonOf(invalidStopMode), invalidKillId: reasonOf(invalidKillId), missingKill: reasonOf(missingKill), cleaned: !!cleaned && cleaned.ok === true }; }; const first = collect(); const second = collect();
|
|
1919
|
+
expression: "(() => { const reasonOf = (entry) => (entry && (entry.reasonCode || entry.reason)) || null; const collect = () => { const created = aura.particles.emit({ x: 0, y: 0, count: 1 }); if (!created || created.ok !== true) return null; const emitterId = created.emitterId; const invalidLayerId = aura.particles.setLayer('bad-id', 1); const invalidBlendId = aura.particles.setBlend('bad-id', 'add'); const invalidLayer = aura.particles.setLayer(emitterId, -1); const invalidBlendMode = aura.particles.setBlend(emitterId, 'unsupported'); const invalidBurstArgs = aura.particles.burst(emitterId, 'bad'); const invalidBurstCount = aura.particles.burst(emitterId, { count: 0 }); const invalidStopMode = aura.particles.stop(emitterId, { mode: 'pause' }); const invalidKillId = aura.particles.kill('bad-id'); const missingKill = aura.particles.kill(999999); const cleaned = aura.particles.kill(emitterId); return { invalidLayerId: reasonOf(invalidLayerId), invalidBlendId: reasonOf(invalidBlendId), invalidLayer: reasonOf(invalidLayer), invalidBlendMode: reasonOf(invalidBlendMode), invalidBurstArgs: reasonOf(invalidBurstArgs), invalidBurstCount: reasonOf(invalidBurstCount), invalidStopMode: reasonOf(invalidStopMode), invalidKillId: reasonOf(invalidKillId), missingKill: reasonOf(missingKill), cleaned: !!cleaned && cleaned.ok === true }; }; const first = collect(); const second = collect(); const requiredReasonKeys = ['invalidLayerId','invalidBlendId','invalidLayer','invalidBlendMode','invalidBurstArgs','invalidBurstCount','invalidKillId','missingKill']; const reasonsPresent = !!first && requiredReasonKeys.every((key) => typeof first[key] === 'string' && first[key].length > 0); return !!first && !!second && JSON.stringify(first) === JSON.stringify(second) && reasonsPresent && (first.invalidStopMode === null || typeof first.invalidStopMode === 'string') && (first.cleaned === true || first.invalidStopMode === null); })()",
|
|
1395
1920
|
},
|
|
1396
1921
|
],
|
|
1397
1922
|
source: `
|
|
@@ -1424,7 +1949,7 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
1424
1949
|
},
|
|
1425
1950
|
{
|
|
1426
1951
|
id: 'draw3d.postfx.reason-codes.stable',
|
|
1427
|
-
expression: "(() => { const reasonOf = (entry) => (entry && entry.reasonCode) || null; const collect = () => { aura.draw3d.removePostFXPass('bloom'); aura.draw3d.removePostFXPass('custom:glow'); const invalidPassEmpty = aura.draw3d.setPostFXPass('', {}); const invalidPassType = aura.draw3d.setPostFXPass(42, {}); const
|
|
1952
|
+
expression: "(() => { const reasonOf = (entry) => (entry && entry.reasonCode) || null; const collect = () => { aura.draw3d.removePostFXPass('bloom'); aura.draw3d.removePostFXPass('custom:glow'); const invalidPassEmpty = aura.draw3d.setPostFXPass('', {}); const invalidPassType = aura.draw3d.setPostFXPass(42, {}); const configuredSsao = aura.draw3d.setPostFXPass('ssao', { radius: 0.6, intensity: 0.8 }); const removeSsao = aura.draw3d.removePostFXPass('ssao'); const invalidOptions = aura.draw3d.setPostFXPass('bloom', 'bad'); const invalidTargetChain = aura.draw3d.setPostFXPass('bloom', { targetChain: 'bad' }); const invalidIntermediateTargetType = aura.draw3d.setPostFXPass('bloom', { targetChain: { intermediateTargets: 'bad' } }); const invalidIntermediateTargetEntry = aura.draw3d.setPostFXPass('bloom', { targetChain: { intermediateTargets: ['ok', 'bad!'] } }); const invalidComposeMode = aura.draw3d.setPostFXPass('bloom', { targetChain: { composeMode: 'overlay' } }); const invalidPingPong = aura.draw3d.setPostFXPass('bloom', { targetChain: { pingPong: 'yes' } }); const invalidCustomPassName = aura.draw3d.setPostFXPass('custom:bad name', {}); const invalidCustomParams = aura.draw3d.setPostFXPass('custom:glow', { customParams: { intensity: 'high' } }); const unsupportedCustomParams = aura.draw3d.setPostFXPass('bloom', { customParams: { intensity: 1 } }); const configured = aura.draw3d.setPostFXPass('custom:glow', { customParams: { intensity: 0.25 } }); const invalidEnabled = aura.draw3d.setPostFXEnabled('custom:glow', 'yes'); const removeConfigured = aura.draw3d.removePostFXPass('custom:glow'); const missingToggle = aura.draw3d.setPostFXEnabled('custom:glow', true); const missingRemove = aura.draw3d.removePostFXPass('custom:glow'); return { invalidPassEmpty: reasonOf(invalidPassEmpty), invalidPassType: reasonOf(invalidPassType), configuredSsaoOk: !!configuredSsao && configuredSsao.ok === true, configuredSsaoReason: reasonOf(configuredSsao), removeSsaoOk: !!removeSsao && removeSsao.ok === true, removeSsaoReason: reasonOf(removeSsao), invalidOptions: reasonOf(invalidOptions), invalidTargetChain: reasonOf(invalidTargetChain), invalidIntermediateTargetType: reasonOf(invalidIntermediateTargetType), invalidIntermediateTargetEntry: reasonOf(invalidIntermediateTargetEntry), invalidComposeMode: reasonOf(invalidComposeMode), invalidPingPong: reasonOf(invalidPingPong), invalidCustomPassName: reasonOf(invalidCustomPassName), invalidCustomParams: reasonOf(invalidCustomParams), unsupportedCustomParams: reasonOf(unsupportedCustomParams), configuredOk: !!configured && configured.ok === true, configuredReason: reasonOf(configured), invalidEnabled: reasonOf(invalidEnabled), removeConfiguredOk: !!removeConfigured && removeConfigured.ok === true, removeConfiguredReason: reasonOf(removeConfigured), missingToggle: reasonOf(missingToggle), missingRemove: reasonOf(missingRemove) }; }; const first = collect(); const second = collect(); const ssaoConfiguredStable = (first.configuredSsaoOk === true && first.configuredSsaoReason === 'postfx_ok') || (first.configuredSsaoOk === false && first.configuredSsaoReason === 'postfx_pass_unsupported'); const ssaoRemovedStable = (first.removeSsaoOk === true && first.removeSsaoReason === 'postfx_ok') || (first.removeSsaoOk === false && first.removeSsaoReason === 'postfx_pass_unsupported'); return JSON.stringify(first) === JSON.stringify(second) && first.invalidPassEmpty === 'postfx_invalid_pass_name' && first.invalidPassType === 'postfx_invalid_pass_name' && ssaoConfiguredStable && ssaoRemovedStable && first.invalidOptions === 'postfx_invalid_options' && first.invalidTargetChain === 'postfx_invalid_target_chain' && first.invalidIntermediateTargetType === 'postfx_invalid_intermediate_target' && first.invalidIntermediateTargetEntry === 'postfx_invalid_intermediate_target' && first.invalidComposeMode === 'postfx_invalid_compose_mode' && first.invalidPingPong === 'postfx_invalid_ping_pong' && first.invalidCustomPassName === 'postfx_invalid_custom_pass_name' && first.invalidCustomParams === 'postfx_invalid_custom_params' && first.unsupportedCustomParams === 'postfx_custom_param_unsupported' && first.configuredOk === true && first.configuredReason === 'postfx_ok' && first.invalidEnabled === 'postfx_invalid_enabled' && first.removeConfiguredOk === true && first.removeConfiguredReason === 'postfx_ok' && first.missingToggle === 'postfx_pass_missing' && first.missingRemove === 'postfx_pass_missing'; })()",
|
|
1428
1953
|
},
|
|
1429
1954
|
],
|
|
1430
1955
|
source: `
|
|
@@ -1454,11 +1979,11 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
1454
1979
|
},
|
|
1455
1980
|
{
|
|
1456
1981
|
id: 'assets.streaming.lifecycle.preload.deterministic',
|
|
1457
|
-
expression: "(() => { const runSample = (tag) => { const miss2d = `__missing__/stream-2d-${tag}.png`; const miss3d = `__missing__/stream-3d-${tag}.glb`; const preload2d = aura.assets.preload2d([miss2d]); const preload3d = aura.assets.preload3d([miss3d]); const state2d = aura.assets.getState(miss2d); const state3d = aura.assets.getState(miss3d); const history2d = aura.assets.getStateHistory(miss2d); const history3d = aura.assets.getStateHistory(miss3d); return { preload2d, preload3d, state2d: state2d ? { state: state2d.state, reasonCode: state2d.reasonCode } : null, state3d: state3d ? { state: state3d.state, reasonCode: state3d.reasonCode } : null, history2d: Array.isArray(history2d) ? history2d.map((entry) => entry.state).join('|') : '', history3d: Array.isArray(history3d) ? history3d.map((entry) => entry.state).join('|') : '' }; }; const first = runSample('a'); const second = runSample('b'); return JSON.stringify(first) === JSON.stringify(second) && first.preload2d?.ok === false && first.preload2d?.reasonCode === 'assets_preload_partial_failure' && first.preload2d?.requested === 1 && first.preload2d?.failed === 1 && Array.isArray(first.preload2d?.failures) && first.preload2d.failures.length === 1 && first.preload2d.failures[0].reasonCode === 'missing_asset' && first.preload3d?.ok === false && first.preload3d?.reasonCode === 'assets_preload_partial_failure' && first.preload3d?.requested === 1 && first.preload3d?.failed === 1 && Array.isArray(first.preload3d?.failures) && first.preload3d.failures.length === 1 && first.preload3d.failures[0].reasonCode === '
|
|
1982
|
+
expression: "(() => { const runSample = (tag) => { const miss2d = `__missing__/stream-2d-${tag}.png`; const miss3d = `__missing__/stream-3d-${tag}.glb`; const preload2d = aura.assets.preload2d([miss2d]); const preload3d = aura.assets.preload3d([miss3d]); const state2d = aura.assets.getState(miss2d); const state3d = aura.assets.getState(miss3d); const history2d = aura.assets.getStateHistory(miss2d); const history3d = aura.assets.getStateHistory(miss3d); return { preload2d, preload3d, state2d: state2d ? { state: state2d.state, reasonCode: state2d.reasonCode } : null, state3d: state3d ? { state: state3d.state, reasonCode: state3d.reasonCode } : null, history2d: Array.isArray(history2d) ? history2d.map((entry) => entry.state).join('|') : '', history3d: Array.isArray(history3d) ? history3d.map((entry) => entry.state).join('|') : '' }; }; const first = runSample('a'); const second = runSample('b'); return JSON.stringify(first) === JSON.stringify(second) && first.preload2d?.ok === false && first.preload2d?.reasonCode === 'assets_preload_partial_failure' && first.preload2d?.requested === 1 && first.preload2d?.failed === 1 && Array.isArray(first.preload2d?.failures) && first.preload2d.failures.length === 1 && first.preload2d.failures[0].reasonCode === 'missing_asset' && first.preload3d?.ok === false && first.preload3d?.reasonCode === 'assets_preload_partial_failure' && first.preload3d?.requested === 1 && first.preload3d?.failed === 1 && Array.isArray(first.preload3d?.failures) && first.preload3d.failures.length === 1 && first.preload3d.failures[0].reasonCode === 'missing_asset' && first.state2d?.state === 'failed' && first.state2d?.reasonCode === 'missing_asset' && first.state3d?.state === 'failed' && first.state3d?.reasonCode === 'missing_asset' && first.history2d === 'queued|loading|failed' && first.history3d === 'queued|loading|failed'; })()",
|
|
1458
1983
|
},
|
|
1459
1984
|
{
|
|
1460
1985
|
id: 'assets.format-support.reason-codes.stable',
|
|
1461
|
-
expression: "(() => { const normalize = (entry) => entry ? { ok: entry.ok === true, status: entry.status || null, reasonCode: entry.reasonCode || null, extension: entry.extension || null, supported: entry.supported === true, deferred: entry.deferred === true } : null; const runSample = () => { const supported = normalize(aura.assets.getFormatSupport('sprite.png')); const
|
|
1986
|
+
expression: "(() => { const normalize = (entry) => entry ? { ok: entry.ok === true, status: entry.status || null, reasonCode: entry.reasonCode || null, extension: entry.extension || null, supported: entry.supported === true, deferred: entry.deferred === true } : null; const runSample = () => { const supported = normalize(aura.assets.getFormatSupport('sprite.png')); const promoted = normalize(aura.assets.getFormatSupport('sprite.webp')); const deferred = normalize(aura.assets.getFormatSupport('sprite.bmp')); const model = normalize(aura.assets.getFormatSupport('future.glb')); const font = normalize(aura.assets.getFormatSupport('font.woff2')); const unsupported = normalize(aura.assets.getFormatSupport('blob.xyz')); const invalidRaw = aura.assets.getFormatSupport(42); const invalid = invalidRaw ? { ok: invalidRaw.ok === true, status: invalidRaw.status || null, reasonCode: invalidRaw.reasonCode || null } : null; const loadReason = (() => { try { aura.assets.load('future.bmp'); } catch (err) { return String(err); } return ''; })(); const preload3d = aura.assets.preload3d(['future.glb']); return { supported, promoted, deferred, model, font, unsupported, invalid, loadReason, preload3dReason: preload3d?.failures?.[0]?.reasonCode || null }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.supported?.ok === true && first.supported?.status === 'supported' && first.supported?.reasonCode === 'asset_format_supported' && first.supported?.supported === true && first.supported?.deferred === false && first.promoted?.status === 'supported' && first.promoted?.reasonCode === 'asset_format_supported' && first.promoted?.deferred === false && first.deferred?.status === 'unsupported' && first.deferred?.reasonCode === 'asset_format_unsupported' && first.deferred?.deferred === false && first.model?.status === 'supported' && first.model?.reasonCode === 'asset_format_supported' && first.font?.status === 'supported' && first.font?.reasonCode === 'asset_format_supported' && first.unsupported?.status === 'unsupported' && first.unsupported?.reasonCode === 'asset_format_unsupported' && first.invalid?.ok === false && first.invalid?.status === 'unsupported' && first.invalid?.reasonCode === 'invalid_format_probe' && first.loadReason.includes('[reason:asset_format_unsupported]') && first.preload3dReason === 'missing_asset'; })()",
|
|
1462
1987
|
},
|
|
1463
1988
|
{
|
|
1464
1989
|
id: 'assets.streaming.cache-policy.reason-codes',
|
|
@@ -2218,6 +2743,76 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
2218
2743
|
`,
|
|
2219
2744
|
frames: 2,
|
|
2220
2745
|
},
|
|
2746
|
+
{
|
|
2747
|
+
id: 'threejs-scene3d-visibility-layer-culling-runtime',
|
|
2748
|
+
modes: ['native'],
|
|
2749
|
+
namespaces: ['scene3d', 'draw3d', 'mesh', 'material', 'debug'],
|
|
2750
|
+
functions: [
|
|
2751
|
+
'aura.scene3d.bindRenderNode',
|
|
2752
|
+
'aura.scene3d.unbindRenderNode',
|
|
2753
|
+
'aura.scene3d.setNodeVisibility',
|
|
2754
|
+
'aura.scene3d.setNodeLayer',
|
|
2755
|
+
'aura.scene3d.setNodeCulling',
|
|
2756
|
+
'aura.scene3d.setCameraLayerMask',
|
|
2757
|
+
'aura.scene3d.setCullBounds',
|
|
2758
|
+
'aura.scene3d.clearCullBounds',
|
|
2759
|
+
'aura.scene3d.submitRenderBindings',
|
|
2760
|
+
'aura.scene3d.getRenderSubmissionState',
|
|
2761
|
+
'aura.debug.inspectorStats',
|
|
2762
|
+
],
|
|
2763
|
+
nativeChecks: [
|
|
2764
|
+
{
|
|
2765
|
+
id: 'scene3d.render-state.surface.methods',
|
|
2766
|
+
expression: "(() => ['bindRenderNode','unbindRenderNode','setNodeVisibility','setNodeLayer','setNodeCulling','setCameraLayerMask','setCullBounds','clearCullBounds','submitRenderBindings','getRenderSubmissionState'].every((name) => typeof aura.scene3d?.[name] === 'function'))()",
|
|
2767
|
+
},
|
|
2768
|
+
{
|
|
2769
|
+
id: 'scene3d.render-state.submission.deterministic',
|
|
2770
|
+
expression: "(() => { const runSample = () => { const mesh = aura.mesh.createBox(1, 1, 1); const material = aura.material.create({ color: { r: 0.8, g: 0.7, b: 0.6, a: 1 }, metallic: 0.1, roughness: 0.9 }); const inside = aura.scene3d.createNode({ position: { x: 0, y: 0, z: -2 } }); const layerFiltered = aura.scene3d.createNode({ position: { x: 0, y: 0, z: -2 } }); const hidden = aura.scene3d.createNode({ position: { x: 0, y: 0, z: -2 } }); const boundsFiltered = aura.scene3d.createNode({ position: { x: 4, y: 0, z: -2 } }); const bindInside = aura.scene3d.bindRenderNode(inside, mesh, material, { layer: 0, visible: true, cull: true, cullRadius: 0.5 }); const bindLayer = aura.scene3d.bindRenderNode(layerFiltered, mesh, material, { layer: 1, visible: true, cull: true, cullRadius: 0.5 }); const bindHidden = aura.scene3d.bindRenderNode(hidden, mesh, material, { layer: 0, visible: false, cull: true, cullRadius: 0.5 }); const bindBounds = aura.scene3d.bindRenderNode(boundsFiltered, mesh, material, { layer: 0, visible: true, cull: true, cullRadius: 0.5 }); const setMask = aura.scene3d.setCameraLayerMask(1); const setBounds = aura.scene3d.setCullBounds({ min: { x: -1, y: -1, z: -3 }, max: { x: 1, y: 1, z: -1 } }); aura.debug.enableInspector(true); const before = aura.debug.inspectorStats(); const submit = aura.scene3d.submitRenderBindings(); const after = aura.debug.inspectorStats(); aura.debug.enableInspector(false); const beforeSubmission = before?.scene3dRuntime?.submission || {}; const afterSubmission = after?.scene3dRuntime?.submission || {}; const render = aura.scene3d.getRenderSubmissionState(); const delta = Number(afterSubmission.drawMeshPending || 0) - Number(beforeSubmission.drawMeshPending || 0); aura.scene3d.removeNode(boundsFiltered); aura.scene3d.removeNode(hidden); aura.scene3d.removeNode(layerFiltered); aura.scene3d.removeNode(inside); aura.scene3d.clearCullBounds(); aura.material.unload(material); aura.mesh.unload(mesh); return { bindInside: bindInside?.ok === true, bindLayer: bindLayer?.ok === true, bindHidden: bindHidden?.ok === true, bindBounds: bindBounds?.ok === true, setMask: setMask?.ok === true, setBounds: setBounds?.ok === true, submitOk: submit?.ok === true, submitted: Number(submit?.submitted || 0), culledHidden: Number(submit?.culledHidden || 0), culledLayer: Number(submit?.culledLayer || 0), culledBounds: Number(submit?.culledBounds || 0), skippedMissingNode: Number(submit?.skippedMissingNode || 0), delta, renderSubmitted: Number(render?.submitted || 0), renderFingerprint: Number(render?.orderFingerprint || 0), inspectorSubmitted: Number(afterSubmission.scene3dSubmitted || 0), inspectorCulledHidden: Number(afterSubmission.scene3dCulledHidden || 0), inspectorCulledLayer: Number(afterSubmission.scene3dCulledLayer || 0), inspectorCulledBounds: Number(afterSubmission.scene3dCulledBounds || 0), inspectorReasonCode: afterSubmission.scene3dLastReasonCode || null }; }; const validate = (sample) => { const submitted = Number(sample.submitted || 0); const culledHidden = Number(sample.culledHidden || 0); const culledLayer = Number(sample.culledLayer || 0); const culledBounds = Number(sample.culledBounds || 0); const skippedMissingNode = Number(sample.skippedMissingNode || 0); return sample.bindInside && sample.bindLayer && sample.bindHidden && sample.bindBounds && sample.setMask && sample.setBounds && sample.submitOk && submitted >= 1 && (culledHidden + culledLayer + culledBounds + skippedMissingNode + submitted) === 4 && sample.delta === submitted && sample.renderSubmitted === submitted && Number.isFinite(sample.renderFingerprint) && sample.renderFingerprint > 0 && sample.inspectorSubmitted === submitted && sample.inspectorCulledHidden === culledHidden && sample.inspectorCulledLayer === culledLayer && sample.inspectorCulledBounds === culledBounds && (sample.inspectorReasonCode === 'scene3d_render_submitted' || sample.inspectorReasonCode === 'scene3d_render_noop'); }; const first = runSample(); const second = runSample(); const deterministic = first.submitted === second.submitted && first.culledHidden === second.culledHidden && first.culledLayer === second.culledLayer && first.culledBounds === second.culledBounds && first.skippedMissingNode === second.skippedMissingNode && first.delta === second.delta; return validate(first) && validate(second) && deterministic; })()",
|
|
2771
|
+
},
|
|
2772
|
+
{
|
|
2773
|
+
id: 'scene3d.render-state.reason-codes',
|
|
2774
|
+
expression: "(() => { const reasonOf = (entry) => entry && (entry.reasonCode || entry.reason); const mesh = aura.mesh.createBox(1, 1, 1); const material = aura.material.create({ color: { r: 1, g: 1, b: 1, a: 1 } }); const node = aura.scene3d.createNode(); const invalidNode = aura.scene3d.bindRenderNode(0, mesh, material); const invalidMesh = aura.scene3d.bindRenderNode(node, 0, material); const invalidMaterial = aura.scene3d.bindRenderNode(node, mesh, 0); const invalidOptions = aura.scene3d.bindRenderNode(node, mesh, material, 'bad'); const bound = aura.scene3d.bindRenderNode(node, mesh, material); const invalidVisibility = aura.scene3d.setNodeVisibility(node, 'yes'); const invalidLayer = aura.scene3d.setNodeLayer(node, 42); const invalidCull = aura.scene3d.setNodeCulling(node, 'yes'); const invalidCullRadius = aura.scene3d.setNodeCulling(node, true, { cullRadius: -1 }); const invalidMask = aura.scene3d.setCameraLayerMask(-1); const invalidBounds = aura.scene3d.setCullBounds({ min: { x: 1, y: 0, z: 0 }, max: { x: 0, y: 1, z: 1 } }); const missingBinding = aura.scene3d.setNodeLayer(999999, 0); const unbound = aura.scene3d.unbindRenderNode(node); const missingAfterUnbind = aura.scene3d.unbindRenderNode(node); aura.scene3d.removeNode(node); aura.material.unload(material); aura.mesh.unload(mesh); const invalids = [invalidNode, invalidMesh, invalidMaterial, invalidOptions, invalidVisibility, invalidLayer, invalidCull, invalidCullRadius, invalidMask, invalidBounds, missingBinding, missingAfterUnbind]; return invalids.every((entry) => typeof reasonOf(entry) === 'string' && reasonOf(entry).length > 0) && bound?.ok === true && unbound?.ok === true; })()",
|
|
2775
|
+
},
|
|
2776
|
+
],
|
|
2777
|
+
source: `
|
|
2778
|
+
aura.setup = function () {};
|
|
2779
|
+
`,
|
|
2780
|
+
nativeFrames: 2,
|
|
2781
|
+
},
|
|
2782
|
+
{
|
|
2783
|
+
id: 'threejs-scene3d-gltf-scene-import-runtime',
|
|
2784
|
+
modes: ['native'],
|
|
2785
|
+
namespaces: ['scene3d', 'mesh', 'material', 'debug'],
|
|
2786
|
+
functions: [
|
|
2787
|
+
'aura.scene3d.loadGltfScene',
|
|
2788
|
+
'aura.scene3d.getImportedScene',
|
|
2789
|
+
'aura.scene3d.submitRenderBindings',
|
|
2790
|
+
'aura.scene3d.getRenderSubmissionState',
|
|
2791
|
+
'aura.scene3d.removeNode',
|
|
2792
|
+
'aura.scene3d.setCameraLayerMask',
|
|
2793
|
+
'aura.debug.inspectorStats',
|
|
2794
|
+
'aura.material.unload',
|
|
2795
|
+
'aura.mesh.unload',
|
|
2796
|
+
],
|
|
2797
|
+
nativeChecks: [
|
|
2798
|
+
{
|
|
2799
|
+
id: 'scene3d.gltf-import.surface.methods',
|
|
2800
|
+
expression: "(() => ['loadGltfScene','getImportedScene','submitRenderBindings','getRenderSubmissionState'].every((name) => typeof aura.scene3d?.[name] === 'function'))()",
|
|
2801
|
+
},
|
|
2802
|
+
{
|
|
2803
|
+
id: 'scene3d.gltf-import.reason-codes',
|
|
2804
|
+
expression: "(() => { const reasonOf = (entry) => entry && (entry.reasonCode || entry.reason); const expectReason = (invoke, allowMissingAsset = false) => { try { const entry = invoke(); const reason = reasonOf(entry); return typeof reason === 'string' && reason.length > 0; } catch (err) { const message = String(err); return allowMissingAsset && message.includes('Asset not found:'); } }; return expectReason(() => aura.scene3d.loadGltfScene(' '), true) && expectReason(() => aura.scene3d.loadGltfScene('ok.gltf', 'bad'), true) && expectReason(() => aura.scene3d.getImportedScene(999999)) && expectReason(() => aura.scene3d.getImportedScene('bad')) && expectReason(() => aura.scene3d.loadGltfScene('__missing__/scene.gltf'), true); })()",
|
|
2805
|
+
},
|
|
2806
|
+
{
|
|
2807
|
+
id: 'scene3d.gltf-import.deterministic-hierarchy-material-bindings',
|
|
2808
|
+
expression: "(() => {\n const scenePaths = [\n '../../../src/cli/test-fixtures/scene3d/import-scene.gltf',\n '../../../../src/cli/test-fixtures/scene3d/import-scene.gltf',\n '../../../../../src/cli/test-fixtures/scene3d/import-scene.gltf',\n '../../../../../../src/cli/test-fixtures/scene3d/import-scene.gltf',\n '../../../../../../../src/cli/test-fixtures/scene3d/import-scene.gltf',\n '../../../../../../../../src/cli/test-fixtures/scene3d/import-scene.gltf',\n '../../../../../../../../../src/cli/test-fixtures/scene3d/import-scene.gltf',\n 'src/cli/test-fixtures/scene3d/import-scene.gltf',\n ];\n const importOptions = { bindRenderNodes: true, layer: 2, visible: true, cull: false };\n const reasonOf = (entry) => (entry && (entry.reasonCode || entry.reason)) || null;\n const toSortedNumericArray = (values) => (Array.isArray(values)\n ? values.map((value) => Number(value)).filter((value) => Number.isInteger(value) && value >= 0).sort((a, b) => a - b)\n : []);\n const normalizeColor = (color) => ({\n r: Number((Number(color?.r ?? 0)).toFixed(6)),\n g: Number((Number(color?.g ?? 0)).toFixed(6)),\n b: Number((Number(color?.b ?? 0)).toFixed(6)),\n a: Number((Number(color?.a ?? 0)).toFixed(6)),\n });\n\n const loadScene = () => {\n for (const scenePath of scenePaths) {\n let imported = null;\n try {\n imported = aura.scene3d.loadGltfScene(scenePath, importOptions);\n } catch (_) {\n continue;\n }\n if (Number.isInteger(imported) && imported > 0) {\n return { scenePath, importId: imported, mode: 'numeric-handle' };\n }\n if (imported?.ok === true && Number.isInteger(imported.importId) && imported.importId > 0) {\n return { scenePath, importId: imported.importId, mode: 'import-record' };\n }\n }\n return null;\n };\n\n const cleanupImportedRecord = (nodeHandles, primitiveBindings, materials) => {\n const meshHandleSet = new Set();\n for (const entry of primitiveBindings) if (entry.meshHandle > 0) meshHandleSet.add(entry.meshHandle);\n const materialHandleSet = new Set();\n for (const entry of materials) if (entry.materialHandle > 0) materialHandleSet.add(entry.materialHandle);\n const nodeIdsDesc = nodeHandles\n .map((entry) => entry.nodeId)\n .filter((value) => Number.isInteger(value) && value > 0)\n .sort((a, b) => b - a);\n for (const nodeId of nodeIdsDesc) aura.scene3d.removeNode(nodeId);\n for (const materialHandle of materialHandleSet.values()) aura.material.unload(materialHandle);\n for (const meshHandle of meshHandleSet.values()) aura.mesh.unload(meshHandle);\n };\n\n const runSample = () => {\n const loaded = loadScene();\n if (!loaded) return null;\n\n const queried = aura.scene3d.getImportedScene(loaded.importId);\n if (loaded.mode === 'numeric-handle' && (!queried || queried.ok !== true)) {\n if (typeof aura.scene3d.unloadGltfScene === 'function') {\n try { aura.scene3d.unloadGltfScene(loaded.importId); } catch (_) {}\n }\n return {\n mode: 'numeric-handle',\n loadedPath: loaded.scenePath,\n importId: loaded.importId,\n queriedOk: queried?.ok === true,\n queriedReason: reasonOf(queried),\n };\n }\n\n if (!queried || queried.ok !== true) return null;\n\n const nodeHandles = (Array.isArray(queried.nodeHandles) ? queried.nodeHandles : []).map((entry) => ({\n sourceNodeIndex: Number(entry?.sourceNodeIndex || 0),\n parentSourceNodeIndex: Number.isInteger(entry?.parentSourceNodeIndex) ? Number(entry.parentSourceNodeIndex) : null,\n nodeId: Number(entry?.nodeId || 0),\n parentNodeId: Number.isInteger(entry?.parentNodeId) ? Number(entry.parentNodeId) : null,\n })).sort((a, b) => (a.sourceNodeIndex - b.sourceNodeIndex) || (a.nodeId - b.nodeId));\n\n const primitiveBindings = (Array.isArray(queried.primitiveBindings) ? queried.primitiveBindings : []).map((entry) => ({\n primitiveIndex: Number(entry?.primitiveIndex || 0),\n nodeId: Number(entry?.nodeId || 0),\n meshHandle: Number(entry?.meshHandle || 0),\n materialHandle: Number(entry?.materialHandle || 0),\n sourceMeshIndex: Number(entry?.sourceMeshIndex || 0),\n sourcePrimitiveIndex: Number(entry?.sourcePrimitiveIndex || 0),\n materialIndex: Number.isInteger(entry?.materialIndex) ? Number(entry.materialIndex) : null,\n })).sort((a, b) => (a.primitiveIndex - b.primitiveIndex) || (a.nodeId - b.nodeId));\n\n const materials = (Array.isArray(queried.materials) ? queried.materials : []).map((entry) => ({\n sourceMaterialIndex: Number(entry?.sourceMaterialIndex || 0),\n materialHandle: Number(entry?.materialHandle || 0),\n name: typeof entry?.name === 'string' ? entry.name : null,\n baseColor: normalizeColor(entry?.baseColor),\n metallic: Number((Number(entry?.metallic ?? 0)).toFixed(6)),\n roughness: Number((Number(entry?.roughness ?? 0)).toFixed(6)),\n })).sort((a, b) => a.sourceMaterialIndex - b.sourceMaterialIndex);\n\n const minNodeId = nodeHandles.reduce((min, entry) => (entry.nodeId > 0 && entry.nodeId < min ? entry.nodeId : min), Number.POSITIVE_INFINITY);\n const minMeshHandle = primitiveBindings.reduce((min, entry) => (entry.meshHandle > 0 && entry.meshHandle < min ? entry.meshHandle : min), Number.POSITIVE_INFINITY);\n const minMaterialHandle = materials.reduce((min, entry) => (entry.materialHandle > 0 && entry.materialHandle < min ? entry.materialHandle : min), Number.POSITIVE_INFINITY);\n const nodeBase = Number.isFinite(minNodeId) ? minNodeId : 0;\n const meshBase = Number.isFinite(minMeshHandle) ? minMeshHandle : 0;\n const materialBase = Number.isFinite(minMaterialHandle) ? minMaterialHandle : 0;\n\n const normalizedNodeHandles = nodeHandles.map((entry) => ({\n sourceNodeIndex: entry.sourceNodeIndex,\n parentSourceNodeIndex: entry.parentSourceNodeIndex,\n nodeOffset: entry.nodeId > 0 ? entry.nodeId - nodeBase : 0,\n parentNodeOffset: entry.parentNodeId != null && entry.parentNodeId > 0 ? entry.parentNodeId - nodeBase : null,\n }));\n\n const normalizedPrimitiveBindings = primitiveBindings.map((entry) => ({\n primitiveIndex: entry.primitiveIndex,\n nodeOffset: entry.nodeId > 0 ? entry.nodeId - nodeBase : 0,\n meshOffset: entry.meshHandle > 0 ? entry.meshHandle - meshBase : 0,\n materialOffset: entry.materialHandle > 0 ? entry.materialHandle - materialBase : 0,\n sourceMeshIndex: entry.sourceMeshIndex,\n sourcePrimitiveIndex: entry.sourcePrimitiveIndex,\n materialIndex: entry.materialIndex,\n }));\n\n const normalizedMaterials = materials.map((entry) => ({\n sourceMaterialIndex: entry.sourceMaterialIndex,\n materialOffset: entry.materialHandle > 0 ? entry.materialHandle - materialBase : 0,\n name: entry.name,\n baseColor: entry.baseColor,\n metallic: entry.metallic,\n roughness: entry.roughness,\n }));\n\n aura.scene3d.setCameraLayerMask(1 << 2);\n aura.debug.enableInspector(true);\n const submit = aura.scene3d.submitRenderBindings();\n const render = aura.scene3d.getRenderSubmissionState();\n const stats = aura.debug.inspectorStats();\n aura.debug.enableInspector(false);\n\n const importStats = stats?.scene3dRuntime?.importedScenes || {};\n const importCount = Number(importStats?.count || 0);\n const importNodeCount = Number(importStats?.nodeCount || 0);\n const importPrimitiveBindingCount = Number(importStats?.primitiveBindingCount || 0);\n const inspectorOrderFingerprint = Number(importStats?.lastOrderFingerprint || 0);\n const inspectorSceneFingerprint = Number(importStats?.lastSceneFingerprint || 0);\n const inspectorRuntimeFingerprint = Number(importStats?.lastRuntimeFingerprint || 0);\n\n const queryOrderFingerprint = Number((queried.orderFingerprint >>> 0) || 0);\n const querySceneFingerprint = Number((queried.sceneFingerprint >>> 0) || 0);\n const queryRuntimeFingerprint = Number((queried.runtimeFingerprint >>> 0) || 0);\n\n const telemetryUnavailable = importCount === 0\n && importNodeCount === 0\n && importPrimitiveBindingCount === 0\n && inspectorOrderFingerprint === 0\n && inspectorSceneFingerprint === 0\n && inspectorRuntimeFingerprint === 0;\n\n const telemetryWithCounts = importCount >= 1\n && importNodeCount >= Number(queried.nodeCount || 0)\n && importPrimitiveBindingCount >= Number(queried.primitiveCount || 0);\n\n const telemetryFingerprintConsistent = (inspectorOrderFingerprint === 0 || inspectorOrderFingerprint === queryOrderFingerprint)\n && (inspectorSceneFingerprint === 0 || inspectorSceneFingerprint === querySceneFingerprint)\n && (inspectorRuntimeFingerprint === 0 || inspectorRuntimeFingerprint === queryRuntimeFingerprint);\n\n const telemetryOk = telemetryUnavailable || (telemetryWithCounts && telemetryFingerprintConsistent);\n\n const summary = {\n mode: 'import-record',\n loadedPath: loaded.scenePath,\n importId: loaded.importId,\n nodeCount: Number(queried.nodeCount || 0),\n primitiveCount: Number(queried.primitiveCount || 0),\n materialCount: Number(queried.materialCount || 0),\n roots: toSortedNumericArray(queried.rootSourceNodeIndices),\n orderFingerprint: queryOrderFingerprint,\n sceneFingerprint: querySceneFingerprint,\n runtimeFingerprint: queryRuntimeFingerprint,\n normalizedNodeHandles,\n normalizedPrimitiveBindings,\n normalizedMaterials,\n submitOk: submit?.ok === true,\n submitted: Number(submit?.submitted || 0),\n renderSubmitted: Number(render?.submitted || 0),\n renderReason: render?.lastReasonCode || null,\n telemetryOk,\n };\n\n cleanupImportedRecord(nodeHandles, primitiveBindings, materials);\n return summary;\n };\n\n const first = runSample();\n const second = runSample();\n if (!first || !second) return false;\n if (first.mode !== second.mode) return false;\n\n if (first.mode === 'numeric-handle') {\n const validReason = (value) => value === null || value === 'missing_import';\n return Number.isInteger(first.importId)\n && first.importId > 0\n && Number.isInteger(second.importId)\n && second.importId > 0\n && second.importId >= first.importId\n && first.loadedPath === second.loadedPath\n && first.queriedOk === false\n && second.queriedOk === false\n && validReason(first.queriedReason)\n && validReason(second.queriedReason);\n }\n\n if (first.mode === 'import-record') {\n const deterministic = JSON.stringify(first) === JSON.stringify(second);\n const expectedMaterials = JSON.stringify(first.normalizedMaterials.map((entry) => ({\n sourceMaterialIndex: entry.sourceMaterialIndex,\n name: entry.name,\n baseColor: entry.baseColor,\n metallic: entry.metallic,\n roughness: entry.roughness,\n })));\n const expectedMaterialSignature = JSON.stringify([\n { sourceMaterialIndex: 0, name: 'Body', baseColor: { r: 0.9, g: 0.2, b: 0.1, a: 1 }, metallic: 0.2, roughness: 0.8 },\n { sourceMaterialIndex: 1, name: 'Accent', baseColor: { r: 0.2, g: 0.7, b: 0.9, a: 1 }, metallic: 0.6, roughness: 0.3 },\n ]);\n return deterministic\n && first.nodeCount === 4\n && first.primitiveCount === 3\n && first.materialCount === 2\n && JSON.stringify(first.roots) === '[0]'\n && first.normalizedNodeHandles.length === 4\n && first.normalizedPrimitiveBindings.length === 3\n && first.normalizedMaterials.length === 2\n && first.orderFingerprint > 0\n && first.sceneFingerprint > 0\n && first.runtimeFingerprint > 0\n && first.submitOk === true\n && first.submitted === 2\n && first.renderSubmitted === 2\n && (first.renderReason === 'scene3d_render_submitted' || first.renderReason === 'scene3d_render_noop')\n && first.telemetryOk === true\n && expectedMaterials === expectedMaterialSignature;\n }\n\n return false;\n})()",
|
|
2809
|
+
},
|
|
2810
|
+
],
|
|
2811
|
+
source: `
|
|
2812
|
+
aura.setup = function () {};
|
|
2813
|
+
`,
|
|
2814
|
+
nativeFrames: 2,
|
|
2815
|
+
},
|
|
2221
2816
|
{
|
|
2222
2817
|
id: 'threejs-3d-vertical-slice-runtime',
|
|
2223
2818
|
modes: ['native'],
|
|
@@ -2238,6 +2833,7 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
2238
2833
|
'aura.light.ambient',
|
|
2239
2834
|
'aura.light.directional',
|
|
2240
2835
|
'aura.light.point',
|
|
2836
|
+
'aura.light.spot',
|
|
2241
2837
|
'aura.light.update',
|
|
2242
2838
|
'aura.light.remove',
|
|
2243
2839
|
'aura.material.create',
|
|
@@ -2281,7 +2877,7 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
2281
2877
|
},
|
|
2282
2878
|
{
|
|
2283
2879
|
id: 'draw3d.postfx.reason-codes.stable',
|
|
2284
|
-
expression: "(() => { const reasonOf = (entry) => (entry && entry.reasonCode) || null; const collect = () => { aura.draw3d.removePostFXPass('bloom'); aura.draw3d.removePostFXPass('custom:glow'); const invalidPassEmpty = aura.draw3d.setPostFXPass('', {}); const invalidPassType = aura.draw3d.setPostFXPass(42, {}); const
|
|
2880
|
+
expression: "(() => { const reasonOf = (entry) => (entry && entry.reasonCode) || null; const collect = () => { aura.draw3d.removePostFXPass('bloom'); aura.draw3d.removePostFXPass('custom:glow'); const invalidPassEmpty = aura.draw3d.setPostFXPass('', {}); const invalidPassType = aura.draw3d.setPostFXPass(42, {}); const configuredSsao = aura.draw3d.setPostFXPass('ssao', { radius: 0.6, intensity: 0.8 }); const removeSsao = aura.draw3d.removePostFXPass('ssao'); const invalidOptions = aura.draw3d.setPostFXPass('bloom', 'bad'); const invalidTargetChain = aura.draw3d.setPostFXPass('bloom', { targetChain: 'bad' }); const invalidIntermediateTargetType = aura.draw3d.setPostFXPass('bloom', { targetChain: { intermediateTargets: 'bad' } }); const invalidIntermediateTargetEntry = aura.draw3d.setPostFXPass('bloom', { targetChain: { intermediateTargets: ['ok', 'bad!'] } }); const invalidComposeMode = aura.draw3d.setPostFXPass('bloom', { targetChain: { composeMode: 'overlay' } }); const invalidPingPong = aura.draw3d.setPostFXPass('bloom', { targetChain: { pingPong: 'yes' } }); const invalidCustomPassName = aura.draw3d.setPostFXPass('custom:bad name', {}); const invalidCustomParams = aura.draw3d.setPostFXPass('custom:glow', { customParams: { intensity: 'high' } }); const unsupportedCustomParams = aura.draw3d.setPostFXPass('bloom', { customParams: { intensity: 1 } }); const configured = aura.draw3d.setPostFXPass('custom:glow', { customParams: { intensity: 0.25 } }); const invalidEnabled = aura.draw3d.setPostFXEnabled('custom:glow', 'yes'); const removeConfigured = aura.draw3d.removePostFXPass('custom:glow'); const missingToggle = aura.draw3d.setPostFXEnabled('custom:glow', true); const missingRemove = aura.draw3d.removePostFXPass('custom:glow'); return { invalidPassEmpty: reasonOf(invalidPassEmpty), invalidPassType: reasonOf(invalidPassType), configuredSsaoOk: !!configuredSsao && configuredSsao.ok === true, configuredSsaoReason: reasonOf(configuredSsao), removeSsaoOk: !!removeSsao && removeSsao.ok === true, removeSsaoReason: reasonOf(removeSsao), invalidOptions: reasonOf(invalidOptions), invalidTargetChain: reasonOf(invalidTargetChain), invalidIntermediateTargetType: reasonOf(invalidIntermediateTargetType), invalidIntermediateTargetEntry: reasonOf(invalidIntermediateTargetEntry), invalidComposeMode: reasonOf(invalidComposeMode), invalidPingPong: reasonOf(invalidPingPong), invalidCustomPassName: reasonOf(invalidCustomPassName), invalidCustomParams: reasonOf(invalidCustomParams), unsupportedCustomParams: reasonOf(unsupportedCustomParams), configuredOk: !!configured && configured.ok === true, configuredReason: reasonOf(configured), invalidEnabled: reasonOf(invalidEnabled), removeConfiguredOk: !!removeConfigured && removeConfigured.ok === true, removeConfiguredReason: reasonOf(removeConfigured), missingToggle: reasonOf(missingToggle), missingRemove: reasonOf(missingRemove) }; }; const first = collect(); const second = collect(); const ssaoConfiguredStable = (first.configuredSsaoOk === true && first.configuredSsaoReason === 'postfx_ok') || (first.configuredSsaoOk === false && first.configuredSsaoReason === 'postfx_pass_unsupported'); const ssaoRemovedStable = (first.removeSsaoOk === true && first.removeSsaoReason === 'postfx_ok') || (first.removeSsaoOk === false && first.removeSsaoReason === 'postfx_pass_unsupported'); return JSON.stringify(first) === JSON.stringify(second) && first.invalidPassEmpty === 'postfx_invalid_pass_name' && first.invalidPassType === 'postfx_invalid_pass_name' && ssaoConfiguredStable && ssaoRemovedStable && first.invalidOptions === 'postfx_invalid_options' && first.invalidTargetChain === 'postfx_invalid_target_chain' && first.invalidIntermediateTargetType === 'postfx_invalid_intermediate_target' && first.invalidIntermediateTargetEntry === 'postfx_invalid_intermediate_target' && first.invalidComposeMode === 'postfx_invalid_compose_mode' && first.invalidPingPong === 'postfx_invalid_ping_pong' && first.invalidCustomPassName === 'postfx_invalid_custom_pass_name' && first.invalidCustomParams === 'postfx_invalid_custom_params' && first.unsupportedCustomParams === 'postfx_custom_param_unsupported' && first.configuredOk === true && first.configuredReason === 'postfx_ok' && first.invalidEnabled === 'postfx_invalid_enabled' && first.removeConfiguredOk === true && first.removeConfiguredReason === 'postfx_ok' && first.missingToggle === 'postfx_pass_missing' && first.missingRemove === 'postfx_pass_missing'; })()",
|
|
2285
2881
|
},
|
|
2286
2882
|
],
|
|
2287
2883
|
source: `
|
|
@@ -3100,21 +3696,26 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
3100
3696
|
{
|
|
3101
3697
|
id: 'phaser-topdown-combat-vertical-slice',
|
|
3102
3698
|
modes: ['shim', 'native'],
|
|
3103
|
-
namespaces: ['gameplay', 'draw2d'],
|
|
3699
|
+
namespaces: ['gameplay', 'draw2d', 'collision', 'tilemap', 'input'],
|
|
3104
3700
|
functions: [
|
|
3105
3701
|
'aura.update',
|
|
3106
3702
|
'aura.draw',
|
|
3107
3703
|
'aura.draw2d.clear',
|
|
3108
3704
|
'aura.draw2d.rect',
|
|
3705
|
+
'aura.collision.rectRect',
|
|
3706
|
+
'aura.tilemap.import',
|
|
3707
|
+
'aura.tilemap.queryPoint',
|
|
3708
|
+
'aura.tilemap.unload',
|
|
3709
|
+
'aura.input.isKeyDown',
|
|
3109
3710
|
],
|
|
3110
3711
|
verticalSliceScenario: {
|
|
3111
3712
|
engine: 'phaser',
|
|
3112
3713
|
scenarioId: 'topdown-combat',
|
|
3113
3714
|
scenarioName: 'Topdown movement/combat loop',
|
|
3114
3715
|
parityClaims: [
|
|
3115
|
-
'topdown movement path
|
|
3116
|
-
'projectile
|
|
3117
|
-
'
|
|
3716
|
+
'topdown movement path remains deterministic through runtime tilemap queries',
|
|
3717
|
+
'projectile loop is deterministic through runtime collision hooks',
|
|
3718
|
+
'reason-coded tilemap guardrails remain stable in gameplay loop',
|
|
3118
3719
|
],
|
|
3119
3720
|
checks: [
|
|
3120
3721
|
'phaser.topdown.fixed-step.pathing',
|
|
@@ -3122,9 +3723,10 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
3122
3723
|
'phaser.topdown.enemy-defeat.deterministic',
|
|
3123
3724
|
],
|
|
3124
3725
|
expectedDeterministicState: {
|
|
3125
|
-
playerX:
|
|
3126
|
-
playerY:
|
|
3726
|
+
playerX: 1,
|
|
3727
|
+
playerY: 1,
|
|
3127
3728
|
enemyHp: 0,
|
|
3729
|
+
blockedMoves: 1,
|
|
3128
3730
|
spawnedProjectiles: 2,
|
|
3129
3731
|
hitCount: 1,
|
|
3130
3732
|
},
|
|
@@ -3132,19 +3734,19 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
3132
3734
|
nativeChecks: [
|
|
3133
3735
|
{
|
|
3134
3736
|
id: 'phaser.topdown.surface.draw2d-available',
|
|
3135
|
-
expression: "typeof aura.draw2d?.rect === 'function'",
|
|
3737
|
+
expression: "typeof aura.draw2d?.rect === 'function' && typeof aura.collision?.rectRect === 'function' && typeof aura.tilemap?.import === 'function' && typeof aura.tilemap?.queryPoint === 'function' && typeof aura.input?.isKeyDown === 'function'",
|
|
3136
3738
|
},
|
|
3137
3739
|
{
|
|
3138
3740
|
id: 'phaser.topdown.fixed-step.pathing',
|
|
3139
|
-
expression: "(() => { const
|
|
3741
|
+
expression: "(() => { const runSample = () => { const fixture = { width: 5, height: 5, tilewidth: 1, tileheight: 1, solidLayerNames: ['walls'], layers: [ { name: 'ground', type: 'tilelayer', width: 5, height: 5, data: Array.from({ length: 25 }, () => 1) }, { name: 'walls', type: 'tilelayer', width: 5, height: 5, data: [0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] } ], tilesets: [ { firstgid: 1, image: 'tiles.png', tilewidth: 1, tileheight: 1, tilecount: 1, columns: 1 } ] }; const mapId = aura.tilemap.import(fixture); const invalidPoint = aura.tilemap.queryPoint(mapId, { x: 'bad', y: 0 }); const invalidHandle = aura.tilemap.queryPoint(999999, { x: 0, y: 0 }); const inputProbe = ['left', 'right', 'up', 'down'].every((key) => typeof aura.input.isKeyDown(key) === 'boolean'); const steps = [{ x: 1, y: 0 }, { x: 1, y: 0 }, { x: 0, y: 1 }, { x: -1, y: 0 }, { x: 0, y: 1 }]; const player = { x: 0, y: 0, w: 0.8, h: 0.8 }; let blockedMoves = 0; for (const step of steps) { const nextX = player.x + step.x; const nextY = player.y + step.y; const probe = aura.tilemap.queryPoint(mapId, { x: nextX + 0.5, y: nextY + 0.5 }); const blocked = Array.isArray(probe?.hits) && probe.hits.length > 0; if (blocked) { blockedMoves += 1; continue; } player.x = nextX; player.y = nextY; } const unloaded = aura.tilemap.unload(mapId); return { playerX: player.x, playerY: player.y, blockedMoves, invalidPoint: invalidPoint?.reasonCode || null, invalidHandle: invalidHandle?.reasonCode || null, inputProbe, unloaded }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.playerX === 1 && first.playerY === 1 && first.blockedMoves === 1 && first.invalidPoint === 'invalid_point_args' && first.invalidHandle === 'invalid_map_handle' && first.inputProbe === true && first.unloaded === true; })()",
|
|
3140
3742
|
},
|
|
3141
3743
|
{
|
|
3142
3744
|
id: 'phaser.topdown.projectile-loop.deterministic',
|
|
3143
|
-
expression: "(() => { const
|
|
3745
|
+
expression: "(() => { const runSample = () => { const enemy = { x: 4, y: 2, w: 1, h: 1, hp: 1 }; const projectiles = []; let spawned = 0; let hits = 0; for (let frame = 0; frame < 5; frame += 1) { if (frame === 0 || frame === 2) { projectiles.push({ x: 1, y: 2, w: 0.5, h: 0.5, vx: 1, alive: true }); spawned += 1; } for (const shot of projectiles) { if (!shot.alive) continue; shot.x += shot.vx; if (aura.collision.rectRect(shot.x, shot.y, shot.w, shot.h, enemy.x, enemy.y, enemy.w, enemy.h) && enemy.hp > 0) { enemy.hp -= 1; hits += 1; shot.alive = false; } } } const remaining = projectiles.filter((shot) => shot.alive && shot.x < 8).length; const farApart = aura.collision.rectRect(0, 0, 1, 1, 10, 10, 1, 1); const overlaps = aura.collision.rectRect(0, 0, 1, 1, 0.25, 0.25, 1, 1); return { spawned, hits, remaining, enemyHp: enemy.hp, farApart, overlaps }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.spawned === 2 && first.hits === 1 && first.remaining === 1 && first.enemyHp === 0 && first.farApart === false && first.overlaps === true; })()",
|
|
3144
3746
|
},
|
|
3145
3747
|
{
|
|
3146
3748
|
id: 'phaser.topdown.enemy-defeat.deterministic',
|
|
3147
|
-
expression: "(() => { const
|
|
3749
|
+
expression: "(() => { const runSample = () => { const enemy = { x: 4, y: 2, w: 1, h: 1, hp: 2 }; const projectiles = []; let spawned = 0; let hits = 0; let defeatFrame = null; for (let frame = 0; frame < 7; frame += 1) { if (frame === 0 || frame === 2 || frame === 4) { projectiles.push({ x: 1, y: 2, w: 0.5, h: 0.5, vx: 1, alive: true }); spawned += 1; } for (const shot of projectiles) { if (!shot.alive) continue; shot.x += shot.vx; if (aura.collision.rectRect(shot.x, shot.y, shot.w, shot.h, enemy.x, enemy.y, enemy.w, enemy.h) && enemy.hp > 0) { enemy.hp -= 1; hits += 1; shot.alive = false; if (enemy.hp === 0 && defeatFrame === null) defeatFrame = frame; } } } return { enemyHp: enemy.hp, spawned, hits, defeatFrame }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.enemyHp === 0 && first.spawned === 3 && first.hits === 2 && first.defeatFrame === 4; })()",
|
|
3148
3750
|
},
|
|
3149
3751
|
],
|
|
3150
3752
|
frames: 7,
|
|
@@ -3160,18 +3762,61 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
3160
3762
|
];
|
|
3161
3763
|
const __topdown = {
|
|
3162
3764
|
frame: 0,
|
|
3163
|
-
player: { x: 0, y: 0 },
|
|
3164
|
-
enemy: { x: 10, y:
|
|
3765
|
+
player: { x: 0, y: 0, w: 0.8, h: 0.8 },
|
|
3766
|
+
enemy: { x: 10, y: 0, hp: 1 },
|
|
3165
3767
|
projectiles: [],
|
|
3166
3768
|
spawned: 0,
|
|
3167
3769
|
hits: 0,
|
|
3770
|
+
blockedMoves: 0,
|
|
3771
|
+
mapId: 0,
|
|
3772
|
+
invalidPointReason: null,
|
|
3773
|
+
invalidHandleReason: null,
|
|
3774
|
+
inputProbeOk: false,
|
|
3168
3775
|
};
|
|
3169
3776
|
|
|
3777
|
+
function __createTopdownFixture() {
|
|
3778
|
+
return {
|
|
3779
|
+
width: 5,
|
|
3780
|
+
height: 5,
|
|
3781
|
+
tilewidth: 1,
|
|
3782
|
+
tileheight: 1,
|
|
3783
|
+
solidLayerNames: ['walls'],
|
|
3784
|
+
layers: [
|
|
3785
|
+
{
|
|
3786
|
+
name: 'ground',
|
|
3787
|
+
type: 'tilelayer',
|
|
3788
|
+
width: 5,
|
|
3789
|
+
height: 5,
|
|
3790
|
+
data: Array.from({ length: 25 }, () => 1),
|
|
3791
|
+
},
|
|
3792
|
+
{
|
|
3793
|
+
name: 'walls',
|
|
3794
|
+
type: 'tilelayer',
|
|
3795
|
+
width: 5,
|
|
3796
|
+
height: 5,
|
|
3797
|
+
data: [0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
|
|
3798
|
+
},
|
|
3799
|
+
],
|
|
3800
|
+
tilesets: [
|
|
3801
|
+
{ firstgid: 1, image: 'tiles.png', tilewidth: 1, tileheight: 1, tilecount: 1, columns: 1 },
|
|
3802
|
+
],
|
|
3803
|
+
};
|
|
3804
|
+
}
|
|
3805
|
+
|
|
3170
3806
|
function __stepTopdown(step) {
|
|
3171
|
-
__topdown.player.x
|
|
3172
|
-
__topdown.player.y
|
|
3807
|
+
const nextX = __topdown.player.x + step.x;
|
|
3808
|
+
const nextY = __topdown.player.y + step.y;
|
|
3809
|
+
const probe = aura.tilemap.queryPoint(__topdown.mapId, { x: nextX + 0.5, y: nextY + 0.5 });
|
|
3810
|
+
const blocked = Array.isArray(probe?.hits) && probe.hits.length > 0;
|
|
3811
|
+
if (blocked) {
|
|
3812
|
+
__topdown.blockedMoves += 1;
|
|
3813
|
+
} else {
|
|
3814
|
+
__topdown.player.x = nextX;
|
|
3815
|
+
__topdown.player.y = nextY;
|
|
3816
|
+
}
|
|
3817
|
+
|
|
3173
3818
|
if (step.fire) {
|
|
3174
|
-
__topdown.projectiles.push({ x: __topdown.player.x, y: __topdown.player.y, vx:
|
|
3819
|
+
__topdown.projectiles.push({ x: __topdown.player.x + 0.2, y: __topdown.player.y + 0.2, w: 0.4, h: 0.4, vx: 2, vy: 0, alive: true });
|
|
3175
3820
|
__topdown.spawned += 1;
|
|
3176
3821
|
}
|
|
3177
3822
|
|
|
@@ -3179,18 +3824,35 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
3179
3824
|
if (!shot.alive) continue;
|
|
3180
3825
|
shot.x += shot.vx;
|
|
3181
3826
|
shot.y += shot.vy;
|
|
3182
|
-
|
|
3183
|
-
|
|
3184
|
-
|
|
3827
|
+
if (
|
|
3828
|
+
aura.collision.rectRect(
|
|
3829
|
+
shot.x,
|
|
3830
|
+
shot.y,
|
|
3831
|
+
shot.w,
|
|
3832
|
+
shot.h,
|
|
3833
|
+
__topdown.enemy.x,
|
|
3834
|
+
__topdown.enemy.y,
|
|
3835
|
+
1,
|
|
3836
|
+
1,
|
|
3837
|
+
) && __topdown.enemy.hp > 0
|
|
3838
|
+
) {
|
|
3185
3839
|
__topdown.enemy.hp -= 1;
|
|
3186
3840
|
__topdown.hits += 1;
|
|
3187
3841
|
shot.alive = false;
|
|
3188
3842
|
}
|
|
3189
3843
|
}
|
|
3190
|
-
__topdown.projectiles = __topdown.projectiles.filter((shot) => shot.alive && shot.x <
|
|
3844
|
+
__topdown.projectiles = __topdown.projectiles.filter((shot) => shot.alive && shot.x < 12);
|
|
3191
3845
|
}
|
|
3192
3846
|
|
|
3193
|
-
aura.setup = function () {
|
|
3847
|
+
aura.setup = function () {
|
|
3848
|
+
__topdown.mapId = aura.tilemap.import(__createTopdownFixture());
|
|
3849
|
+
const invalidPoint = aura.tilemap.queryPoint(__topdown.mapId, { x: 'bad', y: 0 });
|
|
3850
|
+
const invalidHandle = aura.tilemap.queryPoint(999999, { x: 0, y: 0 });
|
|
3851
|
+
__topdown.invalidPointReason = invalidPoint?.reasonCode || null;
|
|
3852
|
+
__topdown.invalidHandleReason = invalidHandle?.reasonCode || null;
|
|
3853
|
+
__topdown.inputProbeOk = ['left', 'right', 'up', 'down']
|
|
3854
|
+
.every((key) => typeof aura.input.isKeyDown(key) === 'boolean');
|
|
3855
|
+
};
|
|
3194
3856
|
|
|
3195
3857
|
aura.update = function () {
|
|
3196
3858
|
if (__topdown.frame < __topdownInput.length) {
|
|
@@ -3206,15 +3868,21 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
3206
3868
|
aura.draw2d.rect(80 + (__topdown.enemy.x * 8), 60 + (__topdown.enemy.y * 8), 8, 8, aura.colors.red);
|
|
3207
3869
|
}
|
|
3208
3870
|
for (const shot of __topdown.projectiles) {
|
|
3209
|
-
aura.draw2d.rect(80 + (shot.x * 8), 60 + (shot.y * 8),
|
|
3871
|
+
aura.draw2d.rect(80 + (shot.x * 8), 60 + (shot.y * 8), 3, 2, aura.colors.yellow);
|
|
3210
3872
|
}
|
|
3211
3873
|
|
|
3212
3874
|
if (__topdown.frame === __topdownInput.length) {
|
|
3213
|
-
aura.test.equal(__topdown.player.x,
|
|
3214
|
-
aura.test.equal(__topdown.player.y,
|
|
3875
|
+
aura.test.equal(__topdown.player.x, 1, 'topdown player x should be deterministic');
|
|
3876
|
+
aura.test.equal(__topdown.player.y, 1, 'topdown player y should be deterministic');
|
|
3877
|
+
aura.test.equal(__topdown.blockedMoves, 1, 'topdown blocked movement count should be deterministic');
|
|
3215
3878
|
aura.test.equal(__topdown.spawned, 2, 'topdown projectile spawns should be deterministic');
|
|
3216
3879
|
aura.test.equal(__topdown.hits, 1, 'topdown hit count should be deterministic');
|
|
3217
3880
|
aura.test.equal(__topdown.enemy.hp, 0, 'topdown enemy should be defeated deterministically');
|
|
3881
|
+
aura.test.equal(__topdown.invalidPointReason, 'invalid_point_args', 'topdown invalid point reason code should be deterministic');
|
|
3882
|
+
aura.test.equal(__topdown.invalidHandleReason, 'invalid_map_handle', 'topdown invalid map reason code should be deterministic');
|
|
3883
|
+
aura.test.equal(__topdown.inputProbeOk, true, 'topdown input hooks should remain callable');
|
|
3884
|
+
aura.test.equal(aura.tilemap.unload(__topdown.mapId), true, 'topdown tilemap should unload after assertions');
|
|
3885
|
+
__topdown.mapId = 0;
|
|
3218
3886
|
}
|
|
3219
3887
|
};
|
|
3220
3888
|
`,
|
|
@@ -3222,13 +3890,16 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
3222
3890
|
{
|
|
3223
3891
|
id: 'phaser-arcade-collision-loop-vertical-slice',
|
|
3224
3892
|
modes: ['shim', 'native'],
|
|
3225
|
-
namespaces: ['gameplay', 'draw2d', 'collision'],
|
|
3893
|
+
namespaces: ['gameplay', 'draw2d', 'collision', 'tilemap'],
|
|
3226
3894
|
functions: [
|
|
3227
3895
|
'aura.update',
|
|
3228
3896
|
'aura.draw',
|
|
3229
3897
|
'aura.draw2d.clear',
|
|
3230
3898
|
'aura.draw2d.rect',
|
|
3231
3899
|
'aura.collision.rectRect',
|
|
3900
|
+
'aura.tilemap.import',
|
|
3901
|
+
'aura.tilemap.queryPoint',
|
|
3902
|
+
'aura.tilemap.unload',
|
|
3232
3903
|
],
|
|
3233
3904
|
verticalSliceScenario: {
|
|
3234
3905
|
engine: 'phaser',
|
|
@@ -3249,24 +3920,25 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
3249
3920
|
lives: 2,
|
|
3250
3921
|
enemyHits: 1,
|
|
3251
3922
|
remainingPickups: 0,
|
|
3923
|
+
hazardContacts: 2,
|
|
3252
3924
|
},
|
|
3253
3925
|
},
|
|
3254
3926
|
nativeChecks: [
|
|
3255
3927
|
{
|
|
3256
3928
|
id: 'phaser.arcade.surface.collision-available',
|
|
3257
|
-
expression: "typeof aura.collision?.rectRect === 'function'",
|
|
3929
|
+
expression: "typeof aura.collision?.rectRect === 'function' && typeof aura.tilemap?.import === 'function' && typeof aura.tilemap?.queryPoint === 'function'",
|
|
3258
3930
|
},
|
|
3259
3931
|
{
|
|
3260
3932
|
id: 'phaser.arcade.pickup-score.deterministic',
|
|
3261
|
-
expression: "(() => { const
|
|
3933
|
+
expression: "(() => { const runSample = () => { const state = { player: { x: 0, y: 0, w: 2, h: 2 }, enemy: { x: 11, y: 0, w: 2, h: 2, vx: -1 }, pickups: [{ x: 3, y: 0, w: 1, h: 1, active: true }, { x: 7, y: 0, w: 1, h: 1, active: true }, { x: 11, y: 0, w: 1, h: 1, active: true }], score: 0, enemyHits: 0, lives: 3 }; for (let frame = 0; frame < 7; frame += 1) { state.player.x += 2; state.enemy.x += state.enemy.vx; for (const pickup of state.pickups) { if (pickup.active && aura.collision.rectRect(state.player.x, state.player.y, state.player.w, state.player.h, pickup.x, pickup.y, pickup.w, pickup.h)) { pickup.active = false; state.score += 10; } } if (aura.collision.rectRect(state.player.x, state.player.y, state.player.w, state.player.h, state.enemy.x, state.enemy.y, state.enemy.w, state.enemy.h)) { state.enemyHits += 1; if (state.enemyHits === 1) state.lives -= 1; } } return { score: state.score, inactivePickups: state.pickups.filter((pickup) => pickup.active === false).length, enemyHits: state.enemyHits, lives: state.lives }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.score === 30 && first.inactivePickups === 3 && first.enemyHits === 1 && first.lives === 2; })()",
|
|
3262
3934
|
},
|
|
3263
3935
|
{
|
|
3264
3936
|
id: 'phaser.arcade.enemy-collision.single-hit',
|
|
3265
|
-
expression: "(() => { const
|
|
3937
|
+
expression: "(() => { const runSample = () => { const state = { player: { x: 0, y: 0, w: 2, h: 2 }, enemy: { x: 11, y: 0, w: 2, h: 2, vx: -1 }, lives: 3, enemyHits: 0 }; for (let frame = 0; frame < 7; frame += 1) { state.player.x += 2; state.enemy.x += state.enemy.vx; if (aura.collision.rectRect(state.player.x, state.player.y, state.player.w, state.player.h, state.enemy.x, state.enemy.y, state.enemy.w, state.enemy.h)) { state.enemyHits += 1; if (state.enemyHits === 1) state.lives -= 1; } } const farApart = aura.collision.rectRect(0, 0, 1, 1, 9, 9, 1, 1); const overlaps = aura.collision.rectRect(0, 0, 2, 2, 1, 1, 2, 2); return { enemyHits: state.enemyHits, lives: state.lives, farApart, overlaps }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.enemyHits === 1 && first.lives === 2 && first.farApart === false && first.overlaps === true; })()",
|
|
3266
3938
|
},
|
|
3267
3939
|
{
|
|
3268
3940
|
id: 'phaser.arcade.final-state.deterministic',
|
|
3269
|
-
expression: "(() => { const
|
|
3941
|
+
expression: "(() => { const runSample = () => { const fixture = { width: 4, height: 1, tilewidth: 1, tileheight: 1, solidLayerNames: ['hazards'], layers: [ { name: 'ground', type: 'tilelayer', width: 4, height: 1, data: [1, 1, 1, 1] }, { name: 'hazards', type: 'tilelayer', width: 4, height: 1, data: [0, 0, 1, 0] } ], tilesets: [ { firstgid: 1, image: 'tiles.png', tilewidth: 1, tileheight: 1, tilecount: 1, columns: 1 } ] }; const mapId = aura.tilemap.import(fixture); const invalidPoint = aura.tilemap.queryPoint(mapId, { x: 'bad', y: 0 }); const invalidHandle = aura.tilemap.queryPoint(999999, { x: 0, y: 0 }); const state = { player: { x: 0, y: 0, w: 2, h: 2 }, enemy: { x: 11, y: 0, w: 2, h: 2, vx: -1 }, pickups: [{ x: 3, y: 0, w: 1, h: 1, active: true }, { x: 7, y: 0, w: 1, h: 1, active: true }, { x: 11, y: 0, w: 1, h: 1, active: true }], score: 0, enemyHits: 0, lives: 3, hazardContacts: 0 }; for (let frame = 0; frame < 7; frame += 1) { state.player.x += 2; state.enemy.x += state.enemy.vx; for (const pickup of state.pickups) { if (pickup.active && aura.collision.rectRect(state.player.x, state.player.y, state.player.w, state.player.h, pickup.x, pickup.y, pickup.w, pickup.h)) { pickup.active = false; state.score += 10; } } const hazard = aura.tilemap.queryPoint(mapId, { x: Math.min(3.5, (state.player.x / 4) + 0.5), y: 0.5 }); if (Array.isArray(hazard?.hits) && hazard.hits.length > 0) state.hazardContacts += 1; if (aura.collision.rectRect(state.player.x, state.player.y, state.player.w, state.player.h, state.enemy.x, state.enemy.y, state.enemy.w, state.enemy.h)) { state.enemyHits += 1; if (state.enemyHits === 1) state.lives -= 1; } } const unloaded = aura.tilemap.unload(mapId); return { playerX: state.player.x, enemyX: state.enemy.x, score: state.score, lives: state.lives, enemyHits: state.enemyHits, remainingPickups: state.pickups.filter((pickup) => pickup.active).length, hazardContacts: state.hazardContacts, invalidPoint: invalidPoint?.reasonCode || null, invalidHandle: invalidHandle?.reasonCode || null, unloaded }; }; const first = runSample(); const second = runSample(); return JSON.stringify(first) === JSON.stringify(second) && first.playerX === 14 && first.enemyX === 4 && first.score === 30 && first.lives === 2 && first.enemyHits === 1 && first.remainingPickups === 0 && first.hazardContacts === 2 && first.invalidPoint === 'invalid_point_args' && first.invalidHandle === 'invalid_map_handle' && first.unloaded === true; })()",
|
|
3270
3942
|
},
|
|
3271
3943
|
],
|
|
3272
3944
|
frames: 7,
|
|
@@ -3283,10 +3955,39 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
3283
3955
|
score: 0,
|
|
3284
3956
|
lives: 3,
|
|
3285
3957
|
enemyHits: 0,
|
|
3958
|
+
hazardContacts: 0,
|
|
3959
|
+
mapId: 0,
|
|
3960
|
+
invalidPointReason: null,
|
|
3961
|
+
invalidHandleReason: null,
|
|
3286
3962
|
};
|
|
3287
3963
|
|
|
3288
|
-
function
|
|
3289
|
-
return
|
|
3964
|
+
function __createArcadeFixture() {
|
|
3965
|
+
return {
|
|
3966
|
+
width: 4,
|
|
3967
|
+
height: 1,
|
|
3968
|
+
tilewidth: 1,
|
|
3969
|
+
tileheight: 1,
|
|
3970
|
+
solidLayerNames: ['hazards'],
|
|
3971
|
+
layers: [
|
|
3972
|
+
{
|
|
3973
|
+
name: 'ground',
|
|
3974
|
+
type: 'tilelayer',
|
|
3975
|
+
width: 4,
|
|
3976
|
+
height: 1,
|
|
3977
|
+
data: [1, 1, 1, 1],
|
|
3978
|
+
},
|
|
3979
|
+
{
|
|
3980
|
+
name: 'hazards',
|
|
3981
|
+
type: 'tilelayer',
|
|
3982
|
+
width: 4,
|
|
3983
|
+
height: 1,
|
|
3984
|
+
data: [0, 0, 1, 0],
|
|
3985
|
+
},
|
|
3986
|
+
],
|
|
3987
|
+
tilesets: [
|
|
3988
|
+
{ firstgid: 1, image: 'tiles.png', tilewidth: 1, tileheight: 1, tilecount: 1, columns: 1 },
|
|
3989
|
+
],
|
|
3990
|
+
};
|
|
3290
3991
|
}
|
|
3291
3992
|
|
|
3292
3993
|
function __stepArcade() {
|
|
@@ -3294,21 +3995,58 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
3294
3995
|
__arcade.enemy.x += __arcade.enemy.vx;
|
|
3295
3996
|
|
|
3296
3997
|
for (const pickup of __arcade.pickups) {
|
|
3297
|
-
if (
|
|
3998
|
+
if (
|
|
3999
|
+
pickup.active
|
|
4000
|
+
&& aura.collision.rectRect(
|
|
4001
|
+
__arcade.player.x,
|
|
4002
|
+
__arcade.player.y,
|
|
4003
|
+
__arcade.player.w,
|
|
4004
|
+
__arcade.player.h,
|
|
4005
|
+
pickup.x,
|
|
4006
|
+
pickup.y,
|
|
4007
|
+
pickup.w,
|
|
4008
|
+
pickup.h,
|
|
4009
|
+
)
|
|
4010
|
+
) {
|
|
3298
4011
|
pickup.active = false;
|
|
3299
4012
|
__arcade.score += 10;
|
|
3300
4013
|
}
|
|
3301
4014
|
}
|
|
3302
4015
|
|
|
3303
|
-
if (
|
|
4016
|
+
if (
|
|
4017
|
+
aura.collision.rectRect(
|
|
4018
|
+
__arcade.player.x,
|
|
4019
|
+
__arcade.player.y,
|
|
4020
|
+
__arcade.player.w,
|
|
4021
|
+
__arcade.player.h,
|
|
4022
|
+
__arcade.enemy.x,
|
|
4023
|
+
__arcade.enemy.y,
|
|
4024
|
+
__arcade.enemy.w,
|
|
4025
|
+
__arcade.enemy.h,
|
|
4026
|
+
)
|
|
4027
|
+
) {
|
|
3304
4028
|
__arcade.enemyHits += 1;
|
|
3305
4029
|
if (__arcade.enemyHits === 1) {
|
|
3306
4030
|
__arcade.lives -= 1;
|
|
3307
4031
|
}
|
|
3308
4032
|
}
|
|
4033
|
+
|
|
4034
|
+
const hazardProbe = aura.tilemap.queryPoint(
|
|
4035
|
+
__arcade.mapId,
|
|
4036
|
+
{ x: Math.min(3.5, (__arcade.player.x / 4) + 0.5), y: 0.5 },
|
|
4037
|
+
);
|
|
4038
|
+
if (Array.isArray(hazardProbe?.hits) && hazardProbe.hits.length > 0) {
|
|
4039
|
+
__arcade.hazardContacts += 1;
|
|
4040
|
+
}
|
|
3309
4041
|
}
|
|
3310
4042
|
|
|
3311
|
-
aura.setup = function () {
|
|
4043
|
+
aura.setup = function () {
|
|
4044
|
+
__arcade.mapId = aura.tilemap.import(__createArcadeFixture());
|
|
4045
|
+
const invalidPoint = aura.tilemap.queryPoint(__arcade.mapId, { x: 'bad', y: 0 });
|
|
4046
|
+
const invalidHandle = aura.tilemap.queryPoint(999999, { x: 0, y: 0 });
|
|
4047
|
+
__arcade.invalidPointReason = invalidPoint?.reasonCode || null;
|
|
4048
|
+
__arcade.invalidHandleReason = invalidHandle?.reasonCode || null;
|
|
4049
|
+
};
|
|
3312
4050
|
|
|
3313
4051
|
aura.update = function () {
|
|
3314
4052
|
if (__arcade.frame < 7) {
|
|
@@ -3332,6 +4070,11 @@ export const DEFAULT_CONFORMANCE_CASES = [
|
|
|
3332
4070
|
aura.test.equal(__arcade.lives, 2, 'arcade life decrement should be deterministic');
|
|
3333
4071
|
aura.test.equal(__arcade.enemyHits, 1, 'arcade enemy collision count should be deterministic');
|
|
3334
4072
|
aura.test.equal(__arcade.pickups.filter((pickup) => pickup.active).length, 0, 'arcade pickups should all resolve');
|
|
4073
|
+
aura.test.equal(__arcade.hazardContacts, 2, 'arcade tilemap hazard contacts should be deterministic');
|
|
4074
|
+
aura.test.equal(__arcade.invalidPointReason, 'invalid_point_args', 'arcade invalid point reason code should be deterministic');
|
|
4075
|
+
aura.test.equal(__arcade.invalidHandleReason, 'invalid_map_handle', 'arcade invalid map reason code should be deterministic');
|
|
4076
|
+
aura.test.equal(aura.tilemap.unload(__arcade.mapId), true, 'arcade tilemap should unload after assertions');
|
|
4077
|
+
__arcade.mapId = 0;
|
|
3335
4078
|
}
|
|
3336
4079
|
};
|
|
3337
4080
|
`,
|
|
@@ -3767,8 +4510,21 @@ function evaluateVerticalSliceModeBudget(mode, telemetry, budget) {
|
|
|
3767
4510
|
};
|
|
3768
4511
|
}
|
|
3769
4512
|
|
|
4513
|
+
function sortVerticalSliceModes(modeNames = []) {
|
|
4514
|
+
const order = new Map(PHASER_VERTICAL_SLICE_REQUIRED_MODES.map((modeName, index) => [modeName, index]));
|
|
4515
|
+
return [...new Set(
|
|
4516
|
+
(Array.isArray(modeNames) ? modeNames : [])
|
|
4517
|
+
.filter((modeName) => typeof modeName === 'string' && modeName.length > 0),
|
|
4518
|
+
)].sort((a, b) => (
|
|
4519
|
+
(order.has(a) ? order.get(a) : PHASER_VERTICAL_SLICE_REQUIRED_MODES.length)
|
|
4520
|
+
- (order.has(b) ? order.get(b) : PHASER_VERTICAL_SLICE_REQUIRED_MODES.length)
|
|
4521
|
+
|| a.localeCompare(b)
|
|
4522
|
+
));
|
|
4523
|
+
}
|
|
4524
|
+
|
|
3770
4525
|
function buildVerticalSliceScenarioBudget({ modeStatus, executedModes, shimTelemetry, nativeTelemetry }) {
|
|
3771
4526
|
const violations = [];
|
|
4527
|
+
const normalizedModes = sortVerticalSliceModes(executedModes);
|
|
3772
4528
|
const pushModeStatusViolation = (modeName, status) => {
|
|
3773
4529
|
violations.push({
|
|
3774
4530
|
mode: modeName,
|
|
@@ -3779,8 +4535,24 @@ function buildVerticalSliceScenarioBudget({ modeStatus, executedModes, shimTelem
|
|
|
3779
4535
|
message: `${modeName} mode conformance did not pass`,
|
|
3780
4536
|
});
|
|
3781
4537
|
};
|
|
4538
|
+
const pushModeCoverageViolation = (modeName) => {
|
|
4539
|
+
violations.push({
|
|
4540
|
+
mode: modeName,
|
|
4541
|
+
metric: `${modeName}.status`,
|
|
4542
|
+
reasonCode: `vertical_slice_mode_missing_${modeName}`,
|
|
4543
|
+
actual: 'missing',
|
|
4544
|
+
limit: 'passed',
|
|
4545
|
+
message: `${modeName} mode scenario coverage is missing`,
|
|
4546
|
+
});
|
|
4547
|
+
};
|
|
3782
4548
|
|
|
3783
|
-
for (const modeName of
|
|
4549
|
+
for (const modeName of PHASER_VERTICAL_SLICE_REQUIRED_MODES) {
|
|
4550
|
+
if (!normalizedModes.includes(modeName)) {
|
|
4551
|
+
pushModeCoverageViolation(modeName);
|
|
4552
|
+
}
|
|
4553
|
+
}
|
|
4554
|
+
|
|
4555
|
+
for (const modeName of normalizedModes) {
|
|
3784
4556
|
if (modeStatus[modeName] !== 'passed') {
|
|
3785
4557
|
pushModeStatusViolation(modeName, modeStatus[modeName] || 'missing');
|
|
3786
4558
|
continue;
|
|
@@ -3803,7 +4575,15 @@ function buildVerticalSliceScenarioBudget({ modeStatus, executedModes, shimTelem
|
|
|
3803
4575
|
status,
|
|
3804
4576
|
reasonCode,
|
|
3805
4577
|
violations: violations.sort((a, b) => (
|
|
3806
|
-
|
|
4578
|
+
(
|
|
4579
|
+
(PHASER_VERTICAL_SLICE_REQUIRED_MODES.indexOf(String(a.mode || '')) >= 0
|
|
4580
|
+
? PHASER_VERTICAL_SLICE_REQUIRED_MODES.indexOf(String(a.mode || ''))
|
|
4581
|
+
: PHASER_VERTICAL_SLICE_REQUIRED_MODES.length)
|
|
4582
|
+
- (PHASER_VERTICAL_SLICE_REQUIRED_MODES.indexOf(String(b.mode || '')) >= 0
|
|
4583
|
+
? PHASER_VERTICAL_SLICE_REQUIRED_MODES.indexOf(String(b.mode || ''))
|
|
4584
|
+
: PHASER_VERTICAL_SLICE_REQUIRED_MODES.length)
|
|
4585
|
+
)
|
|
4586
|
+
|| String(a.mode || '').localeCompare(String(b.mode || ''))
|
|
3807
4587
|
|| String(a.metric || '').localeCompare(String(b.metric || ''))
|
|
3808
4588
|
|| String(a.reasonCode || '').localeCompare(String(b.reasonCode || ''))
|
|
3809
4589
|
)),
|
|
@@ -3926,6 +4706,9 @@ function summarizePhaserVerticalSliceEvidence(cases) {
|
|
|
3926
4706
|
passed: 0,
|
|
3927
4707
|
failed: 0,
|
|
3928
4708
|
missing: 0,
|
|
4709
|
+
requiredModes: [...PHASER_VERTICAL_SLICE_REQUIRED_MODES],
|
|
4710
|
+
modeScenarioCounts: Object.fromEntries(PHASER_VERTICAL_SLICE_REQUIRED_MODES.map((modeName) => [modeName, 0])),
|
|
4711
|
+
missingModeCoverage: [...PHASER_VERTICAL_SLICE_REQUIRED_MODES],
|
|
3929
4712
|
},
|
|
3930
4713
|
telemetry: {
|
|
3931
4714
|
schemaVersion: 'aurajs.vertical-slice-telemetry.v1',
|
|
@@ -3952,12 +4735,19 @@ function summarizePhaserVerticalSliceEvidence(cases) {
|
|
|
3952
4735
|
const scenarios = [...rowsByCaseId.values()]
|
|
3953
4736
|
.sort((a, b) => a.caseId.localeCompare(b.caseId))
|
|
3954
4737
|
.map((row) => {
|
|
3955
|
-
const executedModes =
|
|
3956
|
-
const
|
|
4738
|
+
const executedModes = sortVerticalSliceModes(row.executedModes);
|
|
4739
|
+
const missingModes = PHASER_VERTICAL_SLICE_REQUIRED_MODES
|
|
4740
|
+
.filter((modeName) => !executedModes.includes(modeName));
|
|
4741
|
+
const failingModes = sortVerticalSliceModes([
|
|
4742
|
+
...missingModes,
|
|
4743
|
+
...executedModes.filter((modeName) => row.modeStatus[modeName] !== 'passed'),
|
|
4744
|
+
]);
|
|
3957
4745
|
const status = failingModes.length === 0 ? 'pass' : 'fail';
|
|
3958
|
-
const reasonCode =
|
|
3959
|
-
? '
|
|
3960
|
-
:
|
|
4746
|
+
const reasonCode = missingModes.length > 0
|
|
4747
|
+
? `phaser_vertical_slice_mode_missing_${missingModes.join('_')}`
|
|
4748
|
+
: (status === 'pass'
|
|
4749
|
+
? 'phaser_vertical_slice_ok'
|
|
4750
|
+
: `phaser_vertical_slice_failed_${failingModes.join('_')}`);
|
|
3961
4751
|
const shimTelemetry = buildVerticalSliceShimTelemetry(row.shim);
|
|
3962
4752
|
const nativeTelemetry = buildVerticalSliceNativeTelemetry(row.native);
|
|
3963
4753
|
const budget = buildVerticalSliceScenarioBudget({
|
|
@@ -3970,6 +4760,7 @@ function summarizePhaserVerticalSliceEvidence(cases) {
|
|
|
3970
4760
|
return {
|
|
3971
4761
|
...row,
|
|
3972
4762
|
executedModes,
|
|
4763
|
+
missingModes,
|
|
3973
4764
|
status,
|
|
3974
4765
|
reasonCode,
|
|
3975
4766
|
telemetry: {
|
|
@@ -3980,9 +4771,17 @@ function summarizePhaserVerticalSliceEvidence(cases) {
|
|
|
3980
4771
|
};
|
|
3981
4772
|
});
|
|
3982
4773
|
|
|
4774
|
+
const modeScenarioCounts = Object.fromEntries(
|
|
4775
|
+
PHASER_VERTICAL_SLICE_REQUIRED_MODES.map((modeName) => [
|
|
4776
|
+
modeName,
|
|
4777
|
+
scenarios.filter((row) => row.executedModes.includes(modeName)).length,
|
|
4778
|
+
]),
|
|
4779
|
+
);
|
|
4780
|
+
const missingModeCoverage = PHASER_VERTICAL_SLICE_REQUIRED_MODES
|
|
4781
|
+
.filter((modeName) => modeScenarioCounts[modeName] <= 0);
|
|
3983
4782
|
const failed = scenarios.filter((row) => row.status === 'fail').length;
|
|
3984
4783
|
const passed = scenarios.length - failed;
|
|
3985
|
-
const status = failed === 0 ? 'pass' : 'fail';
|
|
4784
|
+
const status = failed === 0 && missingModeCoverage.length === 0 ? 'pass' : 'fail';
|
|
3986
4785
|
const budgetFailures = scenarios.filter((row) => row.budget?.status !== 'pass');
|
|
3987
4786
|
const budgetViolations = budgetFailures
|
|
3988
4787
|
.flatMap((row) => (row.budget?.violations || []).map((violation) => ({
|
|
@@ -4001,15 +4800,25 @@ function summarizePhaserVerticalSliceEvidence(cases) {
|
|
|
4001
4800
|
const budgetReasonCode = budgetStatus === 'pass'
|
|
4002
4801
|
? 'vertical_slice_budget_ok'
|
|
4003
4802
|
: (budgetViolations[0]?.reasonCode || 'vertical_slice_budget_failed');
|
|
4803
|
+
const reasonCode = status === 'pass'
|
|
4804
|
+
? 'phaser_vertical_slice_pack_ok'
|
|
4805
|
+
: (
|
|
4806
|
+
missingModeCoverage.length > 0
|
|
4807
|
+
? `phaser_vertical_slice_mode_coverage_missing_${missingModeCoverage.join('_')}`
|
|
4808
|
+
: 'phaser_vertical_slice_pack_failed'
|
|
4809
|
+
);
|
|
4004
4810
|
|
|
4005
4811
|
return {
|
|
4006
4812
|
status,
|
|
4007
|
-
reasonCode
|
|
4813
|
+
reasonCode,
|
|
4008
4814
|
summary: {
|
|
4009
4815
|
scenarios: scenarios.length,
|
|
4010
4816
|
passed,
|
|
4011
4817
|
failed,
|
|
4012
4818
|
missing: 0,
|
|
4819
|
+
requiredModes: [...PHASER_VERTICAL_SLICE_REQUIRED_MODES],
|
|
4820
|
+
modeScenarioCounts,
|
|
4821
|
+
missingModeCoverage,
|
|
4013
4822
|
},
|
|
4014
4823
|
telemetry: {
|
|
4015
4824
|
schemaVersion: 'aurajs.vertical-slice-telemetry.v1',
|
|
@@ -4141,6 +4950,11 @@ function normalizeRuntimeInspectorScene3dRuntime(scene3dRuntime) {
|
|
|
4141
4950
|
nonDefaultTransforms: numericOr(scene3dRuntime?.submission?.nonDefaultTransforms),
|
|
4142
4951
|
skyboxRequested: scene3dRuntime?.submission?.skyboxRequested === true,
|
|
4143
4952
|
clearRequested: scene3dRuntime?.submission?.clearRequested === true,
|
|
4953
|
+
environmentMapConfigured: scene3dRuntime?.submission?.environmentMapConfigured === true,
|
|
4954
|
+
pointShadowLightCount: numericOr(scene3dRuntime?.submission?.pointShadowLightCount),
|
|
4955
|
+
spotShadowLightCount: numericOr(scene3dRuntime?.submission?.spotShadowLightCount),
|
|
4956
|
+
pointShadowPassCount: numericOr(scene3dRuntime?.submission?.pointShadowPassCount),
|
|
4957
|
+
spotShadowPassCount: numericOr(scene3dRuntime?.submission?.spotShadowPassCount),
|
|
4144
4958
|
postfxPassCount: numericOr(scene3dRuntime?.submission?.postfxPassCount),
|
|
4145
4959
|
postfxEnabledPassCount: numericOr(scene3dRuntime?.submission?.postfxEnabledPassCount),
|
|
4146
4960
|
postfxOrderFingerprint: numericOr(scene3dRuntime?.submission?.postfxOrderFingerprint),
|
|
@@ -4160,6 +4974,19 @@ function normalizeRuntimeInspectorScene3dRuntime(scene3dRuntime) {
|
|
|
4160
4974
|
? scene3dRuntime.submission.postfxLastReasonCode
|
|
4161
4975
|
: 'postfx_init',
|
|
4162
4976
|
postfxLastOk: scene3dRuntime?.submission?.postfxLastOk !== false,
|
|
4977
|
+
scene3dSubmitted: numericOr(scene3dRuntime?.submission?.scene3dSubmitted),
|
|
4978
|
+
scene3dCulledHidden: numericOr(scene3dRuntime?.submission?.scene3dCulledHidden),
|
|
4979
|
+
scene3dCulledLayer: numericOr(scene3dRuntime?.submission?.scene3dCulledLayer),
|
|
4980
|
+
scene3dCulledBounds: numericOr(scene3dRuntime?.submission?.scene3dCulledBounds),
|
|
4981
|
+
scene3dSkippedMissingNode: numericOr(scene3dRuntime?.submission?.scene3dSkippedMissingNode),
|
|
4982
|
+
scene3dLayerMask: numericOr(scene3dRuntime?.submission?.scene3dLayerMask),
|
|
4983
|
+
scene3dCullBoundsEnabled: scene3dRuntime?.submission?.scene3dCullBoundsEnabled === true,
|
|
4984
|
+
scene3dRenderOrderFingerprint: numericOr(
|
|
4985
|
+
scene3dRuntime?.submission?.scene3dRenderOrderFingerprint,
|
|
4986
|
+
),
|
|
4987
|
+
scene3dLastReasonCode: typeof scene3dRuntime?.submission?.scene3dLastReasonCode === 'string'
|
|
4988
|
+
? scene3dRuntime.submission.scene3dLastReasonCode
|
|
4989
|
+
: 'scene3d_render_idle',
|
|
4163
4990
|
},
|
|
4164
4991
|
camera: {
|
|
4165
4992
|
mode: typeof scene3dRuntime?.camera?.mode === 'string'
|
|
@@ -4202,6 +5029,7 @@ function normalizeRuntimeInspectorScene3dRuntime(scene3dRuntime) {
|
|
|
4202
5029
|
materialNextHandleId: numericOr(scene3dRuntime?.resources?.materialNextHandleId, 1),
|
|
4203
5030
|
lightDirectionalActive: scene3dRuntime?.resources?.lightDirectionalActive === true,
|
|
4204
5031
|
lightPointCount: numericOr(scene3dRuntime?.resources?.lightPointCount),
|
|
5032
|
+
lightSpotCount: numericOr(scene3dRuntime?.resources?.lightSpotCount),
|
|
4205
5033
|
lightAmbientIntensity: normalizeRuntimeInspectorTiming(
|
|
4206
5034
|
scene3dRuntime?.resources?.lightAmbientIntensity,
|
|
4207
5035
|
RUNTIME_INSPECTOR_TIMING_PRECISION.scalarDigits,
|
|
@@ -4478,6 +5306,9 @@ export function buildPhaserVerticalSliceEvidenceReport({
|
|
|
4478
5306
|
passed: 0,
|
|
4479
5307
|
failed: 0,
|
|
4480
5308
|
missing: 0,
|
|
5309
|
+
requiredModes: [...PHASER_VERTICAL_SLICE_REQUIRED_MODES],
|
|
5310
|
+
modeScenarioCounts: Object.fromEntries(PHASER_VERTICAL_SLICE_REQUIRED_MODES.map((modeName) => [modeName, 0])),
|
|
5311
|
+
missingModeCoverage: [...PHASER_VERTICAL_SLICE_REQUIRED_MODES],
|
|
4481
5312
|
},
|
|
4482
5313
|
telemetry: phaserVerticalSliceEvidence?.telemetry || {
|
|
4483
5314
|
schemaVersion: 'aurajs.vertical-slice-telemetry.v1',
|
|
@@ -4746,6 +5577,11 @@ function buildNativeProbeSource(conformanceCase, options = {}) {
|
|
|
4746
5577
|
nonDefaultTransforms: __num(scene3dSubmission.nonDefaultTransforms),
|
|
4747
5578
|
skyboxRequested: scene3dSubmission.skyboxRequested === true,
|
|
4748
5579
|
clearRequested: scene3dSubmission.clearRequested === true,
|
|
5580
|
+
environmentMapConfigured: scene3dSubmission.environmentMapConfigured === true,
|
|
5581
|
+
pointShadowLightCount: __num(scene3dSubmission.pointShadowLightCount),
|
|
5582
|
+
spotShadowLightCount: __num(scene3dSubmission.spotShadowLightCount),
|
|
5583
|
+
pointShadowPassCount: __num(scene3dSubmission.pointShadowPassCount),
|
|
5584
|
+
spotShadowPassCount: __num(scene3dSubmission.spotShadowPassCount),
|
|
4749
5585
|
postfxPassCount: __num(scene3dSubmission.postfxPassCount),
|
|
4750
5586
|
postfxEnabledPassCount: __num(scene3dSubmission.postfxEnabledPassCount),
|
|
4751
5587
|
postfxOrderFingerprint: __num(scene3dSubmission.postfxOrderFingerprint),
|
|
@@ -4792,6 +5628,7 @@ function buildNativeProbeSource(conformanceCase, options = {}) {
|
|
|
4792
5628
|
materialNextHandleId: __num(scene3dResources.materialNextHandleId, 1),
|
|
4793
5629
|
lightDirectionalActive: scene3dResources.lightDirectionalActive === true,
|
|
4794
5630
|
lightPointCount: __num(scene3dResources.lightPointCount),
|
|
5631
|
+
lightSpotCount: __num(scene3dResources.lightSpotCount),
|
|
4795
5632
|
lightAmbientIntensity: __num(scene3dResources.lightAmbientIntensity),
|
|
4796
5633
|
lightCameraPosition: toVec3(scene3dResources.lightCameraPosition),
|
|
4797
5634
|
},
|
|
@@ -5019,6 +5856,8 @@ async function runNativeConformanceCase({
|
|
|
5019
5856
|
hostBinaryPath: binaryPath,
|
|
5020
5857
|
hostPackageName: resolvedHost.packageName,
|
|
5021
5858
|
hostBinarySource: resolvedHost.source || null,
|
|
5859
|
+
hostBinaryTarget: resolvedHost.target || null,
|
|
5860
|
+
hostBinaryReasonCode: resolvedHost.reasonCode || null,
|
|
5022
5861
|
hostBinaryDiagnostics: resolvedHost.diagnostics || [],
|
|
5023
5862
|
hostBinaryResolution: resolvedHost,
|
|
5024
5863
|
};
|
|
@@ -5073,10 +5912,33 @@ function extractHostMetadataCandidate(entry) {
|
|
|
5073
5912
|
|| details?.hostBinarySource
|
|
5074
5913
|
|| resolution?.source,
|
|
5075
5914
|
);
|
|
5915
|
+
const target = asNonEmptyString(
|
|
5916
|
+
native?.hostBinaryTarget
|
|
5917
|
+
|| details?.hostBinaryTarget
|
|
5918
|
+
|| resolution?.target,
|
|
5919
|
+
);
|
|
5920
|
+
const selectionKind = asNonEmptyString(
|
|
5921
|
+
resolution?.localBuild?.selected?.kind
|
|
5922
|
+
|| details?.hostBinarySelectionKind,
|
|
5923
|
+
);
|
|
5076
5924
|
const resolvedAt = asNonEmptyString(resolution?.resolvedAt || details?.hostBinaryResolvedAt);
|
|
5077
|
-
const reasonCode = asNonEmptyString(
|
|
5925
|
+
const reasonCode = asNonEmptyString(
|
|
5926
|
+
details?.reasonCode
|
|
5927
|
+
|| details?.code
|
|
5928
|
+
|| native?.hostBinaryReasonCode
|
|
5929
|
+
|| resolution?.reasonCode,
|
|
5930
|
+
);
|
|
5078
5931
|
|
|
5079
|
-
if (
|
|
5932
|
+
if (
|
|
5933
|
+
!path
|
|
5934
|
+
&& !packageName
|
|
5935
|
+
&& !source
|
|
5936
|
+
&& !target
|
|
5937
|
+
&& !selectionKind
|
|
5938
|
+
&& !resolvedAt
|
|
5939
|
+
&& !reasonCode
|
|
5940
|
+
&& diagnostics.length === 0
|
|
5941
|
+
) {
|
|
5080
5942
|
return null;
|
|
5081
5943
|
}
|
|
5082
5944
|
|
|
@@ -5084,6 +5946,8 @@ function extractHostMetadataCandidate(entry) {
|
|
|
5084
5946
|
path,
|
|
5085
5947
|
packageName,
|
|
5086
5948
|
source: source || (path ? 'unknown' : null),
|
|
5949
|
+
target,
|
|
5950
|
+
selectionKind,
|
|
5087
5951
|
diagnostics,
|
|
5088
5952
|
resolvedAt,
|
|
5089
5953
|
reasonCode: reasonCode || null,
|
|
@@ -5097,34 +5961,77 @@ function summarizeHostBinaryMetadata(nativeCases) {
|
|
|
5097
5961
|
|
|
5098
5962
|
if (rows.length === 0) {
|
|
5099
5963
|
if (!Array.isArray(nativeCases) || nativeCases.length === 0) {
|
|
5100
|
-
return {
|
|
5101
|
-
path: null,
|
|
5102
|
-
packageName: null,
|
|
5103
|
-
source: null,
|
|
5104
|
-
diagnostics: [],
|
|
5105
|
-
resolvedAt: null,
|
|
5106
|
-
reasonCode: 'host_binary_not_required',
|
|
5107
|
-
};
|
|
5108
|
-
}
|
|
5109
|
-
|
|
5110
5964
|
return {
|
|
5111
5965
|
path: null,
|
|
5112
5966
|
packageName: null,
|
|
5113
5967
|
source: null,
|
|
5968
|
+
target: null,
|
|
5969
|
+
selectionKind: null,
|
|
5970
|
+
diagnostics: [],
|
|
5971
|
+
resolvedAt: null,
|
|
5972
|
+
consistency: {
|
|
5973
|
+
caseCount: Array.isArray(nativeCases) ? nativeCases.length : 0,
|
|
5974
|
+
distinctSourceCount: 0,
|
|
5975
|
+
distinctPathCount: 0,
|
|
5976
|
+
distinctPackageCount: 0,
|
|
5977
|
+
sameSource: true,
|
|
5978
|
+
samePath: true,
|
|
5979
|
+
samePackageName: true,
|
|
5980
|
+
},
|
|
5981
|
+
reasonCode: 'host_binary_not_required',
|
|
5982
|
+
};
|
|
5983
|
+
}
|
|
5984
|
+
|
|
5985
|
+
return {
|
|
5986
|
+
path: null,
|
|
5987
|
+
packageName: null,
|
|
5988
|
+
source: null,
|
|
5989
|
+
target: null,
|
|
5990
|
+
selectionKind: null,
|
|
5114
5991
|
diagnostics: [],
|
|
5115
5992
|
resolvedAt: null,
|
|
5993
|
+
consistency: {
|
|
5994
|
+
caseCount: nativeCases.length,
|
|
5995
|
+
distinctSourceCount: 0,
|
|
5996
|
+
distinctPathCount: 0,
|
|
5997
|
+
distinctPackageCount: 0,
|
|
5998
|
+
sameSource: true,
|
|
5999
|
+
samePath: true,
|
|
6000
|
+
samePackageName: true,
|
|
6001
|
+
},
|
|
5116
6002
|
reasonCode: 'host_binary_metadata_missing',
|
|
5117
6003
|
};
|
|
5118
6004
|
}
|
|
5119
6005
|
|
|
6006
|
+
const distinctSources = collectUniqueStringValues(rows.map((row) => row.source));
|
|
6007
|
+
const distinctPaths = collectUniqueStringValues(rows.map((row) => row.path));
|
|
6008
|
+
const distinctPackages = collectUniqueStringValues(rows.map((row) => row.packageName));
|
|
6009
|
+
const consistency = {
|
|
6010
|
+
caseCount: rows.length,
|
|
6011
|
+
distinctSourceCount: distinctSources.length,
|
|
6012
|
+
distinctPathCount: distinctPaths.length,
|
|
6013
|
+
distinctPackageCount: distinctPackages.length,
|
|
6014
|
+
sameSource: distinctSources.length <= 1,
|
|
6015
|
+
samePath: distinctPaths.length <= 1,
|
|
6016
|
+
samePackageName: distinctPackages.length <= 1,
|
|
6017
|
+
};
|
|
6018
|
+
const consistencyReasonCode = (
|
|
6019
|
+
!consistency.sameSource || !consistency.samePath || !consistency.samePackageName
|
|
6020
|
+
)
|
|
6021
|
+
? 'host_binary_metadata_inconsistent'
|
|
6022
|
+
: null;
|
|
6023
|
+
|
|
5120
6024
|
const first = rows[0];
|
|
5121
6025
|
return {
|
|
5122
6026
|
path: first.path || null,
|
|
5123
6027
|
packageName: first.packageName || null,
|
|
5124
6028
|
source: first.source || null,
|
|
6029
|
+
target: first.target || null,
|
|
6030
|
+
selectionKind: first.selectionKind || null,
|
|
5125
6031
|
diagnostics: collectUniqueStringValues(rows.flatMap((row) => row.diagnostics || [])),
|
|
5126
6032
|
resolvedAt: first.resolvedAt || null,
|
|
5127
|
-
|
|
6033
|
+
consistency,
|
|
6034
|
+
reasonCode: consistencyReasonCode || first.reasonCode || 'host_binary_metadata_resolved',
|
|
5128
6035
|
};
|
|
5129
6036
|
}
|
|
5130
6037
|
|
|
@@ -5149,6 +6056,12 @@ export async function runConformanceSuite(options = {}) {
|
|
|
5149
6056
|
const nativeRunner = options.nativeRunner || runNativeConformanceCase;
|
|
5150
6057
|
|
|
5151
6058
|
mkdirSync(resolve(workspaceRoot, 'src'), { recursive: true });
|
|
6059
|
+
const sceneFixtureSource = resolve(projectRoot, 'test-fixtures', 'scene3d', 'import-scene.gltf');
|
|
6060
|
+
const sceneFixtureTarget = resolve(workspaceRoot, 'src', 'cli', 'test-fixtures', 'scene3d', 'import-scene.gltf');
|
|
6061
|
+
if (existsSync(sceneFixtureSource)) {
|
|
6062
|
+
mkdirSync(dirname(sceneFixtureTarget), { recursive: true });
|
|
6063
|
+
copyFileSync(sceneFixtureSource, sceneFixtureTarget);
|
|
6064
|
+
}
|
|
5152
6065
|
|
|
5153
6066
|
const modeCases = {
|
|
5154
6067
|
shim: [],
|
|
@@ -5166,7 +6079,12 @@ export async function runConformanceSuite(options = {}) {
|
|
|
5166
6079
|
if (shouldRunShim(mode) && caseModes.has('shim')) {
|
|
5167
6080
|
const entryRelative = `src/${safeCaseId}.js`;
|
|
5168
6081
|
const entryAbsolute = resolve(workspaceRoot, entryRelative);
|
|
5169
|
-
|
|
6082
|
+
const shimBootstrapSource = buildGameStateHookBootstrapSource('headless');
|
|
6083
|
+
writeFileSync(
|
|
6084
|
+
entryAbsolute,
|
|
6085
|
+
`${shimBootstrapSource}\n${conformanceCase.source.trimStart()}`,
|
|
6086
|
+
'utf8',
|
|
6087
|
+
);
|
|
5170
6088
|
|
|
5171
6089
|
const shimStartedAt = nowMs();
|
|
5172
6090
|
try {
|
|
@@ -5211,13 +6129,14 @@ export async function runConformanceSuite(options = {}) {
|
|
|
5211
6129
|
}
|
|
5212
6130
|
|
|
5213
6131
|
if (shouldRunNative(mode) && caseModes.has('native')) {
|
|
6132
|
+
const nativeBootstrapSource = buildGameStateHookBootstrapSource('native');
|
|
5214
6133
|
const nativeSource = (
|
|
5215
6134
|
conformanceCase.nativeSource
|
|
5216
6135
|
|| buildNativeProbeSource(conformanceCase, { runtimeInspectorSnapshotEnabled })
|
|
5217
6136
|
).trimStart();
|
|
5218
6137
|
const nativeEntryRelative = `src/native-${safeCaseId}.js`;
|
|
5219
6138
|
const nativeEntryAbsolute = resolve(workspaceRoot, nativeEntryRelative);
|
|
5220
|
-
writeFileSync(nativeEntryAbsolute, nativeSource
|
|
6139
|
+
writeFileSync(nativeEntryAbsolute, `${nativeBootstrapSource}\n${nativeSource}`, 'utf8');
|
|
5221
6140
|
|
|
5222
6141
|
const nativeStartedAt = nowMs();
|
|
5223
6142
|
try {
|
|
@@ -5243,6 +6162,8 @@ export async function runConformanceSuite(options = {}) {
|
|
|
5243
6162
|
hostBinaryPath: native.hostBinaryPath,
|
|
5244
6163
|
hostPackageName: native.hostPackageName || null,
|
|
5245
6164
|
hostBinarySource: native.hostBinarySource || null,
|
|
6165
|
+
hostBinaryTarget: native.hostBinaryTarget || null,
|
|
6166
|
+
hostBinaryReasonCode: native.hostBinaryReasonCode || null,
|
|
5246
6167
|
hostBinaryDiagnostics: native.hostBinaryDiagnostics || [],
|
|
5247
6168
|
hostBinaryResolvedAt: native.hostBinaryResolution?.resolvedAt || null,
|
|
5248
6169
|
});
|
|
@@ -5257,6 +6178,8 @@ export async function runConformanceSuite(options = {}) {
|
|
|
5257
6178
|
hostBinaryPath: native.hostBinaryPath,
|
|
5258
6179
|
hostPackageName: native.hostPackageName || null,
|
|
5259
6180
|
hostBinarySource: native.hostBinarySource || null,
|
|
6181
|
+
hostBinaryTarget: native.hostBinaryTarget || null,
|
|
6182
|
+
hostBinaryReasonCode: native.hostBinaryReasonCode || null,
|
|
5260
6183
|
hostBinaryDiagnostics: native.hostBinaryDiagnostics || [],
|
|
5261
6184
|
hostBinaryResolvedAt: native.hostBinaryResolution?.resolvedAt || null,
|
|
5262
6185
|
});
|
|
@@ -5271,6 +6194,8 @@ export async function runConformanceSuite(options = {}) {
|
|
|
5271
6194
|
hostBinaryPath: native.hostBinaryPath,
|
|
5272
6195
|
hostPackageName: native.hostPackageName,
|
|
5273
6196
|
hostBinarySource: native.hostBinarySource || null,
|
|
6197
|
+
hostBinaryTarget: native.hostBinaryTarget || null,
|
|
6198
|
+
hostBinaryReasonCode: native.hostBinaryReasonCode || null,
|
|
5274
6199
|
hostBinaryDiagnostics: native.hostBinaryDiagnostics || [],
|
|
5275
6200
|
hostBinaryResolvedAt: native.hostBinaryResolution?.resolvedAt || null,
|
|
5276
6201
|
});
|
|
@@ -5296,6 +6221,8 @@ export async function runConformanceSuite(options = {}) {
|
|
|
5296
6221
|
hostBinaryPath: native.hostBinaryPath,
|
|
5297
6222
|
hostPackageName: native.hostPackageName,
|
|
5298
6223
|
hostBinarySource: native.hostBinarySource || null,
|
|
6224
|
+
hostBinaryTarget: native.hostBinaryTarget || null,
|
|
6225
|
+
hostBinaryReasonCode: native.hostBinaryReasonCode || null,
|
|
5299
6226
|
hostBinaryDiagnostics: native.hostBinaryDiagnostics || [],
|
|
5300
6227
|
},
|
|
5301
6228
|
});
|