Lilium Dynamic Light - V2.1.0.mcaddon
What's new
Changelog - Lilium Dynamic Light
[2.1.0]
Added
Ignore List (Fixes the Aether Legends Portal Conflict): Any identifier can now be excluded from dynamic lighting entirely, from the
Ignore Listentry in the main menu or/scriptevent ldl:ignored. One list covers items, mobs and blocks, because a single source can be more than one of those:minecraft:end_crystalis the item in your hand andminecraft:ender_crystalthe entity once placed, and ignoring only one of them leaves the other lit.RuntimeConfig.isIgnored()is checked at the top of all three resolver entry points —getItemLightLevel(),getEntityLightInfo()andgetBlockLightInfo()— ahead of the user config, theldl:lightcomponent,ldl:*tags and the vanilla tables, so an ignored identifier emits nothing no matter who declared it. Equipped items are covered throughgetEquippedLightInfo(), which resolves each slot withgetItemLightLevel().This exists because an invisible light block is still a block. Any add-on that requires a specific space near a light source to stay air fails its own check, since
ldl:light_block_*is a real custom block andisAiris false for it. Aether Legends is the case that surfaced it: its portal frame is built fromminecraft:glowstoneand its validator rejects the frame unless every cell of the 2x3 interior is air. Glowstone is a level-15 emitter in the catalog and the first entry ofSEARCH_OFFSETSis the block directly above, so the light for a frame's bottom glowstone lands inside the portal and the portal can never be lit. Addingminecraft:glowstoneto the list resolves it without giving up block lights everywhere else.Adding or removing an entry releases what is already lit immediately, rather than leaving it glowing until the next 100-tick block sweep. Eleven new translation strings ship in all 19 languages.
Changed
- Block Light Precedence Now Matches Items and Mobs:
LightResolver.getBlockLightInfo()resolved in the order component → tags → user config → vanilla tables, while items and mobs resolved user config first. A block carrying another creator'sldl:lightcomponent orldl:*tags could therefore not be overridden from the Blocks menu, even though the equivalent item could — the entry was saved, the menu showed it, and nothing changed in the world. The block path is now user config → component → tags → vanilla, identical to the item and mob paths, so an entry added in the menu always wins. - Single Shared
LIGHT_COLOR_SET:api/components.js,api/tags.js,modules/config.jsandmodules/color.jseach importedLIGHT_COLOR_NAMESand built their own privatenew Set(...)from it, four copies of the same lookup table that had to stay in sync by hand. The set is now exported once frommodules/catalog.jsalongside the names array and imported where needed.
Fixed
Blank Menu Screen When Another Pack Ships Its Own
_ui_defs.json: On a server with several resource packs the menu could open to nothing at all — the screen took over, the HUD disappeared, and no card was drawn. The custom menu was split across two files that are registered in different ways.ui/server_form.jsonreplaces a vanilla file, so it applies by path alone; it hides the vanilla form whenever the title carries theldl:prefix and inserts the factory that should draw the replacement.ui/ldl_screen.jsonwas a new file, and existed only becauseui/_ui_defs.jsondeclared it. Bedrock does not merge_ui_defs.jsonacross packs — the highest-priority one wins outright — so any other pack shipping its own list silently dropped ours. That left exactly the broken half applied: the vanilla form hidden, and the card meant to replace it never defined. Aether Legends is one such pack, listing nine files of its own and noserver_form.json. Everything now lives inui/server_form.jsonunder theserver_formnamespace, which Bedrock loads through the vanilla path, andui/ldl_screen.jsonandui/_ui_defs.jsonare gone. There is no longer a file another pack can deregister, and no UI behavior changed for a world where it already worked.668 Missing Translation Strings Across 17 Languages: Two feature waves were never localized past
en_USandes_ES. The Blocks menu (12 keys:ldl.blocks.*,ldl.addblock.title,ldl.msg.block*,ldl.placeholder.block,ldl.toggle.deleteBlock) was missing from 17 languages,es_MXincluded. Colored light and hand-swap mode (29 more keys: the 16ldl.color.*plusldl.color.auto,ldl.field.color,ldl.items.vibrant,ldl.msg.vibrant*,ldl.gesture.*,ldl.field.gestureMode,ldl.common.on/off) were missing from 16. A player on any of those languages saw literal keys such asldl.color.magentawherever those screens appeared. Every language now carries the fullen_USkey set, in the same order, with the wording derived from each file's own existing conventions, and the two new sections introduced by localized headers. The fourth Features toggle,ldl.feature.blockLights, was part of the same gap. The deadldl.feature.gestureskey, orphaned in every file since the gesture toggle became theldl.field.gestureModedropdown, was removed.Owner Key Sets Leaked on Purge and Release:
_ownerKeys()creates the owner's key set when it does not exist, and both_releasePosition()and_purgeEntriesInBox()called it purely to remove a key. For an owner that was already untracked — an entity removed moments earlier, or any owner of an entry cleared by/scriptevent ldl:fix— this created an empty set in_keysByOwnerthat no cleanup path ever deleted, sincecleanupAll()only runs for owners the engine still knows about. Both call sites now usethis._keysByOwner.get(ownerId)?.delete(key)and no longer create state as a side effect of removing it.Stuck Block-Interaction Mark Swallowing Interact Swaps: In
interactgesture mode,itemStartUseOnmarks the player so that theitemUsethat follows is treated as a block interaction rather than an offhand swap. The mark was aSetentry cleared only by that followingitemUse, so any interaction that did not produce one — a cancelled placement, a failed use, an item consumed by another handler — left the player marked indefinitely and silently consumed their next intentional swap. The mark is now aMapholding the tick it was set, and only suppresses a swap withinBLOCK_INTERACTION_WINDOW_TICKS(5 ticks); it is consumed on every attempt either way, so no residue builds up.
Removed
- Light Registry Persistence and the Persistence Toggle (Breaking For the Features Menu): The engine mirrored its light registry into world dynamic properties every 200 ticks and on
shutdown, hydrated it back on load, and exposed the whole thing as aPersistence Across Reloadstoggle. The mirror never did anything: after a reload_entriesis empty, soisPositionActive()is false for every surviving light block and the block's ownminecraft:tickreverts it to itsldl:basewithin two ticks — roughly 98 ticks beforesweepRestoredEntries()would have looked at the hydrated entry. The registry path also lost a second race it could not win:hydrate()ran only when the toggle was on, so turning it off and reloading left the saved snapshot unread while its blocks stayed in the world, tracked by nothing and removable only with/scriptevent ldl:fix. Both the write and the toggle are gone andapi/storage.jsis deleted; what remains is the passive self-destruct that was already doing the work. Worlds saved by 2.0.0 and earlier are migrated on load —purgeLegacyLightDatabase()inmain.jsclearsldl:lightdb_countand itsldl:lightdb_chunk_*parts once, so the old snapshot does not sit in the world file forever. Cleanup behavior is unchanged for players;persistenceis no longer a key inFEATURE_KEYSand worlds that had it saved simply ignore it. - Hydration Timeout on the Pending-Restore Queue:
_restoredEntriesserved two purposes: replaying the on-disk snapshot, and retrying restores whose chunk was not loaded (the 2.0.0 orphaned-light fix). With the first gone, every entry is queued by_releasePosition()withhydratedAtTick: -Infinity, which made thePENDING_RESTORE_TIMEOUT_TICKScomparison unconditionally true. The field, the constant and thecurrentTickparameter ofsweepRestoredEntries()are removed; the queue is now a plain retry list of{ dimensionId, pos }drained every 40 ticks until each write lands. The orphaned-light behavior it guards is unchanged.
This mod has no additional files