From 8d3492ba30eeec7604bbbb89d98bfc259d8fbb5f Mon Sep 17 00:00:00 2001 From: Nassim Jahnke Date: Tue, 12 Mar 2024 13:10:27 +0100 Subject: [PATCH] Add more structured data -> nbt converters --- .../api/minecraft/item/data/Filterable.java | 10 +- .../data/MapDecorations1_20_3.java | 4 + .../BlockItemPacketRewriter1_20_5.java | 6 +- .../rewriter/StructuredDataConverter.java | 213 ++++++++++++++++-- .../viaversion/util/KeyMappings.java | 1 + 5 files changed, 215 insertions(+), 19 deletions(-) diff --git a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/Filterable.java b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/Filterable.java index 707ffadb3..3a608e276 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/Filterable.java +++ b/api/src/main/java/com/viaversion/viaversion/api/minecraft/item/data/Filterable.java @@ -39,10 +39,18 @@ public abstract class Filterable { return raw; } - public T filtered() { + public boolean isFiltered() { + return filtered != null; + } + + public @Nullable T filtered() { return filtered; } + public T get() { + return filtered != null ? filtered : raw; + } + public abstract static class FilterableType> extends Type { private final Type elementType; private final Type optionalElementType; diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/data/MapDecorations1_20_3.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/data/MapDecorations1_20_3.java index f1010de5a..37925b79f 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/data/MapDecorations1_20_3.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/data/MapDecorations1_20_3.java @@ -61,4 +61,8 @@ public final class MapDecorations1_20_3 { public static String idToKey(final int index) { return index < 0 || index >= MAP_DECORATIONS.size() ? "player" : MAP_DECORATIONS.idToKey(index); } + + public static int keyToId(final String key) { + return MAP_DECORATIONS.keyToId(key); + } } diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/rewriter/BlockItemPacketRewriter1_20_5.java b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/rewriter/BlockItemPacketRewriter1_20_5.java index 46e85e69c..a4691acc2 100644 --- a/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/rewriter/BlockItemPacketRewriter1_20_5.java +++ b/common/src/main/java/com/viaversion/viaversion/protocols/protocol1_20_5to1_20_3/rewriter/BlockItemPacketRewriter1_20_5.java @@ -250,7 +250,7 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter structuredData : data.data().values()) { - StructuredDataConverter.rewrite(structuredData, tag); + StructuredDataConverter.writeToTag(structuredData, tag); } return dataItem; @@ -374,7 +374,9 @@ public final class BlockItemPacketRewriter1_20_5 extends ItemRewriter, Rewriter> REWRITERS = new Reference2ObjectOpenHashMap<>(); + private static final Map, DataConverter> REWRITERS = new Reference2ObjectOpenHashMap<>(); static { register(StructuredDataKey.DAMAGE, (data, tag) -> tag.putInt("Damage", data)); @@ -52,10 +61,8 @@ final class StructuredDataConverter { } tag.put("Lore", lore); }); - register(StructuredDataKey.ENCHANTMENTS, StructuredDataConverter::convertEnchantments); - register(StructuredDataKey.STORED_ENCHANTMENTS, StructuredDataConverter::convertEnchantments); - //register(StructuredDataKey.CAN_PLACE_ON, (data, tag) -> ); // TODO - //register(StructuredDataKey.CAN_BREAK, (data, tag) -> ); // TODO + register(StructuredDataKey.ENCHANTMENTS, (data, tag) -> convertEnchantments(data, tag, false)); + register(StructuredDataKey.STORED_ENCHANTMENTS, (data, tag) -> convertEnchantments(data, tag, true)); register(StructuredDataKey.ATTRIBUTE_MODIFIERS, (data, tag) -> { final ListTag modifiers = new ListTag<>(CompoundTag.class); for (final AttributeModifier modifier : data.modifiers()) { @@ -81,10 +88,184 @@ final class StructuredDataConverter { register(StructuredDataKey.CUSTOM_MODEL_DATA, (data, tag) -> tag.putInt("CustomModelData", data)); register(StructuredDataKey.HIDE_ADDITIONAL_TOOLTIP, (data, tag) -> putHideFlag(tag, 0x20)); register(StructuredDataKey.REPAIR_COST, (data, tag) -> tag.putInt("RepairCost", data)); + register(StructuredDataKey.DYED_COLOR, (data, tag) -> { + tag.putInt("color", data.rgb()); + if (!data.showInTooltip()) { + putHideFlag(tag, 0x40); + } + }); + register(StructuredDataKey.MAP_COLOR, (data, tag) -> tag.putInt("MapColor", data)); + register(StructuredDataKey.MAP_ID, (data, tag) -> tag.putInt("map", data)); + register(StructuredDataKey.MAP_DECORATIONS, (data, tag) -> { + final ListTag decorations = new ListTag<>(CompoundTag.class); + for (final Map.Entry entry : data.entrySet()) { + final CompoundTag decorationTag = (CompoundTag) entry.getValue(); + final int id = MapDecorations1_20_3.keyToId(decorationTag.getString("type")); + if (id == -1) { + continue; + } + final CompoundTag convertedDecoration = new CompoundTag(); + convertedDecoration.putString("id", entry.getKey()); + convertedDecoration.putInt("type", id); + convertedDecoration.putDouble("x", decorationTag.getDouble("x")); + convertedDecoration.putDouble("z", decorationTag.getDouble("z")); + convertedDecoration.putFloat("rot", decorationTag.getFloat("rotation")); + decorations.add(convertedDecoration); + } + tag.put("Decorations", decorations); + }); + register(StructuredDataKey.WRITABLE_BOOK_CONTENT, (data, tag) -> { + final ListTag pages = new ListTag<>(StringTag.class); + final CompoundTag filteredPages = new CompoundTag(); + for (int i = 0; i < data.length; i++) { + final FilterableString page = data[i]; + pages.add(new StringTag(page.raw())); + if (page.filtered() != null) { + filteredPages.putString(Integer.toString(i), page.filtered()); + } + } + tag.put("pages", pages); + tag.put("filtered_pages", filteredPages); + }); + register(StructuredDataKey.WRITTEN_BOOK_CONTENT, (data, tag) -> { + final ListTag pages = new ListTag<>(StringTag.class); + final CompoundTag filteredPages = new CompoundTag(); + for (int i = 0; i < data.pages().length; i++) { + final FilterableComponent page = data.pages()[i]; + pages.add(new StringTag(ComponentUtil.tagToJsonString(page.raw()))); + if (page.filtered() != null) { + filteredPages.putString(Integer.toString(i), ComponentUtil.tagToJsonString(page.filtered())); + } + } + tag.put("pages", pages); + tag.put("filtered_pages", filteredPages); + + tag.putString("author", data.author()); + tag.putInt("generation", data.generation()); + tag.putBoolean("resolved", data.resolved()); + tag.putString("title", data.title().raw()); + if (data.title().filtered() != null) { + tag.putString("filtered_title", data.title().filtered()); + } + }); + register(StructuredDataKey.BASE_COLOR, (data, tag) -> tag.putInt("Base", data)); + register(StructuredDataKey.CHARGED_PROJECTILES, (data, tag) -> convertItemList(data, tag, "ChargedProjectiles")); + register(StructuredDataKey.BUNDLE_CONTENTS, (data, tag) -> convertItemList(data, tag, "Items")); + register(StructuredDataKey.LODESTONE_TRACKER, (data, tag) -> { + final CompoundTag positionTag = new CompoundTag(); + tag.put("LodestonePos", positionTag); + tag.putBoolean("LodestoneTracked", data.tracked()); + tag.putString("LodestoneDimension", data.pos().dimension()); + positionTag.putInt("X", data.pos().x()); + positionTag.putInt("Y", data.pos().y()); + positionTag.putInt("Z", data.pos().z()); + }); + register(StructuredDataKey.FIREWORKS, (data, tag) -> { + final CompoundTag fireworksTag = new CompoundTag(); + fireworksTag.putInt("Flight", data.flightDuration()); + tag.put("Fireworks", fireworksTag); + + final ListTag explosionsTag = new ListTag<>(CompoundTag.class); + for (final FireworkExplosion explosion : data.explosions()) { + explosionsTag.add(convertExplosion(explosion)); + } + fireworksTag.put("Explosions", explosionsTag); + }); + register(StructuredDataKey.FIREWORK_EXPLOSION, (data, tag) -> tag.put("Explosion", convertExplosion(data))); + register(StructuredDataKey.PROFILE, (data, tag) -> { + if (data.name() != null && data.id() == null && data.properties().length == 0) { + tag.putString("SkullOwner", data.name()); + return; + } + + final CompoundTag profileTag = new CompoundTag(); + tag.put("SkullOwner", profileTag); + if (data.name() != null) { + profileTag.putString("Name", data.name()); + } + if (data.id() != null) { + profileTag.put("Id", new IntArrayTag(UUIDUtil.toIntArray(data.id()))); + } + + final CompoundTag propertiesTag = new CompoundTag(); + for (final GameProfile.Property property : data.properties()) { + final ListTag values = new ListTag<>(CompoundTag.class); + final CompoundTag propertyTag = new CompoundTag(); + propertyTag.putString("Value", property.value()); + if (property.signature() != null) { + propertyTag.putString("Signature", property.signature()); + } + values.add(propertyTag); + propertiesTag.put(property.name(), values); + } + }); + register(StructuredDataKey.INSTRUMENT, (data, tag) -> { + if (!data.hasId()) { + // Can't do anything with direct values + return; + } + + final String identifier = Instruments1_20_3.idToKey(data.id()); + if (identifier != null) { + tag.putString("instrument", identifier); + } + }); + //register(StructuredDataKey., (data, tag) -> ); + + //TODO + // StructuredDataKey.CAN_PLACE_ON + // StructuredDataKey.CAN_BREAK + // StructuredDataKey POTION_CONTENT + // StructuredDataKey SUSPICIOUS_STEW_EFFECT + // StructuredDataKey TRIM + // StructuredDataKey DEBUG_STICK_STATE + // StructuredDataKey ENTITY_DATA + // StructuredDataKey BUCKET_ENTITY_DATA + // StructuredDataKey BLOCK_ENTITY_DATA + // StructuredDataKey RECIPES + // StructuredDataKey BANNER_PATTERNS + // StructuredDataKey BLOCK_STATE + // StructuredDataKey POT_DECORATIONS + // StructuredDataKey NOTE_BLOCK_SOUND + // StructuredDataKey BEES + // StructuredDataKey LOCK + // StructuredDataKey CONTAINER_LOOT + // StructuredDataKey CONTAINER + // StructuredDataKey CREATIVE_SLOT_LOCK + // StructuredDataKey ENCHANTMENT_GLINT_OVERRIDE + // StructuredDataKey INTANGIBLE_PROJECTILE + // StructuredDataKey MAP_POST_PROCESSING } - private static void convertEnchantments(final Enchantments data, final CompoundTag tag) { + private static CompoundTag convertExplosion(final FireworkExplosion explosion) { + final CompoundTag explosionTag = new CompoundTag(); + explosionTag.putInt("Type", explosion.shape()); + explosionTag.put("Colors", new IntArrayTag(explosion.colors().clone())); + explosionTag.put("FadeColors", new IntArrayTag(explosion.fadeColors().clone())); + explosionTag.putBoolean("Trail", explosion.hasTrail()); + explosionTag.putBoolean("Flicker", explosion.hasTwinkle()); + return explosionTag; + } + + private static void convertItemList(final Item[] items, final CompoundTag tag, final String key) { + final ListTag itemsTag = new ListTag<>(CompoundTag.class); + for (final Item item : items) { + final CompoundTag savedItem = new CompoundTag(); + savedItem.putString("id", "stone"); // TODO + savedItem.putByte("Count", (byte) item.amount()); + + final CompoundTag itemTag = new CompoundTag(); + for (final StructuredData data : item.structuredData().data().values()) { + writeToTag(data, itemTag); + } + savedItem.put("tag", itemTag); + itemsTag.add(savedItem); + } + tag.put(key, itemsTag); + } + + private static void convertEnchantments(final Enchantments data, final CompoundTag tag, final boolean storedEnchantments) { final ListTag enchantments = new ListTag<>(CompoundTag.class); for (final Int2IntMap.Entry entry : data.enchantments().int2IntEntrySet()) { final String identifier = Enchantments1_20_3.idToKey(entry.getIntKey()); @@ -97,10 +278,10 @@ final class StructuredDataConverter { enchantment.putShort("lvl", (short) entry.getIntKey()); enchantments.add(enchantment); } - tag.put("Enchantments", enchantments); + tag.put(storedEnchantments ? "StoredEnchantments" : "Enchantments", enchantments); if (!data.showInTooltip()) { - putHideFlag(tag, 0x01); + putHideFlag(tag, storedEnchantments ? 0x20 : 0x01); } } @@ -108,25 +289,25 @@ final class StructuredDataConverter { tag.putInt("HideFlags", tag.getInt("HideFlags") | value); } - public static void rewrite(final StructuredData data, final CompoundTag tag) { + public static void writeToTag(final StructuredData data, final CompoundTag tag) { if (data.isEmpty()) { return; } //noinspection unchecked - final Rewriter rewriter = (Rewriter) REWRITERS.get(data.key()); - if (rewriter != null) { - rewriter.rewrite(data.value(), tag); + final DataConverter converter = (DataConverter) REWRITERS.get(data.key()); + if (converter != null) { + converter.convert(data.value(), tag); } } - private static void register(final StructuredDataKey key, final Rewriter rewriter) { - REWRITERS.put(key, rewriter); + private static void register(final StructuredDataKey key, final DataConverter converter) { + REWRITERS.put(key, converter); } @FunctionalInterface - interface Rewriter { + interface DataConverter { - void rewrite(T data, CompoundTag tag); + void convert(T data, CompoundTag tag); } } diff --git a/common/src/main/java/com/viaversion/viaversion/util/KeyMappings.java b/common/src/main/java/com/viaversion/viaversion/util/KeyMappings.java index 24cb95b8c..2ee36206a 100644 --- a/common/src/main/java/com/viaversion/viaversion/util/KeyMappings.java +++ b/common/src/main/java/com/viaversion/viaversion/util/KeyMappings.java @@ -29,6 +29,7 @@ public final class KeyMappings { public KeyMappings(final String... keys) { this.keys = keys; keyToId = new Object2IntOpenHashMap<>(keys.length); + keyToId.defaultReturnValue(-1); for (int i = 0; i < keys.length; i++) { keyToId.put(keys[i], i); }