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
Run the following command
/give @p shulker_box[minecraft:rarity=epic]
Place the shulker box while in Creative Mode
Place items inside the shulker box and break it
✔ You get the shulker box with the correct components
Do the same thing again but in Survival Mode
❌ You don't get the shulker box with the correct components
Related issues
Attachments
Comments


Can confirm:
[media]
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.