Expand voxel gameplay, lighting, full-height streaming and world imports
MVP checks / mvp (push) Canceled after 0s
MVP checks / mvp (push) Canceled after 0s
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.
This commit is contained in:
+44
-6
@@ -4,21 +4,47 @@ The client lives in `client/` and is served by `shacraft-server` at `/`. It uses
|
||||
|
||||
## Controls
|
||||
|
||||
- Click the world to capture the mouse. If the browser blocks pointer lock, hold the mouse button and drag to look around.
|
||||
- Click the world to capture the mouse. A drag or rejected request never disables the next capture attempt. If the browser blocks pointer lock, select the explicit drag-control mode in the game menu; holding a mouse button turns the camera and short clicks edit blocks in that mode. The canvas is focusable, and F3 reports capture status and browser errors.
|
||||
- WASD or arrow keys move; Space jumps. Movement follows the camera; +Y is up, and yaw 0 looks along −Z. The player's position specifies their feet.
|
||||
- Hold Ctrl to sprint and Shift to crouch or descend in creative flight. Crouching reduces the collider and eye height and prevents walking off supported edges. Double-tap Space within 300 ms, or press F, to toggle flight when the world permits it; Space ascends. The same controls handle swimming and ladders.
|
||||
- Left-click removes the selected block, right-click places a block against the selected face, and middle-click copies the selected material into the current hotbar slot.
|
||||
- 1–9 or the mouse wheel selects a hotbar slot. E opens the library of all states; search uses English Minecraft identifiers. Selecting a state replaces the current slot.
|
||||
- T or Enter opens chat, Enter sends, and Esc closes it.
|
||||
- The game menu includes **Render distance**, from 2 to 64 chunks (up to 1,024 blocks) around the player, with Apply and a loading counter. It remembers the choice locally; increasing it also extends fog, the projection range, and actual streaming bounds. Larger views load progressively with nearby sections first.
|
||||
- The world-name button opens the instance selector. F3 opens diagnostics. The menu contains the player name, return-to-spawn action, and Spleef start action.
|
||||
- Esc releases the mouse. Losing focus or opening a panel sends zero input so the player does not keep moving.
|
||||
|
||||
## Rendering and synchronization
|
||||
|
||||
Geometry is built from each material's local box shapes. Meshes are divided into 16³ sections; shared faces between full opaque cubes are culled. A block change rebuilds its section and adjacent sections. The shader uses directional lighting, fog, a pixelated surface pattern, and an original texture from a verified package. The sky and sun are also drawn with WebGL. Transparent materials use a separate pass with sections sorted by distance; transparent surface ordering within each section is simplified. A package with the declarative style `effect: bounce` (including the trampoline and custom blocks) supplies a texture, a verified GLSL highlight function, and an original sound; the response is tied to the player's upward movement received from the server. Entity shapes come from the catalog; players have separate multipart avatars. These are original, simplified visuals rather than an exact reproduction of Minecraft.
|
||||
Geometry is built from each material's local box shapes. Meshes are divided into 16³ sections; shared faces between full opaque cubes are culled. A block change rebuilds affected sections, including diagonal neighbors whose corner lighting changes. Transparent materials use a separate pass with sections sorted by distance; transparent surface ordering within each section is simplified. A package with the declarative style `effect: bounce` (including the trampoline and custom blocks) supplies a texture, a verified GLSL highlight function, and an original sound; the response is tied to the player's upward movement received from the server. Entity shapes come from the catalog; players have separate multipart avatars. These are original, simplified visuals rather than an exact reproduction of Minecraft.
|
||||
|
||||
The client sends input at most 20 times per second, never declares its own position, and does not edit blocks optimistically. The server computes movement, collisions, and edits, while the client smooths received positions between frames. This introduces a small movement delay but keeps the displayed position aligned with the authoritative simulation. The camera responds to the mouse locally.
|
||||
Static terrain uses separate persistent module workers for whole-view light and local terrain meshing. Protocol v2 uses dense `SectionVoxelMap` arrays and transfers copied section buffers, retaining main-thread collision ownership. The legacy path uses indexed `SectionBlockMap` records. Later updates send block deltas, complete entering sections, section unloads, sky occlusion columns and changed material definitions. Nearby meshes compute their own bounded light tiles and do not wait for whole-view light. Eight cached tiles cover 2×2 chunk columns each, with an 18-block halo and the full active vertical range. Spatial invalidation and per-section tickets prevent unrelated distant batches or view-edge changes from cancelling useful mesh work. Mesh vertices are section-local; camera-relative drawing preserves precision at distant coordinates. The controller permits one mesh request in flight and at most two completed meshes waiting for upload, prioritizing received nearby dirty sections. See [world streaming](WORLD_STREAMING.md) for storage, wire format and bounds.
|
||||
|
||||
`welcome` and `snapshot` replace all visible geometry. A full `registry` is optional: snapshots contain at most 256 definitions of materials in use, and the client gradually fetches the remaining shapes through `/api/catalog?ids=...&limit=128`. Each response rebuilds only affected sections; a neutral cube is displayed until its shape arrives. `blocks` events are applied only in revision order; data outside the latest 64×40×64 window is discarded while the revision still advances. A gap triggers `resync`; old revisions are ignored. After a disconnect, the client retries after 1, 2, 4, 8, 16, then 20 seconds. Connection failures, incompatible protocols, resource errors, and actions rejected by the server are shown to the user.
|
||||
The main thread uploads completed section buffers in `bufferSubData` steps of at most 64 KiB, targeting a 2 ms upload budget per frame. It keeps the previous visible mesh until both opaque and translucent replacement buffers are complete, then replaces their GPU handles while retaining the section object. This avoids displaying partially uploaded geometry. GPU allocation and an individual driver call cannot be interrupted, so the budget is a scheduling target rather than a guaranteed frame duration. Dynamic avatars/entities, their light samples, draw submission and UI remain on the main thread. If the terrain worker cannot start or later fails, the renderer retains visible meshes and uses synchronous terrain meshing with the former `BlockLightController`; diagnostics identify this fallback explicitly.
|
||||
|
||||
The default shader has **Moonlight** and **Daylight** modes. The game starts at night, with a dark sky, stars and weak cool moonlight; the lighting button in the game menu switches to daylight and remembers the choice locally. Daylight combines warm direct sunlight with cool sky illumination and muted ground bounce. Texture colors are decoded from sRGB, lit in linear space and encoded for display; texture sampling stays nearest-neighbor. One fixed world-space light direction controls the sun/moon and shadows. The sky, distance fog and water reflections follow the selected mode. Emissive materials also illuminate nearby surfaces and actors through separate propagated block/sky light fields, each with canonical levels 0–15. Skylight controls natural illumination, reflection and fog inside roofed spaces; source light remains independent of the time of day. See [block lighting](LIGHTING.md) for measured rules, worker integration and boundaries.
|
||||
|
||||
Sun shadows use a 2048² depth map (bounded by GPU support), a light-space grid that snaps to texels, and a stable 3×3 PCF filter. Coverage fades near the 48-block radius. Opaque terrain, cutout texture silhouettes, players and entities cast shadows; translucent glass and water do not cast solid shadows. Shadow geometry updates with world edits and actor movement. Geometry-aware corner ambient occlusion also considers slabs, stairs and shapes extending beyond their owning cells, while leaving unobstructed flat planes clean. Shader sources live in `lighting-shaders.js`, shadow projection in `shadow-frame.js`, and AO sampling in `ambient-occlusion.js`. F3 shows the active lighting and shadow-map size. If a depth framebuffer is unavailable, the client keeps daylight and AO without sun shadows.
|
||||
|
||||
Open `/tests/renderer-smoke.html` for a deterministic lighting fixture with overview, corner, sun-facing and enclosed-room cameras, a shadow toggle and a removable pillar. The room includes a torch/sea-lantern/off control, glass window and removable partition, plus canonical block/sky readings. Its DOM diagnostics retain WebGL errors and mesh counters for browser verification. Unit tests cover corner occlusion, partial shapes, chunk-boundary invalidation, shadow projection, propagation, measured Java light transitions and asynchronous result ordering; the fixture checks the actual GPU programs and rendering path.
|
||||
|
||||
The client runs the same Rust movement solver as the server, compiled into `client/physics.wasm`. It loads the module before joining and advertises `movement_prediction_v1` only after its ABI has been verified. Input is sampled at fixed 50 ms steps independently of display FPS. A stalled frame contributes at most five catch-up commands, and hidden pages reset their accumulator. The client sends controls and sequence numbers, never its own position, and does not edit blocks optimistically.
|
||||
|
||||
The server's per-player `motion` message supplies the authoritative body, processed input sequence, tick, movement settings, and reset epoch. The client removes acknowledged commands, then replays remaining commands from that body using the shared solver and its current collision neighbourhood. A maximum of 120 commands is retained; an overflow suspends prediction until the missing history has been acknowledged. Out-of-order ticks and decreasing acknowledgements are ignored. Respawn, world changes, reconnects, and input-reset epochs clear old commands. A teleport also clears interpolation and pending commands.
|
||||
|
||||
Render frames interpolate toward one disposable predicted next step, so local movement and mouse look respond before the round trip to the server. Small authoritative corrections decay visually without changing the simulated position or velocity. Crouching and swimming change the camera eye height. The local collision query includes the swept body volume, extended collision boxes, climbable blocks, and fluids even when their collision shape is empty. If a required chunk or material definition has not arrived, prediction waits for authoritative motion instead of treating unknown space as air. Block and chunk changes invalidate the render preview. A missing or incompatible WebAssembly module leaves the client in a visible server-only fallback mode.
|
||||
|
||||
Opening a panel, losing focus, or hiding the page sends `input_reset` when prediction is negotiated. The server clears queued movement and acknowledges the discarded commands with a new epoch, while gravity continues. A `look` packet immediately before a block action updates the server's selection ray without queuing another movement step. On legacy servers the client uses the previous zero-input packet and smooths server positions. Server-side package jump hooks remain authoritative; their impulses can produce a small correction on the first predicted jump.
|
||||
|
||||
Remote player yaw has a separate server target and displayed angle. Each render frame smooths position and yaw with `1 - exp(-19 * dt)`, using the shortest angular arc for yaw. New packets replace the target without snapping the displayed pose. Initial appearances and world changes initialize the pose immediately. This avoids 20 Hz rotation steps and long spins across the angle wrap.
|
||||
|
||||
The client prefers `chunk_stream_v2`, `view_buffer_v1` and `full_height_v1`, with a default 9×9×24 data window of 16³ sections (configurable from 7×7×24 to 131×131×24), typed cell arrays, palette/RLE batches and explicit readiness. Uniform sections use one uint32 value, including in worker transfers; editing expands only the affected section. The entire world height, Y=−64 through 319, remains resident during vertical flight. This includes one loaded chunk ring beyond the selected radius. Horizontal loading anchors move after two chunk crossings, keeping overlap and avoiding immediate unloads on a single crossing. Distance fog blends the visible edge before buffer unloads. A view generation rejects late batches from previous windows; revisions still order actual edits. Same-world resync keeps existing visible geometry while replacing authoritative sections. The v1 path remains available for older nonprocedural servers: an aligned 64×48×64 window with indexed block records. See [section protocol v2](WORLD_STREAMING.md#section-protocol-v2).
|
||||
|
||||
F3 includes movement recordings, automatic routes, a rolling frame/CPU chart and JSON export. Automatic scenarios deliberately continue through the diagnostics panel; Stop/Esc or hiding the tab stops them. Manual play still resets held input when a panel opens. Diagnostics is a nonmodal overlay: clicking the world, resuming play and starting a recording leave it open. F3 or its close button hides it. Focusing its controls pauses manual movement; clicking the canvas restores control. Trace limits, timing definitions and measured local results are in [movement diagnostics](WORLD_STREAMING.md#diagnostics).
|
||||
|
||||
A full `registry` is optional: snapshots contain at most 256 definitions of materials in use, and the client gradually fetches the remaining shapes through `/api/catalog?ids=...&limit=128`. Newly received material definitions also refresh retained sections that were displaying neutral fallback cubes. `blocks` events are applied only in revision order; data outside the current view bounds is discarded while the revision still advances. A `chunks` message must match the current world, revision, and previous center, with exactly the expected entering and departing sections; it cannot advance the world revision. A revision gap or an inconsistent transition triggers `resync` before any chunk data is changed. Servers without the negotiated feature can still send full snapshots using the legacy 64×40×64 window. After a disconnect, the client retries after 1, 2, 4, 8, 16, then 20 seconds. Connection failures, incompatible protocols, resource errors, and actions rejected by the server are shown to the user.
|
||||
|
||||
Dynamic actors already contain camera-relative vertices. Both the color and shadow passes explicitly reset their own program's section offset before drawing players and entities. They must not inherit the last terrain mesh's offset. Regression checks cover mesh-order changes, empty terrain frames, negative/distant coordinates and camera-origin crossings. The renderer fixture includes paired actor cameras at X=15.99 and X=16.01 for a visual boundary check and reports actual WebGL errors.
|
||||
|
||||
## Packages
|
||||
|
||||
@@ -28,6 +54,18 @@ The Control API token is never sent to the client. Packages do not execute arbit
|
||||
|
||||
## Debugging and checks
|
||||
|
||||
F3 displays real values: world, revision, visible block and entity counts, players, WebGL, FPS, triangles, tick, acknowledged input, coordinates, packages, and available server metrics. `#world-canvas` exposes `data-world`, `data-revision`, `data-blocks`, `data-entities`, `data-webgl`, and `data-connected`; these update once per second for automated browser checks. Secrets and control commands are not exposed through the DOM.
|
||||
F3 displays real values: world, revision, visible block and entity counts, players, WebGL, FPS, triangles, section mesh counts, mesh rebuilds, geometry resets, full snapshots, chunk updates, tick, acknowledged input, physics mode, pose, pending input count, correction distance, coordinates, packages, the active block texture pack and its image count, and available server metrics. `#world-canvas` exposes `data-world`, `data-revision`, `data-blocks`, `data-entities`, `data-webgl`, `data-connected`, `data-texture-pack`, `data-texture-count`, `data-texture-size`, `data-full-snapshots`, `data-chunk-updates`, `data-mesh-resets`, `data-mesh-rebuilds`, `data-section-meshes`, and `data-view-center`; these update once per second for automated browser checks. Movement checks also use `data-physics` (`predicted`, `waiting-world`, or `server`), `data-player-position`, `data-player-pose`, `data-prediction-pending`, and `data-prediction-correction`. Secrets and control commands are not exposed through the DOM. See [local block texture packs](PACKAGES.md#local-block-texture-packs) for the package descriptor and preparation command.
|
||||
|
||||
Run the pure math checks with `cd client && npm test`. They cover camera axes and projection, negative coordinates, the nearest face, reach limits, and exact hits on non-full-block shapes. Visual and end-to-end checks use a real server separately from these unit tests.
|
||||
Terrain diagnostics include frame-time p95/maximum over the most recent 240 frame intervals, the number of dirty sections, worker/fallback mode, the last packet-preparation time, worker section-build time and current frame's geometry-upload time. The canvas exposes `data-terrain-mode` (`worker` or `main-thread-fallback`), `data-terrain-prepare-ms`, `data-mesh-build-ms`, and `data-mesh-upload-ms`. Lighting diagnostics include `data-block-lighting`, `data-light-build-ms`, `data-light-sources`, `data-block-light`, and `data-sky-light`. Worker build time is background CPU time; it is distinct from the frame and upload measurements.
|
||||
|
||||
F3 also shows the geometry worker mode (`data-terrain-meshing`), first nonempty terrain publication (`data-first-terrain-ms`), completed/received sections in the camera's 3×3×3 neighborhood (`data-near-meshes` / `data-near-loaded`), and local light tile work (`data-local-light-cells` / `data-local-light-ms`). These distinguish nearby rendering progress from background completion of the full draw distance.
|
||||
|
||||
Run the client checks with `cd client && npm test`. They cover camera axes and projection, negative coordinates, the nearest face, reach limits, exact hits on non-full-block shapes, partial-pack fallback, per-face texture selection, cutout/tint metadata, horizontal log UV orientation, retained block maps and meshes during streaming, empty incoming sections, exact view bounds, same-world snapshot differences, and rejection of inconsistent or out-of-order chunk transitions. Physics checks cover FPS-independent timing, bounded catch-up, acknowledgement replay, stale packets, reset epochs, teleports, missing collision data, history overflow, and disposable render predictions. They also instantiate the shipped WebAssembly through the browser loader, compare its per-tick positions and velocities with independently measured Java 26.2 movement fixtures, and replay delayed authoritative updates through the actual solver. Rebuild the asset with `scripts/build_physics.sh` after changing the Rust crate. Visual and end-to-end checks use a real server separately from these unit tests.
|
||||
|
||||
Confirmed small block edits use a separate edit-mesh worker, independent of whole-view lighting. It receives only the section and its sampling neighborhood, builds current geometry/culling/AO with the previous light field, and gets upload priority. Lighting is corrected by the regular terrain worker afterwards. At most one edit job is in flight and two completed buffers are queued. Section tickets reject superseded edits, unloads and world resets; unrelated chunk updates do not discard a valid local edit. Large bulk edits keep the regular terrain path. If the edit worker fails, edits still use the ordinary terrain queue.
|
||||
|
||||
F3 separates the last local edit’s server acknowledgement, confirmed-to-mesh and total time. `data-edit-timings` contains `acknowledgementMs`, `geometryMs` and `totalMs`; `data-edit-prepare-ms`, `data-edit-mesh-ms` and `data-edit-worker` expose the small worker path. Timings end at mesh publication in the render loop, not GPU completion or monitor scanout. `block-edit` events are included in recorded traces. Server authority, edit validation and collision updates are unchanged.
|
||||
|
||||
Terrain tests cover indexed world changes, persistent worker state, transferred deltas, stale generations, material/texture changes, pure geometry output and staged GPU uploads. Fake-GL upload tests check byte offsets and the 64 KiB step limit, preservation of old opaque/translucent meshes until completion, partial-upload disposal, and yielding after an expensive allocation. They also check independent edit uploads against newer edits and unrelated terrain versions. Local edit geometry is compared byte-for-byte with whole-world geometry at section boundaries and distant coordinates, including glass and lamps; live voxel/light buffers must survive worker transfer. They do not establish real GPU frame-time guarantees.
|
||||
|
||||
Open `/tests/terrain-streaming-smoke.html` for an isolated browser comparison between worker and main-thread meshing. It warms a synthetic 64×64 view before a 12-second run at eight blocks per second, crossing six section boundaries without modifying a server world. Results include frame and JavaScript draw timings, the duration of each chunk-update stage, retained-mesh checks and WebGL errors. Keep the tab visible and compare modes in the same browser at the same canvas size. CPU draw timing measures JavaScript and graphics submission, not GPU completion.
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
# Block and sky lighting
|
||||
|
||||
Light-emitting block states illuminate their surroundings. The renderer computes
|
||||
two independent integer fields, **block light** and **sky light**, each ranging
|
||||
from 0 to 15. These fields determine the brightness of terrain, transparent
|
||||
surfaces and moving avatars/entities. Emissive surfaces themselves remain bright.
|
||||
|
||||
## Rules and reference data
|
||||
|
||||
Emission comes from the server catalog's state-specific `light` value. An ordinary
|
||||
torch emits 14, a sea lantern 15, and an unlit lamp emits 0. State changes,
|
||||
installation and removal trigger a new calculation. Overlapping lights select
|
||||
the strongest level; they do not add their levels together.
|
||||
|
||||
Block light travels to the six adjacent cells and loses at least one level at
|
||||
each transition. Target material dampening and the combined source/target
|
||||
occlusion faces can reduce or stop transmission. The solver uses actual face
|
||||
rectangle unions, including complementary slab and stair shapes, rather than
|
||||
treating every collision box as an opaque cube. Light can travel around an open
|
||||
doorway with attenuation along that path.
|
||||
|
||||
Open vertical sky has level 15. Unobstructed downward travel through clear cells
|
||||
keeps 15; other propagation loses at least one level. Roofs, material dampening
|
||||
and face occlusion interrupt direct skylight. A sealed room therefore has sky
|
||||
level 0 even during the daytime scene, while a light source inside it
|
||||
continues to illuminate the room.
|
||||
|
||||
`client/light-properties.json` contains measured Java 26.2 dampening and effective
|
||||
light-occlusion shapes for all **32,366 states**, with 60 deduplicated shapes.
|
||||
The loader resolves either the catalog's `minecraft_id` or state strings,
|
||||
including partial properties with original defaults. These IDs are independent
|
||||
of Shacraft's runtime material IDs. Examples include transparent glass (0),
|
||||
tinted glass (15), water/leaves/ordinary ice (1), and packed/blue ice (15).
|
||||
Metadata is about light transmission, which can differ from collision geometry.
|
||||
|
||||
The data and 78 directional crossing measurements come from the pinned original
|
||||
executable through an independently authored probe. The propagation tests agree
|
||||
with those crossings. This is evidence for the measured properties and local
|
||||
transitions, not execution of an entire original Minecraft world-light engine.
|
||||
|
||||
```sh
|
||||
python3 scripts/measure_lighting.py --java /path/to/java25/bin/java
|
||||
node --test client/tests/light-properties.test.js client/tests/block-light.test.js
|
||||
```
|
||||
|
||||
The probe checks the official bundle and extracted executable hashes. No original
|
||||
runtime or proprietary source code is distributed. The compact runtime metadata
|
||||
is about 150 KB before HTTP compression.
|
||||
|
||||
## Rendering and updates
|
||||
|
||||
`terrain-controller.js` maintains one persistent `terrain-worker.js` instance.
|
||||
The worker owns the section-indexed block map, material light properties, texture
|
||||
metadata and bounded light fields. A normal streamed view has 196,608 cells;
|
||||
the field buffers total about 1.18 MB. Initial blocks are packed in short main-thread
|
||||
time slices and transferred as `Int32Array` records `(x, y, z, block)`. Subsequent
|
||||
messages carry numeric block deltas, section unloads and changed definitions,
|
||||
instead of repeatedly cloning the full loaded block map.
|
||||
|
||||
`terrain-state.js` computes fields through `block-light.js`, compares old/new
|
||||
light values around section neighborhoods, and builds affected static geometry
|
||||
through `mesh-geometry.js`. Propagation, light comparison, AO sampling, face culling
|
||||
and mesh generation therefore all run in the worker. Vertex samples blend
|
||||
accessible neighbors outside each face and avoid sampling through solid corners.
|
||||
The worker keeps its field arrays and transfers copies for moving actors to
|
||||
sample on the main thread. Epoch/version checks reject obsolete results; dirty
|
||||
section notifications from skipped intermediate fields are retained so a later
|
||||
result cannot leave an old visible mesh with stale lighting.
|
||||
|
||||
Material definitions also invalidate geometry independently of light values.
|
||||
The worker compares transmitted material signatures, finds loaded sections using
|
||||
changed definitions, and refreshes those sections and their AO/culling neighbors.
|
||||
This replaces a retained placeholder when its real opaque material arrives even
|
||||
if the light field is unchanged. Repeated identical definitions do not trigger
|
||||
extra remeshing; pending material changes are combined across coalesced updates.
|
||||
|
||||
The main thread receives transferable opaque/translucent vertex arrays. It
|
||||
uploads them in steps of at most 64 KiB with a 2 ms scheduling budget per frame,
|
||||
retaining the old visible section until both replacements are complete. The
|
||||
section object keeps its identity when its GPU handles are replaced. There is
|
||||
one mesh request in flight and at most two completed results waiting for upload.
|
||||
GPU allocation and individual driver calls cannot be preempted; the scheduling
|
||||
budget does not guarantee that every frame finishes in 2 ms.
|
||||
|
||||
The browser's world map is also indexed by section, allowing validated chunk
|
||||
transitions to update entering/departing blocks without reparsing the entire
|
||||
retained volume. If the terrain worker is unavailable or fails at runtime, the
|
||||
renderer keeps existing visible meshes and falls back to main-thread terrain
|
||||
meshing plus the former `BlockLightController`. That controller can use its own
|
||||
light worker, or calculate light on the main thread when workers are unavailable.
|
||||
Fallback mode can therefore pause rendering during heavy calculations.
|
||||
|
||||
Canonical levels remain scalar. Warm torch light, cool sea-lantern light and
|
||||
other source tints are a Shacraft visual treatment of those levels. They are not
|
||||
a claim that vanilla Java lighting stores RGB light. The shader converts the
|
||||
field to linear irradiance and combines it with the existing sun shadows and
|
||||
ambient occlusion. At block/sky level 0 only a small visibility floor remains;
|
||||
the daytime sky is not reflected or fogged brightly through a sealed room.
|
||||
|
||||
F3 shows block/sky levels at the camera, source count and build status, plus
|
||||
worker/fallback mode, pending geometry, packet-preparation time, worker mesh-build
|
||||
time, geometry-upload time and recent frame-time p95/maximum. Canvas attributes
|
||||
`data-terrain-mode`, `data-terrain-prepare-ms`, `data-mesh-build-ms`, and
|
||||
`data-mesh-upload-ms` distinguish background work from main-thread upload work.
|
||||
The enclosed room in `/tests/renderer-smoke.html` supports source on/off/color changes
|
||||
and removal/restoration of a partition, with explicit canonical readings.
|
||||
|
||||
The game opens at night by default. The lighting button in the game menu switches
|
||||
between day and night and remembers the choice in this browser. Night uses a
|
||||
dark sky, stars, moonlight and matching fog/water reflections. This changes sky
|
||||
illumination in the shader only: propagated block/sky levels, lamp strength and
|
||||
terrain meshes remain unchanged. The setting is local; there is no synchronized
|
||||
server day/night cycle yet. The renderer fixture keeps its daytime default.
|
||||
|
||||
## Boundaries
|
||||
|
||||
Lighting is computed for the client's loaded view, with an exposed sky boundary
|
||||
above that volume and closed unknown side/bottom boundaries. It does not yet
|
||||
receive a full-world authoritative sky heightmap, so a roof above the loaded
|
||||
vertical range can require more world context. Rebuilding a field after an edit
|
||||
has a short worker/update delay. Colored tints and display brightness are visual
|
||||
choices; global illumination and ray-traced point-light shadows are not used.
|
||||
|
||||
The fields are currently renderer state. This change does not add light-dependent
|
||||
mob spawning, crop growth, redstone updates, or other absent gameplay systems.
|
||||
|
||||
## Procedural world columns
|
||||
|
||||
In the v2 world stream, the server supplies full-height opaque column maxima. These seed the top of the local skylight volume and preserve dark caves when the roof is outside the vertical view. Roof edits update the column; unloading a horizontal column releases its data. Sky rendering follows the camera sky exposure so the unloaded underground horizon stays dark. The active light field remains bounded to the streamed window; this is not global propagation over the entire world. See [world streaming](WORLD_STREAMING.md).
|
||||
@@ -40,4 +40,37 @@ This is a limited, working extension API, with no claim of arbitrary Forge/Fabri
|
||||
|
||||
The base package provides a pixel texture; the trampoline provides its own texture, a GLSL color function, style settings, and a short synthesized sound. The custom WebGL2 renderer applies them. The core and server contain no graphics engine. Packages do not load arbitrary privileged JavaScript.
|
||||
|
||||
### Local block texture packs
|
||||
|
||||
A `client-style` resource may declare a `texture_pack` with a display `name`, a `pixel_size` from 1 to 128, and `textures: [{name, path}]`. Each image path must name a resource from the same package that has already passed size and SHA-256 verification, and every image must be square with the declared pixel size. The client decodes the PNGs into a WebGL2 texture array and uses nearest-neighbor sampling. The local study uses actual 32×32 images. Known block faces select images by Minecraft texture name; missing faces keep the core's procedural material. Grass-top and oak-leaf masks receive a green tint, and transparent pixels are cut out. This is a partial block-texture layer, not support for arbitrary resource-pack models, animations, entities, or shaders.
|
||||
|
||||
Prepare the local 24-texture study using the standard-library helper:
|
||||
|
||||
```sh
|
||||
python3 scripts/prepare_texture_pack.py \
|
||||
--source artifacts/pixel-pack-32/pack \
|
||||
--output artifacts/pixel-pack-32/shacraft-packages
|
||||
target/release/shacraft-server \
|
||||
--data artifacts/mvp-demo \
|
||||
--packages artifacts/pixel-pack-32/shacraft-packages \
|
||||
--listen 127.0.0.1:4000
|
||||
```
|
||||
|
||||
The helper copies the selected images verbatim and bundles the required base and trampoline packages. All 24 default images must exist and match the declared dimensions. The default is 32×32; `--pixel-size 64` packages real 64×64 inputs (16, 32, 64 and 128 are supported). It validates dimensions without resampling, writes the matching `pixel_size`, and updates resource sizes and hashes. Use `--textures stone dirt ...` to select a different subset, and `--name`, `--id`, `--version`, and `--license` to describe another pack. `--overwrite` rebuilds only a directory marked as a previous output of this helper. It never downloads source art. Preparing a pack does not grant distribution rights; the reference-derived study and its bundle remain under ignored `artifacts/` and are not included in the repository. Restart a running server with the generated `--packages` directory and reload the game to activate it. F3 shows the active pack name and texture count.
|
||||
|
||||
When modifying a package, recalculate the size and SHA-256 of each changed resource, increase the version, and update exact dependencies. `scripts/catalog_assets.py` reproducibly generates the bundled original resources and manifests. The format is designed for a future launcher: it can consume the same manifest and cache by `(id,version,sha256)`; integration with a specific launcher is outside this repository's scope.
|
||||
|
||||
### Local full-catalog atlas
|
||||
|
||||
For local compatibility testing, `scripts/prepare_vanilla_texture_pack.py` accepts an existing Java client JAR, its matching generated block-state report, and a new output directory. It does not download assets. Pillow is required.
|
||||
|
||||
```sh
|
||||
python3 scripts/prepare_vanilla_texture_pack.py \
|
||||
--jar artifacts/pixel-pack-32/source-cache/minecraft-26.2-client.jar \
|
||||
--states artifacts/catalog-cache/generated/reports/blocks.json \
|
||||
--output artifacts/vanilla-local/packages
|
||||
```
|
||||
|
||||
The resulting package declares `atlas: {path, columns, rows}` alongside the texture names. All tiles have `pixel_size` dimensions; integer texel sampling prevents bleeding and avoids the GPU's array-layer limit. The atlas and style are verified package resources. Optional `block_faces: {sets, states, defaults}` maps canonical states to six-face sets in east/west/up/down/south/north order. Each face supplies a texture name, optional tint/cutout flag, and optional eight-number UV transform (two affine rows over local x/y/z/1). Validated mappings are transferred to terrain and edit workers with the texture map.
|
||||
|
||||
The importer resolves model inheritance, variants, rotations, and face materials onto the engine's existing geometry. It selects a representative face for multipart geometry and uses particle materials for entity-rendered blocks. It does not import full models, block-entity rendering, biome tint maps, or animation playback. Animated sprites use the first declared frame; native 16-pixel sprites are doubled without changing their colors in 32-pixel atlas cells. These are local testing assets with Mojang/Microsoft ownership, not open-source repository assets or a redistributable default pack. Keep the output under ignored `artifacts/`. Select its directory with the server's `--packages` option; selecting the previous package directory restores the previous textures.
|
||||
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
# Player physics
|
||||
|
||||
Shacraft uses one Rust player-movement implementation on the authoritative server
|
||||
and in the browser through WebAssembly. It targets the movement of Minecraft
|
||||
Java 26.2, with measured reference cases from the original executable. The
|
||||
measurements establish specific numerical agreements; they do not establish
|
||||
complete Minecraft compatibility.
|
||||
|
||||
## Playing
|
||||
|
||||
- **WASD / arrow keys:** walk; **Ctrl + forward:** sprint.
|
||||
- **Space:** jump, swim upward, or ascend while flying.
|
||||
- **Shift:** crouch and avoid walking off edges; descend in water or flight.
|
||||
- **Double-tap Space / F:** toggle flight when the server permits it. Creative
|
||||
worlds permit flight; ordinary players in other world modes cannot enable it.
|
||||
- **F3:** inspect the active physics mode, pose, unacknowledged input count,
|
||||
and the latest position correction.
|
||||
|
||||
Walking accelerates and retains momentum. Ground friction, air control, the
|
||||
sprint jump impulse, held-jump cooldown, gravity and drag all operate on fixed
|
||||
50 ms ticks. Crouching changes the collision box and eye height. A player who
|
||||
cannot stand or crouch fits into the swimming-sized crawling pose when possible.
|
||||
|
||||
Collision handling uses the catalog's block-state boxes, resolves vertical
|
||||
movement before horizontal movement, and chooses step heights from obstacle
|
||||
surfaces. Slabs, stairs, ceilings, corners and sneak-edge support participate in
|
||||
the same solver. Scaffolding and powder snow have additional player-dependent
|
||||
collision rules.
|
||||
|
||||
The solver also handles ice variants, slime and bed bounces, honey slowing and
|
||||
wall sliding, soul sand, climbable blocks, cobwebs, berry bushes, powder snow,
|
||||
water, lava, waterlogged cells, bubble columns, swimming and creative flight.
|
||||
Water/lava behavior includes acceleration, drag, buoyancy inputs, fluid levels
|
||||
and currents derived from the provided nearby block states.
|
||||
|
||||
`PhysicsSettings` exposes movement and flight speed, step height, launch-pad
|
||||
jump overrides, leather boots and coefficients for speed, slowness, jump boost,
|
||||
levitation, slow falling, Dolphin's Grace and Depth Strider. These are solver
|
||||
inputs for server integrations; their presence does not add an inventory,
|
||||
equipment or potion gameplay system. `apply_impulse` accepts an external
|
||||
velocity change for future combat, explosions or scripted launch effects.
|
||||
|
||||
## Shared simulation and networking
|
||||
|
||||
The implementation is in
|
||||
[`crates/shacraft-physics`](../crates/shacraft-physics/src/lib.rs).
|
||||
Positions use blocks and velocities use blocks per tick. Angles are radians;
|
||||
Shacraft yaw zero faces negative Z. Reference tests convert Minecraft's positive
|
||||
Z convention explicitly.
|
||||
|
||||
The server supplies authoritative nearby block states, shape boxes and settings.
|
||||
The browser runs the same compiled solver immediately for local input. Each
|
||||
server motion update includes a sequence acknowledgement, tick and motion epoch.
|
||||
The client restores the acknowledged state and replays pending inputs. Teleports
|
||||
and world changes reset prediction; render smoothing affects the displayed
|
||||
position without feeding a smoothed position back into physics.
|
||||
|
||||
[`client/player-physics.js`](../client/player-physics.js) samples the swept
|
||||
collision neighborhood, keeps a bounded input history and interpolates display
|
||||
frames between fixed simulation ticks. It suspends prediction when that
|
||||
neighborhood extends into unknown chunks instead of treating missing data as
|
||||
air. A failed or incompatible WebAssembly load falls back to server movement.
|
||||
Server support is negotiated with `movement_prediction_v1` so older clients can
|
||||
continue using authoritative updates.
|
||||
|
||||
## Reference evidence
|
||||
|
||||
[`scripts/physics_reference.java`](../scripts/physics_reference.java) runs
|
||||
against the pinned official 26.2 server executable already used for catalog
|
||||
extraction. It does not start a Minecraft server. Registry initialization is
|
||||
real; player and world constructors are bypassed for an isolated measurement
|
||||
harness. Only factual measurements and the independently authored harness are
|
||||
stored in the repository.
|
||||
|
||||
The committed
|
||||
[`java26.2.json`](../crates/shacraft-physics/tests/fixtures/java26.2.json)
|
||||
contains:
|
||||
|
||||
- Friction, speed, jump and bounce factors for 16 surfaces; default player
|
||||
attributes; five pose dimensions; water/lava heights for all 16 level values.
|
||||
- Thirteen trajectories from original `Player.travel`, `jumpFromGround` and
|
||||
`Entity.collideWithShapes` calls: walking, sprinting, jumping, ice movement,
|
||||
water, lava and creative flight.
|
||||
- Eight original `Entity.collide` measurements, including low-ceiling steps,
|
||||
thin steps, descending into a step and choosing the lowest useful step.
|
||||
- Separate original collision restitution, honey slide, slime step, bubble
|
||||
column and current-application measurements.
|
||||
|
||||
The trajectory harness supplies inputs, small-velocity threshold preparation,
|
||||
the measured sprint attribute modifier, constant medium/depth and flat-plane
|
||||
position/collision bookkeeping. It deliberately does not claim to execute the
|
||||
entire original game tick. Callback samples measure the callbacks separately,
|
||||
not their complete automatic dispatch in a running world. The water sprint
|
||||
kernel keeps the swimming pose disabled to isolate water travel.
|
||||
|
||||
Some details differ from older Minecraft physics descriptions: the sprint
|
||||
attribute modifier is the float-derived `0.30000001192092896`, the player's
|
||||
horizontal small-velocity threshold applies to the vector's squared length,
|
||||
bed restitution is `0.75`, and bounce velocity includes the fraction of motion
|
||||
completed before collision. The measured standing jump reaches
|
||||
`1.2522033402537238` blocks above its starting position.
|
||||
|
||||
Native trajectory tests compare position and velocity on every tested tick with
|
||||
an absolute tolerance of `2e-6`. They exercise twelve of the thirteen kernels:
|
||||
the artificial constant shallow-lava depth is excluded because an integrated
|
||||
world changes immersion as the player falls. Water sprint comparison stops when
|
||||
input is released, because the integrated controller ends sprinting while the
|
||||
isolated kernel keeps its sprint flag set. Separate step cases use `1e-7`, and
|
||||
restitution, honey and bubble callback tests use `1e-12`.
|
||||
|
||||
Browser tests execute the shipped WebAssembly through the actual JavaScript ABI,
|
||||
compare the eight air/ground/flight reference trajectories with `2e-6` tolerance,
|
||||
and exercise replay under delayed acknowledgements. Additional tests cover
|
||||
movement, collision, fluids, body poses, prediction resets, missing chunks and
|
||||
input ordering. These tolerances describe the tested cases, not an error bound
|
||||
for every possible world, angle or interaction.
|
||||
|
||||
The HTTP/WebSocket integration check starts a real release server on an isolated
|
||||
port with a temporary database and loads the WebAssembly it serves. It compares
|
||||
consecutive authoritative states against the same consumed command in the
|
||||
browser solver with `1e-10` tolerance. It also checks burst acknowledgements,
|
||||
duplicate commands, crouching, jumping, flight permissions, reset epochs,
|
||||
continuous movement across chunk boundaries, and a client without prediction
|
||||
support. Only explicit world changes may produce full snapshots during that
|
||||
check. The report records both binary hashes and the largest observed error.
|
||||
|
||||
## Building and verification
|
||||
|
||||
After changing the Rust solver, rebuild the browser artifact before opening the
|
||||
game or running browser physics tests:
|
||||
|
||||
```sh
|
||||
rustup target add wasm32-unknown-unknown
|
||||
bash scripts/build_physics.sh
|
||||
cargo test -p shacraft-physics
|
||||
cd client
|
||||
npm test
|
||||
```
|
||||
|
||||
The complete workspace checks remain `cargo test --workspace`,
|
||||
`cargo fmt --all -- --check` and
|
||||
`cargo clippy --workspace --all-targets -- -D warnings` from the repository root.
|
||||
|
||||
Run the isolated real-network check from the repository root after rebuilding
|
||||
both the native server and browser module:
|
||||
|
||||
```sh
|
||||
cargo build -p shacraft-server --release
|
||||
bash scripts/build_physics.sh
|
||||
node scripts/check_player_physics.mjs \
|
||||
--port 4013 \
|
||||
--binary target/release/shacraft-server \
|
||||
--output artifacts/physics/network-report.json
|
||||
```
|
||||
|
||||
The harness refuses a port already serving HTTP and does not use the running
|
||||
demo server or its database.
|
||||
|
||||
To reproduce measurements with an existing Java 25 JDK and the catalog cache:
|
||||
|
||||
```sh
|
||||
python3 scripts/measure_physics.py \
|
||||
--java /path/to/java25/bin/java \
|
||||
--output crates/shacraft-physics/tests/fixtures/java26.2.json
|
||||
```
|
||||
|
||||
If the cache is absent, prepare it using the catalog generation instructions
|
||||
before running the probe. The measurement runner verifies the pinned official
|
||||
bundle hash and verifies that the extracted executable matches that bundle.
|
||||
The fixture records the source URL, source and executable hashes, Java version,
|
||||
probe hash and reproduction command. Runtime binaries, diagnostic bytecode
|
||||
output and logs remain in ignored `artifacts/`.
|
||||
|
||||
## Remaining scope
|
||||
|
||||
This change implements player locomotion, not Minecraft's complete simulation.
|
||||
It does not add fluid spreading or scheduled fluid/block updates, pistons and
|
||||
moving block machinery, boats/minecarts, entity pushing, an elytra model, or the
|
||||
full combat/damage/knockback system. The external impulse API is a building block
|
||||
for those systems rather than their implementation.
|
||||
|
||||
Collision accuracy also depends on the catalog's measured boxes and on the
|
||||
states supplied by the server. Additional entity-dependent shapes, moving
|
||||
obstacles, complete fluid flow rules, every status-effect interaction and
|
||||
arbitrary input-angle trajectories need further reference cases. The browser
|
||||
and server share the same numerical implementation, but prediction can still
|
||||
be corrected when world edits or authoritative settings arrive after an input.
|
||||
@@ -0,0 +1,22 @@
|
||||
# Java 26.2 physics implementation
|
||||
|
||||
The target is measured Java 26.2 player movement, using one original fixed-tick
|
||||
Rust implementation on the server and in the browser through WebAssembly.
|
||||
Existing texture, chunk-streaming, and remote-rotation improvements must remain.
|
||||
|
||||
1. Measure movement attributes, block friction and speed/jump factors, poses,
|
||||
and available reference trajectories from the pinned official executable.
|
||||
2. Build the shared movement solver: velocity, acceleration, friction, gravity,
|
||||
jumping, sprinting, sneak-edge protection, swept collisions, step-up, poses,
|
||||
climbing, fluids, special surfaces, and permitted creative flight.
|
||||
3. Integrate server-authoritative commands, processed acknowledgments, and
|
||||
bounded client prediction/replay. Preserve package jump hooks and arena rules.
|
||||
4. Verify original reference facts, trajectory behavior, native/WASM parity,
|
||||
packet ordering, latency recovery, and the running browser with the local pack.
|
||||
5. Record measured compatibility and explicit remaining mechanics. Movement
|
||||
parity must not be confused with complete vanilla AI, redstone, or world-fluid
|
||||
simulation. Additional interactions must be described by their actual tests.
|
||||
|
||||
Progress and final evidence belong in `docs/PHYSICS.md` and ignored
|
||||
`artifacts/physics/`. Do not describe simulated reference formulas as measurements
|
||||
from the official executable.
|
||||
+18
-6
@@ -17,9 +17,9 @@ The first launch creates a lobby, the `gallery` world, an immutable Spleef base,
|
||||
|
||||
## State ownership
|
||||
|
||||
One dedicated thread owns WorldStore, physics, and game state. HTTP and WebSocket handlers enqueue bounded commands. Movement runs at 20 ticks per second: speed is 5 blocks/s, gravity is 20 blocks/s², and the normal jump impulse is 7 blocks/s. The player's AABB is 0.6×1.8 blocks; the position specifies the center of their feet. +Y points up, yaw increases to the right, and pitch increases upward; the viewing direction is `[sin(yaw)*cos(pitch),sin(pitch),-cos(yaw)*cos(pitch)]`.
|
||||
One dedicated thread owns WorldStore, physics, and game state. HTTP and WebSocket handlers enqueue bounded commands. The shared Rust/WASM movement solver runs at 20 ticks per second with measured Java 26.2 acceleration, friction, gravity, jump, fluid and collision behavior. Position specifies the center of the player's feet. Standing, crouching and swimming/crawling use different body and eye heights. +Y points up, yaw increases to the right, and pitch increases upward; the viewing direction is `[sin(yaw)*cos(pitch),sin(pitch),-cos(yaw)*cos(pitch)]`. See [player physics](PHYSICS.md) for reference evidence and limits.
|
||||
|
||||
Gameplay positions and spawn points are limited to ±32,700 on each axis because this client's physics uses `f32`. Storage and the converter retain their wider ±30,000,000 contract; this does not guarantee gameplay physics at distant coordinates. A player who leaves the gameplay range returns to spawn, or is eliminated during an active match.
|
||||
Player positions, spawn points, physics and action rays use double precision. Procedural worlds support horizontal gameplay through ±29,999,872 and build layers Y=−64…319; section-local rendering preserves distant geometry. Nonprocedural arena configuration and entity administration retain their ±32,700 bounds. Storage and conversion retain their ±30,000,000 contract. See [procedural worlds and diagnostics](WORLD_STREAMING.md) for the bounded CPU worker pool, durable natural baselines and streaming limits.
|
||||
|
||||
Input expresses movement intent rather than position. The server checks the 6-block reach, the nearest shape intersection, the placement cell, intersections with players, and arena rules. Stale input is cleared after one second. Gameplay physics reads a bounded region of neighboring blocks for each tick; independent worlds without players need no array of loaded sections.
|
||||
|
||||
@@ -35,15 +35,27 @@ The token is not secret from the local machine's administrator. The manifest has
|
||||
|
||||
## WebSocket, snapshots, and edits
|
||||
|
||||
The current client prefers **`chunk_stream_v2`** with **`view_buffer_v1`**, documented in [section protocol v2](WORLD_STREAMING.md#section-protocol-v2). It receives an incremental view (405 sections by default, configurable from 245 to 1,805) and palette/RLE batches with generation IDs. The loaded radius includes one extra chunk ring; horizontal view anchors move only after a two-chunk displacement. Older v2 clients without the buffer feature retain 245 sections by default, configurable from 125 to 1,445. Procedural worlds require v2. The descriptions below of full snapshots and `chunks` apply to the retained v1/legacy paths.
|
||||
|
||||
Send the first message to `/ws` within 10 seconds:
|
||||
|
||||
```json
|
||||
{"type":"join","protocol":1,"manifest_hash":"from /api/manifest","name":"Player","world":"lobby"}
|
||||
{"type":"join","protocol":1,"manifest_hash":"from /api/manifest","name":"Player","world":"lobby","features":["chunk_stream_v1","movement_prediction_v1"]}
|
||||
```
|
||||
|
||||
`welcome` contains `id`, `world`, `revision`, `blocks:[{pos,block}]`, definitions of the `materials` in use, `players`, `entities`, `spawn`, `view_center`, and `manifest_hash`. A snapshot covers `[centerX-32, centerY-8, centerZ-32]…[centerX+31, centerY+31, centerZ+31]`, with inclusive upper bounds. When the view center moves, the server sends a new `snapshot`; the client replaces its geometry completely. Snapshots do not include the full string registry. `materials` is limited to 256 entries and 128 KiB; the client requests the remaining definitions sequentially through `/api/catalog?ids=1,2,...&limit=128`. This allows players to join worlds with large palettes without overflowing the outbound queue.
|
||||
`welcome` contains `id`, `world`, `revision`, `blocks:[{pos,block}]`, definitions of the `materials` in use, `players`, `entities`, `spawn`, `view_center`, and `manifest_hash`. The view center uses world coordinates rounded down to multiples of 16, including negative coordinates. A client requesting `chunk_stream_v1` receives that feature in `features`, plus inclusive `view_min` and `view_max` bounds. Its full snapshot covers `[centerX-32, centerY-16, centerZ-32]…[centerX+31, centerY+31, centerZ+31]`: 4×3×4 complete 16³ sections.
|
||||
|
||||
The client sends `input` with `seq,yaw,pitch,forward,strafe,jump`; `break` with `pos`; `place` with `pos,block` or `state`; and `switch_world`, `resync`, `respawn`, `start_match`, `chat`, and `ping`. A `state` response contains authoritative positions, `tick`, the acknowledged input `ack`, and match state. Blocks arrive in `blocks` messages with a new revision. If a revision is missing, the client requests a snapshot. Subscription and snapshot creation are serialized with edits on the same thread, so an edit made between those steps cannot be lost.
|
||||
As the negotiated client's center moves, the server sends `chunks` updates at most once every 250 ms. Each contains `world`, the current `revision`, `from_center`, `view_center`, `view_min`, `view_max`, `unload:[[sectionX,sectionY,sectionZ]]`, `sections:[{section:[sectionX,sectionY,sectionZ],blocks:[{pos,block}]}]`, and `materials`. Section addresses are integer section coordinates; block positions and view centers are world coordinates. Only newly visible sections are read and sent, including empty ones, while retained sections keep their existing client geometry. Unloaded sections are removed. A jump with no view overlap sends every entering section through the same message. Chunk messages do not change the spawn, players, or world revision: they contain a view of the current revision. Initial joins, world switches, and explicit resyncs still send a complete snapshot.
|
||||
|
||||
Clients that omit `chunk_stream_v1` retain protocol 1's original bounds, `[centerX-32, centerY-8, centerZ-32]…[centerX+31, centerY+31, centerZ+31]`, and receive replacement `snapshot` messages on movement at most once every 2 seconds. Unknown feature names are ignored.
|
||||
|
||||
Snapshots do not include the full string registry. `materials` is limited to 256 entries and 128 KiB; chunk updates include only definitions used by entering blocks. The client requests any remaining definitions sequentially through `/api/catalog?ids=1,2,...&limit=128`. This allows players to join worlds with large palettes without overflowing the outbound queue.
|
||||
|
||||
The client sends `input` with `seq,yaw,pitch,forward,strafe,jump,sprint,sneak,fly_toggle`; `break` with `pos`; `place` with `pos,block` or `state`; and `switch_world`, `resync`, `respawn`, `start_match`, `chat`, and `ping`. The three added movement flags default to false for older clients. A `state` response contains authoritative positions, `tick`, the processed input `ack`, and match state. Public players also contain velocity in blocks per tick, pose, height, eye height, sprinting and flying. Blocks arrive in `blocks` messages with a new revision. If a revision is missing, the client requests a snapshot. Subscription and snapshot creation are serialized with edits on the same thread, so an edit made between those steps cannot be lost.
|
||||
|
||||
Clients negotiating `movement_prediction_v1` send one movement command every 50 ms. At most 32 commands are queued, and one is consumed per server tick; packet bursts never create additional simulation ticks. Duplicate sequence numbers are ignored. When the queue is empty the last controls remain held, with flight toggles consumed once. Stale controls clear after one second. `welcome`, `snapshot` and `state` include `motion:{body,ack,tick,epoch,settings}`. The body is the complete shared solver state, including velocity, pose, contact flags and jump cooldown. Settings are chosen by the server, including creative flight permission and arena countdown freeze. The client restores this state and replays unacknowledged commands through the shipped `/physics.wasm` module.
|
||||
|
||||
`input_reset` clears queued and held controls, acknowledges discarded commands and advances the motion epoch. Respawn and world/arena teleports also advance the epoch so prediction discards obsolete input. `look` with `yaw,pitch` updates the action ray without consuming a movement sequence; the browser uses it immediately before a block action. Final client positions, velocities and flight permission are never accepted as authority. Clients without the movement feature keep the earlier latest-input behavior and authoritative state updates.
|
||||
|
||||
Control edits retain the core contract: an expected revision, a unique `operation_id`, a durable acknowledgment, replay of the same request without a second write, and rejection on conflict. A build plan covers at most 32,768 cells and is retained for 5 minutes, with at most 16 plans stored; preview does not change the world. Plans are ephemeral and disappear after a restart; accepted edits remain on disk. `camera.capture` returns a PNG of the server's isometric projection and the exact revision; it is not a frame from the player's WebGL camera.
|
||||
|
||||
@@ -67,7 +79,7 @@ The core idempotency journal covers block edits, undo/reset, and build commits.
|
||||
## Limits and observability
|
||||
|
||||
- 32 WebSocket sessions, including those waiting to join; 16 concurrently served HTTP requests; a 64-command queue.
|
||||
- WebSocket input messages up to 64 KiB, with at most 80 messages/s per connection; block actions no more often than every 110 ms, chat every 700 ms, explicit resync every 500 ms, world switches every second, and automatic snapshots on section changes every 2 seconds.
|
||||
- WebSocket input messages up to 64 KiB, with at most 80 messages/s per connection; block actions no more often than every 110 ms, chat every 700 ms, explicit resync every 500 ms, and world switches every second. Negotiated chunk updates run at most every 250 ms on section changes; legacy automatic snapshots retain their 2-second interval. Movement streaming uses a separate timer and does not delay explicit resyncs or world switches.
|
||||
- Control API JSON up to 2 MiB; standard edits up to 32,768 cells; reads up to 262,144 cells and 6 MiB of response data. Exceeding the byte limit requires a smaller region.
|
||||
- Each client's outbound queue holds up to 16 messages and 8 MiB. A slow connection is closed if its queue overflows or sending times out. The aggregate queue bound depends on the number of clients; these bytes are separate from the section cache.
|
||||
- Server metadata up to 8 MiB, at most 4096 entities, and up to 16 KiB of properties per entity. Gameplay snapshots contain compact representations without arbitrary property JSON; full properties are available through `entity.list` with offset and limit (default 64, maximum 128).
|
||||
|
||||
+7
-3
@@ -1,6 +1,6 @@
|
||||
# Project status
|
||||
|
||||
Updated 2026-09-14. The agreed local MVP profile is implemented and verified. The original plan was committed before implementation as `ddfcef2`; claims from the previous cloud environment were not used as evidence. The verified implementation is commit `b6ba064`, published at [emil28092005/shacraft-core](https://github.com/emil28092005/shacraft-core). Its GitHub Actions run passed. Subsequent documentation changes do not change the runtime.
|
||||
Updated 2026-09-17. The agreed local MVP profile is implemented and verified. The original plan was committed before implementation as `ddfcef2`; claims from the previous cloud environment were not used as evidence. The earlier published baseline is commit `b6ba064` at [emil28092005/shacraft-core](https://github.com/emil28092005/shacraft-core), whose GitHub Actions run passed. The current implementation adds shared Rust/WASM movement, block lighting, worker meshing, procedural worlds, movement diagnostics, atlas texture packs, and full-height streaming at up to 64 chunks of render distance. The README includes an in-game screenshot of an imported minigames lobby.
|
||||
|
||||
## Implemented
|
||||
|
||||
@@ -13,7 +13,11 @@ Updated 2026-09-14. The agreed local MVP profile is implemented and verified. Th
|
||||
- Spleef: two independent arenas sharing a map, countdown, elimination, winner/draw, reset, spectators, persistent rules, and recovery after interruption. A lobby and a gallery of 1,197 samples.
|
||||
- Anvil and Sponge v3 import/export: typed NBT, original preservation, atomic output publication, loss/conversion reports, and transfer of block and entity edits made in Shacraft.
|
||||
|
||||
## Verified
|
||||
## Current checks
|
||||
|
||||
On 2026-09-17, `bash scripts/verify.sh` passed formatting, a physics WASM rebuild, workspace Clippy with warnings denied, 130 Rust tests, 190 JavaScript tests, storage crash recovery, and real HTTP/WebSocket scenarios for gameplay, chunk transitions, and movement prediction. Native and WASM movement agreed within the test tolerance. The browser also accepted the 64-chunk, full-height view, built all 27 nearest sections, and continued loading distant terrain. These checks do not establish a performance guarantee at the maximum distance.
|
||||
|
||||
## Verified baseline
|
||||
|
||||
84 Rust tests, 6 JavaScript tests, formatting, and Clippy without warnings; 14 groups of real HTTP/WebSocket scenarios, a separate MCP SDK session, SIGKILL, and recovery. Exports were read by the official Java 26.2 codecs and an independent NBT reader. The client was inspected in a real browser: rendering, state search, gallery navigation, material loading, reconnection, and resource caching were confirmed.
|
||||
|
||||
@@ -24,7 +28,7 @@ Final release benchmark: 100 idle map instances used 50.50 MiB RSS; 10 moving cl
|
||||
- This is an independent minigame platform with a Minecraft catalog, not a complete vanilla simulation: no full AI, redstone, inventories, or fluid simulation. Context-dependent shapes are marked; models do not reproduce the vanilla assets.
|
||||
- `exact` conservatively refuses export after edits; `best-effort` reflects changes and produces a report. Anvil and `.schem` conversion does not translate all biome/block-entity semantics; source data is archived. Version, size, and context limits are documented in [interop.md](interop.md).
|
||||
- Block history is stored on disk without automatic deletion. Undo accepts only the current target edit. Preview plans are ephemeral and expire after five minutes. Entity/rule metadata has its own revision; its API does not claim the core's idempotency journal.
|
||||
- The playable range with f32 physics is ±32700; storage and conversion support the core's ±30 million coordinates. Several bounded structures control memory use; there is no single hard RSS cap.
|
||||
- Procedural gameplay uses double precision through ±29,999,872 with Y=−64…319 building layers. The menu selects a visible radius of 2–64 chunks, default 3. Including a preloaded ring, the default data window is 144×384×144; the maximum is 2,096×384×2,096. The full world height remains loaded during vertical flight. Horizontal unload hysteresis retains overlap across single chunk crossings. Nearby terrain uses local lighting in an independent worker; distant data and meshes load progressively. Uniform sections stay compact, but large views still incur substantial loading time and memory costs. Distant-terrain LOD is not implemented, and there is no single hard RSS cap. See [current world implementation and local measurements](WORLD_STREAMING.md).
|
||||
- Linux x86_64 and local workloads have been tested. A long production soak, power-loss testing, all operating systems, and an exported-world playtest in a running Minecraft game have not been claimed as passed.
|
||||
- Launcher integration, accounts, production deployment, and existing servers are future integration work. The source repository is public; the game server has not been deployed to a remote machine.
|
||||
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
# Procedural worlds and movement diagnostics
|
||||
|
||||
The server creates `overworld` on startup if that name is unused. Open
|
||||
`http://localhost:4000/?world=overworld`, or select it in the world menu.
|
||||
Existing worlds, packages, edits and arenas remain available. An existing world
|
||||
named `overworld` is never automatically converted.
|
||||
|
||||
## World dimensions and generation
|
||||
|
||||
- Playable horizontal coordinates: **−29,999,872 through +29,999,872**.
|
||||
- Building layers: **Y = −64 through 319**, inclusive: **384 layers**.
|
||||
- Default seed: `20260914`; generator version: `1`.
|
||||
- Original deterministic terrain: continents, hills, mountains, rivers,
|
||||
oceans, forests, plains, desert areas, snow, trees, caves, tunnels, aquifers,
|
||||
deep stone, ores, low lava pockets and a bedrock bottom.
|
||||
- A spawn clearing and a narrow ravine near X=30 give access to the surface and
|
||||
underground. Fluids are generated blocks; they do not yet simulate flow.
|
||||
|
||||
The height range follows the modern Java Overworld convention described in
|
||||
[Mojang's Caves & Cliffs Part II announcement](https://www.minecraft.net/fr-ca/article/caves---cliffs--part-ii-out-today-java).
|
||||
Terrain generation is independently authored. Matching Minecraft seed output,
|
||||
all vanilla biomes, structures and gameplay systems is outside this implementation.
|
||||
|
||||
The version and seed are durable in `server.sqlite3`. Generation depends on
|
||||
absolute integer coordinates, so request order and section boundaries do not
|
||||
change trees, caves or terrain. Keep generator version 1 stable for saved worlds;
|
||||
future algorithms need a new version and an explicit migration strategy.
|
||||
|
||||
Player simulation, action rays and spawn coordinates use double precision.
|
||||
Meshes store section-local float positions; draw matrices and dynamic actors use
|
||||
an origin near the camera. Small geometry and movement steps remain representable
|
||||
near the horizontal border. Arena/entity administration retains its earlier
|
||||
configuration limits where documented.
|
||||
|
||||
## CPU work, streaming and storage
|
||||
|
||||
The server uses **two CPU generation threads** with **32 pending jobs** at most.
|
||||
The simulation thread owns storage and gameplay. Requests are shared between
|
||||
players and ordered by distance, with extra weight on vertical distance. Old
|
||||
view requests are dropped; completed jobs outside current interests are discarded.
|
||||
Control reads retain bounded, temporary interests so remote reads can complete
|
||||
without a nearby player. Generation does not require a GPU.
|
||||
|
||||
**Game menu → Render distance** selects a radius from 2 to 64 chunks
|
||||
(32–1,024 blocks around the player), default 3. Each player chooses their own
|
||||
radius; changes apply without reconnecting and persist locally. The current
|
||||
client negotiates an extra loaded chunk ring beyond that visible radius. Its
|
||||
default data window is **9×9×24 sections**, or **144×384×144 blocks / 1,944 sections**.
|
||||
At radius 8 it is **19×19×24**, or **304×384×304 blocks / 8,664 sections**.
|
||||
At radius 64 it is **131×131×24**, or **2,096×384×2,096 blocks / 411,864 sections**,
|
||||
including the one-chunk preload ring. The queue covers this entire window.
|
||||
It is sorted once when the view changes; each tick examines a bounded lookahead
|
||||
of 256 pending sections plus collision neighborhoods. Up to 128 sections are
|
||||
sent per 50 ms interval, including at most 32 nonuniform sections, with a
|
||||
256 KiB estimated section-payload budget, backpressure and time budgets.
|
||||
|
||||
Horizontal streaming has one chunk of hysteresis: crossing a single chunk
|
||||
boundary does not move the loading anchor or unload its opposite edge. The
|
||||
anchor moves after a two-chunk displacement. The extra ring preserves coverage
|
||||
of the selected radius between anchor changes. Generation, delivery and meshing
|
||||
prioritize nearby sections; overlapping cells and meshes remain in place.
|
||||
Expansion retains overlap; explicit reduction unloads excess sections. Distance
|
||||
fog completely blends the visible edge into the sky before the buffer is
|
||||
unloaded. Horizontal culling and the projection far plane follow the radius.
|
||||
|
||||
The current client negotiates `full_height_v1`: every loaded column spans
|
||||
**Y=−64 through 319**, all 24 vertical sections. Climbing and flying above the
|
||||
world ceiling retain ground and roof sections. Missing interior terrain still
|
||||
blocks prediction; space outside the world height is known void. Clients that
|
||||
do not negotiate this feature retain the legacy 80-block sliding window.
|
||||
Distant-terrain LOD is not implemented; these bounds and local benchmarks do
|
||||
not establish parity with Minecraft's renderer.
|
||||
|
||||
Already delivered distant sections do not need to remain in the server cache;
|
||||
only collision neighborhoods and unsent client sections trigger regeneration.
|
||||
Requests from multiple players still deduplicate, including a new player
|
||||
requesting terrain previously sent to someone else. Natural sections live in a bounded LRU: at most 8,192 decoded sections, or
|
||||
128 MiB of voxel payload. This is separate from the core's encoded-section
|
||||
cache, SQLite, metadata, meshes and total process RSS. Visiting more terrain does
|
||||
not allocate a permanent world-sized array. Before editing a natural section,
|
||||
the server materializes its immutable baseline in `generated_sections` in
|
||||
`worlds.sqlite3`; the existing revision/idempotency journal then stores the edit.
|
||||
An explicit air deletion survives cache eviction and restart. Undo/reset use
|
||||
the saved baseline. World templates inherit the generator configuration and
|
||||
pin existing edited sections. Natural materialization does not advance the
|
||||
edit revision. Standalone core exports contain stored sections; exporting the
|
||||
entire procedural world is not supported. Back up both SQLite databases.
|
||||
|
||||
The browser stores nonuniform cells in `Uint32Array` sections: **30.38 MiB** for a dense
|
||||
1,944-section default voxel map, or **135.38 MiB** at radius 8, excluding worker
|
||||
copies, lighting and GPU geometry. Uniform received sections use four bytes
|
||||
instead of 16 KiB, retain known-air readiness, and expand on edit. Empty sections
|
||||
need no mesh job. Lighting has a separate allocation cap of 7,393,280 cells;
|
||||
it does not scale with the maximum stream volume. With the local mesh worker,
|
||||
the shared actor field is limited to 112×384×112 blocks around the view center.
|
||||
Palette/RLE avoids JSON objects and position strings per block.
|
||||
|
||||
A dedicated terrain-mesh worker builds local light, AO, culling and geometry
|
||||
without waiting for whole-view lighting. A light tile covers 2×2 chunk columns
|
||||
plus an 18-block horizontal halo, at most **68×384×68 / 1,775,616 cells**. The full
|
||||
active vertical range preserves skylight under tall roofs. An eight-entry LRU
|
||||
shares these fields between nearby and vertically stacked sections. Spatial
|
||||
invalidation and per-section tickets reject obsolete work without discarding
|
||||
nearby results on every distant section batch. Moving a distant view edge keeps
|
||||
an interior tile's light and mesh valid if its sampling bounds are unchanged.
|
||||
|
||||
A separate persistent worker computes a bounded local light field for actor and quick-edit
|
||||
sampling, starting after 225 ms without a new sync. Its completion does not gate
|
||||
terrain publication. Large views still need time to finish distant meshes.
|
||||
Uploads are staged in 64 KiB steps targeting 2 ms per frame; old meshes remain
|
||||
until their replacements are ready. Driver calls are not preemptible.
|
||||
|
||||
Unknown sections are distinct from received air. Both authoritative simulation
|
||||
and prediction wait for collision data instead of falling through missing
|
||||
terrain. The server resets the input epoch on a terrain wait, holds the body,
|
||||
and resumes when its neighborhood is available. Teleports also reset prediction.
|
||||
|
||||
Skylight includes full-height column occlusion data. A cave remains dark when
|
||||
its roof lies above the streamed vertical window. Editing a roof updates its
|
||||
column data. The background uses the camera's sky exposure to avoid bright sky
|
||||
appearing through unloaded underground terrain. Full lateral lighting outside
|
||||
the active window is not simulated.
|
||||
|
||||
## Section protocol v2
|
||||
|
||||
Join with `features:["chunk_stream_v2","movement_prediction_v1","view_buffer_v1","full_height_v1"]` and optional
|
||||
`view_distance:2..64` (default 3). A server advertising `view_distance_v1` accepts
|
||||
`{"type":"view_distance","chunks":64}` while connected. Noninteger or out-of-range
|
||||
values are rejected before mutation; changes are limited to once per 500 ms.
|
||||
The server responds with a normal `view` transition containing `view_distance`
|
||||
and the new `total_sections`, preserving position, motion epoch and revision.
|
||||
Negotiating `view_buffer_v1` adds the extra loaded ring and horizontal hysteresis;
|
||||
`stream_radius` reports the loaded radius, while `view_distance` remains the
|
||||
selected visible radius. Older v2 clients retain the unbuffered behavior: 245
|
||||
sections by default and 1,445 at radius 8.
|
||||
`full_height_v1` fixes the vertical bounds to the world height and adds
|
||||
`full_height:true` to view/welcome/snapshot messages. It also enables batches
|
||||
of up to 128 sections; older clients receive at most 32.
|
||||
Welcome/snapshot messages also expose `max_view_distance:64`. The initial
|
||||
`welcome` or explicit `snapshot` supplies the world, revision, motion, generation,
|
||||
`view_center`, inclusive `view_min`/`view_max`, `total_sections`, terrain settings
|
||||
and world bounds. Its `blocks` array is empty; sections follow incrementally.
|
||||
Generation numbers increase for each new view or resync.
|
||||
|
||||
A `view` message updates bounds and lists departing section coordinates in
|
||||
`unload`. Retained sections keep their current data. A `sections` message contains
|
||||
at most 128 complete sections (32 for legacy clients), each as:
|
||||
|
||||
```json
|
||||
{"section":[0,4,0],"palette":[0,123],"runs":[256,1,3840,0]}
|
||||
```
|
||||
|
||||
Runs are `(count, paletteIndex)` pairs totaling exactly 4096 cells. Cell order is
|
||||
`x + 16*z + 256*y`. Section coordinates are integers; world coordinates are
|
||||
section coordinates multiplied by 16. Even empty sections are sent explicitly.
|
||||
Messages include `world`, `revision`, `generation`, material definitions and
|
||||
`columns:[{column:[sectionX,sectionZ],heights:[256 values]}]`.
|
||||
|
||||
Batches run at most once per 50 ms per client. The server pauses section sends
|
||||
above 512 KiB of queued output; existing queue and timeout limits still apply.
|
||||
Packing has a soft 10 ms work budget. Clients validate the complete batch before
|
||||
mutation, ignore obsolete generations and resync on revision gaps. Ordinary
|
||||
block edits advance revisions; view changes never do.
|
||||
|
||||
Old clients retain v1/full-snapshot behavior in nonprocedural worlds. A client
|
||||
without v2 is rejected from a procedural world with an instruction to refresh.
|
||||
|
||||
## Diagnostics
|
||||
|
||||
Open **F3 → Movement diagnostics**. Choose manual recording, automatic flight,
|
||||
automatic running, or flight with turns, then start a 30-second recording.
|
||||
Automatic scenarios use ordinary authoritative input, climb obstacles during
|
||||
flight, and need no pointer lock. Recording starts after terrain/meshes settle.
|
||||
Stop or Esc cancels automatic movement. Hiding the tab stops recording and marks
|
||||
the result `interrupted: "tab-hidden"`.
|
||||
|
||||
The panel stays open while playing or recording; clicking the world restores control, and F3 or the close button hides it. Its controls pause manual input while focused. The panel shows a rolling frame/CPU chart and a summary. **Download JSON** exports
|
||||
bounded frame samples, network/stream events and server samples. It measures
|
||||
frame p50/p95/p99/max, frames over 25/50 ms, main-thread physics/update and draw
|
||||
submission time, long tasks, position corrections, terrain waits, distance,
|
||||
view changes, batches and mesh resets. Draw CPU duration is not GPU duration.
|
||||
The canvas DOM exposes the summary as `data-dynamics-result`, plus current
|
||||
section counts, coordinates, readiness and streaming timings for browser checks.
|
||||
`data-first-terrain-ms` measures first nonempty mesh publication after the world
|
||||
reset. `data-near-meshes` / `data-near-loaded` count completed and received
|
||||
sections in the camera's 3×3×3 neighborhood, including completed empty sections.
|
||||
`data-terrain-meshing`, `data-local-light-cells` and `data-local-light-ms` expose
|
||||
the independent geometry path and its local lighting work.
|
||||
|
||||
Authenticated Control API methods for repeatable diagnosis:
|
||||
|
||||
```json
|
||||
{"method":"diagnostics.snapshot","params":{}}
|
||||
{"method":"terrain.inspect","params":{"world":"overworld","position":[1000000,0,-1000000]}}
|
||||
{"method":"player.teleport","params":{"id":"player-id","position":[1000000.125,180,-1000000.875],"flying":true}}
|
||||
{"method":"world.create","params":{"world":"another-overworld","terrain":{"seed":42,"version":1}}}
|
||||
```
|
||||
|
||||
`diagnostics.snapshot` reports generation queues/cache, simulation timings,
|
||||
positions, velocity, input backlog, waits and per-player stream state, including
|
||||
`view_distance` and `stream_radius`. Teleport
|
||||
is an administrative API, not a client movement permission. `/api/metrics`
|
||||
adds generation/stream counters and the most recent 600 tick durations.
|
||||
`world.read`, `world.edit`, build commit and `camera.capture` may return
|
||||
`terrain pending` for cold sections; retry the same read or idempotent operation
|
||||
after a short interval. No blocking generation is performed for a cold read.
|
||||
|
||||
## Local verification, 2026-09-14
|
||||
|
||||
A release server and the real browser ran against an isolated copy of the local
|
||||
data, using the Pixel32 Study package. Automatic flight traversed **495.08 m in
|
||||
30.003 s**, with **39 view changes and 78 section batches**. Across 4,317 frames,
|
||||
frame p95 was **7.0 ms**, p99 **7.1 ms**, maximum **13.8 ms**; no frame exceeded
|
||||
25 ms. The run reported no terrain-wait frames, position correction or full mesh
|
||||
reset. Section application p95 was 5.6 ms, maximum 7.3 ms. At the last server
|
||||
sample, tick work was 0.53 ms and RSS was 78.5 MiB. Three entering sections were
|
||||
still in transit at recording end; all 245 completed after stopping.
|
||||
|
||||
These measurements are one local workload, not a comparison with Minecraft or
|
||||
a guarantee for other devices, multiplayer loads or long sessions. The local
|
||||
summary is saved in ignored `artifacts/large-world-flight.json`.
|
||||
|
||||
Automated checks cover generation determinism, negative/far coordinates, deep
|
||||
content, skyline accuracy, durable mining/undo/restart, remote reads without
|
||||
players, bounded batches, overlap retention, malformed/stale data, known air,
|
||||
shared physics and section-local geometry precision. Browser checks also cover
|
||||
an open ravine at Y=10, a closed cave at Y=−29, and coordinates near ±29,999,000.
|
||||
|
||||
## Render distance and live edit verification, 2026-09-15
|
||||
|
||||
These measurements predate the additional loaded ring described above.
|
||||
|
||||
In an isolated copy of the Overworld, the real browser expanded from radius 3
|
||||
to 8 and received all 1445 sections (23,674,880 raw voxel bytes). Shrinking to
|
||||
radius 2 retained 125 sections (2,048,000 bytes), without a full snapshot, mesh
|
||||
reset, revision change or player displacement. Reloading restored the selected
|
||||
radius from local storage. The server regression also checks per-player
|
||||
isolation, invalid values, overlap retention and the world-border margin.
|
||||
|
||||
At radius 8, confirmed-to-visible-section publication took 121.9 ms for a break
|
||||
and 120.6 ms for placement in two browser samples. Including server response,
|
||||
they took 149.4 ms and 137.2 ms. Local packet preparation was 0.6–0.9 ms. The
|
||||
regular whole-view light calculation in the same session took about 4.7 seconds;
|
||||
the independent edit worker published geometry before it finished. These are
|
||||
local observations at 1280×720 with shadows and about 1.27 million triangles,
|
||||
not frame-time or network guarantees. Propagated light can still catch up later
|
||||
on large views.
|
||||
|
||||
F3 remained open on canvas clicks, block edits and recording start; explicit F3
|
||||
toggled it closed and open. The full verification script passed (126 Rust and
|
||||
171 JavaScript tests, storage crash checks and all three network suites); an
|
||||
additional staged edit-upload regression also passed afterwards.
|
||||
|
||||
## Nearby mesh priority and buffered streaming verification, 2026-09-15
|
||||
|
||||
In the real browser at radius 8 with the buffered 1,805-section window, first
|
||||
nonempty mesh publication took **354 ms and 388 ms** in two startup samples.
|
||||
All **27 nearest sections** had completed while only **1,167 of 1,805** sections
|
||||
had arrived and whole-view lighting was still building. Subsequent local-light
|
||||
cache tiling improves reuse; those startup samples were taken before that tuning.
|
||||
A final reload with the tiled cache and a warm server published its first mesh
|
||||
in **189 ms**, completed all 1,805 sections and logged no browser warnings/errors.
|
||||
|
||||
A 30-second automatic flight at 1280×720 with 2048² shadows traversed **497.12 m**.
|
||||
Across 3,746 frames, frame p95 was **13.9 ms**, p99 **20.9 ms**, maximum **48.6 ms**.
|
||||
There were no frames above 50 ms, terrain-wait frames or full mesh resets during
|
||||
the recording. It recorded 39 view changes and 147 batches. Stream application
|
||||
p95 was 9.4 ms. These are local workload observations, not cross-device or
|
||||
Minecraft comparison results.
|
||||
|
||||
Server regressions exercise repeated crossings of a single chunk boundary,
|
||||
two-chunk anchor changes, coverage of the selected radius and departing rows
|
||||
outside that radius. Client regressions compare local-light mesh bytes with the
|
||||
whole-view result, including roofs, water, glass and negative coordinates; they
|
||||
also check distant-update retention, bounded caches, stale jobs, unload/re-entry
|
||||
tickets and unchanged interior tiles during view shifts. The verification script
|
||||
passed 127 Rust tests, storage crash checks and all three network suites. The
|
||||
final client suite passed 180 JavaScript tests.
|
||||
@@ -0,0 +1,28 @@
|
||||
# Large world and movement diagnostics
|
||||
|
||||
## Objective
|
||||
|
||||
Create a playable, deterministic Overworld with 384 block layers (-64 through 319), wide horizontal coordinates, caves and surface terrain. Keep server simulation responsive while terrain is generated and streamed. Preserve existing lobby, arenas, packages and user edits.
|
||||
|
||||
## Implementation sequence
|
||||
|
||||
1. Add durable procedural section baselines to the core store. Only sections touched by edits need materialization; natural terrain is reproducible from a versioned seed. Cover air deletion, undo/reset, restart and template behavior.
|
||||
2. Add a deterministic terrain generator and a bounded server worker pool/cache. Prioritize player collision neighborhoods, then nearby visible sections; never simulate unknown terrain as empty air. Add generation, cache, queue and tick timing metrics.
|
||||
3. Add a negotiated compact section-stream protocol with explicit loaded sections, cancellation of obsolete view requests, bounded batches and overlap retention. Keep legacy clients supported. Use dense typed section arrays on the browser side and transferable worker payloads.
|
||||
4. Support precise distant positions and rendering relative to the camera's section origin. Seed skylight from authoritative column occlusion heights so deep caves stay dark even when roofs lie above the loaded vertical window.
|
||||
5. Add reusable movement diagnostics: rolling timings, trace recording/export, automatic flight/running scenarios, streaming and prediction counters, accessible controls and machine-readable results.
|
||||
6. Test deterministic seams, underground content, persistence, streaming order/unknown sections, distant coordinates and worker behavior. Run live browser routes against the real server and report observed results and remaining limits.
|
||||
7. Create/open `overworld` on the local server; keep the existing worlds available. Update documentation and the project graph.
|
||||
|
||||
## Working choices
|
||||
|
||||
- Original terrain algorithm, inspired by voxel sandbox worlds; no claim of matching Minecraft's exact seed output or every biome/structure.
|
||||
- Server remains CPU-only. Background work uses a small fixed worker pool and bounded queues/cache.
|
||||
- 16x16x16 storage/transfer sections; independent vertical streaming instead of loading all underground layers.
|
||||
- Explicit readiness for collision data, and staged GPU publication retaining old meshes.
|
||||
- Seed and generator version are durable world configuration. User edits survive cache eviction and restart.
|
||||
- Local benchmark routes use normal player inputs. They never clear or overwrite existing worlds.
|
||||
|
||||
## Completion
|
||||
|
||||
All seven steps are implemented. The main server runs the release build at `http://localhost:4000/`, with the separate `overworld` open in the browser; the existing lobby remains at revision 210. Full verification passed: 125 Rust tests, 158 JavaScript tests, Clippy, formatting, crash recovery and HTTP/WebSocket scenarios. A focused follow-up test also verifies that legacy clients cannot enter procedural worlds through a world switch. See [implementation, measured route and limits](WORLD_STREAMING.md).
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
@@ -14,6 +14,9 @@ cargo build --release -p shacraft-compat -p shacraft-server
|
||||
# Import a copy of a stopped Java world into a new native store.
|
||||
target/release/shacraft-compat import-anvil /path/to/java-world data/imported
|
||||
|
||||
# Large playable maps: load compressed section baselines instead of block edit history.
|
||||
target/release/shacraft-compat import-anvil /path/to/java-world data/imported-large --baseline
|
||||
|
||||
# Open the import in the browser through the server; select main in the world list.
|
||||
target/release/shacraft-server --data data/imported --listen 127.0.0.1:4000
|
||||
|
||||
@@ -34,6 +37,8 @@ target/release/shacraft-compat export-schem data artifacts/build --world lobby -
|
||||
|
||||
The CLI prints a JSON report to stdout; errors produce JSON on stderr and a nonzero exit code. Conversion runs offline: `WorldStore` holds the same single-writer lock as the server, preventing concurrent edits from mixing revisions within one export. The source Java world must itself be a copy of a stopped world: changes to file size/timestamps during copying are detected, but this does not replace a consistent backup of a running Minecraft instance.
|
||||
|
||||
`import-anvil --baseline` keeps the original source and conversion checks, but stores each nonempty section directly as the immutable world baseline. It avoids creating an undo record for every imported block. Imported revisions start at zero; later edits use the normal journal, and reset restores the imported map. Snapshots include the baseline. Unchanged exact exports and edited best-effort exports remain supported. Without this flag, the existing import behavior is unchanged.
|
||||
|
||||
## Implemented features
|
||||
|
||||
- Big-endian NBT: all 12 payload types, numeric widths, float/double bit patterns, signed arrays, Java modified UTF-8/CESU-8, Unicode, the element type of an empty list, and unknown compound fields. Duplicate compound keys, impossible lengths, excessive depth, and trailing decompressed bytes are rejected.
|
||||
|
||||
Reference in New Issue
Block a user