minecraft-src/net/minecraft/util/datafix/fixes/ItemStackComponentizationFix.java
2025-07-04 03:45:38 +03:00

784 lines
30 KiB
Java

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<String> POTION_HOLDER_IDS = Set.of(
"minecraft:potion", "minecraft:splash_potion", "minecraft:lingering_potion", "minecraft:tipped_arrow"
);
private static final Set<String> 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<String> BUCKETED_MOB_TAGS = List.of(
"NoAI", "Silent", "NoGravity", "Glowing", "Invulnerable", "Health", "Age", "Variant", "HuntingCooldown", "BucketVariantTag"
);
private static final Set<String> 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<? extends Dynamic<?>> 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<?>, ? extends Dynamic<?>> map = (Map<? extends Dynamic<?>, ? 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<Boolean> optional = dynamic.asBoolean().result();
if (optional.isPresent()) {
return dynamic.createString(String.valueOf(optional.get()));
}
}
Optional<Number> 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<Integer> 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<String> 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 <T> Dynamic<T> fixBlockEntityTag(ItemStackComponentizationFix.ItemStackData itemStackData, Dynamic<T> tag, String entityId) {
itemStackData.setComponent("minecraft:lock", tag.get("Lock"));
tag = tag.remove("Lock");
Optional<Dynamic<T>> optional = tag.get("LootTable").result();
if (optional.isPresent()) {
Dynamic<T> 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<Dynamic<T>> 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<Number> 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<Dynamic<T>> 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<Pair<String, Integer>> 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<String, Integer> 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<Pair<String, Integer>> parseEnchantment(Dynamic<?> enchantmentTag) {
return enchantmentTag.get("id")
.asString()
.<Number, Pair<String, Integer>>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<? extends Dynamic<?>> 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<? extends Dynamic<?>> 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<?>, 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<String> 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<String> 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<String> list = itemStackData.removeTag("pages").asList(dynamic -> dynamic.asString(""));
Map<String, String> map = itemStackData.removeTag("filtered_pages").asMap(dynamic -> dynamic.asString("0"), dynamic -> dynamic.asString(""));
if (list.isEmpty()) {
return null;
} else {
List<Dynamic<?>> 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<String> 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<? extends Dynamic<?>> optional = itemStackData.removeTag("LodestonePos").result();
Optional<? extends Dynamic<?>> 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<? extends Dynamic<?>> 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<String> 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<? extends Dynamic<?>> 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<String, List<Pair<String, Optional<String>>>> map = tag.asMap(dynamic -> dynamic.asString(""), dynamic -> dynamic.asList(dynamicx -> {
String string = dynamicx.get("Value").asString("");
Optional<String> 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<String> optional = (Optional<String>)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<? extends Dynamic<?>> 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<ItemStackComponentizationFix.ItemStackData> read(Dynamic<?> tag) {
return tag.get("id")
.asString()
.<Number, ItemStackComponentizationFix.ItemStackData>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<? extends Dynamic<?>> 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<? extends Dynamic<?>> 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<Dynamic<?>> 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 <T> Dynamic<T> mergeRemainder(Dynamic<T> tag, Dynamic<?> remainder) {
DynamicOps<T> dynamicOps = tag.getOps();
return (Dynamic<T>)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<String> items) {
return items.contains(this.item);
}
public boolean hasComponent(String component) {
return this.components.get(component).result().isPresent();
}
}
}