minecraft_25w45a_unobfuscated/net/minecraft/util/datafix/fixes/ItemStackComponentizationFix.java
2025-11-24 22:52:51 +03:00

663 lines
35 KiB
Java

/*
* Decompiled with CFR 0.152.
*
* Could not load the following classes:
* com.google.common.base.Splitter
* com.mojang.datafixers.DataFix
* com.mojang.datafixers.DataFixUtils
* com.mojang.datafixers.TypeRewriteRule
* com.mojang.datafixers.schemas.Schema
* com.mojang.datafixers.util.Pair
* com.mojang.serialization.Dynamic
* com.mojang.serialization.DynamicOps
* com.mojang.serialization.OptionalDynamic
* org.jspecify.annotations.Nullable
*/
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.fixes.References;
import net.minecraft.util.datafix.schemas.NamespacedSchema;
import org.jspecify.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((char)',');
public ItemStackComponentizationFix(Schema outputSchema) {
super(outputSchema, true);
}
private static void fixItemStack(ItemStackData itemStack, Dynamic<?> dynamic) {
int hideFlags = itemStack.removeTag("HideFlags").asInt(0);
itemStack.moveTagToComponent("Damage", "minecraft:damage", dynamic.createInt(0));
itemStack.moveTagToComponent("RepairCost", "minecraft:repair_cost", dynamic.createInt(0));
itemStack.moveTagToComponent("CustomModelData", "minecraft:custom_model_data");
itemStack.removeTag("BlockStateTag").result().ifPresent(blockStateTag -> itemStack.setComponent("minecraft:block_state", ItemStackComponentizationFix.fixBlockStateTag(blockStateTag)));
itemStack.moveTagToComponent("EntityTag", "minecraft:entity_data");
itemStack.fixSubTag("BlockEntityTag", false, blockEntityTag -> {
String id = NamespacedSchema.ensureNamespaced(blockEntityTag.get("id").asString(""));
Dynamic withoutId = (blockEntityTag = ItemStackComponentizationFix.fixBlockEntityTag(itemStack, blockEntityTag, id)).remove("id");
if (withoutId.equals((Object)blockEntityTag.emptyMap())) {
return withoutId;
}
return blockEntityTag;
});
itemStack.moveTagToComponent("BlockEntityTag", "minecraft:block_entity_data");
if (itemStack.removeTag("Unbreakable").asBoolean(false)) {
Dynamic component = dynamic.emptyMap();
if ((hideFlags & 4) != 0) {
component = component.set("show_in_tooltip", dynamic.createBoolean(false));
}
itemStack.setComponent("minecraft:unbreakable", component);
}
ItemStackComponentizationFix.fixEnchantments(itemStack, dynamic, "Enchantments", "minecraft:enchantments", (hideFlags & 1) != 0);
if (itemStack.is("minecraft:enchanted_book")) {
ItemStackComponentizationFix.fixEnchantments(itemStack, dynamic, "StoredEnchantments", "minecraft:stored_enchantments", (hideFlags & 0x20) != 0);
}
itemStack.fixSubTag("display", false, display -> ItemStackComponentizationFix.fixDisplay(itemStack, display, hideFlags));
ItemStackComponentizationFix.fixAdventureModeChecks(itemStack, dynamic, hideFlags);
ItemStackComponentizationFix.fixAttributeModifiers(itemStack, dynamic, hideFlags);
Optional trim = itemStack.removeTag("Trim").result();
if (trim.isPresent()) {
Dynamic fixedTrim = (Dynamic)trim.get();
if ((hideFlags & 0x80) != 0) {
fixedTrim = fixedTrim.set("show_in_tooltip", fixedTrim.createBoolean(false));
}
itemStack.setComponent("minecraft:trim", fixedTrim);
}
if ((hideFlags & 0x20) != 0) {
itemStack.setComponent("minecraft:hide_additional_tooltip", dynamic.emptyMap());
}
if (itemStack.is("minecraft:crossbow")) {
itemStack.removeTag("Charged");
itemStack.moveTagToComponent("ChargedProjectiles", "minecraft:charged_projectiles", dynamic.createList(Stream.empty()));
}
if (itemStack.is("minecraft:bundle")) {
itemStack.moveTagToComponent("Items", "minecraft:bundle_contents", dynamic.createList(Stream.empty()));
}
if (itemStack.is("minecraft:filled_map")) {
itemStack.moveTagToComponent("map", "minecraft:map_id");
Map<Dynamic, Dynamic> decorations = itemStack.removeTag("Decorations").asStream().map(ItemStackComponentizationFix::fixMapDecoration).collect(Collectors.toMap(Pair::getFirst, Pair::getSecond, (first, second) -> first));
if (!decorations.isEmpty()) {
itemStack.setComponent("minecraft:map_decorations", dynamic.createMap(decorations));
}
}
if (itemStack.is(POTION_HOLDER_IDS)) {
ItemStackComponentizationFix.fixPotionContents(itemStack, dynamic);
}
if (itemStack.is("minecraft:writable_book")) {
ItemStackComponentizationFix.fixWritableBook(itemStack, dynamic);
}
if (itemStack.is("minecraft:written_book")) {
ItemStackComponentizationFix.fixWrittenBook(itemStack, dynamic);
}
if (itemStack.is("minecraft:suspicious_stew")) {
itemStack.moveTagToComponent("effects", "minecraft:suspicious_stew_effects");
}
if (itemStack.is("minecraft:debug_stick")) {
itemStack.moveTagToComponent("DebugProperty", "minecraft:debug_stick_state");
}
if (itemStack.is(BUCKETED_MOB_IDS)) {
ItemStackComponentizationFix.fixBucketedMobData(itemStack, dynamic);
}
if (itemStack.is("minecraft:goat_horn")) {
itemStack.moveTagToComponent("instrument", "minecraft:instrument");
}
if (itemStack.is("minecraft:knowledge_book")) {
itemStack.moveTagToComponent("Recipes", "minecraft:recipes");
}
if (itemStack.is("minecraft:compass")) {
ItemStackComponentizationFix.fixLodestoneTracker(itemStack, dynamic);
}
if (itemStack.is("minecraft:firework_rocket")) {
ItemStackComponentizationFix.fixFireworkRocket(itemStack);
}
if (itemStack.is("minecraft:firework_star")) {
ItemStackComponentizationFix.fixFireworkStar(itemStack);
}
if (itemStack.is("minecraft:player_head")) {
itemStack.removeTag("SkullOwner").result().ifPresent(skullOwner -> itemStack.setComponent("minecraft:profile", ItemStackComponentizationFix.fixProfile(skullOwner)));
}
}
private static Dynamic<?> fixBlockStateTag(Dynamic<?> blockStateTag) {
return (Dynamic)DataFixUtils.orElse(blockStateTag.asMapOpt().result().map(entries -> entries.collect(Collectors.toMap(Pair::getFirst, entry -> {
Optional bool;
String key = ((Dynamic)entry.getFirst()).asString("");
Dynamic value = (Dynamic)entry.getSecond();
if (BOOLEAN_BLOCK_STATE_PROPERTIES.contains(key) && (bool = value.asBoolean().result()).isPresent()) {
return value.createString(String.valueOf(bool.get()));
}
Optional number = value.asNumber().result();
if (number.isPresent()) {
return value.createString(((Number)number.get()).toString());
}
return value;
}))).map(arg_0 -> blockStateTag.createMap(arg_0)), blockStateTag);
}
private static Dynamic<?> fixDisplay(ItemStackData itemStack, Dynamic<?> display, int hideFlags) {
Optional locName;
boolean hideDye;
display.get("Name").result().filter(LegacyComponentDataFixUtils::isStrictlyValidJson).ifPresent(name -> itemStack.setComponent("minecraft:custom_name", (Dynamic<?>)name));
OptionalDynamic lore = display.get("Lore");
if (lore.result().isPresent()) {
itemStack.setComponent("minecraft:lore", display.createList(display.get("Lore").asStream().filter(LegacyComponentDataFixUtils::isStrictlyValidJson)));
}
Optional<Integer> color = display.get("color").asNumber().result().map(Number::intValue);
boolean bl = hideDye = (hideFlags & 0x40) != 0;
if (color.isPresent() || hideDye) {
Dynamic dyedColor = display.emptyMap().set("rgb", display.createInt(color.orElse(10511680).intValue()));
if (hideDye) {
dyedColor = dyedColor.set("show_in_tooltip", display.createBoolean(false));
}
itemStack.setComponent("minecraft:dyed_color", dyedColor);
}
if ((locName = display.get("LocName").asString().result()).isPresent()) {
itemStack.setComponent("minecraft:item_name", LegacyComponentDataFixUtils.createTranslatableComponent(display.getOps(), (String)locName.get()));
}
if (itemStack.is("minecraft:filled_map")) {
itemStack.setComponent("minecraft:map_color", display.get("MapColor"));
display = display.remove("MapColor");
}
return display.remove("Name").remove("Lore").remove("color").remove("LocName");
}
private static <T> Dynamic<T> fixBlockEntityTag(ItemStackData itemStack, Dynamic<T> blockEntity, String id) {
itemStack.setComponent("minecraft:lock", blockEntity.get("Lock"));
blockEntity = blockEntity.remove("Lock");
Optional lootTable = blockEntity.get("LootTable").result();
if (lootTable.isPresent()) {
Dynamic containerLoot = blockEntity.emptyMap().set("loot_table", (Dynamic)lootTable.get());
long seed = blockEntity.get("LootTableSeed").asLong(0L);
if (seed != 0L) {
containerLoot = containerLoot.set("seed", blockEntity.createLong(seed));
}
itemStack.setComponent("minecraft:container_loot", containerLoot);
blockEntity = blockEntity.remove("LootTable").remove("LootTableSeed");
}
return switch (id) {
case "minecraft:skull" -> {
itemStack.setComponent("minecraft:note_block_sound", blockEntity.get("note_block_sound"));
yield blockEntity.remove("note_block_sound");
}
case "minecraft:decorated_pot" -> {
itemStack.setComponent("minecraft:pot_decorations", blockEntity.get("sherds"));
Optional item = blockEntity.get("item").result();
if (item.isPresent()) {
itemStack.setComponent("minecraft:container", blockEntity.createList(Stream.of(blockEntity.emptyMap().set("slot", blockEntity.createInt(0)).set("item", (Dynamic)item.get()))));
}
yield blockEntity.remove("sherds").remove("item");
}
case "minecraft:banner" -> {
itemStack.setComponent("minecraft:banner_patterns", blockEntity.get("patterns"));
Optional base = blockEntity.get("Base").asNumber().result();
if (base.isPresent()) {
itemStack.setComponent("minecraft:base_color", blockEntity.createString(ExtraDataFixUtils.dyeColorIdToName(((Number)base.get()).intValue())));
}
yield blockEntity.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 items = blockEntity.get("Items").asList(dynamic -> dynamic.emptyMap().set("slot", dynamic.createInt(dynamic.get("Slot").asByte((byte)0) & 0xFF)).set("item", dynamic.remove("Slot")));
if (!items.isEmpty()) {
itemStack.setComponent("minecraft:container", blockEntity.createList(items.stream()));
}
yield blockEntity.remove("Items");
}
case "minecraft:beehive" -> {
itemStack.setComponent("minecraft:bees", blockEntity.get("bees"));
yield blockEntity.remove("bees");
}
default -> blockEntity;
};
}
private static void fixEnchantments(ItemStackData itemStack, Dynamic<?> dynamic, String key, String componentType, boolean hideInTooltip) {
OptionalDynamic<?> rawEnchantments = itemStack.removeTag(key);
List<Pair> enchantments = rawEnchantments.asList(Function.identity()).stream().flatMap(enchantment -> ItemStackComponentizationFix.parseEnchantment(enchantment).stream()).filter(enchantment -> (Integer)enchantment.getSecond() > 0).toList();
if (!enchantments.isEmpty() || hideInTooltip) {
Dynamic component = dynamic.emptyMap();
Dynamic levels = dynamic.emptyMap();
for (Pair enchantment2 : enchantments) {
levels = levels.set((String)enchantment2.getFirst(), dynamic.createInt(((Integer)enchantment2.getSecond()).intValue()));
}
component = component.set("levels", levels);
if (hideInTooltip) {
component = component.set("show_in_tooltip", dynamic.createBoolean(false));
}
itemStack.setComponent(componentType, component);
}
if (rawEnchantments.result().isPresent() && enchantments.isEmpty()) {
itemStack.setComponent("minecraft:enchantment_glint_override", dynamic.createBoolean(true));
}
}
private static Optional<Pair<String, Integer>> parseEnchantment(Dynamic<?> entry) {
return entry.get("id").asString().apply2stable((id, level) -> Pair.of((Object)id, (Object)Mth.clamp(level.intValue(), 0, 255)), entry.get("lvl").asNumber()).result();
}
private static void fixAdventureModeChecks(ItemStackData itemStack, Dynamic<?> dynamic, int hideFlags) {
ItemStackComponentizationFix.fixBlockStatePredicates(itemStack, dynamic, "CanDestroy", "minecraft:can_break", (hideFlags & 8) != 0);
ItemStackComponentizationFix.fixBlockStatePredicates(itemStack, dynamic, "CanPlaceOn", "minecraft:can_place_on", (hideFlags & 0x10) != 0);
}
private static void fixBlockStatePredicates(ItemStackData itemStack, Dynamic<?> dynamic, String tag, String componentId, boolean hideInTooltip) {
Optional oldPredicate = itemStack.removeTag(tag).result();
if (oldPredicate.isEmpty()) {
return;
}
Dynamic component = dynamic.emptyMap().set("predicates", dynamic.createList(((Dynamic)oldPredicate.get()).asStream().map(value -> (Dynamic)DataFixUtils.orElse((Optional)value.asString().map(string -> ItemStackComponentizationFix.fixBlockStatePredicate(value, string)).result(), (Object)value))));
if (hideInTooltip) {
component = component.set("show_in_tooltip", dynamic.createBoolean(false));
}
itemStack.setComponent(componentId, component);
}
private static Dynamic<?> fixBlockStatePredicate(Dynamic<?> dynamic, String string) {
int startProperties = string.indexOf(91);
int startNbt = string.indexOf(123);
int blockNameEnd = string.length();
if (startProperties != -1) {
blockNameEnd = startProperties;
}
if (startNbt != -1) {
blockNameEnd = Math.min(blockNameEnd, startNbt);
}
String blockOrTagName = string.substring(0, blockNameEnd);
Dynamic predicate = dynamic.emptyMap().set("blocks", dynamic.createString(blockOrTagName.trim()));
int endProperties = string.indexOf(93);
if (startProperties != -1 && endProperties != -1) {
Dynamic properties = dynamic.emptyMap();
Iterable flatProperties = PROPERTY_SPLITTER.split((CharSequence)string.substring(startProperties + 1, endProperties));
for (String property : flatProperties) {
int assignment = property.indexOf(61);
if (assignment == -1) continue;
String key = property.substring(0, assignment).trim();
String value = property.substring(assignment + 1).trim();
properties = properties.set(key, dynamic.createString(value));
}
predicate = predicate.set("state", properties);
}
int endNbt = string.indexOf(125);
if (startNbt != -1 && endNbt != -1) {
predicate = predicate.set("nbt", dynamic.createString(string.substring(startNbt, endNbt + 1)));
}
return predicate;
}
private static void fixAttributeModifiers(ItemStackData itemStack, Dynamic<?> dynamic, int hideFlags) {
OptionalDynamic<?> attributeModifiersField = itemStack.removeTag("AttributeModifiers");
if (attributeModifiersField.result().isEmpty()) {
return;
}
boolean hideInTooltip = (hideFlags & 2) != 0;
List attributeModifiers = attributeModifiersField.asList(ItemStackComponentizationFix::fixAttributeModifier);
Dynamic component = dynamic.emptyMap().set("modifiers", dynamic.createList(attributeModifiers.stream()));
if (hideInTooltip) {
component = component.set("show_in_tooltip", dynamic.createBoolean(false));
}
itemStack.setComponent("minecraft:attribute_modifiers", component);
}
private static Dynamic<?> fixAttributeModifier(Dynamic<?> input) {
Dynamic result = input.emptyMap().set("name", input.createString("")).set("amount", input.createDouble(0.0)).set("operation", input.createString("add_value"));
result = Dynamic.copyField(input, (String)"AttributeName", (Dynamic)result, (String)"type");
result = Dynamic.copyField(input, (String)"Slot", (Dynamic)result, (String)"slot");
result = Dynamic.copyField(input, (String)"UUID", (Dynamic)result, (String)"uuid");
result = Dynamic.copyField(input, (String)"Name", (Dynamic)result, (String)"name");
result = Dynamic.copyField(input, (String)"Amount", (Dynamic)result, (String)"amount");
result = Dynamic.copyAndFixField(input, (String)"Operation", (Dynamic)result, (String)"operation", operation -> operation.createString(switch (operation.asInt(0)) {
default -> "add_value";
case 1 -> "add_multiplied_base";
case 2 -> "add_multiplied_total";
}));
return result;
}
private static Pair<Dynamic<?>, Dynamic<?>> fixMapDecoration(Dynamic<?> decoration) {
Dynamic id = (Dynamic)DataFixUtils.orElseGet((Optional)decoration.get("id").result(), () -> decoration.createString(""));
Dynamic value = decoration.emptyMap().set("type", decoration.createString(ItemStackComponentizationFix.fixMapDecorationType(decoration.get("type").asInt(0)))).set("x", decoration.createDouble(decoration.get("x").asDouble(0.0))).set("z", decoration.createDouble(decoration.get("z").asDouble(0.0))).set("rotation", decoration.createFloat((float)decoration.get("rot").asDouble(0.0)));
return Pair.of((Object)id, (Object)value);
}
private static String fixMapDecorationType(int id) {
return switch (id) {
default -> "player";
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";
};
}
private static void fixPotionContents(ItemStackData itemStack, Dynamic<?> dynamic) {
Dynamic<?> component = dynamic.emptyMap();
Optional<String> potion = itemStack.removeTag("Potion").asString().result().filter(id -> !id.equals("minecraft:empty"));
if (potion.isPresent()) {
component = component.set("potion", dynamic.createString(potion.get()));
}
component = itemStack.moveTagInto("CustomPotionColor", component, "custom_color");
if (!(component = itemStack.moveTagInto("custom_potion_effects", component, "custom_effects")).equals((Object)dynamic.emptyMap())) {
itemStack.setComponent("minecraft:potion_contents", component);
}
}
private static void fixWritableBook(ItemStackData itemStack, Dynamic<?> dynamic) {
Dynamic<?> pages = ItemStackComponentizationFix.fixBookPages(itemStack, dynamic);
if (pages != null) {
itemStack.setComponent("minecraft:writable_book_content", dynamic.emptyMap().set("pages", pages));
}
}
private static void fixWrittenBook(ItemStackData itemStack, Dynamic<?> dynamic) {
Dynamic<?> pages = ItemStackComponentizationFix.fixBookPages(itemStack, dynamic);
String title = itemStack.removeTag("title").asString("");
Optional filteredTitle = itemStack.removeTag("filtered_title").asString().result();
Dynamic component = dynamic.emptyMap();
component = component.set("title", ItemStackComponentizationFix.createFilteredText(dynamic, title, filteredTitle));
component = itemStack.moveTagInto("author", component, "author");
component = itemStack.moveTagInto("resolved", component, "resolved");
component = itemStack.moveTagInto("generation", component, "generation");
if (pages != null) {
component = component.set("pages", pages);
}
itemStack.setComponent("minecraft:written_book_content", component);
}
private static @Nullable Dynamic<?> fixBookPages(ItemStackData itemStack, Dynamic<?> dynamic) {
List pages = itemStack.removeTag("pages").asList(page -> page.asString(""));
Map filteredPages = itemStack.removeTag("filtered_pages").asMap(key -> key.asString("0"), page -> page.asString(""));
if (pages.isEmpty()) {
return null;
}
ArrayList fixedPages = new ArrayList(pages.size());
for (int i = 0; i < pages.size(); ++i) {
String page2 = (String)pages.get(i);
String filteredPage = (String)filteredPages.get(String.valueOf(i));
fixedPages.add(ItemStackComponentizationFix.createFilteredText(dynamic, page2, Optional.ofNullable(filteredPage)));
}
return dynamic.createList(fixedPages.stream());
}
private static Dynamic<?> createFilteredText(Dynamic<?> dynamic, String text, Optional<String> filtered) {
Dynamic fixedPage = dynamic.emptyMap().set("raw", dynamic.createString(text));
if (filtered.isPresent()) {
fixedPage = fixedPage.set("filtered", dynamic.createString(filtered.get()));
}
return fixedPage;
}
private static void fixBucketedMobData(ItemStackData itemStack, Dynamic<?> dynamic) {
Dynamic<?> data = dynamic.emptyMap();
for (String key : BUCKETED_MOB_TAGS) {
data = itemStack.moveTagInto(key, data, key);
}
if (!data.equals((Object)dynamic.emptyMap())) {
itemStack.setComponent("minecraft:bucket_entity_data", data);
}
}
private static void fixLodestoneTracker(ItemStackData itemStack, Dynamic<?> dynamic) {
Optional lodestonePos = itemStack.removeTag("LodestonePos").result();
Optional lodestoneDimension = itemStack.removeTag("LodestoneDimension").result();
if (lodestonePos.isEmpty() && lodestoneDimension.isEmpty()) {
return;
}
boolean lodestoneTracked = itemStack.removeTag("LodestoneTracked").asBoolean(true);
Dynamic component = dynamic.emptyMap();
if (lodestonePos.isPresent() && lodestoneDimension.isPresent()) {
component = component.set("target", dynamic.emptyMap().set("pos", (Dynamic)lodestonePos.get()).set("dimension", (Dynamic)lodestoneDimension.get()));
}
if (!lodestoneTracked) {
component = component.set("tracked", dynamic.createBoolean(false));
}
itemStack.setComponent("minecraft:lodestone_tracker", component);
}
private static void fixFireworkStar(ItemStackData itemStack) {
itemStack.fixSubTag("Explosion", true, explosion -> {
itemStack.setComponent("minecraft:firework_explosion", ItemStackComponentizationFix.fixFireworkExplosion(explosion));
return explosion.remove("Type").remove("Colors").remove("FadeColors").remove("Trail").remove("Flicker");
});
}
private static void fixFireworkRocket(ItemStackData itemStack) {
itemStack.fixSubTag("Fireworks", true, fireworks -> {
Stream<Dynamic> explosions = fireworks.get("Explosions").asStream().map(ItemStackComponentizationFix::fixFireworkExplosion);
int flight = fireworks.get("Flight").asInt(0);
itemStack.setComponent("minecraft:fireworks", fireworks.emptyMap().set("explosions", fireworks.createList(explosions)).set("flight_duration", fireworks.createByte((byte)flight)));
return fireworks.remove("Explosions").remove("Flight");
});
}
private static Dynamic<?> fixFireworkExplosion(Dynamic<?> explosion) {
explosion = explosion.set("shape", explosion.createString(switch (explosion.get("Type").asInt(0)) {
default -> "small_ball";
case 1 -> "large_ball";
case 2 -> "star";
case 3 -> "creeper";
case 4 -> "burst";
})).remove("Type");
explosion = explosion.renameField("Colors", "colors");
explosion = explosion.renameField("FadeColors", "fade_colors");
explosion = explosion.renameField("Trail", "has_trail");
explosion = explosion.renameField("Flicker", "has_twinkle");
return explosion;
}
public static Dynamic<?> fixProfile(Dynamic<?> dynamic) {
Optional simpleName = dynamic.asString().result();
if (simpleName.isPresent()) {
if (ItemStackComponentizationFix.isValidPlayerName((String)simpleName.get())) {
return dynamic.emptyMap().set("name", dynamic.createString((String)simpleName.get()));
}
return dynamic.emptyMap();
}
String name = dynamic.get("Name").asString("");
Optional id = dynamic.get("Id").result();
Dynamic<?> properties = ItemStackComponentizationFix.fixProfileProperties(dynamic.get("Properties"));
Dynamic profile = dynamic.emptyMap();
if (ItemStackComponentizationFix.isValidPlayerName(name)) {
profile = profile.set("name", dynamic.createString(name));
}
if (id.isPresent()) {
profile = profile.set("id", (Dynamic)id.get());
}
if (properties != null) {
profile = profile.set("properties", properties);
}
return profile;
}
private static boolean isValidPlayerName(String name) {
if (name.length() > 16) {
return false;
}
return name.chars().filter(c -> c <= 32 || c >= 127).findAny().isEmpty();
}
private static @Nullable Dynamic<?> fixProfileProperties(OptionalDynamic<?> dynamic) {
Map properties = dynamic.asMap(key -> key.asString(""), list -> list.asList(property -> {
String value = property.get("Value").asString("");
Optional signature = property.get("Signature").asString().result();
return Pair.of((Object)value, (Object)signature);
}));
if (properties.isEmpty()) {
return null;
}
return dynamic.createList(properties.entrySet().stream().flatMap(entry -> ((List)entry.getValue()).stream().map(pair -> {
Dynamic property = dynamic.emptyMap().set("name", dynamic.createString((String)entry.getKey())).set("value", dynamic.createString((String)pair.getFirst()));
Optional signature = (Optional)pair.getSecond();
if (signature.isPresent()) {
return property.set("signature", dynamic.createString((String)signature.get()));
}
return property;
})));
}
protected TypeRewriteRule makeRule() {
return this.writeFixAndRead("ItemStack componentization", this.getInputSchema().getType(References.ITEM_STACK), this.getOutputSchema().getType(References.ITEM_STACK), dynamic -> {
Optional<Dynamic> fixedItemStack = ItemStackData.read(dynamic).map(itemStack -> {
ItemStackComponentizationFix.fixItemStack(itemStack, itemStack.tag);
return itemStack.write();
});
return (Dynamic)DataFixUtils.orElse(fixedItemStack, (Object)dynamic);
});
}
private static class ItemStackData {
private final String item;
private final int count;
private Dynamic<?> components;
private final Dynamic<?> remainder;
private Dynamic<?> tag;
private ItemStackData(String item, int count, Dynamic<?> remainder) {
this.item = NamespacedSchema.ensureNamespaced(item);
this.count = count;
this.components = remainder.emptyMap();
this.tag = remainder.get("tag").orElseEmptyMap();
this.remainder = remainder.remove("tag");
}
public static Optional<ItemStackData> read(Dynamic<?> dynamic) {
return dynamic.get("id").asString().apply2stable((item, count) -> new ItemStackData((String)item, count.intValue(), (Dynamic<?>)dynamic.remove("id").remove("Count")), dynamic.get("Count").asNumber()).result();
}
public OptionalDynamic<?> removeTag(String key) {
OptionalDynamic value = this.tag.get(key);
this.tag = this.tag.remove(key);
return value;
}
public void setComponent(String type, Dynamic<?> value) {
this.components = this.components.set(type, value);
}
public void setComponent(String type, OptionalDynamic<?> optionalValue) {
optionalValue.result().ifPresent(value -> {
this.components = this.components.set(type, value);
});
}
public Dynamic<?> moveTagInto(String fromKey, Dynamic<?> target, String toKey) {
Optional value = this.removeTag(fromKey).result();
if (value.isPresent()) {
return target.set(toKey, (Dynamic)value.get());
}
return target;
}
public void moveTagToComponent(String key, String type, Dynamic<?> defaultValue) {
Optional value = this.removeTag(key).result();
if (value.isPresent() && !((Dynamic)value.get()).equals(defaultValue)) {
this.setComponent(type, (Dynamic)value.get());
}
}
public void moveTagToComponent(String key, String type) {
this.removeTag(key).result().ifPresent(value -> this.setComponent(type, (Dynamic<?>)value));
}
public void fixSubTag(String key, boolean dontFixWhenFieldIsMissing, UnaryOperator<Dynamic<?>> function) {
OptionalDynamic value = this.tag.get(key);
if (dontFixWhenFieldIsMissing && value.result().isEmpty()) {
return;
}
Dynamic map = value.orElseEmptyMap();
this.tag = (map = (Dynamic)function.apply(map)).equals((Object)map.emptyMap()) ? this.tag.remove(key) : this.tag.set(key, map);
}
public Dynamic<?> write() {
Dynamic result = this.tag.emptyMap().set("id", this.tag.createString(this.item)).set("count", this.tag.createInt(this.count));
if (!this.tag.equals((Object)this.tag.emptyMap())) {
this.components = this.components.set("minecraft:custom_data", this.tag);
}
if (!this.components.equals((Object)this.tag.emptyMap())) {
result = result.set("components", this.components);
}
return ItemStackData.mergeRemainder(result, this.remainder);
}
private static <T> Dynamic<T> mergeRemainder(Dynamic<T> itemStack, Dynamic<?> remainder) {
DynamicOps ops = itemStack.getOps();
return ops.getMap(itemStack.getValue()).flatMap(itemStackMap -> ops.mergeToMap(remainder.convert(ops).getValue(), itemStackMap)).map(merged -> new Dynamic(ops, merged)).result().orElse(itemStack);
}
public boolean is(String id) {
return this.item.equals(id);
}
public boolean is(Set<String> ids) {
return ids.contains(this.item);
}
public boolean hasComponent(String id) {
return this.components.get(id).result().isPresent();
}
}
}