diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/ItemFrameEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/ItemFrameEntity.java index ba3dfb79d..afe0dcdc6 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/ItemFrameEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/ItemFrameEntity.java @@ -118,7 +118,17 @@ public class ItemFrameEntity extends Entity { NbtMapBuilder builder = NbtMap.builder(); builder.putByte("Count", (byte) itemData.getCount()); NbtMap itemDataTag = itemData.getTag(); - if (itemData.getTag() != null) { + if (itemDataTag != null) { + // Remove custom name that Geyser sets for items due to translating non-"custom_name" components + String customName = ItemTranslator.getCustomName(session, heldItem.getDataComponents(), + session.getItemMappings().getMapping(heldItem), 'f', true, false); + if (customName == null) { + // No custom name found, must modify tag if custom name exists + NbtMapBuilder copy = itemDataTag.toBuilder(); + copy.remove("display"); // Also removes lore, but, should not matter + itemDataTag = copy.build(); + } + builder.put("tag", itemDataTag); } builder.putShort("Damage", (short) itemData.getDamage()); diff --git a/core/src/main/java/org/geysermc/geyser/item/type/PotionItem.java b/core/src/main/java/org/geysermc/geyser/item/type/PotionItem.java index 4cc3756cd..89e60b325 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/PotionItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/PotionItem.java @@ -34,9 +34,7 @@ import org.geysermc.geyser.inventory.item.Potion; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.item.BedrockItemBuilder; import org.geysermc.geyser.translator.item.CustomItemTranslator; -import org.geysermc.geyser.translator.item.ItemTranslator; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; import org.geysermc.mcprotocollib.protocol.data.game.item.component.PotionContents; @@ -70,17 +68,6 @@ public class PotionItem extends Item { return super.translateToBedrock(session, count, components, mapping, mappings); } - @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - // Make custom effect information visible - PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS); - if (potionContents != null) { - ItemTranslator.addPotionEffectLore(potionContents, builder, session.locale()); - } - - super.translateComponentsToBedrock(session, components, builder); - } - @Override public @NonNull GeyserItemStack translateToJava(GeyserSession session, @NonNull ItemData itemData, @NonNull ItemMapping mapping, @NonNull ItemMappings mappings) { Potion potion = Potion.getByBedrockId(itemData.getDamage()); diff --git a/core/src/main/java/org/geysermc/geyser/item/type/ShulkerBoxItem.java b/core/src/main/java/org/geysermc/geyser/item/type/ShulkerBoxItem.java index d8d2c347d..5d14f748c 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/ShulkerBoxItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/ShulkerBoxItem.java @@ -35,6 +35,7 @@ import org.geysermc.geyser.item.Items; import org.geysermc.geyser.level.block.type.Block; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.translator.item.BedrockItemBuilder; import org.geysermc.geyser.translator.item.CustomItemTranslator; import org.geysermc.geyser.translator.item.ItemTranslator; @@ -98,8 +99,12 @@ public class ShulkerBoxItem extends BlockItem { // Only the display name is what we have interest in, so just translate that if relevant if (boxComponents != null) { - String customName = ItemTranslator.getCustomName(session, boxComponents, boxMapping, '7', true); + String customName = ItemTranslator.getCustomName(session, boxComponents, boxMapping, '7', false, true); if (customName != null) { + // Fix count display (e.g., x16) with incorrect color due to some items with colored names + if (customName.contains("" + ChatColor.ESCAPE)) { + customName += ChatColor.RESET + ChatColor.GRAY; + } boxItemNbt.putCompound("tag", NbtMap.builder() .putCompound("display", NbtMap.builder() .putString("Name", customName) diff --git a/core/src/main/java/org/geysermc/geyser/item/type/TippedArrowItem.java b/core/src/main/java/org/geysermc/geyser/item/type/TippedArrowItem.java index 9e212ebef..09e4ee21f 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/TippedArrowItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/TippedArrowItem.java @@ -25,15 +25,12 @@ package org.geysermc.geyser.item.type; -import org.checkerframework.checker.nullness.qual.NonNull; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.inventory.item.Potion; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.item.BedrockItemBuilder; -import org.geysermc.geyser.translator.item.ItemTranslator; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; import org.geysermc.mcprotocollib.protocol.data.game.item.component.PotionContents; @@ -60,15 +57,4 @@ public class TippedArrowItem extends ArrowItem { } return super.translateToBedrock(session, count, components, mapping, mappings); } - - @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - // Make custom effect information visible - PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS); - if (potionContents != null) { - ItemTranslator.addPotionEffectLore(potionContents, builder, session.locale()); - } - - super.translateComponentsToBedrock(session, components, builder); - } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/item/ItemTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/item/ItemTranslator.java index b8959c7b8..3f9bf7446 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/item/ItemTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/item/ItemTranslator.java @@ -41,6 +41,7 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.block.custom.CustomBlockData; import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.inventory.GeyserItemStack; +import org.geysermc.geyser.inventory.item.Potion; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.components.Rarity; import org.geysermc.geyser.item.type.Item; @@ -70,6 +71,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.item.component.ItemAttribut import org.geysermc.mcprotocollib.protocol.data.game.item.component.MobEffectDetails; import org.geysermc.mcprotocollib.protocol.data.game.item.component.MobEffectInstance; import org.geysermc.mcprotocollib.protocol.data.game.item.component.PotionContents; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.WrittenBookContent; import java.text.DecimalFormat; import java.util.ArrayList; @@ -163,18 +165,25 @@ public final class ItemTranslator { .build(); } - public static ItemData.@NonNull Builder translateToBedrock(GeyserSession session, Item javaItem, ItemMapping bedrockItem, int count, @Nullable DataComponents components) { + public static ItemData.@NonNull Builder translateToBedrock(GeyserSession session, Item javaItem, ItemMapping bedrockItem, int count, @Nullable DataComponents customComponents) { BedrockItemBuilder nbtBuilder = new BedrockItemBuilder(); // Populates default components that aren't sent over the network - components = javaItem.gatherComponents(components); + DataComponents components = javaItem.gatherComponents(customComponents); // Translate item-specific components javaItem.translateComponentsToBedrock(session, components, nbtBuilder); Rarity rarity = Rarity.fromId(components.getOrDefault(DataComponentType.RARITY, 0)); - String customName = getCustomName(session, components, bedrockItem, rarity.getColor(), false); + String customName = getCustomName(session, customComponents, bedrockItem, rarity.getColor(), false, false); if (customName != null) { + PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS); + // Make custom effect information visible + // Ignore when item have "hide_additional_tooltip" component + if (potionContents != null && components.get(DataComponentType.HIDE_ADDITIONAL_TOOLTIP) == null) { + customName += getPotionEffectInfo(potionContents, session.locale()); + } + nbtBuilder.setCustomName(customName); } @@ -336,7 +345,8 @@ public final class ItemTranslator { Effect.INFESTED ); - public static void addPotionEffectLore(PotionContents contents, BedrockItemBuilder builder, String language) { + public static String getPotionEffectInfo(PotionContents contents, String language) { + StringBuilder finalText = new StringBuilder(); List effectInstanceList = contents.getCustomEffects(); for (MobEffectInstance effectInstance : effectInstanceList) { Effect effect = effectInstance.getEffect(); @@ -372,8 +382,40 @@ public final class ItemTranslator { .color((negativeEffectList.contains(effect)) ? NamedTextColor.RED : NamedTextColor.BLUE) .append(appendTranslatable) .build(); - builder.getOrCreateLore().add(MessageTranslator.convertMessage(component, language)); + // Bedrock supports wrap lines with '\n' in a single string in custom name + finalText.append('\n').append(MessageTranslator.convertMessage(component, language)); } + return finalText.toString(); + } + + public static String getPotionName(PotionContents contents, ItemMapping mapping, boolean hideAdditionalTooltip, String language) { + String customPotionName = contents.getCustomName(); + Potion potion = Potion.getByJavaId(contents.getPotionId()); + + if (customPotionName != null) { + // "custom_name" tag in "potion_contents" component + return MessageTranslator.convertMessage( + Component.translatable(mapping.getJavaItem().translationKey() + ".effect." + customPotionName), + language); + } + if (!hideAdditionalTooltip && !contents.getCustomEffects().isEmpty()) { + // Make a name when has custom effects + String potionName; + if (potion != null) { + potionName = potion.toString().toLowerCase(Locale.ROOT); + if (potionName.startsWith("strong_")) { + potionName = potionName.substring(6); + } else if (potionName.startsWith("long_")) { + potionName = potionName.substring(4); + } + } else { + potionName = "empty"; + } + return MessageTranslator.convertMessage( + Component.translatable(mapping.getJavaItem().translationKey() + ".effect." + potionName), + language); + } + return null; } private static void addAdvancedTooltips(@Nullable DataComponents components, BedrockItemBuilder builder, Item item, String language) { @@ -493,31 +535,34 @@ public final class ItemTranslator { * @param translationColor if this item is not available on Java, the color that the new name should be. * Normally, this should just be white, but for shulker boxes this should be gray. */ - public static String getCustomName(GeyserSession session, DataComponents components, ItemMapping mapping, char translationColor, boolean includeDefault) { + public static String getCustomName(GeyserSession session, DataComponents components, ItemMapping mapping, char translationColor, boolean customNameOnly, boolean includeAll) { if (components != null) { // ItemStack#getHoverName as of 1.20.5 Component customName = components.get(DataComponentType.CUSTOM_NAME); if (customName != null) { return MessageTranslator.convertMessage(customName, session.locale()); } - PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS); - if (potionContents != null) { - // "custom_name" tag in "potion_contents" component - String customPotionName = potionContents.getCustomName(); - if (customPotionName != null) { - Component component = Component.text() - .resetStyle() - .color(NamedTextColor.WHITE) - .append(Component.translatable(mapping.getJavaItem().translationKey() + ".effect." + customPotionName)) - .build(); - return MessageTranslator.convertMessage(component, session.locale()); + if (!customNameOnly) { + PotionContents potionContents = components.get(DataComponentType.POTION_CONTENTS); + if (potionContents != null) { + String potionName = getPotionName(potionContents, mapping, components.get(DataComponentType.HIDE_ADDITIONAL_TOOLTIP) != null, session.locale()); + if (potionName != null) { + return ChatColor.RESET + ChatColor.ESCAPE + translationColor + potionName; + } + } + if (includeAll) { + // Fix book title display in tooltips of shulker box + WrittenBookContent bookContent = components.get(DataComponentType.WRITTEN_BOOK_CONTENT); + if (bookContent != null) { + return ChatColor.RESET + ChatColor.ESCAPE + translationColor + bookContent.getTitle().getRaw(); + } + } + customName = components.get(DataComponentType.ITEM_NAME); + if (customName != null) { + // Get the translated name and prefix it with a reset char to prevent italics - matches Java Edition + // behavior as of 1.21 + return ChatColor.RESET + ChatColor.ESCAPE + translationColor + MessageTranslator.convertMessage(customName, session.locale()); } - } - customName = components.get(DataComponentType.ITEM_NAME); - if (customName != null && includeDefault) { - // Get the translated name and prefix it with a reset char to prevent italics - matches Java Edition - // behavior as of 1.21 - return ChatColor.RESET + ChatColor.ESCAPE + translationColor + MessageTranslator.convertMessage(customName, session.locale()); } }