NBT pieces with block entity deadlocks or stalls the game. For example, if a fossil attempts to replace a bed from a village or sign from an igloo basement and such.
- Attached datapack has all fossil NBT files replaced with files which only contain furnaces, barrels, and lecterns.
- Overrode the PlacedFeature for the fossil to increase its spawnrate to 40.
- Created a minecraft:desert_barebones biome json that only contains this PlacedFeature to make testing show it is that feature that is
- Replaced the overworld to only spawn that biome.
- Enter the world with the datapack. It may take a bit of time to create. Start travelling. The next chunk created will be stuck and the game will be stalled. (IF IT DOES NOT DEADLOCK HERE, exit and re-enter the world so that the block entities' stored level is not null and it should definitely deadlock when re-entering and trying to generate chunks.)
I attached a thread dump as well. Look at the Worker-Main-17 thread which is stuck.
This maximizes the chances of the new fossil to replace the blockentities placed by other fossils and deadlock the game. By default, fossils will not replace minecraft:feature_cannot_replace tagged blocks which includes spawners and chests. This is why this deadlock is trickier to trigger in vanilla.
The issue lies in StructureTemplate's placeInWorld method at line 329 where it calls blockentity2.setChanged(). Since placeInWorld is called during worldgen in the FossilFeature's class, the passed in world is a WorldGenRegion which is what all getBlockState/setBlockState calls must use. If a ServerLevel is used instead in a thread during worldgen, ServerLevel will park and wait for the chunk to finish generating before returning/setting a block which means, it causes the worldgen thread to wait on itself forever. A deadlock. Inside blockentity2.setChanged();, it is using the block entity's level which is a ServerLevel instead of a WorldGenRegion and later, it calls .getBlockState(...) and the deadlock is achieved. A temporary solution can be to only ever call blockentity2.setChanged(); if the level passed into StructureTemplate's placeInWorld is not a WorldGenRegion, thus bypassing the deadlock entirely.
There is a second spot that can deadlock and that is with the Clearable.tryClear(blockentity); in StructureTemplate's placeInWorld if the block entity being cleared is a lectern. This is because the lectern tries to call clearContent from the tryClear and it then calls this.setBook(ItemStack.EMPTY); and in there, it calls this.setChanged(); and triggers the deadlock described in the paragraph above this one.