784 lines
30 KiB
Java
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();
|
|
}
|
|
}
|
|
}
|