Advance bounded editor and cache workflows
This commit is contained in:
@@ -61,6 +61,19 @@ export interface PhysicsFamilyCapabilityIR {
|
||||
serverJob: "BLOCKED";
|
||||
}
|
||||
|
||||
export interface BrowserTransformCacheObjectIR {
|
||||
objectId: string;
|
||||
translation: [number, number, number];
|
||||
rotationQuaternion: [number, number, number, number];
|
||||
scale: [number, number, number];
|
||||
}
|
||||
|
||||
export interface BrowserTransformCacheFrameIR {
|
||||
schemaVersion: 1;
|
||||
frame: number;
|
||||
objects: BrowserTransformCacheObjectIR[];
|
||||
}
|
||||
|
||||
export class PhysicsSimulationValidationError extends Error {
|
||||
readonly code: ErrorCode;
|
||||
|
||||
@@ -237,3 +250,46 @@ export function selectPhysicsCacheFrame(system: PhysicsSystemIR, requestedFrame:
|
||||
}
|
||||
return { cacheKey: cache.cacheKey, frame: requestedFrame };
|
||||
}
|
||||
|
||||
const BROWSER_TRANSFORM_CACHE_MAGIC = 0x31465442; // BTF1
|
||||
const BROWSER_TRANSFORM_CACHE_HEADER_BYTES = 16;
|
||||
const BROWSER_TRANSFORM_CACHE_OBJECT_BYTES = 72;
|
||||
|
||||
export function decodeBrowserTransformCacheFrame(value: ArrayBuffer): BrowserTransformCacheFrameIR {
|
||||
if (!(value instanceof ArrayBuffer) || value.byteLength < BROWSER_TRANSFORM_CACHE_HEADER_BYTES) {
|
||||
throw new PhysicsSimulationValidationError("PHYSICS_CACHE_FRAME_MISMATCH", "Browser transform cache frame is truncated");
|
||||
}
|
||||
const view = new DataView(value);
|
||||
if (view.getUint32(0, true) !== BROWSER_TRANSFORM_CACHE_MAGIC || view.getUint16(4, true) !== 1 || view.getUint16(6, true) !== BROWSER_TRANSFORM_CACHE_HEADER_BYTES) {
|
||||
throw new PhysicsSimulationValidationError("PROTOCOL_MISMATCH", "Unsupported browser transform cache frame schema");
|
||||
}
|
||||
const frameNumber = view.getInt32(8, true);
|
||||
const count = view.getUint32(12, true);
|
||||
if (frameNumber < -1_000_000 || frameNumber > 1_000_000 || count > PHYSICS_SIMULATION_BUDGET.maxSystems || value.byteLength !== BROWSER_TRANSFORM_CACHE_HEADER_BYTES + count * BROWSER_TRANSFORM_CACHE_OBJECT_BYTES) {
|
||||
throw new PhysicsSimulationValidationError("PHYSICS_CACHE_FRAME_MISMATCH", "Browser transform cache frame length or count is invalid");
|
||||
}
|
||||
const decoder = new TextDecoder("utf-8", { fatal: true });
|
||||
const objects: BrowserTransformCacheObjectIR[] = [];
|
||||
const ids = new Set<string>();
|
||||
for (let index = 0; index < count; index += 1) {
|
||||
const offset = BROWSER_TRANSFORM_CACHE_HEADER_BYTES + index * BROWSER_TRANSFORM_CACHE_OBJECT_BYTES;
|
||||
const idLength = view.getUint8(offset);
|
||||
if (idLength === 0 || idLength > 31) throw new PhysicsSimulationValidationError("PHYSICS_CACHE_FRAME_MISMATCH", `Browser transform cache object ${index} has an invalid ID length`);
|
||||
let objectId: string;
|
||||
try { objectId = decoder.decode(new Uint8Array(value, offset + 1, idLength)); }
|
||||
catch { throw new PhysicsSimulationValidationError("PHYSICS_CACHE_FRAME_MISMATCH", `Browser transform cache object ${index} has invalid UTF-8`); }
|
||||
if (!objectId.startsWith("object:") || ids.has(objectId)) throw new PhysicsSimulationValidationError("PHYSICS_CACHE_FRAME_MISMATCH", `Browser transform cache object ${index} has an invalid or duplicate ID`);
|
||||
ids.add(objectId);
|
||||
const numbers = Array.from({ length: 10 }, (_, component) => view.getFloat32(offset + 32 + component * 4, true));
|
||||
if (numbers.some((component) => !Number.isFinite(component))) throw new PhysicsSimulationValidationError("PHYSICS_CACHE_FRAME_MISMATCH", `Browser transform cache object ${index} has non-finite transforms`);
|
||||
const quaternionLength = Math.hypot(numbers[3], numbers[4], numbers[5], numbers[6]);
|
||||
if (Math.abs(quaternionLength - 1) > 1e-3 || numbers.slice(7).some((component) => component === 0)) throw new PhysicsSimulationValidationError("PHYSICS_CACHE_FRAME_MISMATCH", `Browser transform cache object ${index} has an invalid rotation or scale`);
|
||||
objects.push({
|
||||
objectId,
|
||||
translation: numbers.slice(0, 3) as [number, number, number],
|
||||
rotationQuaternion: numbers.slice(3, 7) as [number, number, number, number],
|
||||
scale: numbers.slice(7, 10) as [number, number, number],
|
||||
});
|
||||
}
|
||||
return { schemaVersion: 1, frame: frameNumber, objects };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user