Files
shacraft-core/client/tests/physics-wasm.test.js
Emil c7e86663d8
MVP checks / mvp (push) Canceled after 0s
Expand voxel gameplay, lighting, full-height streaming and world imports
Add shared Rust/WASM physics, worker meshing and diagnostics, 64-chunk full-height streaming, atlas texture support, and baseline world import. Document the current implementation and include the supplied in-game lobby screenshot.
2026-09-17 02:10:53 +03:00

134 lines
4.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import test from "node:test";
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import {
loadPhysics,
LocalPrediction,
samplePhysicsWorld,
} from "../player-physics.js";
const binary = await readFile(new URL("../physics.wasm", import.meta.url));
const fixture = JSON.parse(
await readFile(
new URL(
"../../crates/shacraft-physics/tests/fixtures/java26.2.json",
import.meta.url,
),
"utf8",
),
);
const originalFetch = globalThis.fetch;
let step;
try {
// Exercise the actual browser ABI and its non-streaming MIME fallback.
globalThis.fetch = async () =>
new Response(binary, {
headers: { "Content-Type": "application/octet-stream" },
});
step = await loadPhysics("http://local-test/physics.wasm");
} finally {
globalThis.fetch = originalFetch;
}
const bounds = { min: [-64, -32, -64], max: [63, 63, 63] };
function floor(surface = "stone") {
const blocks = new Map();
for (let x = -4; x <= 4; x++)
for (let z = -55; z <= 5; z++) blocks.set(`${x},-1,${z}`, 1);
const materials = new Map([
[
1,
{
state: `minecraft:${surface}`,
collision: [{ min: [0, 0, 0], max: [1, 1, 1] }],
},
],
]);
return (body) => samplePhysicsWorld(body, blocks, materials, bounds);
}
const settings = { allow_flight: true };
test("the shipped browser WebAssembly follows independently measured Java 26.2 trajectories", () => {
let checked = 0;
for (const [name, trajectory] of Object.entries(
fixture.travel_kernel_trajectories,
)) {
if (trajectory.medium !== "air") continue;
const getWorld = floor(trajectory.surface);
let body = {
position: trajectory.initial_position,
velocity: trajectory.initial_velocity,
flying: trajectory.flying,
on_ground: !trajectory.flying,
};
for (const sample of trajectory.samples) {
const input = {
forward: sample.tick <= trajectory.input_ticks ? 1 : 0,
sprint: trajectory.sprint,
jump: sample.tick === 1 && trajectory.jump_first_tick,
};
body = step({ body, input, world: getWorld(body), settings });
for (const field of ["position", "velocity"])
for (let axis = 0; axis < 3; axis++) {
const expected = sample[field][axis] * (axis === 2 ? -1 : 1);
assert.ok(
Math.abs(body[field][axis] - expected) < 2e-6,
`${name} tick ${sample.tick} ${field}[${axis}]: ${body[field][axis]} != ${expected}`,
);
}
assert.equal(
body.on_ground,
sample.on_ground,
`${name} tick ${sample.tick} grounded`,
);
checked++;
}
}
assert.ok(checked >= 200);
});
test("real WebAssembly prediction replays delayed authority without introducing trajectory drift", () => {
const getWorld = floor();
const prediction = new LocalPrediction(step, getWorld);
let authority = {
position: [0, 0, 0],
velocity: [0, -0.0784000015258789, 0],
on_ground: true,
};
prediction.receive({ body: authority, ack: 0, tick: 0, epoch: 1, settings });
const packets = [];
for (let tick = 1; tick <= 80; tick++) {
const input = {
forward: tick < 55 ? 1 : 0,
sprint: tick > 20 && tick < 50,
jump: tick === 25,
sneak: tick >= 55,
};
prediction.push(tick, input);
authority = step({
body: authority,
input,
world: getWorld(authority),
settings,
});
packets.push({ body: authority, ack: tick, tick, epoch: 1, settings });
// Variable 150250 ms inbound latency; authority still acknowledges order.
if (packets.length > (tick % 3) + 3) prediction.receive(packets.shift());
for (let axis = 0; axis < 3; axis++) {
assert.ok(
Math.abs(prediction.body.position[axis] - authority.position[axis]) <
1e-11,
`position drift at tick ${tick}`,
);
assert.ok(
Math.abs(prediction.body.velocity[axis] - authority.velocity[axis]) <
1e-11,
`velocity drift at tick ${tick}`,
);
}
}
for (const packet of packets) prediction.receive(packet);
assert.equal(prediction.pending.length, 0);
assert.deepEqual(prediction.body, authority);
});