From ed5195a84211a9aa0e0c0a63b42cb3910b0cfbc4 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 13 Sep 2024 13:48:54 -0400 Subject: [PATCH 1/3] Fix some instances of enchantment names not deserializing --- .../geyser/item/enchantment/Enchantment.java | 6 +++--- .../translator/text/MessageTranslator.java | 18 +++++++++++++----- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java b/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java index 5cac45534..5a5a37e4e 100644 --- a/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java +++ b/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java @@ -25,8 +25,6 @@ package org.geysermc.geyser.item.enchantment; -import java.util.List; -import java.util.function.Function; import net.kyori.adventure.key.Key; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.nbt.NbtMap; @@ -35,11 +33,13 @@ import org.geysermc.geyser.item.Items; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.session.cache.registry.RegistryEntryContext; import org.geysermc.geyser.translator.text.MessageTranslator; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; -import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet; +import java.util.function.Function; /** * @param description only populated if {@link #bedrockEnchantment()} is not null. diff --git a/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java index 0547a21c9..1932d3e47 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/text/MessageTranslator.java @@ -40,18 +40,25 @@ import net.kyori.adventure.text.serializer.legacy.CharacterAndFormat; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.cloudburstmc.nbt.NbtMap; -import org.cloudburstmc.nbt.NbtType; import org.cloudburstmc.protocol.bedrock.packet.TextPacket; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.text.*; +import org.geysermc.geyser.text.ChatColor; +import org.geysermc.geyser.text.ChatDecoration; +import org.geysermc.geyser.text.DummyLegacyHoverEventSerializer; +import org.geysermc.geyser.text.GeyserLocale; +import org.geysermc.geyser.text.GsonComponentSerializerWrapper; +import org.geysermc.geyser.text.MinecraftTranslationRegistry; import org.geysermc.mcprotocollib.protocol.data.DefaultComponentSerializer; import org.geysermc.mcprotocollib.protocol.data.game.Holder; import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatType; import org.geysermc.mcprotocollib.protocol.data.game.chat.ChatTypeDecoration; import org.geysermc.mcprotocollib.protocol.data.game.scoreboard.TeamColor; -import java.util.*; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; public class MessageTranslator { // These are used for handling the translations of the messages @@ -434,7 +441,7 @@ public class MessageTranslator { * Deserialize an NbtMap with a description text component (usually provided from a registry) into a Bedrock-formatted string. */ public static String deserializeDescription(GeyserSession session, NbtMap tag) { - NbtMap description = tag.getCompound("description"); + Object description = tag.get("description"); Component parsed = componentFromNbtTag(description); return convertMessage(session, parsed); } @@ -482,7 +489,8 @@ public class MessageTranslator { } } - throw new IllegalArgumentException("Expected tag to be a literal string, a list of components, or a component object with a text/translate key"); + GeyserImpl.getInstance().getLogger().error("Expected tag to be a literal string, a list of components, or a component object with a text/translate key: " + nbtTag); + return Component.empty(); } private static List componentsFromNbtList(List list, Style style) { From 34fda8a743f7a6617aec63d299eb32555ae6f664 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:51:39 -0400 Subject: [PATCH 2/3] Small optimizations in Enchantment --- .../geyser/item/enchantment/Enchantment.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java b/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java index 5a5a37e4e..301f69a5f 100644 --- a/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java +++ b/core/src/main/java/org/geysermc/geyser/item/enchantment/Enchantment.java @@ -25,6 +25,7 @@ package org.geysermc.geyser.item.enchantment; +import it.unimi.dsi.fastutil.ints.IntArrays; import net.kyori.adventure.key.Key; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.nbt.NbtMap; @@ -33,13 +34,14 @@ import org.geysermc.geyser.item.Items; import org.geysermc.geyser.registry.Registries; import org.geysermc.geyser.session.cache.registry.RegistryEntryContext; import org.geysermc.geyser.translator.text.MessageTranslator; +import org.geysermc.geyser.util.MinecraftKey; import org.geysermc.mcprotocollib.protocol.data.game.item.component.HolderSet; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.function.Function; +import java.util.function.ToIntFunction; /** * @param description only populated if {@link #bedrockEnchantment()} is not null. @@ -86,21 +88,21 @@ public record Enchantment(String identifier, } // TODO holder set util? - private static HolderSet readHolderSet(@Nullable Object holderSet, Function keyIdMapping) { + private static HolderSet readHolderSet(@Nullable Object holderSet, ToIntFunction keyIdMapping) { if (holderSet == null) { - return new HolderSet(new int[]{}); + return new HolderSet(IntArrays.EMPTY_ARRAY); } if (holderSet instanceof String stringTag) { // Tag if (stringTag.startsWith("#")) { - return new HolderSet(Key.key(stringTag.substring(1))); // Remove '#' at beginning that indicates tag + return new HolderSet(MinecraftKey.key(stringTag.substring(1))); // Remove '#' at beginning that indicates tag } else { - return new HolderSet(new int[]{keyIdMapping.apply(Key.key(stringTag))}); + return new HolderSet(new int[]{keyIdMapping.applyAsInt(MinecraftKey.key(stringTag))}); } } else if (holderSet instanceof List list) { // Assume the list is a list of strings - return new HolderSet(list.stream().map(o -> (String) o).map(Key::key).map(keyIdMapping).mapToInt(Integer::intValue).toArray()); + return new HolderSet(list.stream().map(o -> (String) o).map(Key::key).mapToInt(keyIdMapping).toArray()); } throw new IllegalArgumentException("Holder set must either be a tag, a string ID or a list of string IDs"); } From a5e45ad7eddb190ced403af42dccc109b33c8dfb Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Sat, 14 Sep 2024 18:30:25 -0400 Subject: [PATCH 3/3] Workaround for goat horns on the client side --- .../geyser/session/cache/WorldCache.java | 31 +++++++++++++++++++ ...BedrockInventoryTransactionTranslator.java | 21 +++++++++++++ .../java/level/JavaCooldownTranslator.java | 2 ++ 3 files changed, 54 insertions(+) diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java index fb5137b05..86cb69314 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java @@ -34,6 +34,7 @@ import lombok.Setter; import org.checkerframework.checker.nullness.qual.Nullable; import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.protocol.bedrock.packet.SetTitlePacket; +import org.geysermc.geyser.item.type.Item; import org.geysermc.geyser.scoreboard.Scoreboard; import org.geysermc.geyser.scoreboard.ScoreboardUpdater.ScoreboardSession; import org.geysermc.geyser.session.GeyserSession; @@ -70,6 +71,8 @@ public final class WorldCache { @Setter private boolean editingSignOnFront; + private final Object2IntMap activeCooldowns = new Object2IntOpenHashMap<>(2); + public WorldCache(GeyserSession session) { this.session = session; this.scoreboard = new Scoreboard(session); @@ -201,4 +204,32 @@ public final class WorldCache { public String removeActiveRecord(Vector3i pos) { return this.activeRecords.remove(pos); } + + public void setCooldown(Item item, int ticks) { + if (ticks == 0) { + // As of Java 1.21 + this.activeCooldowns.removeInt(item); + return; + } + this.activeCooldowns.put(item, session.getTicks() + ticks); + } + + public boolean hasCooldown(Item item) { + return this.activeCooldowns.containsKey(item); + } + + public void tick() { + // Implementation note: technically we could empty the field during hasCooldown checks, + // but we don't want the cooldown field to balloon in size from overuse. + if (!this.activeCooldowns.isEmpty()) { + int ticks = session.getTicks(); + Iterator> it = Object2IntMaps.fastIterator(this.activeCooldowns); + while (it.hasNext()) { + Object2IntMap.Entry entry = it.next(); + if (entry.getIntValue() <= ticks) { + it.remove(); + } + } + } + } } 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 1e4c82da1..6ae21067f 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 @@ -31,6 +31,7 @@ import org.cloudburstmc.math.vector.Vector3d; import org.cloudburstmc.math.vector.Vector3f; import org.cloudburstmc.math.vector.Vector3i; import org.cloudburstmc.protocol.bedrock.data.LevelEvent; +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; @@ -42,6 +43,7 @@ import org.cloudburstmc.protocol.bedrock.data.inventory.transaction.LegacySetIte import org.cloudburstmc.protocol.bedrock.packet.ContainerOpenPacket; import org.cloudburstmc.protocol.bedrock.packet.InventoryTransactionPacket; import org.cloudburstmc.protocol.bedrock.packet.LevelEventPacket; +import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket; import org.cloudburstmc.protocol.bedrock.packet.UpdateBlockPacket; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.Entity; @@ -75,12 +77,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.mcprotocollib.protocol.data.game.Holder; 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.DataComponentType; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.Instrument; 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; @@ -373,6 +378,22 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator instrument = session.getPlayerInventory() + .getItemInHand() + .getComponent(DataComponentType.INSTRUMENT); + if (instrument != null && instrument.isId()) { + // 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.id())); + soundPacket.setPosition(session.getPlayerEntity().getPosition()); + soundPacket.setIdentifier("minecraft:player"); + soundPacket.setExtraData(-1); + session.sendUpstreamPacket(soundPacket); + } + } } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaCooldownTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaCooldownTranslator.java index 8e07a7d89..636671651 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaCooldownTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/level/JavaCooldownTranslator.java @@ -57,5 +57,7 @@ public class JavaCooldownTranslator extends PacketTranslator