1
0
mirror of https://github.com/GeyserMC/Geyser.git synced 2025-12-19 14:59:27 +00:00

Support new tooltip display component and other 1.21.5 things (#5417)

* Work on supporting new tooltip display component

* Fix some stuff and allow any item to function as saddle with the right components

* Some fixes and TODOs

* Re-implement tropical fish variant tooltip

* Fix hiding advanced tooltips

* Fix ominous banner tooltip, custom name and some TODOs

* Implement RegistryEntryData to allow getting an object from registry cache by its key

* Fix goat horns (I think)

* We prefer checkers for the nullable/nonnull annotations

* Remove unused NotNull import

Co-authored-by: chris <github@onechris.mozmail.com>

---------

Co-authored-by: chris <github@onechris.mozmail.com>
This commit is contained in:
Eclipse
2025-03-22 08:02:46 +00:00
committed by onebeastchris
parent acb858f0ab
commit 8035a26198
34 changed files with 348 additions and 145 deletions

View File

@@ -26,7 +26,7 @@ dependencies {
}
repositories {
// mavenLocal()
mavenLocal()
mavenCentral()
@@ -69,6 +69,4 @@ repositories {
maven("https://jitpack.io") {
content { includeGroupByRegex("com\\.github\\..*") }
}
mavenLocal()
}

View File

@@ -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());
}

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -89,14 +89,19 @@ public interface GeyserInstrument {
return -1;
}
// TODO 1.21.5
// TODO test in 1.21.5
static GeyserInstrument fromComponent(GeyserSession session, InstrumentComponent component) {
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());
}
throw new IllegalStateException("InstrumentComponent must have either a location or a holder");
}
record Wrapper(InstrumentComponent.Instrument instrument, String locale) implements GeyserInstrument {
@Override

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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<BannerPatternLayer> 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)))

View File

@@ -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) {

View File

@@ -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<ItemStack> chargedProjectiles = components.get(DataComponentTypes.CHARGED_PROJECTILES);
if (chargedProjectiles != null && !chargedProjectiles.isEmpty()) {

View File

@@ -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<Integer> decorations = components.get(DataComponentTypes.POT_DECORATIONS); // TODO maybe unbox in MCProtocolLib
if (decorations != null) {

View File

@@ -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!

View File

@@ -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<NbtMap> bedrockEnchants = new ArrayList<>();
ItemEnchantments enchantments = components.get(DataComponentTypes.STORED_ENCHANTMENTS);

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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)));

View File

@@ -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());

View File

@@ -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<Component> loreComponents = components.get(DataComponentTypes.LORE);
// TODO 1.21.5
// if (loreComponents != null && components.get(DataComponentTypes.HIDE_TOOLTIP) == null) {
// List<String> lore = builder.getOrCreateLore();
// for (Component loreComponent : loreComponents) {
// lore.add(MessageTranslator.convertMessage(loreComponent, session.locale()));
// }
// }
if (loreComponents != null && tooltip.showInTooltip(DataComponentTypes.LORE)) {
List<String> lore = builder.getOrCreateLore();
for (Component loreComponent : loreComponents) {
lore.add(MessageTranslator.convertMessage(loreComponent, session.locale()));
}
}
Integer damage = components.get(DataComponentTypes.DAMAGE);
if (damage != null) {

View File

@@ -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) {

View File

@@ -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();

View File

@@ -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<BannerPatternLayer> patterns = components.get(DataComponentTypes.BANNER_PATTERNS);
if (patterns != null) {

View File

@@ -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<ItemStack> contents = components.get(DataComponentTypes.CONTAINER);
if (contents == null || contents.isEmpty()) {

View File

@@ -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<String> 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) {
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), LORE_STYLE));
.append(Component.translatable("color.minecraft." + TropicalFishEntity.getColorName(patternColor.byteValue()), LORE_STYLE));
}
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);
}
}

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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<T> builder = new ArrayList<>(entries.size());
List<RegistryEntryData<T>> 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);
});

View File

@@ -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> {
*/
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<T> 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<T> entryByValue(T value);
/**
* Resets the objects by these IDs.
*/
void reset(List<T> values);
void reset(List<RegistryEntryData<T>> values);
/**
* All values of this registry, as a list.

View File

@@ -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.

View File

@@ -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<T>(Key key, T data) {
}

View File

@@ -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<T> implements JavaRegistry<T> {
protected final ObjectArrayList<T> values = new ObjectArrayList<>();
protected final ObjectArrayList<RegistryEntryData<T>> 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<T> entry : values) {
if (entry.key().equals(key)) {
return entry.data();
}
}
return null;
}
@Override
public RegistryEntryData<T> entryById(@NonNegative int id) {
if (id < 0 || id >= this.values.size()) {
return null;
}
@@ -43,11 +62,26 @@ public class SimpleJavaRegistry<T> implements JavaRegistry<T> {
@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<T> values) {
public RegistryEntryData<T> entryByValue(T value) {
for (RegistryEntryData<T> entry : this.values) {
if (entry.data().equals(value)) {
return entry;
}
}
return null;
}
@Override
public void reset(List<RegistryEntryData<T>> values) {
this.values.clear();
this.values.addAll(values);
this.values.trim();
@@ -55,7 +89,7 @@ public class SimpleJavaRegistry<T> implements JavaRegistry<T> {
@Override
public List<T> values() {
return this.values;
return this.values.stream().map(RegistryEntryData::data).toList();
}
@Override

View File

@@ -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) {
if (mapping.getJavaItem() instanceof PotionItem) {
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());
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

View File

@@ -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<Inve
session.setCurrentBook(packet.getItemInHand());
} else if (session.getPlayerInventory().getItemInHand().asItem() == Items.GOAT_HORN) {
// Temporary workaround while we don't have full item/block use tracking.
// TODO 1.21.5
if (!session.getWorldCache().hasCooldown(session.getPlayerInventory().getItemInHand())) {
// Holder<Instrument> 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);
}
}
}
}
}