diff --git a/api/src/main/java/com/viaversion/viaversion/api/protocol/Protocol.java b/api/src/main/java/com/viaversion/viaversion/api/protocol/Protocol.java index 8c5e8e835..da6cbc09f 100644 --- a/api/src/main/java/com/viaversion/viaversion/api/protocol/Protocol.java +++ b/api/src/main/java/com/viaversion/viaversion/api/protocol/Protocol.java @@ -33,6 +33,7 @@ import com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType; import com.viaversion.viaversion.api.protocol.packet.State; import com.viaversion.viaversion.api.protocol.packet.provider.PacketTypesProvider; import com.viaversion.viaversion.api.protocol.remapper.PacketHandler; +import com.viaversion.viaversion.api.rewriter.ComponentRewriter; import com.viaversion.viaversion.api.rewriter.EntityRewriter; import com.viaversion.viaversion.api.rewriter.ItemRewriter; import com.viaversion.viaversion.api.rewriter.TagRewriter; @@ -382,6 +383,10 @@ public interface Protocol tagRewriter = new TagRewriter<>(this); + private final ComponentRewriter componentRewriter = new ComponentRewriter1_21(this); public Protocol1_20_5To1_21() { super(ClientboundPacket1_20_5.class, ClientboundPacket1_21.class, ServerboundPacket1_20_5.class, ServerboundPacket1_20_5.class); @@ -76,8 +79,18 @@ public final class Protocol1_20_5To1_21 extends AbstractProtocol(this).register(ClientboundPackets1_20_5.AWARD_STATS); + componentRewriter.registerOpenScreen(ClientboundPackets1_20_5.OPEN_SCREEN); + componentRewriter.registerComponentPacket(ClientboundPackets1_20_5.SET_ACTION_BAR_TEXT); + componentRewriter.registerComponentPacket(ClientboundPackets1_20_5.SET_TITLE_TEXT); + componentRewriter.registerComponentPacket(ClientboundPackets1_20_5.SET_SUBTITLE_TEXT); + componentRewriter.registerBossEvent(ClientboundPackets1_20_5.BOSS_EVENT); + componentRewriter.registerComponentPacket(ClientboundPackets1_20_5.DISCONNECT); + componentRewriter.registerTabList(ClientboundPackets1_20_5.TAB_LIST); + componentRewriter.registerPlayerCombatKill1_20(ClientboundPackets1_20_5.PLAYER_COMBAT_KILL); + componentRewriter.registerComponentPacket(ClientboundPackets1_20_5.SYSTEM_CHAT); + registerClientbound(ClientboundPackets1_20_5.DISGUISED_CHAT, wrapper -> { - wrapper.passthrough(Types.TAG); // Message + componentRewriter.processTag(wrapper.user(), wrapper.passthrough(Types.TAG)); // Message // Holder time final int chatType = wrapper.read(Types.VAR_INT); @@ -99,7 +112,7 @@ public final class Protocol1_20_5To1_21 extends AbstractProtocol getComponentRewriter() { + return componentRewriter; + } + @Override protected PacketTypesProvider createPacketTypesProvider() { return new SimplePacketTypesProvider<>( diff --git a/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/rewriter/ComponentRewriter1_21.java b/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/rewriter/ComponentRewriter1_21.java new file mode 100644 index 000000000..0c3128037 --- /dev/null +++ b/common/src/main/java/com/viaversion/viaversion/protocols/v1_20_5to1_21/rewriter/ComponentRewriter1_21.java @@ -0,0 +1,60 @@ +/* + * This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion + * Copyright (C) 2016-2024 ViaVersion and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.viaversion.viaversion.protocols.v1_20_5to1_21.rewriter; + +import com.viaversion.nbt.tag.CompoundTag; +import com.viaversion.nbt.tag.ListTag; +import com.viaversion.viaversion.api.connection.UserConnection; +import com.viaversion.viaversion.protocols.v1_20_3to1_20_5.packet.ClientboundPacket1_20_5; +import com.viaversion.viaversion.protocols.v1_20_5to1_21.Protocol1_20_5To1_21; +import com.viaversion.viaversion.rewriter.ComponentRewriter; +import com.viaversion.viaversion.util.SerializerVersion; +import com.viaversion.viaversion.util.TagUtil; +import com.viaversion.viaversion.util.UUIDUtil; +import java.util.UUID; + +public final class ComponentRewriter1_21 extends ComponentRewriter { + + public ComponentRewriter1_21(final Protocol1_20_5To1_21 protocol) { + super(protocol, ReadType.NBT); + } + + @Override + protected void handleShowItem(final UserConnection connection, final CompoundTag componentsTag) { + final CompoundTag attributeModifiers = TagUtil.getNamespacedCompoundTag(componentsTag, "minecraft:attribute_modifiers"); + if (attributeModifiers != null) { + final ListTag modifiers = attributeModifiers.getListTag("modifiers", CompoundTag.class); + for (final CompoundTag modifier : modifiers) { + final String name = modifier.getString("name"); + final UUID uuid = UUIDUtil.fromIntArray(modifier.getIntArrayTag("uuid").getValue()); + final String id = Protocol1_20_5To1_21.mapAttributeUUID(uuid, name); + modifier.putString("id", id); + } + } + } + + @Override + protected SerializerVersion inputSerializerVersion() { + return SerializerVersion.V1_20_5; + } + + @Override + protected SerializerVersion outputSerializerVersion() { + return SerializerVersion.V1_20_5; + } +} diff --git a/common/src/main/java/com/viaversion/viaversion/rewriter/ComponentRewriter.java b/common/src/main/java/com/viaversion/viaversion/rewriter/ComponentRewriter.java index 41bd7d003..0e4b82598 100644 --- a/common/src/main/java/com/viaversion/viaversion/rewriter/ComponentRewriter.java +++ b/common/src/main/java/com/viaversion/viaversion/rewriter/ComponentRewriter.java @@ -36,13 +36,15 @@ import com.viaversion.viaversion.api.protocol.packet.State; import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers; import com.viaversion.viaversion.api.type.Types; import com.viaversion.viaversion.protocols.base.ClientboundLoginPackets; +import com.viaversion.viaversion.util.ComponentUtil; +import com.viaversion.viaversion.util.SerializerVersion; import org.checkerframework.checker.nullness.qual.Nullable; /** * Handles json and tag components, containing methods to override certain parts of the handling. * Also contains methods to register a few of the packets using components. */ -public class ComponentRewriter { +public class ComponentRewriter implements com.viaversion.viaversion.api.rewriter.ComponentRewriter { protected final Protocol protocol; protected final ReadType type; @@ -249,6 +251,7 @@ public class ComponentRewriter { // ----------------------------------------------------------------------- // Tag methods + @Override public void processTag(final UserConnection connection, @Nullable final Tag tag) { if (tag == null) { return; @@ -309,6 +312,48 @@ public class ComponentRewriter { if (contents != null) { processTag(connection, contents.get("name")); } + } else if (action.equals("show_item")) { + convertLegacyContents(hoverEventTag); + + final CompoundTag contentsTag = hoverEventTag.getCompoundTag("contents"); + if (contentsTag == null) { + return; + } + + final CompoundTag componentsTag = contentsTag.getCompoundTag("components"); + if (componentsTag != null) { + handleShowItem(connection, componentsTag); + } + } + } + + protected void handleShowItem(final UserConnection connection, final CompoundTag componentsTag) { + // To override if needed + } + + protected SerializerVersion inputSerializerVersion() { + return null; + } + + protected SerializerVersion outputSerializerVersion() { + return null; + } + + private void convertLegacyContents(final CompoundTag hoverEvent) { + if (inputSerializerVersion() == null || outputSerializerVersion() == null) { + return; + } + + final Tag valueTag = hoverEvent.remove("value"); + if (valueTag != null) { + final CompoundTag tag = ComponentUtil.deserializeShowItem(valueTag, inputSerializerVersion()); + final CompoundTag contentsTag = new CompoundTag(); + contentsTag.put("id", tag.getStringTag("id")); + contentsTag.put("count", tag.getIntTag("count")); + if (tag.get("tag") instanceof CompoundTag) { + contentsTag.putString("tag", outputSerializerVersion().toSNBT(tag.getCompoundTag("tag"))); + } + hoverEvent.put("contents", contentsTag); } } diff --git a/common/src/main/java/com/viaversion/viaversion/rewriter/ItemRewriter.java b/common/src/main/java/com/viaversion/viaversion/rewriter/ItemRewriter.java index 9e1d7b137..2d3d6f88f 100644 --- a/common/src/main/java/com/viaversion/viaversion/rewriter/ItemRewriter.java +++ b/common/src/main/java/com/viaversion/viaversion/rewriter/ItemRewriter.java @@ -17,6 +17,7 @@ */ package com.viaversion.viaversion.rewriter; +import com.viaversion.nbt.tag.Tag; import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.data.Mappings; import com.viaversion.viaversion.api.data.ParticleMappings; @@ -28,6 +29,7 @@ import com.viaversion.viaversion.api.protocol.packet.PacketWrapper; import com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType; import com.viaversion.viaversion.api.protocol.remapper.PacketHandler; import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers; +import com.viaversion.viaversion.api.rewriter.ComponentRewriter; import com.viaversion.viaversion.api.rewriter.RewriterBase; import com.viaversion.viaversion.api.type.Type; import com.viaversion.viaversion.api.type.Types; @@ -381,15 +383,7 @@ public class ItemRewriter componentType) { protocol.registerClientbound(packetType, wrapper -> { wrapper.passthrough(Types.BOOLEAN); // Reset/clear int size = wrapper.passthrough(Types.VAR_INT); // Mapping size @@ -399,8 +393,14 @@ public class ItemRewriter customNameData = dataContainer.getNonEmpty(StructuredDataKey.CUSTOM_NAME); + if (customNameData != null) { + final Tag originalName = customNameData.value().copy(); + componentRewriter.processTag(connection, customNameData.value()); + if (!customNameData.value().equals(originalName)) { + saveTag(createCustomTag(item), originalName, "Name"); + } + } + + final StructuredData loreData = dataContainer.getNonEmpty(StructuredDataKey.LORE); + if (loreData != null) { + for (final Tag tag : loreData.value()) { + componentRewriter.processTag(connection, tag); + } + } + } + updateItemComponents(connection, dataContainer, this::handleItemToClient, mappingData != null ? mappingData::getNewItemId : null); return item; } @@ -88,11 +111,12 @@ public class StructuredItemRewriter trimData = container.getNonEmpty(StructuredDataKey.TRIM); if (trimData != null && idRewriter != null) { @@ -128,6 +152,47 @@ public class StructuredItemRewriter customData = data.getNonEmpty(StructuredDataKey.CUSTOM_DATA); + if (customData == null) { + return; + } + + // Remove custom name + if (customData.value().remove(nbtTagName("customName")) != null) { + data.remove(StructuredDataKey.CUSTOM_NAME); + } else { + final Tag name = removeBackupTag(customData.value(), "Name"); + if (name != null) { + data.set(StructuredDataKey.CUSTOM_NAME, name); + } + } + } + + protected CompoundTag createCustomTag(final Item item) { + final StructuredDataContainer data = item.structuredData(); + final StructuredData customData = data.getNonEmpty(StructuredDataKey.CUSTOM_DATA); + if (customData != null) { + return customData.value(); + } + + final CompoundTag tag = new CompoundTag(); + data.set(StructuredDataKey.CUSTOM_DATA, tag); + return tag; + } + + protected void saveTag(final CompoundTag customData, final Tag tag, final String name) { + final String backupName = nbtTagName(name); + if (!customData.contains(backupName)) { + customData.put(backupName, tag); + } + } + + protected @Nullable Tag removeBackupTag(final CompoundTag customData, final String tagName) { + return customData.remove(nbtTagName(tagName)); + } + @FunctionalInterface public interface ItemHandler { diff --git a/common/src/main/java/com/viaversion/viaversion/util/TagUtil.java b/common/src/main/java/com/viaversion/viaversion/util/TagUtil.java index 5febaecd7..c862fb81d 100644 --- a/common/src/main/java/com/viaversion/viaversion/util/TagUtil.java +++ b/common/src/main/java/com/viaversion/viaversion/util/TagUtil.java @@ -30,12 +30,9 @@ public final class TagUtil { } public static ListTag getRegistryEntries(final CompoundTag tag, final String key, final @Nullable ListTag defaultValue) { - CompoundTag registry = tag.getCompoundTag(Key.namespaced(key)); + CompoundTag registry = getNamespacedCompoundTag(tag, key); if (registry == null) { - registry = tag.getCompoundTag(Key.stripMinecraftNamespace(key)); - if (registry == null) { - return defaultValue; - } + return defaultValue; } return registry.getListTag("value", CompoundTag.class); } @@ -62,6 +59,11 @@ public final class TagUtil { return tag.remove(Key.namespaced(key)) != null || tag.remove(Key.stripMinecraftNamespace(key)) != null; } + public static @Nullable CompoundTag getNamespacedCompoundTag(final CompoundTag tag, final String key) { + final CompoundTag compoundTag = tag.getCompoundTag(Key.namespaced(key)); + return compoundTag != null ? compoundTag : tag.getCompoundTag(Key.stripMinecraftNamespace(key)); + } + public static Tag handleDeep(final Tag tag, final TagUpdater consumer) { return handleDeep(null, tag, consumer); }