mojira.dev
MC-276161

Shulker boxes don't keep all component data when broken in survival

In creative mode, when breaking a shulker box with items inside it drops, it with the correct added components. However, in survival mode, it does not drop with the correct added components like in creative mode.

Steps to reproduce

  1. Run the following command

    /give @p shulker_box[minecraft:rarity=epic]
  1. Place the shulker box while in Creative Mode

  2. Place items inside the shulker box and break it

  3. ✔ You get the shulker box with the correct components

  4. Do the same thing again but in Survival Mode

  5. ❌ You don't get the shulker box with the correct components

Related issues

Attachments

Comments

migrated
[media]
BugTracker

Can confirm:

[media]
Fungus

Cause any item dropped from the block that was broken by player in survival mode depends on the loot table, and the the contents of include and exclude in copy_components function of item modifier of the loot table determines what components to keep when a block entity is broken. Checking data/minecraft/loot_table/blocks/shulker_box.json from vanilla datapack, you can find

{
  "type": "minecraft:block",
  "pools": [
    {
      "bonus_rolls": 0.0,
      "entries": [
        {
          "type": "minecraft:item",
          "functions": [
            {
              "function": "minecraft:copy_components",
              "include": [
                "minecraft:custom_name",
                "minecraft:container",
                "minecraft:lock",
                "minecraft:container_loot"
              ],
              "source": "block_entity"
            }
          ],
          "name": "minecraft:shulker_box"
        }
      ],
      "rolls": 1.0
    }
  ],
  "random_sequence": "minecraft:blocks/shulker_box"
}

Obviously there are 4 kinds of components in total are kept. If you wanna add new component types, just write down a datapack where the loot table of shulker box are copied to the same relative path, and yet plus your expected components like minecraft:rarity into include. Though all shulker boxes might resolved by the same steps, it is the most effective methods without modifying the source code of Minecraft by modding or something else.


If you are familiar with modding, taking yarn mapping for example, you need to mixin like

@Mixin(CopyComponentsLootFunction.class)
public abstract class CopyComponentsLootFunctionMixin implements CCLFTouch {
    @Final @Shadow
    @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
    private Optional<List<ComponentType<?>>> include;

    @Mutable @Final @Shadow
    private Predicate<ComponentType<?>> filter;

    @Override
    public void mod$touch(List<ComponentType<?>> types) {
        include.ifPresent(componentTypes ->
                filter = Stream.concat(componentTypes.stream(), types.stream()).toList()::contains);
    }
}

And place the following hook (in Kotlin, you can convert it to Java version) after the server started and datapack reloaded

Registries.BLOCK.iterateEntries(BlockTags.SHULKER_BOXES).forEach { entry ->
    val lootTable = server.reloadableRegistries.getLootTable(entry.value().lootTableKey.get()) as LootTableAccessor
    (lootTable.pools[0]?.entries[0] as? LeafEntry as? LeafEntryAccessor)
        ?.functions?.filterIsInstance<CopyComponentsLootFunction>()?.forEach {
            (it as CCLFTouch).`mod$touch`(listOf(DataComponentTypes.RARITY))
        }
}

Where server is MinecraftServer instance, CCLFTouch is a defined interface, LootTableAccessor and LeafEntryAccessor are used to access certain private fields, which are trivial.

Bluebird

(Unassigned)

Confirmed

Platform

Important

Block states

1.21.1, 24w35a, 24w37a, 24w39a, 24w40a, ..., 1.21.2 Pre-Release 2, 1.21.2, 1.21.3, 1.21.4 Pre-Release 1, 1.21.4

Retrieved