Skip to content

Fixes #1532. Also fixes the Sable side of Creators-of-Create/Create#10255. - #1537

Open
aihaoDIYlove wants to merge 3 commits into
ryanhcode:mainfrom
aihaoDIYlove:fix/zero-mass-item-duplication
Open

Fixes #1532. Also fixes the Sable side of Creators-of-Create/Create#10255.#1537
aihaoDIYlove wants to merge 3 commits into
ryanhcode:mainfrom
aihaoDIYlove:fix/zero-mass-item-duplication

Conversation

@aihaoDIYlove

@aihaoDIYlove aihaoDIYlove commented Sep 5, 2026

Copy link
Copy Markdown

Fixes #1532. Also fixes the Sable side of Creators-of-Create/Create#10255.

Bug 1: destroyAllBlocks drops items when a plot is invalidated

MassTracker.isInvalid() can only trigger once no mass-bearing blocks remain in the plot — any remaining mass-bearing block would keep the tracker valid. ServerLevelPlot.destroyAllBlocks() therefore only ever destroys air/weightless decorations with destroyBlock(pos, true), while those blocks have already been restored, or are in the process of being restored, by the disassembly sweep. This results in item duplication during disassembly, independent of sweep direction.

Storage blocks with empty collision shapes are the worst case — a shelf or rack holding a loaded shulker box can duplicate the box together with all of its contents on every cycle, even though the shulker itself has mass and never triggers the invalidation.

Fix (bc89822)

destroyAllBlocks(boolean dropItems): the invalidation call sites pass false and remove blocks by detaching their block entity and replacing them with the fluid's legacy state. This also suppresses the destroy-level event and prevents container onRemove hooks from spilling contents. The heat-split path keeps dropping, as no sweep races it there.

Bug 2: non-clearable block entities drop their contents when moved

moveBlocks snapshots the source BE and relies on Clearable.tryClear to empty it before the source is destroyed. BEs that do not implement Clearable keep their contents on the source position, and the unconditional onRemove(...) hook can then drop contents that the destination copy already received via the NBT snapshot (on disassembly the duplicate is voided inside the dying sub-level instead).

This was reported independently in Creators-of-Create/Create#10255 (item drain, mechanical arm) — and the existing #sable:silent_assembly_removal tag exists precisely because of this behavior.

Fix (6ef4a6d)

Non-clearable BEs are detached before the source destruction — the same escape hatch as the silent removal tag, applied automatically. Every vanilla item-holding BE implements Clearable via Container, so this only affects modded blocks, and fixes them all at the engine level instead of requiring per-mod tags.

Testing

  • New gametest testMasslessInvalidationDoesNotDrop (f1c0189): assemble a stone block with zero-mass wall torches, invalidate the tracker through the real block-change path, and assert that nothing drops near the plot when it is demolished. Fails on main (torch drops), passes with this PR.
  • Verified in-game (NeoForge 1.21.1, Create 6.0.10 + Aeronautics): torches / signs / storage decorations × assembly / disassembly — no drops, no destroy sounds or particles, and no duplicated container contents.

Note: vanilla has no non-Clearable item-holding BE, so Bug 2 has no automated coverage here; happy to add a gametest-only test block if preferred.

@CLAassistant

CLAassistant commented Sep 5, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Mass invalidation (MassTracker.isInvalid) can only trigger while the plot
contains air and zero-mass blocks - any block with mass would keep the
tracker valid. destroyBlock(pos, true) therefore only ever dropped
weightless decorations, which the disassembly sweep has already moved back
to the world: a clean item duplication. Storage blocks with empty
collision shapes are the worst case - a shelf or rack holding a loaded
shulker box duplicates the box together with all of its contents on every
disassembly, even though the shulker itself has mass and never triggers
the invalidation.

Pass dropItems=false at the invalidation call sites and remove blocks by
detaching their block entity and replacing them with the fluid's legacy
state: this also suppresses the destroy level event and neighbor updates -
nothing needs to react to a wholesale plot deletion, and updates could pop
foreign blocks living in adjacent plot grid cells - and prevents container
onRemove hooks from spilling contents. The heat-split path keeps dropping,
as no sweep races it there.
…level

moveBlocks snapshots the source BE and relies on Clearable.tryClear to
empty it before the source is destroyed. Block entities that do not
implement Clearable keep their contents on the source position, and the
unconditional onRemove(...) hook then drops a duplicate of everything the
destination copy already received via the NBT snapshot (on disassembly the
duplicate is voided inside the dying sub-level instead). Detach the source
block entity - the same escape hatch as #sable:silent_assembly_removal,
applied automatically. Every vanilla item-holding BE implements Clearable
via Container, so this only affects modded blocks.
testMasslessInvalidationDoesNotDrop: assemble a stone block with a
zero-mass wall torch on each side, remove the stone so the mass tracker
invalidates through the real block-change path, and assert that no torch
items drop anywhere in the level when the plot is demolished. The drops
land at the plot's position in the level, far away from the test
structure, and only torches this test placed count as evidence - the
shared plot grid can pop foreign decorations from neighbouring grid
content during assembly.

Fails on main with four torch items, passes with the previous commits.
Non-clearable block entities cannot be covered with vanilla blocks (all
of them implement Clearable via Container).

bin/ is the default output directory of the VS Code Java language server
and Eclipse, mirroring the existing out/ ignore for IntelliJ.
@aihaoDIYlove
aihaoDIYlove force-pushed the fix/zero-mass-item-duplication branch from d5b5839 to f1c0189 Compare September 5, 2026 01:32
@aihaoDIYlove
aihaoDIYlove marked this pull request as ready for review September 5, 2026 05:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Zero-mass blocks get duplicated as item drops during sub-level disassembly (destroyAllBlocks fires with drops when massTracker hits 0)

2 participants