diff --git a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts index 93b4d8c13..3f7b48a2f 100644 --- a/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/geyser.base-conventions.gradle.kts @@ -26,7 +26,7 @@ dependencies { } repositories { - // mavenLocal() + mavenLocal() mavenCentral() @@ -69,6 +69,4 @@ repositories { maven("https://jitpack.io") { content { includeGroupByRegex("com\\.github\\..*") } } - - mavenLocal() } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/FireworkEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/FireworkEntity.java index ebe35320e..d7a9990fe 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/FireworkEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/FireworkEntity.java @@ -31,6 +31,7 @@ import org.cloudburstmc.protocol.bedrock.packet.SetEntityMotionPacket; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.item.Items; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.item.BedrockItemBuilder; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata; @@ -59,7 +60,8 @@ public class FireworkEntity extends Entity { // TODO this looked the same, so I'm going to assume it is and (keep below comment if true) // Translate using item methods to get firework NBT for Bedrock BedrockItemBuilder builder = new BedrockItemBuilder(); - Items.FIREWORK_ROCKET.translateComponentsToBedrock(session, components, builder); + TooltipOptions tooltip = TooltipOptions.fromComponents(components); + Items.FIREWORK_ROCKET.translateComponentsToBedrock(session, components, tooltip, builder); dirtyMetadata.put(EntityDataTypes.DISPLAY_FIREWORK, builder.build()); } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java index 70ef2300e..6ef6ba0c9 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/LivingEntity.java @@ -44,6 +44,8 @@ import org.geysermc.geyser.entity.attribute.GeyserAttributeType; import org.geysermc.geyser.entity.vehicle.ClientVehicle; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.item.Items; +import org.geysermc.geyser.item.type.Item; +import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.scoreboard.Team; import org.geysermc.geyser.session.GeyserSession; @@ -51,6 +53,7 @@ import org.geysermc.geyser.translator.item.ItemTranslator; import org.geysermc.geyser.util.AttributeUtils; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.MathUtils; +import org.geysermc.mcprotocollib.protocol.data.game.entity.EquipmentSlot; import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.Attribute; import org.geysermc.mcprotocollib.protocol.data.game.entity.attribute.AttributeType; import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.EntityMetadata; @@ -62,6 +65,8 @@ import org.geysermc.mcprotocollib.protocol.data.game.entity.metadata.type.Object import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.Equippable; import org.geysermc.mcprotocollib.protocol.data.game.level.particle.ColorParticleData; import org.geysermc.mcprotocollib.protocol.data.game.level.particle.Particle; import org.geysermc.mcprotocollib.protocol.data.game.level.particle.ParticleType; @@ -133,7 +138,16 @@ public class LivingEntity extends Entity { public void setSaddle(ItemStack stack) { this.saddle = ItemTranslator.translateToBedrock(session, stack); - updateSaddled(stack.getId() == Items.SADDLE.javaId()); + + boolean saddled = false; + Item item = Registries.JAVA_ITEMS.get(stack.getId()); + if (item != null) { + DataComponents components = item.gatherComponents(stack.getDataComponentsPatch()); + Equippable equippable = components.get(DataComponentTypes.EQUIPPABLE); + saddled = equippable != null && equippable.slot() == EquipmentSlot.SADDLE; + } + + updateSaddled(saddled); } public void setHand(ItemStack stack) { diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/TropicalFishEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/TropicalFishEntity.java index b6751bc3f..182bb176f 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/TropicalFishEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/living/animal/TropicalFishEntity.java @@ -61,6 +61,10 @@ public class TropicalFishEntity extends AbstractFishEntity { dirtyMetadata.put(EntityDataTypes.COLOR_2, getPatternColor(varNumber)); // Pattern color 0-15 } + public static int getPackedVariant(int pattern, int baseColor, int patternColor) { + return pattern & 65535 | (baseColor & 0xFF) << 16 | (patternColor & 0xFF) << 24; + } + public static int getShape(int variant) { return Math.min(variant & 0xFF, 1); } diff --git a/core/src/main/java/org/geysermc/geyser/inventory/item/GeyserInstrument.java b/core/src/main/java/org/geysermc/geyser/inventory/item/GeyserInstrument.java index 38d4f2cd5..71f43a23e 100644 --- a/core/src/main/java/org/geysermc/geyser/inventory/item/GeyserInstrument.java +++ b/core/src/main/java/org/geysermc/geyser/inventory/item/GeyserInstrument.java @@ -89,13 +89,18 @@ public interface GeyserInstrument { return -1; } - // TODO 1.21.5 + // TODO test in 1.21.5 static GeyserInstrument fromComponent(GeyserSession session, InstrumentComponent component) { - if (component.instrumentHolder().isId()) { - return session.getRegistryCache().instruments().byId(component.instrumentHolder().id()); + if (component.instrumentLocation() != null) { + return session.getRegistryCache().instruments().byKey(component.instrumentLocation()); + } else if (component.instrumentHolder() != null) { + if (component.instrumentHolder().isId()) { + return session.getRegistryCache().instruments().byId(component.instrumentHolder().id()); + } + InstrumentComponent.Instrument custom = component.instrumentHolder().custom(); + return new Wrapper(custom, session.locale()); } - InstrumentComponent.Instrument custom = component.instrumentHolder().custom(); - return new Wrapper(custom, session.locale()); + throw new IllegalStateException("InstrumentComponent must have either a location or a holder"); } record Wrapper(InstrumentComponent.Instrument instrument, String locale) implements GeyserInstrument { diff --git a/core/src/main/java/org/geysermc/geyser/item/TooltipOptions.java b/core/src/main/java/org/geysermc/geyser/item/TooltipOptions.java new file mode 100644 index 000000000..2fa9af789 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/TooltipOptions.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.item; + +import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.TooltipDisplay; + +@FunctionalInterface +public interface TooltipOptions { + + TooltipOptions ALL_SHOWN = component -> true; + + TooltipOptions ALL_HIDDEN = component -> false; + + boolean showInTooltip(DataComponentType component); + + static TooltipOptions fromComponents(DataComponents components) { + TooltipDisplay display = components.get(DataComponentTypes.TOOLTIP_DISPLAY); + if (display == null) { + return ALL_SHOWN; + } else if (display.hideTooltip()) { + return ALL_HIDDEN; + } else if (display.hiddenComponents().isEmpty()) { + return ALL_SHOWN; + } + + return component -> !display.hiddenComponents().contains(component); + } + + static boolean hideTooltip(DataComponents components) { + TooltipDisplay display = components.get(DataComponentTypes.TOOLTIP_DISPLAY); + return display != null && display.hideTooltip(); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/type/ArmorItem.java b/core/src/main/java/org/geysermc/geyser/item/type/ArmorItem.java index c20cc490e..5eca97f87 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/ArmorItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/ArmorItem.java @@ -30,6 +30,7 @@ import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMapBuilder; import org.cloudburstmc.protocol.bedrock.data.TrimMaterial; import org.cloudburstmc.protocol.bedrock.data.TrimPattern; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.item.BedrockItemBuilder; import org.geysermc.mcprotocollib.protocol.data.game.item.component.ArmorTrim; @@ -43,8 +44,8 @@ public class ArmorItem extends Item { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); ArmorTrim trim = components.get(DataComponentTypes.TRIM); if (trim != null) { @@ -54,6 +55,7 @@ public class ArmorItem extends Item { // discard custom trim patterns/materials to prevent visual glitches on bedrock if (!getNamespace(material.getMaterialId()).equals("minecraft") || !getNamespace(pattern.getPatternId()).equals("minecraft")) { + // TODO - how is this shown in tooltip? should we add a custom trim tooltip to the lore here return; } diff --git a/core/src/main/java/org/geysermc/geyser/item/type/AxolotlBucketItem.java b/core/src/main/java/org/geysermc/geyser/item/type/AxolotlBucketItem.java index 8895d45a8..3d3214697 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/AxolotlBucketItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/AxolotlBucketItem.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.item.type; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.geyser.translator.item.BedrockItemBuilder; @@ -37,8 +38,8 @@ public class AxolotlBucketItem extends Item { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); // Bedrock Edition displays the properties of the axolotl. Java does not. // To work around this, set the custom name to the Axolotl translation and it's displayed correctly diff --git a/core/src/main/java/org/geysermc/geyser/item/type/BannerItem.java b/core/src/main/java/org/geysermc/geyser/item/type/BannerItem.java index f7229e202..2f3f64b75 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/BannerItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/BannerItem.java @@ -36,6 +36,7 @@ import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtType; import org.geysermc.geyser.inventory.item.BannerPattern; import org.geysermc.geyser.inventory.item.DyeColor; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.level.block.type.Block; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; @@ -46,6 +47,7 @@ import org.geysermc.mcprotocollib.protocol.data.game.Holder; import org.geysermc.mcprotocollib.protocol.data.game.item.component.BannerPatternLayer; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.TooltipDisplay; import java.util.ArrayList; import java.util.List; @@ -202,8 +204,8 @@ public class BannerItem extends BlockItem { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); List patterns = components.get(DataComponentTypes.BANNER_PATTERNS); if (patterns != null) { @@ -225,7 +227,8 @@ public class BannerItem extends BlockItem { } components.put(DataComponentTypes.BANNER_PATTERNS, patternLayers); - // TODO 1.21.5 hide components??? + // The ominous banner item in the Java creative menu just has banner patterns hidden as of 1.21.5 + components.put(DataComponentTypes.TOOLTIP_DISPLAY, new TooltipDisplay(false, List.of(DataComponentTypes.BANNER_PATTERNS))); components.put(DataComponentTypes.ITEM_NAME, Component .translatable("block.minecraft.ominous_banner") .style(Style.style(TextColor.color(16755200))) diff --git a/core/src/main/java/org/geysermc/geyser/item/type/CompassItem.java b/core/src/main/java/org/geysermc/geyser/item/type/CompassItem.java index d6403a8c3..ef1ca52c5 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/CompassItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/CompassItem.java @@ -29,6 +29,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.geysermc.geyser.inventory.GeyserItemStack; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.session.GeyserSession; @@ -59,8 +60,8 @@ public class CompassItem extends Item { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); LodestoneTracker tracker = components.get(DataComponentTypes.LODESTONE_TRACKER); if (tracker != null) { diff --git a/core/src/main/java/org/geysermc/geyser/item/type/CrossbowItem.java b/core/src/main/java/org/geysermc/geyser/item/type/CrossbowItem.java index 13e79958e..9eb95e4fb 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/CrossbowItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/CrossbowItem.java @@ -28,6 +28,7 @@ package org.geysermc.geyser.item.type; import org.checkerframework.checker.nullness.qual.NonNull; import org.cloudburstmc.nbt.NbtMapBuilder; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.item.BedrockItemBuilder; @@ -44,8 +45,8 @@ public class CrossbowItem extends Item { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); List chargedProjectiles = components.get(DataComponentTypes.CHARGED_PROJECTILES); if (chargedProjectiles != null && !chargedProjectiles.isEmpty()) { diff --git a/core/src/main/java/org/geysermc/geyser/item/type/DecoratedPotItem.java b/core/src/main/java/org/geysermc/geyser/item/type/DecoratedPotItem.java index fa08bd7ec..a81a8cb52 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/DecoratedPotItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/DecoratedPotItem.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.item.type; import org.checkerframework.checker.nullness.qual.NonNull; import org.cloudburstmc.nbt.NbtType; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.level.block.type.Block; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; @@ -44,8 +45,8 @@ public class DecoratedPotItem extends BlockItem { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); List decorations = components.get(DataComponentTypes.POT_DECORATIONS); // TODO maybe unbox in MCProtocolLib if (decorations != null) { diff --git a/core/src/main/java/org/geysermc/geyser/item/type/DyeableArmorItem.java b/core/src/main/java/org/geysermc/geyser/item/type/DyeableArmorItem.java index 480385d07..b85a41782 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/DyeableArmorItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/DyeableArmorItem.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.item.type; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.item.BedrockItemBuilder; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; @@ -36,8 +37,8 @@ public class DyeableArmorItem extends ArmorItem { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); // Note that this is handled as of 1.20.5 in the ItemColors class. // But horse leather armor and body leather armor are now both armor items. So it works! diff --git a/core/src/main/java/org/geysermc/geyser/item/type/EnchantedBookItem.java b/core/src/main/java/org/geysermc/geyser/item/type/EnchantedBookItem.java index f5ddb698b..bc6dcde67 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/EnchantedBookItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/EnchantedBookItem.java @@ -32,6 +32,7 @@ import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtType; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.inventory.item.BedrockEnchantment; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.item.enchantment.Enchantment; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; @@ -50,8 +51,8 @@ public class EnchantedBookItem extends Item { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); List bedrockEnchants = new ArrayList<>(); ItemEnchantments enchantments = components.get(DataComponentTypes.STORED_ENCHANTMENTS); diff --git a/core/src/main/java/org/geysermc/geyser/item/type/FireworkRocketItem.java b/core/src/main/java/org/geysermc/geyser/item/type/FireworkRocketItem.java index 265d3aad7..a0060044f 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/FireworkRocketItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/FireworkRocketItem.java @@ -31,6 +31,7 @@ import org.cloudburstmc.nbt.NbtList; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMapBuilder; import org.cloudburstmc.nbt.NbtType; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.level.FireworkColor; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; @@ -48,8 +49,8 @@ public class FireworkRocketItem extends Item implements BedrockRequiresTagItem { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); Fireworks fireworks = components.get(DataComponentTypes.FIREWORKS); if (fireworks == null) { diff --git a/core/src/main/java/org/geysermc/geyser/item/type/FireworkStarItem.java b/core/src/main/java/org/geysermc/geyser/item/type/FireworkStarItem.java index 170d386fd..ff6a19e61 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/FireworkStarItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/FireworkStarItem.java @@ -27,6 +27,7 @@ package org.geysermc.geyser.item.type; import org.checkerframework.checker.nullness.qual.NonNull; import org.cloudburstmc.nbt.NbtMap; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.item.BedrockItemBuilder; @@ -40,8 +41,8 @@ public class FireworkStarItem extends Item { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); Fireworks.FireworkExplosion explosion = components.get(DataComponentTypes.FIREWORK_EXPLOSION); if (explosion != null) { diff --git a/core/src/main/java/org/geysermc/geyser/item/type/FishingRodItem.java b/core/src/main/java/org/geysermc/geyser/item/type/FishingRodItem.java index 32b1d5df5..a066fdbab 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/FishingRodItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/FishingRodItem.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.item.type; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.item.BedrockItemBuilder; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; @@ -36,8 +37,8 @@ public class FishingRodItem extends Item { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); // Fix damage inconsistency builder.getDamage().ifPresent(damage -> builder.setDamage(getBedrockDamage(damage))); diff --git a/core/src/main/java/org/geysermc/geyser/item/type/GoatHornItem.java b/core/src/main/java/org/geysermc/geyser/item/type/GoatHornItem.java index e4173d2bb..7d0cfa796 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/GoatHornItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/GoatHornItem.java @@ -29,6 +29,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.cloudburstmc.protocol.bedrock.data.inventory.ItemData; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.item.GeyserInstrument; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.registry.type.ItemMappings; import org.geysermc.geyser.session.GeyserSession; @@ -63,12 +64,11 @@ public class GoatHornItem extends Item { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); InstrumentComponent component = components.get(DataComponentTypes.INSTRUMENT); - // TODO 1.21.5 hiding???? - if (component != null) { + if (component != null && tooltip.showInTooltip(DataComponentTypes.INSTRUMENT)) { GeyserInstrument instrument = GeyserInstrument.fromComponent(session, component); if (instrument.bedrockInstrument() == null) { builder.getOrCreateLore().add(instrument.description()); diff --git a/core/src/main/java/org/geysermc/geyser/item/type/Item.java b/core/src/main/java/org/geysermc/geyser/item/type/Item.java index 2c3303689..9919aa308 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/Item.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/Item.java @@ -37,6 +37,7 @@ import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.item.BedrockEnchantment; import org.geysermc.geyser.item.Items; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.item.enchantment.Enchantment; import org.geysermc.geyser.level.block.type.Block; import org.geysermc.geyser.registry.Registries; @@ -46,6 +47,7 @@ import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.ChatColor; import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.geyser.translator.item.BedrockItemBuilder; +import org.geysermc.geyser.translator.text.MessageTranslator; import org.geysermc.geyser.util.MinecraftKey; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentType; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; @@ -155,15 +157,14 @@ public class Item { /** * Takes components from Java Edition and map them into Bedrock. */ - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { List loreComponents = components.get(DataComponentTypes.LORE); - // TODO 1.21.5 -// if (loreComponents != null && components.get(DataComponentTypes.HIDE_TOOLTIP) == null) { -// List lore = builder.getOrCreateLore(); -// for (Component loreComponent : loreComponents) { -// lore.add(MessageTranslator.convertMessage(loreComponent, session.locale())); -// } -// } + if (loreComponents != null && tooltip.showInTooltip(DataComponentTypes.LORE)) { + List lore = builder.getOrCreateLore(); + for (Component loreComponent : loreComponents) { + lore.add(MessageTranslator.convertMessage(loreComponent, session.locale())); + } + } Integer damage = components.get(DataComponentTypes.DAMAGE); if (damage != null) { diff --git a/core/src/main/java/org/geysermc/geyser/item/type/MapItem.java b/core/src/main/java/org/geysermc/geyser/item/type/MapItem.java index f19da5968..332c5210c 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/MapItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/MapItem.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.item.type; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.item.BedrockItemBuilder; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; @@ -37,8 +38,8 @@ public class MapItem extends Item { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); Integer mapValue = components.get(DataComponentTypes.MAP_ID); if (mapValue == null) { diff --git a/core/src/main/java/org/geysermc/geyser/item/type/PlayerHeadItem.java b/core/src/main/java/org/geysermc/geyser/item/type/PlayerHeadItem.java index 502d9be0d..7f29751a4 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/PlayerHeadItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/PlayerHeadItem.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.item.type; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.item.components.Rarity; import org.geysermc.geyser.level.block.type.Block; import org.geysermc.geyser.session.GeyserSession; @@ -42,8 +43,8 @@ public class PlayerHeadItem extends BlockItem { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); // Use the correct color, determined by the rarity of the item char rarity = Rarity.fromId(components.get(DataComponentTypes.RARITY)).getColor(); diff --git a/core/src/main/java/org/geysermc/geyser/item/type/ShieldItem.java b/core/src/main/java/org/geysermc/geyser/item/type/ShieldItem.java index 01cea9c17..9d44920f0 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/ShieldItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/ShieldItem.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.item.type; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.item.BedrockItemBuilder; import org.geysermc.mcprotocollib.protocol.data.game.item.component.BannerPatternLayer; @@ -40,8 +41,8 @@ public class ShieldItem extends Item { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); List patterns = components.get(DataComponentTypes.BANNER_PATTERNS); if (patterns != null) { 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 a53a9b7bc..e2e910b17 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 @@ -32,6 +32,7 @@ import org.cloudburstmc.nbt.NbtType; import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition; import org.geysermc.geyser.inventory.item.Potion; import org.geysermc.geyser.item.Items; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.level.block.type.Block; import org.geysermc.geyser.registry.type.ItemMapping; import org.geysermc.geyser.session.GeyserSession; @@ -53,8 +54,8 @@ public class ShulkerBoxItem extends BlockItem { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); List contents = components.get(DataComponentTypes.CONTAINER); if (contents == null || contents.isEmpty()) { diff --git a/core/src/main/java/org/geysermc/geyser/item/type/TropicalFishBucketItem.java b/core/src/main/java/org/geysermc/geyser/item/type/TropicalFishBucketItem.java index a93cc5934..011d5bb22 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/TropicalFishBucketItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/TropicalFishBucketItem.java @@ -30,8 +30,8 @@ import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.Style; import net.kyori.adventure.text.format.TextDecoration; import org.checkerframework.checker.nullness.qual.NonNull; -import org.cloudburstmc.nbt.NbtMap; import org.geysermc.geyser.entity.type.living.animal.TropicalFishEntity; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.text.MinecraftLocale; import org.geysermc.geyser.translator.item.BedrockItemBuilder; @@ -49,37 +49,54 @@ public class TropicalFishBucketItem extends Item { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); // Prevent name from appearing as "Bucket of" builder.putByte("AppendCustomName", (byte) 1); builder.putString("CustomName", MinecraftLocale.getLocaleString("entity.minecraft.tropical_fish", session.locale())); + // Add Java's client side lore tag - // Do you know how frequently Java NBT used to be before 1.20.5? It was a lot. And now it's just this lowly check. - NbtMap entityTag = components.get(DataComponentTypes.BUCKET_ENTITY_DATA); - if (entityTag != null && !entityTag.isEmpty()) { - //TODO test - int bucketVariant = entityTag.getInt("BucketVariantTag"); + Integer pattern = components.get(DataComponentTypes.TROPICAL_FISH_PATTERN); + Integer baseColor = components.get(DataComponentTypes.TROPICAL_FISH_BASE_COLOR); + Integer patternColor = components.get(DataComponentTypes.TROPICAL_FISH_PATTERN_COLOR); + + // The pattern component decides whether to show the tooltip of all 3 components, as of Java 1.21.5 + if ((pattern != null || (baseColor != null && patternColor != null)) && tooltip.showInTooltip(DataComponentTypes.TROPICAL_FISH_PATTERN)) { + //TODO test this for 1.21.5 + int packedVariant = getPackedVariant(pattern, baseColor, patternColor); List lore = builder.getOrCreateLore(); - int predefinedVariantId = TropicalFishEntity.getPredefinedId(bucketVariant); + int predefinedVariantId = TropicalFishEntity.getPredefinedId(packedVariant); if (predefinedVariantId != -1) { - Component tooltip = Component.translatable("entity.minecraft.tropical_fish.predefined." + predefinedVariantId, LORE_STYLE); - lore.add(0, MessageTranslator.convertMessage(tooltip, session.locale())); + Component line = Component.translatable("entity.minecraft.tropical_fish.predefined." + predefinedVariantId, LORE_STYLE); + lore.add(0, MessageTranslator.convertMessage(line, session.locale())); } else { - Component typeTooltip = Component.translatable("entity.minecraft.tropical_fish.type." + TropicalFishEntity.getVariantName(bucketVariant), LORE_STYLE); + Component typeTooltip = Component.translatable("entity.minecraft.tropical_fish.type." + TropicalFishEntity.getVariantName(packedVariant), LORE_STYLE); lore.add(0, MessageTranslator.convertMessage(typeTooltip, session.locale())); - byte baseColor = TropicalFishEntity.getBaseColor(bucketVariant); - byte patternColor = TropicalFishEntity.getPatternColor(bucketVariant); - Component colorTooltip = Component.translatable("color.minecraft." + TropicalFishEntity.getColorName(baseColor), LORE_STYLE); - if (baseColor != patternColor) { - colorTooltip = colorTooltip.append(Component.text(", ", LORE_STYLE)) - .append(Component.translatable("color.minecraft." + TropicalFishEntity.getColorName(patternColor), LORE_STYLE)); + if (baseColor != null && patternColor != null) { + Component colorTooltip = Component.translatable("color.minecraft." + TropicalFishEntity.getColorName(baseColor.byteValue()), LORE_STYLE); + if (!baseColor.equals(patternColor)) { + colorTooltip = colorTooltip.append(Component.text(", ", LORE_STYLE)) + .append(Component.translatable("color.minecraft." + TropicalFishEntity.getColorName(patternColor.byteValue()), LORE_STYLE)); + } + lore.add(1, MessageTranslator.convertMessage(colorTooltip, session.locale())); } - lore.add(1, MessageTranslator.convertMessage(colorTooltip, session.locale())); } } } + + private static int getPackedVariant(Integer pattern, Integer baseColor, Integer patternColor) { + if (pattern == null) { + pattern = 0; + } + if (baseColor == null) { + baseColor = 0; + } + if (patternColor == null) { + patternColor = 0; + } + return TropicalFishEntity.getPackedVariant(pattern, baseColor, patternColor); + } } diff --git a/core/src/main/java/org/geysermc/geyser/item/type/WolfArmorItem.java b/core/src/main/java/org/geysermc/geyser/item/type/WolfArmorItem.java index 41c72f532..086bf3a9c 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/WolfArmorItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/WolfArmorItem.java @@ -26,6 +26,7 @@ package org.geysermc.geyser.item.type; import org.checkerframework.checker.nullness.qual.NonNull; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.item.BedrockItemBuilder; import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; @@ -36,8 +37,8 @@ public class WolfArmorItem extends Item { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); // Note that this is handled as of 1.21 in the ItemColors class. translateDyedColor(components, builder); diff --git a/core/src/main/java/org/geysermc/geyser/item/type/WritableBookItem.java b/core/src/main/java/org/geysermc/geyser/item/type/WritableBookItem.java index 177ca0b2a..78a744a87 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/WritableBookItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/WritableBookItem.java @@ -29,6 +29,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMapBuilder; import org.cloudburstmc.nbt.NbtType; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.item.BedrockItemBuilder; import org.geysermc.geyser.translator.text.MessageTranslator; @@ -46,8 +47,8 @@ public class WritableBookItem extends Item { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); WritableBookContent bookContent = components.get(DataComponentTypes.WRITABLE_BOOK_CONTENT); if (bookContent == null) { diff --git a/core/src/main/java/org/geysermc/geyser/item/type/WrittenBookItem.java b/core/src/main/java/org/geysermc/geyser/item/type/WrittenBookItem.java index 9cb661e70..22064e44c 100644 --- a/core/src/main/java/org/geysermc/geyser/item/type/WrittenBookItem.java +++ b/core/src/main/java/org/geysermc/geyser/item/type/WrittenBookItem.java @@ -30,6 +30,7 @@ import org.checkerframework.checker.nullness.qual.NonNull; import org.cloudburstmc.nbt.NbtMap; import org.cloudburstmc.nbt.NbtMapBuilder; import org.cloudburstmc.nbt.NbtType; +import org.geysermc.geyser.item.TooltipOptions; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.item.BedrockItemBuilder; import org.geysermc.geyser.translator.text.MessageTranslator; @@ -51,8 +52,8 @@ public class WrittenBookItem extends Item { } @Override - public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull BedrockItemBuilder builder) { - super.translateComponentsToBedrock(session, components, builder); + public void translateComponentsToBedrock(@NonNull GeyserSession session, @NonNull DataComponents components, @NonNull TooltipOptions tooltip, @NonNull BedrockItemBuilder builder) { + super.translateComponentsToBedrock(session, components, tooltip, builder); WrittenBookContent bookContent = components.get(DataComponentTypes.WRITTEN_BOOK_CONTENT); if (bookContent == null) { diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java index ecd293bff..1f0de4444 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/RegistryCache.java @@ -51,6 +51,7 @@ import org.geysermc.geyser.session.cache.registry.JavaRegistries; import org.geysermc.geyser.session.cache.registry.JavaRegistry; import org.geysermc.geyser.session.cache.registry.JavaRegistryKey; import org.geysermc.geyser.session.cache.registry.RegistryEntryContext; +import org.geysermc.geyser.session.cache.registry.RegistryEntryData; import org.geysermc.geyser.session.cache.registry.SimpleJavaRegistry; import org.geysermc.geyser.text.ChatDecoration; import org.geysermc.geyser.translator.level.BiomeTranslator; @@ -189,7 +190,7 @@ public final class RegistryCache { entryIdMap.put(entries.get(i).getId(), i); } - List builder = new ArrayList<>(entries.size()); + List> builder = new ArrayList<>(entries.size()); for (int i = 0; i < entries.size(); i++) { RegistryEntry entry = entries.get(i); // If the data is null, that's the server telling us we need to use our default values. @@ -203,7 +204,7 @@ public final class RegistryCache { RegistryEntryContext context = new RegistryEntryContext(entry, entryIdMap, registryCache.session); // This is what Geyser wants to keep as a value for this registry. T cacheEntry = reader.apply(context); - builder.add(i, cacheEntry); + builder.add(i, new RegistryEntryData<>(entry.getId(), cacheEntry)); } localCache.reset(builder); }); diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistry.java b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistry.java index d7c7782ea..e51dbf043 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistry.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistry.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.session.cache.registry; +import net.kyori.adventure.key.Key; import org.checkerframework.checker.index.qual.NonNegative; import java.util.List; @@ -39,15 +40,30 @@ public interface JavaRegistry { */ T byId(@NonNegative int id); + /** + * Looks up a registry entry by its key. The object can be null, or not present. + */ + T byKey(Key key); + + /** + * Looks up a registry entry by its ID, and returns it wrapped in {@link RegistryEntryData} so that its registered key is also known. The object can be null, or not present. + */ + RegistryEntryData entryById(@NonNegative int id); + /** * Reverse looks-up an object to return its network ID, or -1. */ int byValue(T value); + /** + * Reverse looks-up an object to return it wrapped in {@link RegistryEntryData}, or null. + */ + RegistryEntryData entryByValue(T value); + /** * Resets the objects by these IDs. */ - void reset(List values); + void reset(List> values); /** * All values of this registry, as a list. diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistryKey.java b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistryKey.java index 369bea7a4..364d998ee 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistryKey.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/registry/JavaRegistryKey.java @@ -26,10 +26,9 @@ package org.geysermc.geyser.session.cache.registry; import net.kyori.adventure.key.Key; +import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.geyser.session.GeyserSession; -import javax.annotation.Nullable; - /** * Defines a Java registry, which can be hardcoded or data-driven. This class doesn't store registry contents itself, that is handled by {@link org.geysermc.geyser.session.cache.RegistryCache} in the case of * data-driven registries and other classes in the case of hardcoded registries. diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/registry/RegistryEntryData.java b/core/src/main/java/org/geysermc/geyser/session/cache/registry/RegistryEntryData.java new file mode 100644 index 000000000..ed0b2fdec --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/session/cache/registry/RegistryEntryData.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2025 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.session.cache.registry; + +import net.kyori.adventure.key.Key; + +public record RegistryEntryData(Key key, T data) { +} diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/registry/SimpleJavaRegistry.java b/core/src/main/java/org/geysermc/geyser/session/cache/registry/SimpleJavaRegistry.java index 7b79a40be..3e3d8ba6c 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/registry/SimpleJavaRegistry.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/registry/SimpleJavaRegistry.java @@ -26,15 +26,34 @@ package org.geysermc.geyser.session.cache.registry; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.kyori.adventure.key.Key; import org.checkerframework.checker.index.qual.NonNegative; import java.util.List; public class SimpleJavaRegistry implements JavaRegistry { - protected final ObjectArrayList values = new ObjectArrayList<>(); + protected final ObjectArrayList> values = new ObjectArrayList<>(); @Override public T byId(@NonNegative int id) { + if (id < 0 || id >= this.values.size()) { + return null; + } + return this.values.get(id).data(); + } + + @Override + public T byKey(Key key) { + for (RegistryEntryData entry : values) { + if (entry.key().equals(key)) { + return entry.data(); + } + } + return null; + } + + @Override + public RegistryEntryData entryById(@NonNegative int id) { if (id < 0 || id >= this.values.size()) { return null; } @@ -43,11 +62,26 @@ public class SimpleJavaRegistry implements JavaRegistry { @Override public int byValue(T value) { - return this.values.indexOf(value); + for (int i = 0; i < this.values.size(); i++) { + if (values.get(i).data().equals(value)) { + return i; + } + } + return -1; } @Override - public void reset(List values) { + public RegistryEntryData entryByValue(T value) { + for (RegistryEntryData entry : this.values) { + if (entry.data().equals(value)) { + return entry; + } + } + return null; + } + + @Override + public void reset(List> values) { this.values.clear(); this.values.addAll(values); this.values.trim(); @@ -55,7 +89,7 @@ public class SimpleJavaRegistry implements JavaRegistry { @Override public List values() { - return this.values; + return this.values.stream().map(RegistryEntryData::data).toList(); } @Override 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 9d01f0d8c..04f5f5bb3 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 @@ -43,8 +43,10 @@ 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.TooltipOptions; import org.geysermc.geyser.item.components.Rarity; import org.geysermc.geyser.item.type.Item; +import org.geysermc.geyser.item.type.PotionItem; import org.geysermc.geyser.level.block.type.Block; import org.geysermc.geyser.registry.BlockRegistries; import org.geysermc.geyser.registry.Registries; @@ -168,37 +170,32 @@ public final class ItemTranslator { public static ItemData.@NonNull Builder translateToBedrock(GeyserSession session, Item javaItem, ItemMapping bedrockItem, int count, @Nullable DataComponents customComponents) { BedrockItemBuilder nbtBuilder = new BedrockItemBuilder(); - // TODO 1.21.5: - // - Hiding components - // Populates default components that aren't sent over the network DataComponents components = javaItem.gatherComponents(customComponents); + TooltipOptions tooltip = TooltipOptions.fromComponents(components); // Translate item-specific components - javaItem.translateComponentsToBedrock(session, components, nbtBuilder); + javaItem.translateComponentsToBedrock(session, components, tooltip, nbtBuilder); Rarity rarity = Rarity.fromId(components.getOrDefault(DataComponentTypes.RARITY, 0)); String customName = getCustomName(session, customComponents, bedrockItem, rarity.getColor(), false, false); if (customName != null) { PotionContents potionContents = components.get(DataComponentTypes.POTION_CONTENTS); - // Make custom effect information visible - // Ignore when item have "hide_additional_tooltip" component - if (potionContents != null) { // && components.get(DataComponentTypes.HIDE_ADDITIONAL_TOOLTIP) == null) { - customName += getPotionEffectInfo(potionContents, session.locale()); + // Make custom effect information visible when shown in tooltip + if (potionContents != null && tooltip.showInTooltip(DataComponentTypes.POTION_CONTENTS)) { + customName += getPotionEffectInfo(potionContents, session.locale()); // TODO should this be done with lore instead? } nbtBuilder.setCustomName(customName); } - //boolean hideTooltips = components.get(DataComponentTypes.HIDE_TOOLTIP) != null; - ItemAttributeModifiers attributeModifiers = components.get(DataComponentTypes.ATTRIBUTE_MODIFIERS); - if (attributeModifiers != null) { //&& attributeModifiers.isShowInTooltip() && !hideTooltips) { + if (attributeModifiers != null && tooltip.showInTooltip(DataComponentTypes.ATTRIBUTE_MODIFIERS )) { // only add if attribute modifiers do not indicate to hide them addAttributeLore(session, attributeModifiers, nbtBuilder, session.locale()); } - if (session.isAdvancedTooltips()) { //&& !hideTooltips) { + if (session.isAdvancedTooltips() && !TooltipOptions.hideTooltip(components)) { addAdvancedTooltips(components, nbtBuilder, javaItem, session.locale()); } @@ -391,7 +388,7 @@ public final class ItemTranslator { return finalText.toString(); } - public static String getPotionName(PotionContents contents, ItemMapping mapping, boolean hideAdditionalTooltip, String language) { + public static String getPotionName(PotionContents contents, ItemMapping mapping, String language) { String customPotionName = contents.getCustomName(); Potion potion = Potion.getByJavaId(contents.getPotionId()); @@ -401,22 +398,10 @@ public final class ItemTranslator { Component.translatable(mapping.getJavaItem().translationKey() + ".effect." + customPotionName), language); } - if (!hideAdditionalTooltip && !contents.getCustomEffects().isEmpty()) { + if (!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); + String potionName = potion == null ? "empty" : potion.toString().toLowerCase(Locale.ROOT); + return MessageTranslator.convertMessage(Component.translatable(mapping.getJavaItem().translationKey() + ".effect." + potionName), language); } return null; } @@ -538,22 +523,31 @@ 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 customNameOnly, boolean includeAll) { + public static String getCustomName(GeyserSession session, DataComponents components, ItemMapping mapping, + char translationColor, boolean customNameOnly, boolean includeAll) { if (components != null) { + // If the tooltip is hidden entirely, return an empty custom name + if (TooltipOptions.hideTooltip(components)) { + return ""; // TODO test this + } + // ItemStack#getHoverName as of 1.20.5 Component customName = components.get(DataComponentTypes.CUSTOM_NAME); if (customName != null) { return MessageTranslator.convertMessage(customName, session.locale()); } + if (!customNameOnly) { - PotionContents potionContents = components.get(DataComponentTypes.POTION_CONTENTS); - if (potionContents != null) { - // TODO 1.21.5 - String potionName = getPotionName(potionContents, mapping, false /*components.get(DataComponentTypes.HIDE_ADDITIONAL_TOOLTIP) != null */, session.locale()); - if (potionName != null) { - return ChatColor.RESET + ChatColor.ESCAPE + translationColor + potionName; + if (mapping.getJavaItem() instanceof PotionItem) { + PotionContents potionContents = components.get(DataComponentTypes.POTION_CONTENTS); + if (potionContents != null) { + String potionName = getPotionName(potionContents, mapping, session.locale()); // TODO also test this + 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(DataComponentTypes.WRITTEN_BOOK_CONTENT); @@ -561,6 +555,7 @@ public final class ItemTranslator { return ChatColor.RESET + ChatColor.ESCAPE + translationColor + bookContent.getTitle().getRaw(); } } + customName = components.get(DataComponentTypes.ITEM_NAME); if (customName != null) { // Get the translated name and prefix it with a reset char to prevent italics - matches Java Edition diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java index c44f21213..d3e2ba5df 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockInventoryTransactionTranslator.java @@ -30,6 +30,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; import org.cloudburstmc.math.vector.Vector3d; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3i; +import org.cloudburstmc.protocol.bedrock.data.SoundEvent; import org.cloudburstmc.protocol.bedrock.data.definitions.BlockDefinition; import org.cloudburstmc.protocol.bedrock.data.definitions.ItemDefinition; import org.cloudburstmc.protocol.bedrock.data.inventory.ContainerType; @@ -40,6 +41,8 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.transaction.InventoryTra import org.cloudburstmc.protocol.bedrock.data.inventory.transaction.LegacySetItemSlotData; import org.cloudburstmc.protocol.bedrock.packet.ContainerOpenPacket; import org.cloudburstmc.protocol.bedrock.packet.InventoryTransactionPacket; +import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket; +import org.cloudburstmc.protocol.bedrock.packet.PlaySoundPacket; import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.Entity; @@ -49,6 +52,7 @@ import org.geysermc.geyser.inventory.GeyserItemStack; import org.geysermc.geyser.inventory.Inventory; import org.geysermc.geyser.inventory.PlayerInventory; import org.geysermc.geyser.inventory.click.Click; +import org.geysermc.geyser.inventory.item.GeyserInstrument; import org.geysermc.geyser.item.Items; import org.geysermc.geyser.item.type.BlockItem; import org.geysermc.geyser.item.type.BoatItem; @@ -74,12 +78,15 @@ import org.geysermc.geyser.util.CooldownUtils; import org.geysermc.geyser.util.EntityUtils; import org.geysermc.geyser.util.InteractionResult; import org.geysermc.geyser.util.InventoryUtils; +import org.geysermc.geyser.util.SoundUtils; import org.geysermc.mcprotocollib.protocol.data.game.entity.object.Direction; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.Hand; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.InteractAction; import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerAction; import org.geysermc.mcprotocollib.protocol.data.game.item.ItemStack; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponentTypes; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.InstrumentComponent; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.inventory.ServerboundContainerClickPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundInteractPacket; import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.player.ServerboundMovePlayerPosRotPacket; @@ -380,30 +387,29 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator holder = session.getPlayerInventory() -// .getItemInHand() -// .getComponent(DataComponentTypes.INSTRUMENT); -// if (holder != null) { -// GeyserInstrument instrument = GeyserInstrument.fromComponent(session, holder); -// if (instrument.bedrockInstrument() != null) { -// // BDS uses a LevelSoundEvent2Packet, but that doesn't work here... (as of 1.21.20) -// LevelSoundEventPacket soundPacket = new LevelSoundEventPacket(); -// soundPacket.setSound(SoundEvent.valueOf("GOAT_CALL_" + instrument.bedrockInstrument().ordinal())); -// soundPacket.setPosition(session.getPlayerEntity().getPosition()); -// soundPacket.setIdentifier("minecraft:player"); -// soundPacket.setExtraData(-1); -// session.sendUpstreamPacket(soundPacket); -// } else { -// PlaySoundPacket playSoundPacket = new PlaySoundPacket(); -// playSoundPacket.setPosition(session.getPlayerEntity().position()); -// playSoundPacket.setSound(SoundUtils.translatePlaySound(instrument.soundEvent())); -// playSoundPacket.setPitch(1.0F); -// playSoundPacket.setVolume(instrument.range() / 16.0F); -// session.sendUpstreamPacket(playSoundPacket); -// } -// } + InstrumentComponent component = session.getPlayerInventory() + .getItemInHand() + .getComponent(DataComponentTypes.INSTRUMENT); + if (component != null) { + GeyserInstrument instrument = GeyserInstrument.fromComponent(session, component); + if (instrument.bedrockInstrument() != null) { + // BDS uses a LevelSoundEvent2Packet, but that doesn't work here... (as of 1.21.20) + LevelSoundEventPacket soundPacket = new LevelSoundEventPacket(); + soundPacket.setSound(SoundEvent.valueOf("GOAT_CALL_" + instrument.bedrockInstrument().ordinal())); + soundPacket.setPosition(session.getPlayerEntity().getPosition()); + soundPacket.setIdentifier("minecraft:player"); + soundPacket.setExtraData(-1); + session.sendUpstreamPacket(soundPacket); + } else { + PlaySoundPacket playSoundPacket = new PlaySoundPacket(); + playSoundPacket.setPosition(session.getPlayerEntity().position()); + playSoundPacket.setSound(SoundUtils.translatePlaySound(instrument.soundEvent())); + playSoundPacket.setPitch(1.0F); + playSoundPacket.setVolume(instrument.range() / 16.0F); + session.sendUpstreamPacket(playSoundPacket); + } + } } } }