Repository navigation
Conversation
Entities now sample packed column light on the renderer thread and multiply material color, matching block lighting. Light-only 1.18+ reloads drop the fused raw map_chunk so blocks remesh from the updated column. Authored material retints (leather armor) keep their new base; distant column ingest no longer retints every entity. New Versions Lighting defaults to on. A leaked internal migration flag is stripped from saved options so it is not a user setting. Torch spread in singleplayer still needs Flying Squid to emit update_light.
…sky as 15 Omitted sections stay absent in the authoritative cache; sky=15 is display fill only.
LIGHT_ONLY sections with known light are external solver boundaries, not cells the BFS may rewrite. The invariant is L(p)=max(E(p), neighbor decay); stale queue entries must not resurrect light. enableClientLightOwner stays off.
minecraft-data only maps names to stateIds. Emission comes from the 1.17.1 lightLevel registrations so lit furnaces, lamps, and candles are no longer zero. enableClientLightOwner stays off.
Sky is an independent channel with vanilla-like sources (15 down an open column, skip-up on source entries). Seed is a candidate, LIGHT_ONLY 15 is a boundary, and unknown chunks are not treated as sky 15.
… (default off) Spawn the dedicated owner worker from WorldRendererCommon only when a human flips the flag for live 1.17.1 runs; default stays false so players keep the server-light path.
Sky sources stop on true light-blocking (water/leaves), while neighbor decay still uses max(1, lightBlock). Slabs and stairs get 2x2x2 occupancy so complementary faces occlude without treating a bottom slab as stone.
default_test_tables referenced test-only constants without the same gate.
step(5) now budgets event drain and deduped sky columns so ingest cannot run unbounded. The owner session is starting until ready, and load/worker errors go to failed. Keep ?clientLight=1 as the manual switch; default flag stays off.
…sion Unrelated publications no longer drop a valid remesh; trailing dirty keeps the target revision, and owner-on display light updates only from completed publications.
Opening a path, ingest next to a LIGHT_ONLY boundary, and unload of a supporting column now re-reconcile contacts so residual or missing block light cannot linger.
BFS was rewalking every column height on each source check (~151M Y-steps for 9 air columns). Cache the derived lowest-source Y and invalidate it on block, ingest, availability, sky boundary, unload, and sky-enabled changes.
A view-distance dump was one transaction that published only after every pending column finished. Admit the FIFO prefix of one column so completed lighting can publish while later columns stay queued.
…teId Main-thread ingest was doing 4096 getBlockStateId calls per section. Read prismarine section palette/data (and skip empty/uniform sections) so load no longer walks every cell on the owner path.
…in-worker Versioned owner publications were queued behind the 100ms dirty window after a block edit, and remaining solver slices bounced through main setTimeout.
A remesh of an already-visible section is buffered in pendingSectionUpdates and was released per key: each section waited out its own 500ms deadline and then went to the GPU alone. On a dig the owner stencil remeshes 27 sections whose geometries come back on different worker ticks, so their deadlines expire in different frames — a section was installed while its face neighbour still held geometry culled against the old block state, and the faces between them were missing on both sides for a frame (the sky flash). Group the buffered sections by face adjacency and release a whole group at once, measuring the deadline from its oldest member. A complete group (every outstanding neighbour arrived) flushes without waiting for any deadline, so this is never slower than the per-key policy.
Splits one dig into the terms processColumnTick actually pays: per-column WASM mesh over the full Y range vs the stencil Y window, redundant neighbour parses, typed-array copies of the two-step path, and the per-section world.getBlock walk of the post phase.
…in light delivery trace Compaction was selecting unuploaded replacement candidates, so remeshes dropped visible faces. Separate displayed from candidate and record a default-off causal trace so the next latency work has a real path.
…play path Stale unversioned remeshes and an aliased incoming/display cache could roll back topology or overwrite the last genuine server light. Cover by section+revision and detach incoming once on owner takeover.
…ampled columns An edit column and its 3x3 jump the light queue without inventing missing neighbors. Urgent remeshes skip the 100ms throttle and the 50ms poll, and a mesh light version comes only from columns that were actually meshed.
The state-id tables are the 1.17.1 registry, so another session version keeps server light. Includes prettier formatting.
Worker and IndexedBlock casts no longer overlap their targets under the CI typecheck.
This was referenced Sep 22, 2026
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Client-side 1.17.1 block and sky light in the wasm mesher. Section meshes carry versions so a block edit is not drawn from stale server light or from a neighbor column the mesh did not sample.
Context
Inspired by minecraft-web-client#304 (client-side light engine).
This branch also contains minecraft-renderer#93 (
feat/entity-block-lighting), including the opt-in defaults fix (465cf22). #93 stays open as an independently mergeable entity-lighting PR.In my view this still works roughly and needs review from @zardoy.
How to test in the web client
Use the client integration in minecraft-web-client#599 with a renderer build containing this PR (including
lightOwnerWorker.js).?clientLight=1to the client URL, or&clientLight=1if it already has query parameters, and reload.newVersionsLighting).Both switches are required to see the recomputed lighting. The URL flag starts the experimental light owner; the setting enables lighting display. Both remain off by default.
clientLightTrace=1is optional diagnostics, not required for testing.What turns on
enableClientLightOwnerstays defaultfalse.?clientLight=1sets that flag. Spawn also requires the session version to be1.17.1(shouldSpawnClientLightOwner). Any other version leaves the flag set, does not startlightOwnerWorker, and does not load the 1.17.1 state-id tables.clientLightTrace=1is a separate, default-off trace.With the flag off, or when the version gate refuses spawn, the mesh displays server
update_light. With the flag on and version1.17.1,ClientLightOwnerandlightOwnerWorkerstart, separate from mesh workers. The session staysstartinguntil the worker reports ready, and goes tofailedon worker error.Guarantees
lightBlock, and 2×2×2 occupancy come from 1.17.1Blocks.javalightLevelregistrations.LightEngine::opacity_ofis vanillagetOpacity:max(1, getLightBlock()). Levels are 0–15.shape_occludesfollows 1.17.1LayerLightEngine.LIGHT_ONLYis an external boundary and is not rewritten by BFS.promote_edit_column). Admission is one column at a time. Missing neighbors are not invented as air. ABlockChangefor an unknown column does not jump the queue.shouldAcceptVersionedMeshis per section. Fast A→B→C keeps C. A late reply from the previous column incarnation is dropped when both sides carrycolumnIncarnation. Fresh topology with stale light is installed for the current frame; covering stays outstanding and is not dispatched again while the pending remesh already covers the requirement. Stale topology with fresh light is rejected.hadErrorsis never an empty success.INITIAL_TOPOLOGY_REVISIONis 0.nextTopologyRevisionstarts at 1, so the first edit misses the initial topology cache.provenMeshLightVersionstamps a section from the latest applied version of the 3×3 columns (±16in x and z) that the mesh actually used. The version on the dirty job is not an input.selectReadySectionFlushesinstalls a face-adjacent group together. The group flushes when every visible neighbor has arrived, or when the oldest member hits 500 ms. With the owner live, the wait is topology-outstanding. Displayed GPU slots stay separate from unuploaded candidates.urgent.createMeshTickScheduler.kickruns on the next turn. Urgent columns are taken before the bulk cap of 4 (BULK_COLUMNS_PER_TICK).setSectionDirtydispatches immediately whennextDirtyUrgentor a light publication version is set, and does not enter the 100 ms trailing window. The edit stays ininteractiveSectionsuntil geometry is committed at a publication newer thanlightVersionAtEdit. A rejectedsectionFinisheddecrementssectionsWaiting.legacyBootstrapstill has to match epoch and incarnation when a required version exists.Limitations
1.17.1only.newVersionsLighting(defaultfalsein renderer options) only feedsresolveEnableLighting. It does not spawn the owner.update_light. With the owner on, a block edit isBlockChange(setBlockStateIdInner→onBlockChange). Emission for that edit comes from the 1.17.1 tables and does not wait forupdate_light.lightOwnerWorker.jsfromMESHER_DIST_FILES; the old handwritten artifact list omits it.src/wasm-mesher/runtime-build/wasm_mesher_bg.wasmon this branch is theLightEnginebuild, including commit879d982.Mesh version contract vs checks
An owner mesh is expected to carry
sessionEpoch,columnIncarnation,worldGeneration,topologyRevision, andlightPublicationVersion. The section requirement carriesrequiredVersion,worldGeneration, and optionallytopologyRevision.shouldAcceptVersionedMeshdoes not require every field to be present:hadErrorsrejects.worldGeneration,lightPublicationVersion,topologyRevision, andsessionEpochall absent is rejected, exceptlegacyBootstrapwhen there is no requirement.legacyBootstrapwith a requirement rejects a missinglightPublicationVersion.worldGeneration,topologyRevision, and light version are compared only when that field is present on the mesh (topology also needsrequired.topologyRevision).Checks
Latest integration/defaults validation:
pnpm exec vitest run --maxWorkers=2: 638 tests passed.pnpm typecheck: passed in the renderer.cargo test: 101 tests passed.lightOwnerWorker.jsand WASM and receivedready.pnpm unit-test(vitest) covers accept/reject rules, owner spawn including the 1.17.1 gate, publication skipping the 100 ms window, packed-light length, face-adjacent flush, display clone and revert, sampled-column proof, urgent kick ahead of the 50 ms poll, topology and tick parse caches, and the default-off trace.cargo testinwasm-meshercoversLightEngine: torch place and remove, stone roof sky, slab occlusion, a 1.17.1update_lightfixture, and edit-column promotion ahead of bulk without materializing an unknown section as air.