Files
Emil c7e86663d8
MVP checks / mvp (push) Waiting to run
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

8.0 KiB

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.

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.