package net.minecraft.util.datafix.fixes; import com.google.common.base.Splitter; import com.mojang.datafixers.DataFix; import com.mojang.datafixers.DataFixUtils; import com.mojang.datafixers.TypeRewriteRule; import com.mojang.datafixers.schemas.Schema; import com.mojang.datafixers.util.Pair; import com.mojang.serialization.Dynamic; import com.mojang.serialization.DynamicOps; import com.mojang.serialization.OptionalDynamic; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.function.UnaryOperator; import java.util.stream.Collectors; import java.util.stream.Stream; import net.minecraft.util.Mth; import net.minecraft.util.datafix.ExtraDataFixUtils; import net.minecraft.util.datafix.LegacyComponentDataFixUtils; import net.minecraft.util.datafix.schemas.NamespacedSchema; import org.jetbrains.annotations.Nullable; public class ItemStackComponentizationFix extends DataFix { private static final int HIDE_ENCHANTMENTS = 1; private static final int HIDE_MODIFIERS = 2; private static final int HIDE_UNBREAKABLE = 4; private static final int HIDE_CAN_DESTROY = 8; private static final int HIDE_CAN_PLACE = 16; private static final int HIDE_ADDITIONAL = 32; private static final int HIDE_DYE = 64; private static final int HIDE_UPGRADES = 128; private static final Set POTION_HOLDER_IDS = Set.of( "minecraft:potion", "minecraft:splash_potion", "minecraft:lingering_potion", "minecraft:tipped_arrow" ); private static final Set BUCKETED_MOB_IDS = Set.of( "minecraft:pufferfish_bucket", "minecraft:salmon_bucket", "minecraft:cod_bucket", "minecraft:tropical_fish_bucket", "minecraft:axolotl_bucket", "minecraft:tadpole_bucket" ); private static final List BUCKETED_MOB_TAGS = List.of( "NoAI", "Silent", "NoGravity", "Glowing", "Invulnerable", "Health", "Age", "Variant", "HuntingCooldown", "BucketVariantTag" ); private static final Set BOOLEAN_BLOCK_STATE_PROPERTIES = Set.of( "attached", "bottom", "conditional", "disarmed", "drag", "enabled", "extended", "eye", "falling", "hanging", "has_bottle_0", "has_bottle_1", "has_bottle_2", "has_record", "has_book", "inverted", "in_wall", "lit", "locked", "occupied", "open", "persistent", "powered", "short", "signal_fire", "snowy", "triggered", "unstable", "waterlogged", "berries", "bloom", "shrieking", "can_summon", "up", "down", "north", "east", "south", "west", "slot_0_occupied", "slot_1_occupied", "slot_2_occupied", "slot_3_occupied", "slot_4_occupied", "slot_5_occupied", "cracked", "crafting" ); private static final Splitter PROPERTY_SPLITTER = Splitter.on(','); public ItemStackComponentizationFix(Schema outputSchema) { super(outputSchema, true); } private static void fixItemStack(ItemStackComponentizationFix.ItemStackData itemStackData, Dynamic tag) { int i = itemStackData.removeTag("HideFlags").asInt(0); itemStackData.moveTagToComponent("Damage", "minecraft:damage", tag.createInt(0)); itemStackData.moveTagToComponent("RepairCost", "minecraft:repair_cost", tag.createInt(0)); itemStackData.moveTagToComponent("CustomModelData", "minecraft:custom_model_data"); itemStackData.removeTag("BlockStateTag").result().ifPresent(dynamic -> itemStackData.setComponent("minecraft:block_state", fixBlockStateTag(dynamic))); itemStackData.moveTagToComponent("EntityTag", "minecraft:entity_data"); itemStackData.fixSubTag("BlockEntityTag", false, dynamic -> { String string = NamespacedSchema.ensureNamespaced(dynamic.get("id").asString("")); dynamic = fixBlockEntityTag(itemStackData, dynamic, string); Dynamic dynamic2 = dynamic.remove("id"); return dynamic2.equals(dynamic.emptyMap()) ? dynamic2 : dynamic; }); itemStackData.moveTagToComponent("BlockEntityTag", "minecraft:block_entity_data"); if (itemStackData.removeTag("Unbreakable").asBoolean(false)) { Dynamic dynamic = tag.emptyMap(); if ((i & 4) != 0) { dynamic = dynamic.set("show_in_tooltip", tag.createBoolean(false)); } itemStackData.setComponent("minecraft:unbreakable", dynamic); } fixEnchantments(itemStackData, tag, "Enchantments", "minecraft:enchantments", (i & 1) != 0); if (itemStackData.is("minecraft:enchanted_book")) { fixEnchantments(itemStackData, tag, "StoredEnchantments", "minecraft:stored_enchantments", (i & 32) != 0); } itemStackData.fixSubTag("display", false, dynamic -> fixDisplay(itemStackData, dynamic, i)); fixAdventureModeChecks(itemStackData, tag, i); fixAttributeModifiers(itemStackData, tag, i); Optional> optional = itemStackData.removeTag("Trim").result(); if (optional.isPresent()) { Dynamic dynamic2 = (Dynamic)optional.get(); if ((i & 128) != 0) { dynamic2 = dynamic2.set("show_in_tooltip", dynamic2.createBoolean(false)); } itemStackData.setComponent("minecraft:trim", dynamic2); } if ((i & 32) != 0) { itemStackData.setComponent("minecraft:hide_additional_tooltip", tag.emptyMap()); } if (itemStackData.is("minecraft:crossbow")) { itemStackData.removeTag("Charged"); itemStackData.moveTagToComponent("ChargedProjectiles", "minecraft:charged_projectiles", tag.createList(Stream.empty())); } if (itemStackData.is("minecraft:bundle")) { itemStackData.moveTagToComponent("Items", "minecraft:bundle_contents", tag.createList(Stream.empty())); } if (itemStackData.is("minecraft:filled_map")) { itemStackData.moveTagToComponent("map", "minecraft:map_id"); Map, ? extends Dynamic> map = (Map, ? extends Dynamic>)itemStackData.removeTag("Decorations") .asStream() .map(ItemStackComponentizationFix::fixMapDecoration) .collect(Collectors.toMap(Pair::getFirst, Pair::getSecond, (dynamic, dynamic2) -> dynamic)); if (!map.isEmpty()) { itemStackData.setComponent("minecraft:map_decorations", tag.createMap(map)); } } if (itemStackData.is(POTION_HOLDER_IDS)) { fixPotionContents(itemStackData, tag); } if (itemStackData.is("minecraft:writable_book")) { fixWritableBook(itemStackData, tag); } if (itemStackData.is("minecraft:written_book")) { fixWrittenBook(itemStackData, tag); } if (itemStackData.is("minecraft:suspicious_stew")) { itemStackData.moveTagToComponent("effects", "minecraft:suspicious_stew_effects"); } if (itemStackData.is("minecraft:debug_stick")) { itemStackData.moveTagToComponent("DebugProperty", "minecraft:debug_stick_state"); } if (itemStackData.is(BUCKETED_MOB_IDS)) { fixBucketedMobData(itemStackData, tag); } if (itemStackData.is("minecraft:goat_horn")) { itemStackData.moveTagToComponent("instrument", "minecraft:instrument"); } if (itemStackData.is("minecraft:knowledge_book")) { itemStackData.moveTagToComponent("Recipes", "minecraft:recipes"); } if (itemStackData.is("minecraft:compass")) { fixLodestoneTracker(itemStackData, tag); } if (itemStackData.is("minecraft:firework_rocket")) { fixFireworkRocket(itemStackData); } if (itemStackData.is("minecraft:firework_star")) { fixFireworkStar(itemStackData); } if (itemStackData.is("minecraft:player_head")) { itemStackData.removeTag("SkullOwner").result().ifPresent(dynamic -> itemStackData.setComponent("minecraft:profile", fixProfile(dynamic))); } } private static Dynamic fixBlockStateTag(Dynamic tag) { return DataFixUtils.orElse(tag.asMapOpt().result().map(stream -> (Map)stream.collect(Collectors.toMap(Pair::getFirst, pair -> { String string = ((Dynamic)pair.getFirst()).asString(""); Dynamic dynamic = (Dynamic)pair.getSecond(); if (BOOLEAN_BLOCK_STATE_PROPERTIES.contains(string)) { Optional optional = dynamic.asBoolean().result(); if (optional.isPresent()) { return dynamic.createString(String.valueOf(optional.get())); } } Optional optional = dynamic.asNumber().result(); return optional.isPresent() ? dynamic.createString(((Number)optional.get()).toString()) : dynamic; }))).map(tag::createMap), tag); } private static Dynamic fixDisplay(ItemStackComponentizationFix.ItemStackData itemStackData, Dynamic tag, int hideFlags) { itemStackData.setComponent("minecraft:custom_name", tag.get("Name")); itemStackData.setComponent("minecraft:lore", tag.get("Lore")); Optional optional = tag.get("color").asNumber().result().map(Number::intValue); boolean bl = (hideFlags & 64) != 0; if (optional.isPresent() || bl) { Dynamic dynamic = tag.emptyMap().set("rgb", tag.createInt((Integer)optional.orElse(10511680))); if (bl) { dynamic = dynamic.set("show_in_tooltip", tag.createBoolean(false)); } itemStackData.setComponent("minecraft:dyed_color", dynamic); } Optional optional2 = tag.get("LocName").asString().result(); if (optional2.isPresent()) { itemStackData.setComponent("minecraft:item_name", LegacyComponentDataFixUtils.createTranslatableComponent(tag.getOps(), (String)optional2.get())); } if (itemStackData.is("minecraft:filled_map")) { itemStackData.setComponent("minecraft:map_color", tag.get("MapColor")); tag = tag.remove("MapColor"); } return tag.remove("Name").remove("Lore").remove("color").remove("LocName"); } private static Dynamic fixBlockEntityTag(ItemStackComponentizationFix.ItemStackData itemStackData, Dynamic tag, String entityId) { itemStackData.setComponent("minecraft:lock", tag.get("Lock")); tag = tag.remove("Lock"); Optional> optional = tag.get("LootTable").result(); if (optional.isPresent()) { Dynamic dynamic = tag.emptyMap().set("loot_table", (Dynamic)optional.get()); long l = tag.get("LootTableSeed").asLong(0L); if (l != 0L) { dynamic = dynamic.set("seed", tag.createLong(l)); } itemStackData.setComponent("minecraft:container_loot", dynamic); tag = tag.remove("LootTable").remove("LootTableSeed"); } return switch (entityId) { case "minecraft:skull" -> { itemStackData.setComponent("minecraft:note_block_sound", tag.get("note_block_sound")); yield tag.remove("note_block_sound"); } case "minecraft:decorated_pot" -> { itemStackData.setComponent("minecraft:pot_decorations", tag.get("sherds")); Optional> optional2 = tag.get("item").result(); if (optional2.isPresent()) { itemStackData.setComponent( "minecraft:container", tag.createList(Stream.of(tag.emptyMap().set("slot", tag.createInt(0)).set("item", (Dynamic)optional2.get()))) ); } yield tag.remove("sherds").remove("item"); } case "minecraft:banner" -> { itemStackData.setComponent("minecraft:banner_patterns", tag.get("patterns")); Optional optional2 = tag.get("Base").asNumber().result(); if (optional2.isPresent()) { itemStackData.setComponent("minecraft:base_color", tag.createString(ExtraDataFixUtils.dyeColorIdToName(((Number)optional2.get()).intValue()))); } yield tag.remove("patterns").remove("Base"); } case "minecraft:shulker_box", "minecraft:chest", "minecraft:trapped_chest", "minecraft:furnace", "minecraft:ender_chest", "minecraft:dispenser", "minecraft:dropper", "minecraft:brewing_stand", "minecraft:hopper", "minecraft:barrel", "minecraft:smoker", "minecraft:blast_furnace", "minecraft:campfire", "minecraft:chiseled_bookshelf", "minecraft:crafter" -> { List> list = tag.get("Items") .asList(dynamicx -> dynamicx.emptyMap().set("slot", dynamicx.createInt(dynamicx.get("Slot").asByte((byte)0) & 255)).set("item", dynamicx.remove("Slot"))); if (!list.isEmpty()) { itemStackData.setComponent("minecraft:container", tag.createList(list.stream())); } yield tag.remove("Items"); } case "minecraft:beehive" -> { itemStackData.setComponent("minecraft:bees", tag.get("bees")); yield tag.remove("bees"); } default -> tag; }; } private static void fixEnchantments( ItemStackComponentizationFix.ItemStackData itemStackData, Dynamic tag, String key, String component, boolean hideEnchantments ) { OptionalDynamic optionalDynamic = itemStackData.removeTag(key); List> list = optionalDynamic.asList(Function.identity()).stream().flatMap(dynamicx -> parseEnchantment(dynamicx).stream()).toList(); if (!list.isEmpty() || hideEnchantments) { Dynamic dynamic = tag.emptyMap(); Dynamic dynamic2 = tag.emptyMap(); for (Pair pair : list) { dynamic2 = dynamic2.set(pair.getFirst(), tag.createInt(pair.getSecond())); } dynamic = dynamic.set("levels", dynamic2); if (hideEnchantments) { dynamic = dynamic.set("show_in_tooltip", tag.createBoolean(false)); } itemStackData.setComponent(component, dynamic); } if (optionalDynamic.result().isPresent() && list.isEmpty()) { itemStackData.setComponent("minecraft:enchantment_glint_override", tag.createBoolean(true)); } } private static Optional> parseEnchantment(Dynamic enchantmentTag) { return enchantmentTag.get("id") .asString() .>apply2stable((string, number) -> Pair.of(string, Mth.clamp(number.intValue(), 0, 255)), enchantmentTag.get("lvl").asNumber()) .result(); } private static void fixAdventureModeChecks(ItemStackComponentizationFix.ItemStackData itemStackData, Dynamic tag, int hideFlags) { fixBlockStatePredicates(itemStackData, tag, "CanDestroy", "minecraft:can_break", (hideFlags & 8) != 0); fixBlockStatePredicates(itemStackData, tag, "CanPlaceOn", "minecraft:can_place_on", (hideFlags & 16) != 0); } private static void fixBlockStatePredicates( ItemStackComponentizationFix.ItemStackData itemStackData, Dynamic tag, String key, String component, boolean hide ) { Optional> optional = itemStackData.removeTag(key).result(); if (!optional.isEmpty()) { Dynamic dynamic = tag.emptyMap() .set( "predicates", tag.createList( ((Dynamic)optional.get()) .asStream() .map(dynamicx -> DataFixUtils.orElse(dynamicx.asString().map(string -> fixBlockStatePredicate(dynamicx, string)).result(), dynamicx)) ) ); if (hide) { dynamic = dynamic.set("show_in_tooltip", tag.createBoolean(false)); } itemStackData.setComponent(component, dynamic); } } private static Dynamic fixBlockStatePredicate(Dynamic tag, String blockId) { int i = blockId.indexOf(91); int j = blockId.indexOf(123); int k = blockId.length(); if (i != -1) { k = i; } if (j != -1) { k = Math.min(k, j); } String string = blockId.substring(0, k); Dynamic dynamic = tag.emptyMap().set("blocks", tag.createString(string.trim())); int l = blockId.indexOf(93); if (i != -1 && l != -1) { Dynamic dynamic2 = tag.emptyMap(); for (String string2 : PROPERTY_SPLITTER.split(blockId.substring(i + 1, l))) { int m = string2.indexOf(61); if (m != -1) { String string3 = string2.substring(0, m).trim(); String string4 = string2.substring(m + 1).trim(); dynamic2 = dynamic2.set(string3, tag.createString(string4)); } } dynamic = dynamic.set("state", dynamic2); } int n = blockId.indexOf(125); if (j != -1 && n != -1) { dynamic = dynamic.set("nbt", tag.createString(blockId.substring(j, n + 1))); } return dynamic; } private static void fixAttributeModifiers(ItemStackComponentizationFix.ItemStackData itemStackData, Dynamic tag, int hideFlags) { OptionalDynamic optionalDynamic = itemStackData.removeTag("AttributeModifiers"); if (!optionalDynamic.result().isEmpty()) { boolean bl = (hideFlags & 2) != 0; List> list = optionalDynamic.asList(ItemStackComponentizationFix::fixAttributeModifier); Dynamic dynamic = tag.emptyMap().set("modifiers", tag.createList(list.stream())); if (bl) { dynamic = dynamic.set("show_in_tooltip", tag.createBoolean(false)); } itemStackData.setComponent("minecraft:attribute_modifiers", dynamic); } } private static Dynamic fixAttributeModifier(Dynamic tag) { Dynamic dynamic = tag.emptyMap().set("name", tag.createString("")).set("amount", tag.createDouble(0.0)).set("operation", tag.createString("add_value")); dynamic = Dynamic.copyField(tag, "AttributeName", dynamic, "type"); dynamic = Dynamic.copyField(tag, "Slot", dynamic, "slot"); dynamic = Dynamic.copyField(tag, "UUID", dynamic, "uuid"); dynamic = Dynamic.copyField(tag, "Name", dynamic, "name"); dynamic = Dynamic.copyField(tag, "Amount", dynamic, "amount"); return Dynamic.copyAndFixField(tag, "Operation", dynamic, "operation", dynamicx -> { return dynamicx.createString(switch (dynamicx.asInt(0)) { case 1 -> "add_multiplied_base"; case 2 -> "add_multiplied_total"; default -> "add_value"; }); }); } private static Pair, Dynamic> fixMapDecoration(Dynamic tag) { Dynamic dynamic = DataFixUtils.orElseGet(tag.get("id").result(), () -> tag.createString("")); Dynamic dynamic2 = tag.emptyMap() .set("type", tag.createString(fixMapDecorationType(tag.get("type").asInt(0)))) .set("x", tag.createDouble(tag.get("x").asDouble(0.0))) .set("z", tag.createDouble(tag.get("z").asDouble(0.0))) .set("rotation", tag.createFloat((float)tag.get("rot").asDouble(0.0))); return Pair.of(dynamic, dynamic2); } private static String fixMapDecorationType(int decorationType) { return switch (decorationType) { case 1 -> "frame"; case 2 -> "red_marker"; case 3 -> "blue_marker"; case 4 -> "target_x"; case 5 -> "target_point"; case 6 -> "player_off_map"; case 7 -> "player_off_limits"; case 8 -> "mansion"; case 9 -> "monument"; case 10 -> "banner_white"; case 11 -> "banner_orange"; case 12 -> "banner_magenta"; case 13 -> "banner_light_blue"; case 14 -> "banner_yellow"; case 15 -> "banner_lime"; case 16 -> "banner_pink"; case 17 -> "banner_gray"; case 18 -> "banner_light_gray"; case 19 -> "banner_cyan"; case 20 -> "banner_purple"; case 21 -> "banner_blue"; case 22 -> "banner_brown"; case 23 -> "banner_green"; case 24 -> "banner_red"; case 25 -> "banner_black"; case 26 -> "red_x"; case 27 -> "village_desert"; case 28 -> "village_plains"; case 29 -> "village_savanna"; case 30 -> "village_snowy"; case 31 -> "village_taiga"; case 32 -> "jungle_temple"; case 33 -> "swamp_hut"; default -> "player"; }; } private static void fixPotionContents(ItemStackComponentizationFix.ItemStackData itemStackData, Dynamic tag) { Dynamic dynamic = tag.emptyMap(); Optional optional = itemStackData.removeTag("Potion").asString().result().filter(string -> !string.equals("minecraft:empty")); if (optional.isPresent()) { dynamic = dynamic.set("potion", tag.createString((String)optional.get())); } dynamic = itemStackData.moveTagInto("CustomPotionColor", dynamic, "custom_color"); dynamic = itemStackData.moveTagInto("custom_potion_effects", dynamic, "custom_effects"); if (!dynamic.equals(tag.emptyMap())) { itemStackData.setComponent("minecraft:potion_contents", dynamic); } } private static void fixWritableBook(ItemStackComponentizationFix.ItemStackData itemStackData, Dynamic tag) { Dynamic dynamic = fixBookPages(itemStackData, tag); if (dynamic != null) { itemStackData.setComponent("minecraft:writable_book_content", tag.emptyMap().set("pages", dynamic)); } } private static void fixWrittenBook(ItemStackComponentizationFix.ItemStackData itemStackData, Dynamic tag) { Dynamic dynamic = fixBookPages(itemStackData, tag); String string = itemStackData.removeTag("title").asString(""); Optional optional = itemStackData.removeTag("filtered_title").asString().result(); Dynamic dynamic2 = tag.emptyMap(); dynamic2 = dynamic2.set("title", createFilteredText(tag, string, optional)); dynamic2 = itemStackData.moveTagInto("author", dynamic2, "author"); dynamic2 = itemStackData.moveTagInto("resolved", dynamic2, "resolved"); dynamic2 = itemStackData.moveTagInto("generation", dynamic2, "generation"); if (dynamic != null) { dynamic2 = dynamic2.set("pages", dynamic); } itemStackData.setComponent("minecraft:written_book_content", dynamic2); } @Nullable private static Dynamic fixBookPages(ItemStackComponentizationFix.ItemStackData itemStackData, Dynamic tag) { List list = itemStackData.removeTag("pages").asList(dynamic -> dynamic.asString("")); Map map = itemStackData.removeTag("filtered_pages").asMap(dynamic -> dynamic.asString("0"), dynamic -> dynamic.asString("")); if (list.isEmpty()) { return null; } else { List> list2 = new ArrayList(list.size()); for (int i = 0; i < list.size(); i++) { String string = (String)list.get(i); String string2 = (String)map.get(String.valueOf(i)); list2.add(createFilteredText(tag, string, Optional.ofNullable(string2))); } return tag.createList(list2.stream()); } } private static Dynamic createFilteredText(Dynamic tag, String unfilteredText, Optional filteredText) { Dynamic dynamic = tag.emptyMap().set("raw", tag.createString(unfilteredText)); if (filteredText.isPresent()) { dynamic = dynamic.set("filtered", tag.createString((String)filteredText.get())); } return dynamic; } private static void fixBucketedMobData(ItemStackComponentizationFix.ItemStackData itemStackData, Dynamic tag) { Dynamic dynamic = tag.emptyMap(); for (String string : BUCKETED_MOB_TAGS) { dynamic = itemStackData.moveTagInto(string, dynamic, string); } if (!dynamic.equals(tag.emptyMap())) { itemStackData.setComponent("minecraft:bucket_entity_data", dynamic); } } private static void fixLodestoneTracker(ItemStackComponentizationFix.ItemStackData itemStackData, Dynamic tag) { Optional> optional = itemStackData.removeTag("LodestonePos").result(); Optional> optional2 = itemStackData.removeTag("LodestoneDimension").result(); if (!optional.isEmpty() || !optional2.isEmpty()) { boolean bl = itemStackData.removeTag("LodestoneTracked").asBoolean(true); Dynamic dynamic = tag.emptyMap(); if (optional.isPresent() && optional2.isPresent()) { dynamic = dynamic.set("target", tag.emptyMap().set("pos", (Dynamic)optional.get()).set("dimension", (Dynamic)optional2.get())); } if (!bl) { dynamic = dynamic.set("tracked", tag.createBoolean(false)); } itemStackData.setComponent("minecraft:lodestone_tracker", dynamic); } } private static void fixFireworkStar(ItemStackComponentizationFix.ItemStackData itemStackData) { itemStackData.fixSubTag("Explosion", true, dynamic -> { itemStackData.setComponent("minecraft:firework_explosion", fixFireworkExplosion(dynamic)); return dynamic.remove("Type").remove("Colors").remove("FadeColors").remove("Trail").remove("Flicker"); }); } private static void fixFireworkRocket(ItemStackComponentizationFix.ItemStackData itemStackData) { itemStackData.fixSubTag( "Fireworks", true, dynamic -> { Stream> stream = dynamic.get("Explosions").asStream().map(ItemStackComponentizationFix::fixFireworkExplosion); int i = dynamic.get("Flight").asInt(0); itemStackData.setComponent( "minecraft:fireworks", dynamic.emptyMap().set("explosions", dynamic.createList(stream)).set("flight_duration", dynamic.createByte((byte)i)) ); return dynamic.remove("Explosions").remove("Flight"); } ); } private static Dynamic fixFireworkExplosion(Dynamic tag) { tag = tag.set("shape", tag.createString(switch (tag.get("Type").asInt(0)) { case 1 -> "large_ball"; case 2 -> "star"; case 3 -> "creeper"; case 4 -> "burst"; default -> "small_ball"; })).remove("Type"); tag = tag.renameField("Colors", "colors"); tag = tag.renameField("FadeColors", "fade_colors"); tag = tag.renameField("Trail", "has_trail"); return tag.renameField("Flicker", "has_twinkle"); } public static Dynamic fixProfile(Dynamic tag) { Optional optional = tag.asString().result(); if (optional.isPresent()) { return isValidPlayerName((String)optional.get()) ? tag.emptyMap().set("name", tag.createString((String)optional.get())) : tag.emptyMap(); } else { String string = tag.get("Name").asString(""); Optional> optional2 = tag.get("Id").result(); Dynamic dynamic = fixProfileProperties(tag.get("Properties")); Dynamic dynamic2 = tag.emptyMap(); if (isValidPlayerName(string)) { dynamic2 = dynamic2.set("name", tag.createString(string)); } if (optional2.isPresent()) { dynamic2 = dynamic2.set("id", (Dynamic)optional2.get()); } if (dynamic != null) { dynamic2 = dynamic2.set("properties", dynamic); } return dynamic2; } } private static boolean isValidPlayerName(String name) { return name.length() > 16 ? false : name.chars().filter(i -> i <= 32 || i >= 127).findAny().isEmpty(); } @Nullable private static Dynamic fixProfileProperties(OptionalDynamic tag) { Map>>> map = tag.asMap(dynamic -> dynamic.asString(""), dynamic -> dynamic.asList(dynamicx -> { String string = dynamicx.get("Value").asString(""); Optional optional = dynamicx.get("Signature").asString().result(); return Pair.of(string, optional); })); return map.isEmpty() ? null : tag.createList(map.entrySet().stream().flatMap(entry -> ((List)entry.getValue()).stream().map(pair -> { Dynamic dynamic = tag.emptyMap().set("name", tag.createString((String)entry.getKey())).set("value", tag.createString((String)pair.getFirst())); Optional optional = (Optional)pair.getSecond(); return optional.isPresent() ? dynamic.set("signature", tag.createString((String)optional.get())) : dynamic; }))); } @Override protected TypeRewriteRule makeRule() { return this.writeFixAndRead( "ItemStack componentization", this.getInputSchema().getType(References.ITEM_STACK), this.getOutputSchema().getType(References.ITEM_STACK), dynamic -> { Optional> optional = ItemStackComponentizationFix.ItemStackData.read(dynamic).map(itemStackData -> { fixItemStack(itemStackData, itemStackData.tag); return itemStackData.write(); }); return DataFixUtils.orElse(optional, dynamic); } ); } static class ItemStackData { private final String item; private final int count; private Dynamic components; private final Dynamic remainder; Dynamic tag; private ItemStackData(String item, int count, Dynamic nbt) { this.item = NamespacedSchema.ensureNamespaced(item); this.count = count; this.components = nbt.emptyMap(); this.tag = nbt.get("tag").orElseEmptyMap(); this.remainder = nbt.remove("tag"); } public static Optional read(Dynamic tag) { return tag.get("id") .asString() .apply2stable( (string, number) -> new ItemStackComponentizationFix.ItemStackData(string, number.intValue(), tag.remove("id").remove("Count")), tag.get("Count").asNumber() ) .result(); } public OptionalDynamic removeTag(String key) { OptionalDynamic optionalDynamic = this.tag.get(key); this.tag = this.tag.remove(key); return optionalDynamic; } public void setComponent(String component, Dynamic value) { this.components = this.components.set(component, value); } public void setComponent(String component, OptionalDynamic value) { value.result().ifPresent(dynamic -> this.components = this.components.set(component, dynamic)); } public Dynamic moveTagInto(String oldKey, Dynamic tag, String newKey) { Optional> optional = this.removeTag(oldKey).result(); return optional.isPresent() ? tag.set(newKey, (Dynamic)optional.get()) : tag; } public void moveTagToComponent(String key, String component, Dynamic tag) { Optional> optional = this.removeTag(key).result(); if (optional.isPresent() && !((Dynamic)optional.get()).equals(tag)) { this.setComponent(component, (Dynamic)optional.get()); } } public void moveTagToComponent(String key, String component) { this.removeTag(key).result().ifPresent(dynamic -> this.setComponent(component, dynamic)); } public void fixSubTag(String key, boolean skipIfEmpty, UnaryOperator> fixer) { OptionalDynamic optionalDynamic = this.tag.get(key); if (!skipIfEmpty || !optionalDynamic.result().isEmpty()) { Dynamic dynamic = optionalDynamic.orElseEmptyMap(); dynamic = (Dynamic)fixer.apply(dynamic); if (dynamic.equals(dynamic.emptyMap())) { this.tag = this.tag.remove(key); } else { this.tag = this.tag.set(key, dynamic); } } } public Dynamic write() { Dynamic dynamic = this.tag.emptyMap().set("id", this.tag.createString(this.item)).set("count", this.tag.createInt(this.count)); if (!this.tag.equals(this.tag.emptyMap())) { this.components = this.components.set("minecraft:custom_data", this.tag); } if (!this.components.equals(this.tag.emptyMap())) { dynamic = dynamic.set("components", this.components); } return mergeRemainder(dynamic, this.remainder); } private static Dynamic mergeRemainder(Dynamic tag, Dynamic remainder) { DynamicOps dynamicOps = tag.getOps(); return (Dynamic)dynamicOps.getMap(tag.getValue()) .flatMap(mapLike -> dynamicOps.mergeToMap(remainder.convert(dynamicOps).getValue(), mapLike)) .map(object -> new Dynamic<>(dynamicOps, (T)object)) .result() .orElse(tag); } public boolean is(String item) { return this.item.equals(item); } public boolean is(Set items) { return items.contains(this.item); } public boolean hasComponent(String component) { return this.components.get(component).result().isPresent(); } } }