From f0a002f0e349ef1d3feb0e04f9816fd7762eb08c Mon Sep 17 00:00:00 2001 From: rtm516 Date: Wed, 21 Apr 2021 19:55:08 +0100 Subject: [PATCH 01/11] Add option to forward the connection hostname over to the remote server (#2149) Co-authored-by: Camotoy <20743703+Camotoy@users.noreply.github.com> --- .../configuration/GeyserConfiguration.java | 2 + .../GeyserJacksonConfiguration.java | 3 ++ .../network/session/GeyserSession.java | 45 ++++++++++++------- connector/src/main/resources/config.yml | 3 ++ 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java index b6af30fed..d1d40ea9c 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java @@ -138,6 +138,8 @@ public interface GeyserConfiguration { boolean isPasswordAuthentication(); boolean isUseProxyProtocol(); + + boolean isForwardHost(); } interface IUserAuthenticationInfo { diff --git a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java index f8b652e53..9a400031d 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java @@ -197,6 +197,9 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonProperty("use-proxy-protocol") private boolean useProxyProtocol = false; + + @JsonProperty("forward-hostname") + private boolean forwardHost = false; } @Getter diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index e521673e2..5d68dd55d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -693,26 +693,41 @@ public class GeyserSession implements CommandSender { @Override public void packetSending(PacketSendingEvent event) { //todo move this somewhere else - if (event.getPacket() instanceof HandshakePacket && floodgate) { - String encrypted = ""; - try { - encrypted = EncryptionUtil.encryptBedrockData(publicKey, new BedrockData( - clientData.getGameVersion(), - authData.getName(), - authData.getXboxUUID(), - clientData.getDeviceOS().ordinal(), - clientData.getLanguageCode(), - clientData.getCurrentInputMode().ordinal(), - upstream.getAddress().getAddress().getHostAddress() - )); - } catch (Exception e) { - connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.encrypt_fail"), e); + if (event.getPacket() instanceof HandshakePacket) { + String addressSuffix; + if (floodgate) { + String encrypted = ""; + try { + encrypted = EncryptionUtil.encryptBedrockData(publicKey, new BedrockData( + clientData.getGameVersion(), + authData.getName(), + authData.getXboxUUID(), + clientData.getDeviceOS().ordinal(), + clientData.getLanguageCode(), + clientData.getCurrentInputMode().ordinal(), + upstream.getAddress().getAddress().getHostAddress() + )); + } catch (Exception e) { + connector.getLogger().error(LanguageUtils.getLocaleStringLog("geyser.auth.floodgate.encrypt_fail"), e); + } + + addressSuffix = '\0' + BedrockData.FLOODGATE_IDENTIFIER + '\0' + encrypted; + } else { + addressSuffix = ""; } HandshakePacket handshakePacket = event.getPacket(); + + String address; + if (connector.getConfig().getRemote().isForwardHost()) { + address = clientData.getServerAddress().split(":")[0]; + } else { + address = handshakePacket.getHostname(); + } + event.setPacket(new HandshakePacket( handshakePacket.getProtocolVersion(), - handshakePacket.getHostname() + '\0' + BedrockData.FLOODGATE_IDENTIFIER + '\0' + encrypted, + address + addressSuffix, handshakePacket.getPort(), handshakePacket.getIntent() )); diff --git a/connector/src/main/resources/config.yml b/connector/src/main/resources/config.yml index c81bfc675..7133ec88d 100644 --- a/connector/src/main/resources/config.yml +++ b/connector/src/main/resources/config.yml @@ -53,6 +53,9 @@ remote: # 2) You run Velocity or BungeeCord with the option enabled in the proxy's main config. # IF YOU DON'T KNOW WHAT THIS IS, DON'T TOUCH IT! use-proxy-protocol: false + # Forward the hostname that the Bedrock client used to connect over to the Java server + # This is designed to be used for forced hosts on proxies + forward-hostname: false # Floodgate uses encryption to ensure use from authorised sources. # This should point to the public key generated by Floodgate (Bungee or CraftBukkit) From 0a79eb92ac9a57dc90bc576e4edbfdf20cb1da5f Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Wed, 21 Apr 2021 16:57:01 -0400 Subject: [PATCH 02/11] Fix unwanted behaviour with show-coordinates (#2151) Co-authored-by: rtm516 --- .../network/session/GeyserSession.java | 6 ++--- .../network/session/cache/WorldCache.java | 23 ++++++++++++++----- .../java/JavaJoinGameTranslator.java | 2 ++ .../connector/utils/SettingsUtils.java | 18 +++++++++++---- 4 files changed, 35 insertions(+), 14 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 5d68dd55d..0bcb6ed2f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -1022,7 +1022,6 @@ public class GeyserSession implements CommandSender { startGamePacket.setLightningLevel(0); startGamePacket.setMultiplayerGame(true); startGamePacket.setBroadcastingToLan(true); - startGamePacket.getGamerules().add(new GameRuleData<>("showcoordinates", connector.getConfig().isShowCoordinates())); startGamePacket.setPlatformBroadcastMode(GamePublishSetting.PUBLIC); startGamePacket.setXblBroadcastMode(GamePublishSetting.PUBLIC); startGamePacket.setCommandsEnabled(!connector.getConfig().isXboxAchievementsEnabled()); @@ -1214,13 +1213,14 @@ public class GeyserSession implements CommandSender { /** * Update the cached value for the reduced debug info gamerule. - * This also toggles the coordinates display + * If enabled, also hides the player's coordinates. * * @param value The new value for reducedDebugInfo */ public void setReducedDebugInfo(boolean value) { - worldCache.setShowCoordinates(!value); reducedDebugInfo = value; + // Set the showCoordinates data. This is done because updateShowCoordinates() uses this gamerule as a variable. + getWorldCache().updateShowCoordinates(); } /** diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java index 0cbfffbaf..270d3f4a5 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java @@ -28,6 +28,7 @@ package org.geysermc.connector.network.session.cache; import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; import lombok.Getter; import lombok.Setter; +import org.geysermc.connector.configuration.GeyserConfiguration; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.scoreboard.Objective; import org.geysermc.connector.scoreboard.Scoreboard; @@ -38,7 +39,13 @@ public class WorldCache { private final GeyserSession session; @Setter private Difficulty difficulty = Difficulty.EASY; - private boolean showCoordinates = true; + + /** + * True if the client prefers being shown their coordinates, regardless if they're being shown or not. + * This will be true everytime the client joins the server because neither the client nor server store the preference permanently. + */ + @Setter + private boolean prefersShowCoordinates = true; private Scoreboard scoreboard; private final ScoreboardUpdater scoreboardUpdater; @@ -66,12 +73,16 @@ public class WorldCache { } /** - * Tell the client to hide or show the coordinates + * Tell the client to hide or show the coordinates. + * + * If {@link #isPrefersShowCoordinates()} is true, coordinates will be shown, unless either of the following conditions apply: + * + *
  • {@link GeyserSession#isReducedDebugInfo()} is enabled + *
  • {@link GeyserConfiguration#isShowCoordinates()} is disabled * - * @param value True to show, false to hide */ - public void setShowCoordinates(boolean value) { - showCoordinates = value; - session.sendGameRule("showcoordinates", value); + public void updateShowCoordinates() { + boolean allowShowCoordinates = !session.isReducedDebugInfo() && session.getConnector().getConfig().isShowCoordinates(); + session.sendGameRule("showcoordinates", allowShowCoordinates && prefersShowCoordinates); } } \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java index e9a1901dc..33e46c956 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaJoinGameTranslator.java @@ -86,6 +86,8 @@ public class JavaJoinGameTranslator extends PacketTranslator("doimmediaterespawn", !packet.isEnableRespawnScreen())); session.sendUpstreamPacket(gamerulePacket); + session.setReducedDebugInfo(packet.isReducedDebugInfo()); + session.setRenderDistance(packet.getViewDistance()); // We need to send our skin parts to the server otherwise java sees us with no hat, jacket etc diff --git a/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java b/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java index c01378d00..1d06c8a0f 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java +++ b/connector/src/main/java/org/geysermc/connector/utils/SettingsUtils.java @@ -57,8 +57,12 @@ public class SettingsUtils { CustomFormBuilder builder = new CustomFormBuilder(LanguageUtils.getPlayerLocaleString("geyser.settings.title.main", language)); builder.setIcon(new FormImage(FormImage.FormImageType.PATH, "textures/ui/settings_glyph_color_2x.png")); - builder.addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.title.client", language))); - builder.addComponent(new ToggleComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.option.coordinates", language), session.getWorldCache().isShowCoordinates())); + // Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config. + if (!session.isReducedDebugInfo() && session.getConnector().getConfig().isShowCoordinates()) { + builder.addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.title.client", language))); + + builder.addComponent(new ToggleComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.option.coordinates", language), session.getWorldCache().isPrefersShowCoordinates())); + } if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) { @@ -117,10 +121,14 @@ public class SettingsUtils { } int offset = 0; - offset++; // Client settings title + // Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config. + if (!session.isReducedDebugInfo() && session.getConnector().getConfig().isShowCoordinates()) { + offset++; // Client settings title - session.getWorldCache().setShowCoordinates(settingsResponse.getToggleResponses().get(offset)); - offset++; + session.getWorldCache().setPrefersShowCoordinates(settingsResponse.getToggleResponses().get(offset)); + session.getWorldCache().updateShowCoordinates(); + offset++; + } if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) { offset++; // Server settings title From 873e37e1c081487df830206e2b49ac362cc52370 Mon Sep 17 00:00:00 2001 From: Konicai <71294714+Konicai@users.noreply.github.com> Date: Wed, 21 Apr 2021 19:05:53 -0400 Subject: [PATCH 03/11] simplify javadoc of updateShowCoordinates() in WorldCache.java (#2162) --- .../connector/network/session/cache/WorldCache.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java b/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java index 270d3f4a5..84678c211 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/cache/WorldCache.java @@ -75,11 +75,10 @@ public class WorldCache { /** * Tell the client to hide or show the coordinates. * - * If {@link #isPrefersShowCoordinates()} is true, coordinates will be shown, unless either of the following conditions apply: - * - *
  • {@link GeyserSession#isReducedDebugInfo()} is enabled - *
  • {@link GeyserConfiguration#isShowCoordinates()} is disabled - * + * If {@link #prefersShowCoordinates} is true, coordinates will be shown, unless either of the following conditions apply:
    + *
    + * {@link GeyserSession#reducedDebugInfo} is enabled + * {@link GeyserConfiguration#isShowCoordinates()} is disabled */ public void updateShowCoordinates() { boolean allowShowCoordinates = !session.isReducedDebugInfo() && session.getConnector().getConfig().isShowCoordinates(); From 9b39affd289a9fabb74a3e26c1af84927e151237 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 26 Apr 2021 14:15:24 -0400 Subject: [PATCH 04/11] Reduce the amount of values() calls (#2143) Because apparently it's not just a constant; it makes a new array every time. Also, GeyserSession#tick() does not need to be public and I made enchantments in commands use the Java list. --- .../connector/entity/type/EntityType.java | 2 +- .../network/session/GeyserSession.java | 2 +- .../BedrockLecternUpdateTranslator.java | 2 +- .../EnchantingInventoryTranslator.java | 46 +---------- .../network/translators/item/Enchantment.java | 80 +++++++++++++++---- .../network/translators/item/Potion.java | 6 +- .../translators/item/PotionMixRegistry.java | 2 +- .../translators/item/TippedArrowPotion.java | 8 +- .../java/JavaDeclareCommandsTranslator.java | 2 +- .../java/JavaJoinGameTranslator.java | 4 +- 10 files changed, 83 insertions(+), 71 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java index f38e56fd8..1a30b827b 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java +++ b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java @@ -188,7 +188,7 @@ public enum EntityType { static { List allJavaIdentifiers = new ArrayList<>(); - for (EntityType type : values()) { + for (EntityType type : VALUES) { if (type == AGENT || type == BALLOON || type == CHALKBOARD || type == NPC || type == TRIPOD_CAMERA || type == ENDER_DRAGON_PART) { continue; } diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index 0bcb6ed2f..dd044f2fe 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -850,7 +850,7 @@ public class GeyserSession implements CommandSender { /** * Called every 50 milliseconds - one Minecraft tick. */ - public void tick() { + protected void tick() { // Check to see if the player's position needs updating - a position update should be sent once every 3 seconds if (spawned && (System.currentTimeMillis() - lastMovementTimestamp) > 3000) { // Recalculate in case something else changed position diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockLecternUpdateTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockLecternUpdateTranslator.java index ae99fec07..f693ade96 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockLecternUpdateTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockLecternUpdateTranslator.java @@ -54,7 +54,7 @@ public class BedrockLecternUpdateTranslator extends PacketTranslator= 0 && bedrockId < Enchantment.values().length) { - return Enchantment.values()[bedrockId]; + if (bedrockId >= 0 && bedrockId < VALUES.length) { + return VALUES[bedrockId]; } return null; } + + /** + * Enchantments classified by their Java index + */ + public enum JavaEnchantment { + PROTECTION, + FIRE_PROTECTION, + FEATHER_FALLING, + BLAST_PROTECTION, + PROJECTILE_PROTECTION, + RESPIRATION, + AQUA_AFFINITY, + THORNS, + DEPTH_STRIDER, + FROST_WALKER, + BINDING_CURSE, + SOUL_SPEED, + SHARPNESS, + SMITE, + BANE_OF_ARTHROPODS, + KNOCKBACK, + FIRE_ASPECT, + LOOTING, + SWEEPING, + EFFICIENCY, + SILK_TOUCH, + UNBREAKING, + FORTUNE, + POWER, + PUNCH, + FLAME, + INFINITY, + LUCK_OF_THE_SEA, + LURE, + LOYALTY, + IMPALING, + RIPTIDE, + CHANNELING, + MULTISHOT, + QUICK_CHARGE, + PIERCING, + MENDING, + VANISHING_CURSE; + + private static final JavaEnchantment[] VALUES = JavaEnchantment.values(); + + public static JavaEnchantment of(int index) { + return VALUES[index]; + } + + /** + * A list of all enchantment Java identifiers for use with command suggestions. + */ + public static final String[] ALL_JAVA_IDENTIFIERS; + + static { + ALL_JAVA_IDENTIFIERS = new String[VALUES.length]; + for (int i = 0; i < ALL_JAVA_IDENTIFIERS.length; i++) { + ALL_JAVA_IDENTIFIERS[i] = "minecraft:" + VALUES[i].name().toLowerCase(Locale.ENGLISH); + } + } + } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java index 4e710c550..57f8c756c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/Potion.java @@ -74,6 +74,8 @@ public enum Potion { SLOW_FALLING(40), LONG_SLOW_FALLING(41); + public static final Potion[] VALUES = values(); + private final String javaIdentifier; private final short bedrockId; @@ -83,7 +85,7 @@ public enum Potion { } public static Potion getByJavaIdentifier(String javaIdentifier) { - for (Potion potion : Potion.values()) { + for (Potion potion : VALUES) { if (potion.javaIdentifier.equals(javaIdentifier)) { return potion; } @@ -92,7 +94,7 @@ public enum Potion { } public static Potion getByBedrockId(int bedrockId) { - for (Potion potion : Potion.values()) { + for (Potion potion : VALUES) { if (potion.bedrockId == bedrockId) { return potion; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/PotionMixRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/PotionMixRegistry.java index 16cbc54ac..30be7da23 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/PotionMixRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/PotionMixRegistry.java @@ -80,7 +80,7 @@ public class PotionMixRegistry { // Add all types of potions as inputs ItemEntry fillerIngredient = ingredients.get(0); for (ItemEntry input : inputs) { - for (Potion potion : Potion.values()) { + for (Potion potion : Potion.VALUES) { potionMixes.add(new PotionMixData( input.getBedrockId(), potion.getBedrockId(), fillerIngredient.getBedrockId(), fillerIngredient.getBedrockData(), diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/TippedArrowPotion.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/TippedArrowPotion.java index 5aac09790..312de593c 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/TippedArrowPotion.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/TippedArrowPotion.java @@ -77,6 +77,8 @@ public enum TippedArrowPotion { SLOW_FALLING(41, ArrowParticleColors.SLOW_FALLING), LONG_SLOW_FALLING(42, ArrowParticleColors.SLOW_FALLING); + private static final TippedArrowPotion[] VALUES = values(); + private final String javaIdentifier; private final short bedrockId; /** @@ -92,7 +94,7 @@ public enum TippedArrowPotion { } public static TippedArrowPotion getByJavaIdentifier(String javaIdentifier) { - for (TippedArrowPotion potion : TippedArrowPotion.values()) { + for (TippedArrowPotion potion : VALUES) { if (potion.javaIdentifier.equals(javaIdentifier)) { return potion; } @@ -101,7 +103,7 @@ public enum TippedArrowPotion { } public static TippedArrowPotion getByBedrockId(int bedrockId) { - for (TippedArrowPotion potion : TippedArrowPotion.values()) { + for (TippedArrowPotion potion : VALUES) { if (potion.bedrockId == bedrockId) { return potion; } @@ -114,7 +116,7 @@ public enum TippedArrowPotion { * @return the tipped arrow potion that most closely resembles that color. */ public static TippedArrowPotion getByJavaColor(int color) { - for (TippedArrowPotion potion : TippedArrowPotion.values()) { + for (TippedArrowPotion potion : VALUES) { if (potion.javaColor == color) { return potion; } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java index 942bc7fdc..e8e6ae6b7 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaDeclareCommandsTranslator.java @@ -249,7 +249,7 @@ public class JavaDeclareCommandsTranslator extends PacketTranslator { + private static final List SKIN_PART_VALUES = Arrays.asList(SkinPart.values()); @Override public void translate(ServerJoinGamePacket packet, GeyserSession session) { @@ -92,8 +93,7 @@ public class JavaJoinGameTranslator extends PacketTranslator skinParts = Arrays.asList(SkinPart.values()); - ClientSettingsPacket clientSettingsPacket = new ClientSettingsPacket(locale, (byte) session.getRenderDistance(), ChatVisibility.FULL, true, skinParts, HandPreference.RIGHT_HAND); + ClientSettingsPacket clientSettingsPacket = new ClientSettingsPacket(locale, (byte) session.getRenderDistance(), ChatVisibility.FULL, true, SKIN_PART_VALUES, HandPreference.RIGHT_HAND); session.sendDownstreamPacket(clientSettingsPacket); session.sendDownstreamPacket(new ClientPluginMessagePacket("minecraft:brand", PluginMessageUtils.getGeyserBrandData())); From 9de0ebdba7842eade71477736890304bbef9a21e Mon Sep 17 00:00:00 2001 From: D3ATHBRINGER13 <53559772+D3ATHBRINGER13@users.noreply.github.com> Date: Mon, 26 Apr 2021 18:16:13 +0000 Subject: [PATCH 05/11] Small changes (#2169) --- .../geysermc/connector/command/defaults/OffhandCommand.java | 4 ---- .../java/org/geysermc/connector/dump/BootstrapDumpInfo.java | 4 ++-- .../src/main/java/org/geysermc/connector/dump/DumpInfo.java | 2 +- .../network/translators/world/chunk/BlockStorage.java | 4 ---- 4 files changed, 3 insertions(+), 11 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java index 4d7d74045..fa7994c5d 100644 --- a/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java +++ b/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java @@ -36,12 +36,8 @@ import org.geysermc.connector.network.session.GeyserSession; public class OffhandCommand extends GeyserCommand { - private final GeyserConnector connector; - public OffhandCommand(GeyserConnector connector, String name, String description, String permission) { super(name, description, permission); - - this.connector = connector; } @Override diff --git a/connector/src/main/java/org/geysermc/connector/dump/BootstrapDumpInfo.java b/connector/src/main/java/org/geysermc/connector/dump/BootstrapDumpInfo.java index 4f16de903..b26d117f9 100644 --- a/connector/src/main/java/org/geysermc/connector/dump/BootstrapDumpInfo.java +++ b/connector/src/main/java/org/geysermc/connector/dump/BootstrapDumpInfo.java @@ -43,7 +43,7 @@ public class BootstrapDumpInfo { @Getter @AllArgsConstructor - public class PluginInfo { + public static class PluginInfo { public boolean enabled; public String name; @@ -54,7 +54,7 @@ public class BootstrapDumpInfo { @Getter @AllArgsConstructor - public class ListenerInfo { + public static class ListenerInfo { public String ip; public int port; diff --git a/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java b/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java index 9af740d99..e0f0391d5 100644 --- a/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java +++ b/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java @@ -81,7 +81,7 @@ public class DumpInfo { } @Getter - public class VersionInfo { + public static class VersionInfo { private final String name; private final String version; diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java index 672fa1a35..faf8d6dc8 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/world/chunk/BlockStorage.java @@ -62,10 +62,6 @@ public class BlockStorage { return (version.getId() << 1) | (runtime ? 1 : 0); } - private static BitArrayVersion getVersionFromHeader(byte header) { - return BitArrayVersion.get(header >> 1, true); - } - public int getFullBlock(int index) { return this.palette.getInt(this.bitArray.get(index)); } From e7f933ba6c23e58cea0e96a875ed6374084b33c8 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 26 Apr 2021 14:44:16 -0400 Subject: [PATCH 06/11] Show proper names for Java-only items (#2159) Spectral arrows and knowledge books, if unnamed, will now show their correct, translated names. --- .../translators/item/ItemRegistry.java | 8 +++- .../translators/item/ItemTranslator.java | 42 +++++++++++++++++-- .../item/TranslatableItemEntry.java | 41 ++++++++++++++++++ .../nbt/ShulkerBoxItemTranslator.java | 17 ++++---- 4 files changed, 94 insertions(+), 14 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/item/TranslatableItemEntry.java diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java index 7550bc818..10c14074f 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemRegistry.java @@ -408,6 +408,13 @@ public class ItemRegistry { "", bedrockBlockId, stackSize); } + } else if (entry.getKey().equals("minecraft:spectral_arrow") || entry.getKey().equals("minecraft:knowledge_book")) { + // These items don't exist on Java, so set up a container that indicates they should have custom names + itemEntry = new TranslatableItemEntry( + entry.getKey(), bedrockIdentifier, itemIndex, bedrockId, + entry.getValue().get("bedrock_data").intValue(), + bedrockBlockId, + stackSize); } else { itemEntry = new ItemEntry( entry.getKey(), bedrockIdentifier, itemIndex, bedrockId, @@ -471,7 +478,6 @@ public class ItemRegistry { } itemNames.add("minecraft:furnace_minecart"); - itemNames.add("minecraft:spectral_arrow"); if (lodestoneCompassId == 0) { throw new RuntimeException("Lodestone compass not found in item palette!"); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java index 2cd1aad94..78bfd07ce 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/ItemTranslator.java @@ -40,6 +40,7 @@ import org.geysermc.connector.network.translators.ItemRemapper; import org.geysermc.connector.network.translators.chat.MessageTranslator; import org.geysermc.connector.utils.FileUtils; import org.geysermc.connector.utils.LanguageUtils; +import org.geysermc.connector.utils.LocaleUtils; import org.reflections.Reflections; import java.util.*; @@ -137,8 +138,6 @@ public abstract class ItemTranslator { nbt.put(new IntTag("map", 0)); } - ItemStack itemStack = new ItemStack(stack.getId(), stack.getAmount(), nbt); - if (nbt != null) { for (NbtItemStackTranslator translator : NBT_TRANSLATORS) { if (translator.acceptItem(bedrockItem)) { @@ -147,7 +146,9 @@ public abstract class ItemTranslator { } } - translateDisplayProperties(session, nbt); + nbt = translateDisplayProperties(session, nbt, bedrockItem); + + ItemStack itemStack = new ItemStack(stack.getId(), stack.getAmount(), nbt); ItemData.Builder builder; ItemTranslator itemStackTranslator = ITEM_STACK_TRANSLATORS.get(bedrockItem.getJavaId()); @@ -393,8 +394,20 @@ public abstract class ItemTranslator { * Translates the display name of the item * @param session the Bedrock client's session * @param tag the tag to translate + * @param itemEntry the item entry, in case it requires translation + * + * @return the new tag to use, should the current one be null */ - public static void translateDisplayProperties(GeyserSession session, CompoundTag tag) { + public static CompoundTag translateDisplayProperties(GeyserSession session, CompoundTag tag, ItemEntry itemEntry) { + return translateDisplayProperties(session, tag, itemEntry, 'f'); + } + + /** + * @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 CompoundTag translateDisplayProperties(GeyserSession session, CompoundTag tag, ItemEntry itemEntry, char translationColor) { + boolean hasCustomName = false; if (tag != null) { CompoundTag display = tag.get("display"); if (display != null && display.contains("Name")) { @@ -405,11 +418,32 @@ public abstract class ItemTranslator { // Add the new name tag display.put(new StringTag("Name", name)); + // Indicate that a custom name is present + hasCustomName = true; // Add to the new root tag tag.put(display); } } + + if (!hasCustomName && itemEntry instanceof TranslatableItemEntry) { + // No custom name, but we need to localize the item's name + if (tag == null) { + tag = new CompoundTag(""); + } + CompoundTag display = tag.get("display"); + if (display == null) { + display = new CompoundTag("display"); + // Add to the new root tag + tag.put(display); + } + + String translationKey = ((TranslatableItemEntry) itemEntry).getTranslationString(); + // Reset formatting since Bedrock defaults to italics + display.put(new StringTag("Name", "§r§" + translationColor + LocaleUtils.getLocaleString(translationKey, session.getLocale()))); + } + + return tag; } /** diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/TranslatableItemEntry.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/TranslatableItemEntry.java new file mode 100644 index 000000000..b4ea92a0e --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/TranslatableItemEntry.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019-2021 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.connector.network.translators.item; + +import lombok.Getter; + +/** + * Used when an item should have a custom name applied, if there already isn't one. + */ +public class TranslatableItemEntry extends ItemEntry { + @Getter + private final String translationString; + + public TranslatableItemEntry(String javaIdentifier, String bedrockIdentifier, int javaId, int bedrockId, int bedrockData, int bedrockBlockId, int stackSize) { + super(javaIdentifier, bedrockIdentifier, javaId, bedrockId, bedrockData, bedrockBlockId, stackSize); + this.translationString = "item." + javaIdentifier.replace(":", "."); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java index 5ddaa9975..197e119fc 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/item/translators/nbt/ShulkerBoxItemTranslator.java @@ -28,10 +28,7 @@ package org.geysermc.connector.network.translators.item.translators.nbt; import com.github.steveice10.opennbt.tag.builtin.*; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.ItemRemapper; -import org.geysermc.connector.network.translators.item.ItemEntry; -import org.geysermc.connector.network.translators.item.ItemRegistry; -import org.geysermc.connector.network.translators.item.ItemTranslator; -import org.geysermc.connector.network.translators.item.NbtItemStackTranslator; +import org.geysermc.connector.network.translators.item.*; @ItemRemapper public class ShulkerBoxItemTranslator extends NbtItemStackTranslator { @@ -54,11 +51,13 @@ public class ShulkerBoxItemTranslator extends NbtItemStackTranslator { boxItemTag.put(new StringTag("Name", boxItemEntry.getBedrockIdentifier())); boxItemTag.put(new ShortTag("Damage", (short) boxItemEntry.getBedrockData())); boxItemTag.put(new ByteTag("Count", ((ByteTag) itemData.get("Count")).getValue())); - if (itemData.contains("tag")) { - // Only the display name is what we have interest in, so just translate that if relevant - CompoundTag displayTag = itemData.get("tag"); - ItemTranslator.translateDisplayProperties(session, displayTag); - boxItemTag.put(displayTag); + // Only the display name is what we have interest in, so just translate that if relevant + CompoundTag displayTag = itemData.get("tag"); + if (displayTag == null && boxItemEntry instanceof TranslatableItemEntry) { + displayTag = new CompoundTag("tag"); + } + if (displayTag != null) { + boxItemTag.put(ItemTranslator.translateDisplayProperties(session, displayTag, boxItemEntry, '7')); } itemsList.add(boxItemTag); From 17ad18f4509664e0b8bc8445037df8a50f4b3e58 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 26 Apr 2021 15:22:58 -0400 Subject: [PATCH 07/11] Fix some raid entities up (#2144) * Fix some raid entities up - Show axe when vindicator is attacking - Show arm movement and spell color when evoker summons * Reduce the amount of casting --- .../monster/AbstractSkeletonEntity.java | 2 +- .../raid/SpellcasterIllagerEntity.java | 32 +++++++++++- .../living/monster/raid/VindicatorEntity.java | 49 +++++++++++++++++++ .../connector/entity/type/EntityType.java | 7 +-- 4 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/VindicatorEntity.java diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/AbstractSkeletonEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/AbstractSkeletonEntity.java index cd07faf39..2d1b41765 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/AbstractSkeletonEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/AbstractSkeletonEntity.java @@ -42,7 +42,7 @@ public class AbstractSkeletonEntity extends MonsterEntity { if (entityMetadata.getId() == 14) { byte xd = (byte) entityMetadata.getValue(); // A bit of a loophole so the hands get raised - set the target ID to its own ID - metadata.put(EntityData.TARGET_EID, (xd == 4) ? geyserId : 0); + metadata.put(EntityData.TARGET_EID, ((xd & 4) == 4) ? geyserId : 0); } super.updateBedrockMetadata(entityMetadata, session); } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java index ad9f059ab..d43bd8fb7 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/SpellcasterIllagerEntity.java @@ -25,13 +25,43 @@ package org.geysermc.connector.entity.living.monster.raid; +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; import com.nukkitx.math.vector.Vector3f; -import org.geysermc.connector.entity.living.monster.raid.AbstractIllagerEntity; +import com.nukkitx.protocol.bedrock.data.entity.EntityData; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; public class SpellcasterIllagerEntity extends AbstractIllagerEntity { + private static final int SUMMON_VEX_PARTICLE_COLOR = (179 << 16) | (179 << 8) | 204; + private static final int ATTACK_PARTICLE_COLOR = (102 << 16) | (77 << 8) | 89; + private static final int WOLOLO_PARTICLE_COLOR = (179 << 16) | (128 << 8) | 51; public SpellcasterIllagerEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + if (entityMetadata.getId() == 16) { + int spellType = (int) (byte) entityMetadata.getValue(); + // Summon vex, attack, or wololo + metadata.getFlags().setFlag(EntityFlag.CASTING, spellType == 1 || spellType == 2 || spellType == 3); + int rgbData = 0; + // Set the spell color based on Java values + switch (spellType) { + case 1: + rgbData = SUMMON_VEX_PARTICLE_COLOR; + break; + case 2: + rgbData = ATTACK_PARTICLE_COLOR; + break; + case 3: + rgbData = WOLOLO_PARTICLE_COLOR; + break; + } + metadata.put(EntityData.EVOKER_SPELL_COLOR, rgbData); + } + super.updateBedrockMetadata(entityMetadata, session); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/VindicatorEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/VindicatorEntity.java new file mode 100644 index 000000000..8705a8030 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/monster/raid/VindicatorEntity.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019-2021 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.connector.entity.living.monster.raid; + +import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.nukkitx.math.vector.Vector3f; +import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; +import org.geysermc.connector.entity.type.EntityType; +import org.geysermc.connector.network.session.GeyserSession; + +public class VindicatorEntity extends AbstractIllagerEntity { + + public VindicatorEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + } + + @Override + public void updateBedrockMetadata(EntityMetadata entityMetadata, GeyserSession session) { + // Allow the axe to be shown if necessary + if (entityMetadata.getId() == 14) { + byte xd = (byte) entityMetadata.getValue(); + metadata.getFlags().setFlag(EntityFlag.ANGRY, (xd & 4) == 4); + } + super.updateBedrockMetadata(entityMetadata, session); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java index 1a30b827b..538b9d582 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java +++ b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java @@ -36,10 +36,7 @@ import org.geysermc.connector.entity.living.animal.tameable.WolfEntity; import org.geysermc.connector.entity.living.merchant.AbstractMerchantEntity; import org.geysermc.connector.entity.living.merchant.VillagerEntity; import org.geysermc.connector.entity.living.monster.*; -import org.geysermc.connector.entity.living.monster.raid.AbstractIllagerEntity; -import org.geysermc.connector.entity.living.monster.raid.PillagerEntity; -import org.geysermc.connector.entity.living.monster.raid.RaidParticipantEntity; -import org.geysermc.connector.entity.living.monster.raid.SpellcasterIllagerEntity; +import org.geysermc.connector.entity.living.monster.raid.*; import org.geysermc.connector.entity.player.PlayerEntity; import java.util.ArrayList; @@ -97,7 +94,7 @@ public enum EntityType { SHULKER(ShulkerEntity.class, 54, 1f, 1f), ENDERMITE(MonsterEntity.class, 55, 0.3f, 0.4f), AGENT(Entity.class, 56, 0f), - VINDICATOR(AbstractIllagerEntity.class, 57, 1.8f, 0.6f, 0.6f, 1.62f), + VINDICATOR(VindicatorEntity.class, 57, 1.8f, 0.6f, 0.6f, 1.62f), PILLAGER(PillagerEntity.class, 114, 1.8f, 0.6f, 0.6f, 1.62f), WANDERING_TRADER(AbstractMerchantEntity.class, 118, 1.8f, 0.6f, 0.6f, 1.62f), PHANTOM(FlyingEntity.class, 58, 0.5f, 0.9f, 0.9f, 0.6f), From 48fcb4733d14781ffea07a0456758cf517205634 Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Mon, 26 Apr 2021 16:10:32 -0400 Subject: [PATCH 08/11] Add emote offhand workaround (#2163) This commit add a new config option, `emote-offhand-workaround`. If set to a value, emoting will perform the offhand swap action. --- .../command/defaults/OffhandCommand.java | 4 +- .../EmoteOffhandWorkaroundOption.java | 53 +++++++++++++++++++ .../configuration/GeyserConfiguration.java | 2 + .../GeyserJacksonConfiguration.java | 5 ++ .../network/session/GeyserSession.java | 17 +++--- .../bedrock/BedrockEmoteListTranslator.java | 5 ++ .../entity/player/BedrockEmoteTranslator.java | 22 +++++++- .../entity/JavaEntityStatusTranslator.java | 19 ++++++- connector/src/main/resources/config.yml | 7 +++ 9 files changed, 122 insertions(+), 12 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/configuration/EmoteOffhandWorkaroundOption.java diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java index fa7994c5d..47ca821b9 100644 --- a/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java +++ b/connector/src/main/java/org/geysermc/connector/command/defaults/OffhandCommand.java @@ -25,7 +25,6 @@ package org.geysermc.connector.command.defaults; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.Position; import com.github.steveice10.mc.protocol.data.game.entity.player.PlayerAction; import com.github.steveice10.mc.protocol.data.game.world.block.BlockFace; import com.github.steveice10.mc.protocol.packet.ingame.client.player.ClientPlayerActionPacket; @@ -33,6 +32,7 @@ import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.command.GeyserCommand; import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.BlockUtils; public class OffhandCommand extends GeyserCommand { @@ -46,7 +46,7 @@ public class OffhandCommand extends GeyserCommand { return; } - ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.SWAP_HANDS, new Position(0,0,0), + ClientPlayerActionPacket releaseItemPacket = new ClientPlayerActionPacket(PlayerAction.SWAP_HANDS, BlockUtils.POSITION_ZERO, BlockFace.DOWN); session.sendDownstreamPacket(releaseItemPacket); } diff --git a/connector/src/main/java/org/geysermc/connector/configuration/EmoteOffhandWorkaroundOption.java b/connector/src/main/java/org/geysermc/connector/configuration/EmoteOffhandWorkaroundOption.java new file mode 100644 index 000000000..954e3d32a --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/configuration/EmoteOffhandWorkaroundOption.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019-2021 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.connector.configuration; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; + +import java.io.IOException; + +public enum EmoteOffhandWorkaroundOption { + NO_EMOTES, + EMOTES_AND_OFFHAND, + DISABLED; + + public static class Deserializer extends JsonDeserializer { + @Override + public EmoteOffhandWorkaroundOption deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + String value = p.getValueAsString(); + switch (value) { + case "no-emotes": + return NO_EMOTES; + case "emotes-and-offhand": + return EMOTES_AND_OFFHAND; + default: + return DISABLED; + } + } + } +} diff --git a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java index d1d40ea9c..4ebc048fe 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserConfiguration.java @@ -77,6 +77,8 @@ public interface GeyserConfiguration { boolean isShowCoordinates(); + EmoteOffhandWorkaroundOption getEmoteOffhandWorkaround(); + String getDefaultLocale(); Path getFloodgateKeyPath(); diff --git a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java index 9a400031d..9a3024de7 100644 --- a/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java +++ b/connector/src/main/java/org/geysermc/connector/configuration/GeyserJacksonConfiguration.java @@ -28,6 +28,7 @@ package org.geysermc.connector.configuration; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import lombok.Getter; import lombok.Setter; import org.geysermc.connector.GeyserConnector; @@ -100,6 +101,10 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration @JsonProperty("show-coordinates") private boolean showCoordinates = true; + @JsonDeserialize(using = EmoteOffhandWorkaroundOption.Deserializer.class) + @JsonProperty("emote-offhand-workaround") + private EmoteOffhandWorkaroundOption emoteOffhandWorkaround = EmoteOffhandWorkaroundOption.DISABLED; + @JsonProperty("allow-third-party-ears") private boolean allowThirdPartyEars = false; diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index dd044f2fe..63cdc6ece 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -79,6 +79,7 @@ import org.geysermc.common.window.FormWindow; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.command.CommandSender; import org.geysermc.connector.common.AuthType; +import org.geysermc.connector.configuration.EmoteOffhandWorkaroundOption; import org.geysermc.connector.entity.Entity; import org.geysermc.connector.entity.Tickable; import org.geysermc.connector.entity.attribute.Attribute; @@ -433,9 +434,7 @@ public class GeyserSession implements CommandSender { @Setter private boolean waitingForStatistics = false; - @Setter - private List selectedEmotes = new ArrayList<>(); - private final Set emotes = new HashSet<>(); + private final Set emotes; /** * The thread that will run every 50 milliseconds - one Minecraft tick. @@ -471,9 +470,14 @@ public class GeyserSession implements CommandSender { this.spawned = false; this.loggedIn = false; - // Make a copy to prevent ConcurrentModificationException - final List tmpPlayers = new ArrayList<>(connector.getPlayers()); - tmpPlayers.forEach(player -> this.emotes.addAll(player.getEmotes())); + if (connector.getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.NO_EMOTES) { + this.emotes = new HashSet<>(); + // Make a copy to prevent ConcurrentModificationException + final List tmpPlayers = new ArrayList<>(connector.getPlayers()); + tmpPlayers.forEach(player -> this.emotes.addAll(player.getEmotes())); + } else { + this.emotes = null; + } bedrockServerSession.addDisconnectHandler(disconnectReason -> { InetAddress address = bedrockServerSession.getRealAddress().getAddress(); @@ -1306,7 +1310,6 @@ public class GeyserSession implements CommandSender { } public void refreshEmotes(List emotes) { - this.selectedEmotes = emotes; this.emotes.addAll(emotes); for (GeyserSession player : connector.getPlayers()) { List pieces = new ArrayList<>(); diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEmoteListTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEmoteListTranslator.java index 7e2238f33..2519aa447 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEmoteListTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockEmoteListTranslator.java @@ -26,6 +26,7 @@ package org.geysermc.connector.network.translators.bedrock; import com.nukkitx.protocol.bedrock.packet.EmoteListPacket; +import org.geysermc.connector.configuration.EmoteOffhandWorkaroundOption; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; import org.geysermc.connector.network.translators.Translator; @@ -35,6 +36,10 @@ public class BedrockEmoteListTranslator extends PacketTranslator { @Override public void translate(EmotePacket packet, GeyserSession session) { + if (session.getConnector().getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.DISABLED) { + // Activate the workaround - we should trigger the offhand now + ClientPlayerActionPacket swapHandsPacket = new ClientPlayerActionPacket(PlayerAction.SWAP_HANDS, BlockUtils.POSITION_ZERO, + BlockFace.DOWN); + session.sendDownstreamPacket(swapHandsPacket); + + if (session.getConnector().getConfig().getEmoteOffhandWorkaround() == EmoteOffhandWorkaroundOption.NO_EMOTES) { + return; + } + } + long javaId = session.getPlayerEntity().getEntityId(); for (GeyserSession otherSession : session.getConnector().getPlayers()) { if (otherSession != session) { if (otherSession.isClosed()) continue; Entity otherEntity = otherSession.getEntityCache().getEntityByJavaId(javaId); if (otherEntity == null) continue; - packet.setRuntimeEntityId(otherEntity.getGeyserId()); - otherSession.sendUpstreamPacket(packet); + EmotePacket otherEmotePacket = new EmotePacket(); + otherEmotePacket.setEmoteId(packet.getEmoteId()); + otherEmotePacket.setRuntimeEntityId(otherEntity.getGeyserId()); + otherSession.sendUpstreamPacket(otherEmotePacket); } } } diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java index 59ea29925..e33ef7bf1 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/entity/JavaEntityStatusTranslator.java @@ -30,12 +30,14 @@ import com.nukkitx.protocol.bedrock.data.SoundEvent; import com.nukkitx.protocol.bedrock.data.LevelEventType; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; +import com.nukkitx.protocol.bedrock.data.inventory.ItemData; import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import com.nukkitx.protocol.bedrock.packet.LevelEventPacket; import com.nukkitx.protocol.bedrock.packet.LevelSoundEvent2Packet; import com.nukkitx.protocol.bedrock.packet.SetEntityDataPacket; import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket; import org.geysermc.connector.entity.Entity; +import org.geysermc.connector.entity.LivingEntity; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.PacketTranslator; @@ -47,9 +49,11 @@ public class JavaEntityStatusTranslator extends PacketTranslator Date: Mon, 26 Apr 2021 19:59:12 -0400 Subject: [PATCH 09/11] Play effect when feeding baby animals (#2146) Java plays a client-side-only effect when feeding animals. This commit abstracts out the feeding code we already have for interactive tag and checks it when right-clicking any animal that is a baby. --- .../entity/living/animal/AnimalEntity.java | 10 ++ .../entity/living/animal/BeeEntity.java | 14 ++ .../entity/living/animal/ChickenEntity.java | 41 ++++++ .../entity/living/animal/FoxEntity.java | 5 + .../entity/living/animal/HoglinEntity.java | 5 + .../entity/living/animal/OcelotEntity.java | 5 + .../entity/living/animal/PandaEntity.java | 5 + .../entity/living/animal/PigEntity.java | 5 + .../entity/living/animal/PolarBearEntity.java | 5 + .../entity/living/animal/RabbitEntity.java | 5 + .../entity/living/animal/StriderEntity.java | 5 + .../entity/living/animal/TurtleEntity.java | 5 + .../animal/horse/AbstractHorseEntity.java | 14 ++ .../living/animal/horse/LlamaEntity.java | 5 + .../living/animal/tameable/CatEntity.java | 5 + .../living/animal/tameable/ParrotEntity.java | 5 + .../living/animal/tameable/WolfEntity.java | 16 +++ .../connector/entity/type/EntityType.java | 2 +- .../sound/BlockSoundInteractionHandler.java | 9 +- .../sound/EntitySoundInteractionHandler.java | 10 +- .../translators/sound/SoundHandler.java | 2 +- ...=> ComparatorSoundInteractionHandler.java} | 2 +- .../FeedBabySoundInteractionHandler.java | 57 ++++++++ .../utils/InteractiveTagManager.java | 125 +++--------------- 24 files changed, 245 insertions(+), 117 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java rename connector/src/main/java/org/geysermc/connector/network/translators/sound/block/{ComparatorSoundInteractHandler.java => ComparatorSoundInteractionHandler.java} (96%) create mode 100644 connector/src/main/java/org/geysermc/connector/network/translators/sound/entity/FeedBabySoundInteractionHandler.java diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java index fc5bc722c..075690662 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/AnimalEntity.java @@ -34,4 +34,14 @@ public class AnimalEntity extends AgeableEntity { public AnimalEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); } + + /** + * @param javaIdentifierStripped the stripped Java identifier of the item that is potential breeding food. For example, + * wheat. + * @return true if this is a valid item to breed with for this animal. + */ + public boolean canEat(String javaIdentifierStripped) { + // This is what it defaults to. OK. + return javaIdentifierStripped.equals("wheat"); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java index bdffbbcd5..d076b8a6f 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/BeeEntity.java @@ -26,6 +26,7 @@ package org.geysermc.connector.entity.living.animal; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.google.common.collect.ImmutableSet; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; @@ -34,7 +35,15 @@ import com.nukkitx.protocol.bedrock.packet.EntityEventPacket; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.Set; + public class BeeEntity extends AnimalEntity { + /** + * A list of all flowers. Used for feeding bees. + */ + private static final Set FLOWERS = ImmutableSet.of("dandelion", "poppy", "blue_orchid", "allium", "azure_bluet", + "red_tulip", "pink_tulip", "white_tulip", "orange_tulip", "cornflower", "lily_of_the_valley", "wither_rose", + "sunflower", "lilac", "rose_bush", "peony"); public BeeEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); @@ -63,4 +72,9 @@ public class BeeEntity extends AnimalEntity { } super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return FLOWERS.contains(javaIdentifierStripped); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java new file mode 100644 index 000000000..66e8f9ac8 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/ChickenEntity.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019-2021 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.connector.entity.living.animal; + +import com.nukkitx.math.vector.Vector3f; +import org.geysermc.connector.entity.type.EntityType; + +public class ChickenEntity extends AnimalEntity { + + public ChickenEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { + super(entityId, geyserId, entityType, position, motion, rotation); + } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.contains("seeds"); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java index 90514cf12..bfa376272 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/FoxEntity.java @@ -52,4 +52,9 @@ public class FoxEntity extends AnimalEntity { } super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("sweet_berries"); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java index 1878648b4..b915c07de 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/HoglinEntity.java @@ -47,4 +47,9 @@ public class HoglinEntity extends AnimalEntity { } super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("crimson_fungus"); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java index 87320838a..7c7d74770 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/OcelotEntity.java @@ -44,4 +44,9 @@ public class OcelotEntity extends AnimalEntity { } super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon"); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java index eec07af50..9431d66a0 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PandaEntity.java @@ -79,6 +79,11 @@ public class PandaEntity extends AnimalEntity { super.updateBedrockMetadata(entityMetadata, session); } + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("bamboo"); + } + /** * Update the panda's appearance, and take into consideration the recessive brown and weak traits that only show up * when both main and hidden genes match diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java index e747405b2..551e1c345 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PigEntity.java @@ -46,6 +46,11 @@ public class PigEntity extends AnimalEntity { super.updateBedrockMetadata(entityMetadata, session); } + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("potato") || javaIdentifierStripped.equals("beetroot"); + } + @Override protected float getDefaultMaxHealth() { return 10f; diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java index db658dd8e..7b5d42f35 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/PolarBearEntity.java @@ -44,4 +44,9 @@ public class PolarBearEntity extends AnimalEntity { } super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return false; + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java index 752a0d106..a789b48e4 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/RabbitEntity.java @@ -59,4 +59,9 @@ public class RabbitEntity extends AnimalEntity { metadata.put(EntityData.VARIANT, variant); } } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("dandelion") || javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("golden_carrot"); + } } \ No newline at end of file diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java index 9ea97eb18..2a99a5a03 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/StriderEntity.java @@ -85,4 +85,9 @@ public class StriderEntity extends AnimalEntity { super.updateBedrockMetadata(session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("warped_fungus"); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java index 9456f4d28..89df815d7 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/TurtleEntity.java @@ -46,4 +46,9 @@ public class TurtleEntity extends AnimalEntity { } super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("seagrass"); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java index 1fe8d4362..d2f89e101 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/AbstractHorseEntity.java @@ -26,6 +26,7 @@ package org.geysermc.connector.entity.living.animal.horse; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.google.common.collect.ImmutableSet; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityEventType; @@ -38,7 +39,15 @@ import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.network.translators.item.ItemRegistry; +import java.util.Set; + public class AbstractHorseEntity extends AnimalEntity { + /** + * A list of all foods a horse/donkey can eat on Java Edition. + * Used to display interactive tag if needed. + */ + private static final Set DONKEY_AND_HORSE_FOODS = ImmutableSet.of("golden_apple", "enchanted_golden_apple", + "golden_carrot", "sugar", "apple", "wheat", "hay_block"); public AbstractHorseEntity(long entityId, long geyserId, EntityType entityType, Vector3f position, Vector3f motion, Vector3f rotation) { super(entityId, geyserId, entityType, position, motion, rotation); @@ -101,4 +110,9 @@ public class AbstractHorseEntity extends AnimalEntity { updateBedrockAttributes(session); } } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return DONKEY_AND_HORSE_FOODS.contains(javaIdentifierStripped); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java index 48e321932..0b21c771e 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/horse/LlamaEntity.java @@ -75,4 +75,9 @@ public class LlamaEntity extends ChestedHorseEntity { } super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("wheat") || javaIdentifierStripped.equals("hay_block"); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java index 87d700259..7866d25cf 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/CatEntity.java @@ -83,4 +83,9 @@ public class CatEntity extends TameableEntity { } } } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon"); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java index f9df03d6b..50ec9ed04 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/ParrotEntity.java @@ -45,4 +45,9 @@ public class ParrotEntity extends TameableEntity { } super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + return javaIdentifierStripped.contains("seeds") || javaIdentifierStripped.equals("cookie"); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java index 144c0fe25..91350ef54 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java +++ b/connector/src/main/java/org/geysermc/connector/entity/living/animal/tameable/WolfEntity.java @@ -26,13 +26,23 @@ package org.geysermc.connector.entity.living.animal.tameable; import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata; +import com.google.common.collect.ImmutableSet; import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.entity.EntityData; import com.nukkitx.protocol.bedrock.data.entity.EntityFlag; import org.geysermc.connector.entity.type.EntityType; import org.geysermc.connector.network.session.GeyserSession; +import java.util.Set; + public class WolfEntity extends TameableEntity { + /** + * A list of all foods a wolf can eat on Java Edition. + * Used to display interactive tag or particles if needed. + */ + private static final Set WOLF_FOODS = ImmutableSet.of("pufferfish", "tropical_fish", "chicken", "cooked_chicken", + "porkchop", "beef", "rabbit", "cooked_porkchop", "cooked_beef", "rotten_flesh", "mutton", "cooked_mutton", + "cooked_rabbit"); private byte collarColor; @@ -75,4 +85,10 @@ public class WolfEntity extends TameableEntity { super.updateBedrockMetadata(entityMetadata, session); } + + @Override + public boolean canEat(String javaIdentifierStripped) { + // Cannot be a baby to eat these foods + return WOLF_FOODS.contains(javaIdentifierStripped) && !metadata.getFlags().getFlag(EntityFlag.BABY); + } } diff --git a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java index 538b9d582..43658f63b 100644 --- a/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java +++ b/connector/src/main/java/org/geysermc/connector/entity/type/EntityType.java @@ -45,7 +45,7 @@ import java.util.List; @Getter public enum EntityType { - CHICKEN(AnimalEntity.class, 10, 0.7f, 0.4f), + CHICKEN(ChickenEntity.class, 10, 0.7f, 0.4f), COW(AnimalEntity.class, 11, 1.4f, 0.9f), PIG(PigEntity.class, 12, 0.9f), SHEEP(SheepEntity.class, 13, 1.3f, 0.9f), diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/sound/BlockSoundInteractionHandler.java b/connector/src/main/java/org/geysermc/connector/network/translators/sound/BlockSoundInteractionHandler.java index 2172fd9e6..ea9951b83 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/sound/BlockSoundInteractionHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/sound/BlockSoundInteractionHandler.java @@ -25,11 +25,9 @@ package org.geysermc.connector.network.translators.sound; -import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack; import com.nukkitx.math.vector.Vector3f; import org.geysermc.connector.inventory.GeyserItemStack; import org.geysermc.connector.network.session.GeyserSession; -import org.geysermc.connector.network.translators.item.ItemRegistry; import java.util.Map; @@ -47,6 +45,9 @@ public interface BlockSoundInteractionHandler extends SoundInteractionHandler> interactionEntry : SoundHandlerRegistry.INTERACTION_HANDLERS.entrySet()) { if (!(interactionEntry.getValue() instanceof BlockSoundInteractionHandler)) { continue; @@ -66,7 +67,9 @@ public interface BlockSoundInteractionHandler extends SoundInteractionHandler> interactionEntry : SoundHandlerRegistry.INTERACTION_HANDLERS.entrySet()) { if (!(interactionEntry.getValue() instanceof EntitySoundInteractionHandler)) { continue; @@ -67,7 +68,10 @@ public interface EntitySoundInteractionHandler extends SoundInteractionHandler DONKEY_AND_HORSE_FOODS = ImmutableSet.of("golden_apple", "enchanted_golden_apple", - "golden_carrot", "sugar", "apple", "wheat", "hay_block"); - - /** - * A list of all flowers. Used for feeding bees. - */ - private static final Set FLOWERS = ImmutableSet.of("dandelion", "poppy", "blue_orchid", "allium", "azure_bluet", - "red_tulip", "pink_tulip", "white_tulip", "orange_tulip", "cornflower", "lily_of_the_valley", "wither_rose", - "sunflower", "lilac", "rose_bush", "peony"); - /** * All entity types that can be leashed on Java Edition */ @@ -66,14 +52,6 @@ public class InteractiveTagManager { private static final Set SADDLEABLE_WHEN_TAMED_MOB_TYPES = EnumSet.of(EntityType.DONKEY, EntityType.HORSE, EntityType.ZOMBIE_HORSE, EntityType.MULE); - /** - * A list of all foods a wolf can eat on Java Edition. - * Used to display interactive tag if needed. - */ - private static final Set WOLF_FOODS = ImmutableSet.of("pufferfish", "tropical_fish", "chicken", "cooked_chicken", - "porkchop", "beef", "rabbit", "cooked_porkchop", "cooked_beef", "rotten_flesh", "mutton", "cooked_mutton", - "cooked_rabbit"); - /** * Update the suggestion that the client currently has on their screen for this entity (for example, "Feed" or "Ride") * @@ -85,9 +63,8 @@ public class InteractiveTagManager { ItemEntry itemEntry = session.getPlayerInventory().getItemInHand().getItemEntry(); String javaIdentifierStripped = itemEntry.getJavaIdentifier().replace("minecraft:", ""); - // TODO - in the future, update these in the metadata? So the client doesn't have to wiggle their cursor around for it to happen - // TODO - also, might be good to abstract out the eating thing. I know there will need to be food tracked for https://github.com/GeyserMC/Geyser/issues/1005 but not all food is breeding food InteractiveTag interactiveTag = InteractiveTag.NONE; + if (entityMetadata.getLong(EntityData.LEASH_HOLDER_EID) == session.getPlayerEntity().getGeyserId()) { // Unleash the entity interactiveTag = InteractiveTag.REMOVE_LEASH; @@ -105,31 +82,24 @@ public class InteractiveTagManager { // Holding a leash and the mob is leashable for sure // (Plugins can change this behavior so that's something to look into in the far far future) interactiveTag = InteractiveTag.LEASH; + } else if (interactEntity instanceof AnimalEntity && ((AnimalEntity) interactEntity).canEat(javaIdentifierStripped)) { + // This animal can be fed + interactiveTag = InteractiveTag.FEED; } else { switch (interactEntity.getEntityType()) { - case BEE: - if (FLOWERS.contains(javaIdentifierStripped)) { - interactiveTag = InteractiveTag.FEED; + case BOAT: + if (interactEntity.getPassengers().size() < 2) { + interactiveTag = InteractiveTag.BOARD_BOAT; } break; - case BOAT: - interactiveTag = InteractiveTag.BOARD_BOAT; - break; case CAT: - if (javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon")) { - interactiveTag = InteractiveTag.FEED; - } else if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && + if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && entityMetadata.getLong(EntityData.OWNER_EID) == session.getPlayerEntity().getGeyserId()) { // Tamed and owned by player - can sit/stand interactiveTag = entityMetadata.getFlags().getFlag(EntityFlag.SITTING) ? InteractiveTag.STAND : InteractiveTag.SIT; break; } break; - case CHICKEN: - if (javaIdentifierStripped.contains("seeds")) { - interactiveTag = InteractiveTag.FEED; - } - break; case MOOSHROOM: // Shear the mooshroom if (javaIdentifierStripped.equals("shears")) { @@ -143,9 +113,7 @@ public class InteractiveTagManager { } // Fall down to COW as this works on mooshrooms case COW: - if (javaIdentifierStripped.equals("wheat")) { - interactiveTag = InteractiveTag.FEED; - } else if (javaIdentifierStripped.equals("bucket")) { + if (javaIdentifierStripped.equals("bucket")) { // Milk the cow interactiveTag = InteractiveTag.MILK; } @@ -175,69 +143,28 @@ public class InteractiveTagManager { interactiveTag = InteractiveTag.OPEN_CONTAINER; break; } - // have another switch statement as, while these share mount attributes they don't share food - switch (interactEntity.getEntityType()) { - case LLAMA: - case TRADER_LLAMA: - if (javaIdentifierStripped.equals("wheat") || javaIdentifierStripped.equals("hay_block")) { - interactiveTag = InteractiveTag.FEED; - break; - } - case DONKEY: - case HORSE: - // Undead can't eat - if (DONKEY_AND_HORSE_FOODS.contains(javaIdentifierStripped)) { - interactiveTag = InteractiveTag.FEED; - break; - } - } if (!entityMetadata.getFlags().getFlag(EntityFlag.BABY)) { // Can't ride a baby if (tamed) { interactiveTag = InteractiveTag.RIDE_HORSE; - } else if (itemEntry.equals(ItemEntry.AIR)) { + } else if (itemEntry.getJavaId() == 0) { // Can't hide an untamed entity without having your hand empty interactiveTag = InteractiveTag.MOUNT; } } break; - case FOX: - if (javaIdentifierStripped.equals("sweet_berries")) { - interactiveTag = InteractiveTag.FEED; - } - break; - case HOGLIN: - if (javaIdentifierStripped.equals("crimson_fungus")) { - interactiveTag = InteractiveTag.FEED; - } - break; case MINECART: - interactiveTag = InteractiveTag.RIDE_MINECART; + if (interactEntity.getPassengers().isEmpty()) { + interactiveTag = InteractiveTag.RIDE_MINECART; + } break; case MINECART_CHEST: case MINECART_COMMAND_BLOCK: case MINECART_HOPPER: interactiveTag = InteractiveTag.OPEN_CONTAINER; break; - case OCELOT: - if (javaIdentifierStripped.equals("cod") || javaIdentifierStripped.equals("salmon")) { - interactiveTag = InteractiveTag.FEED; - } - break; - case PANDA: - if (javaIdentifierStripped.equals("bamboo")) { - interactiveTag = InteractiveTag.FEED; - } - break; - case PARROT: - if (javaIdentifierStripped.contains("seeds") || javaIdentifierStripped.equals("cookie")) { - interactiveTag = InteractiveTag.FEED; - } - break; case PIG: - if (javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("potato") || javaIdentifierStripped.equals("beetroot")) { - interactiveTag = InteractiveTag.FEED; - } else if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) { + if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) { interactiveTag = InteractiveTag.MOUNT; } break; @@ -246,15 +173,8 @@ public class InteractiveTagManager { interactiveTag = InteractiveTag.BARTER; } break; - case RABBIT: - if (javaIdentifierStripped.equals("dandelion") || javaIdentifierStripped.equals("carrot") || javaIdentifierStripped.equals("golden_carrot")) { - interactiveTag = InteractiveTag.FEED; - } - break; case SHEEP: - if (javaIdentifierStripped.equals("wheat")) { - interactiveTag = InteractiveTag.FEED; - } else if (!entityMetadata.getFlags().getFlag(EntityFlag.SHEARED)) { + if (!entityMetadata.getFlags().getFlag(EntityFlag.SHEARED)) { if (javaIdentifierStripped.equals("shears")) { // Shear the sheep interactiveTag = InteractiveTag.SHEAR; @@ -265,17 +185,10 @@ public class InteractiveTagManager { } break; case STRIDER: - if (javaIdentifierStripped.equals("warped_fungus")) { - interactiveTag = InteractiveTag.FEED; - } else if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) { + if (entityMetadata.getFlags().getFlag(EntityFlag.SADDLED)) { interactiveTag = InteractiveTag.RIDE_STRIDER; } break; - case TURTLE: - if (javaIdentifierStripped.equals("seagrass")) { - interactiveTag = InteractiveTag.FEED; - } - break; case VILLAGER: if (entityMetadata.getInt(EntityData.VARIANT) != 14 && entityMetadata.getInt(EntityData.VARIANT) != 0 && entityMetadata.getFloat(EntityData.SCALE) >= 0.75f) { // Not a nitwit, has a profession and is not a baby @@ -289,10 +202,6 @@ public class InteractiveTagManager { if (javaIdentifierStripped.equals("bone") && !entityMetadata.getFlags().getFlag(EntityFlag.TAMED)) { // Bone and untamed - can tame interactiveTag = InteractiveTag.TAME; - } else if (WOLF_FOODS.contains(javaIdentifierStripped)) { - // Compatible food in hand - feed - // Sometimes just sits/stands when the wolf isn't hungry - there doesn't appear to be a way to fix this - interactiveTag = InteractiveTag.FEED; } else if (entityMetadata.getFlags().getFlag(EntityFlag.TAMED) && entityMetadata.getLong(EntityData.OWNER_EID) == session.getPlayerEntity().getGeyserId()) { // Tamed and owned by player - can sit/stand From abc3a187d410ad0af6ee41a97411111a573de89d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 30 Apr 2021 12:43:37 -0400 Subject: [PATCH 10/11] Provide the Geyser file's MD5 and SHA-256 hash in Geyser dumps (#2026) --- .../org/geysermc/connector/dump/DumpInfo.java | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java b/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java index e0f0391d5..d8d5d7218 100644 --- a/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java +++ b/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java @@ -27,8 +27,12 @@ package org.geysermc.connector.dump; import com.fasterxml.jackson.annotation.JsonIgnore; import com.github.steveice10.mc.protocol.MinecraftConstants; +import com.google.common.hash.Hashing; +import com.google.common.io.ByteSource; +import com.google.common.io.Files; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import lombok.AllArgsConstructor; import lombok.Getter; import org.geysermc.connector.GeyserConnector; import org.geysermc.connector.common.serializer.AsteriskSerializer; @@ -39,6 +43,7 @@ import org.geysermc.connector.utils.DockerCheck; import org.geysermc.connector.utils.FileUtils; import org.geysermc.floodgate.util.DeviceOS; +import java.io.File; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -55,8 +60,9 @@ public class DumpInfo { private final DumpInfo.VersionInfo versionInfo; private Properties gitInfo; private final GeyserConfiguration config; - private Object2IntMap userPlatforms; - private RamInfo ramInfo; + private final HashInfo hashInfo; + private final Object2IntMap userPlatforms; + private final RamInfo ramInfo; private final BootstrapDumpInfo bootstrapInfo; public DumpInfo() { @@ -69,9 +75,29 @@ public class DumpInfo { this.config = GeyserConnector.getInstance().getConfig(); + String md5Hash = "unknown"; + String sha256Hash = "unknown"; + try { + // https://stackoverflow.com/questions/320542/how-to-get-the-path-of-a-running-jar-file + // https://stackoverflow.com/questions/304268/getting-a-files-md5-checksum-in-java + File file = new File(DumpInfo.class.getProtectionDomain().getCodeSource().getLocation().toURI()); + ByteSource byteSource = Files.asByteSource(file); + // Jenkins uses MD5 for its hash + //noinspection UnstableApiUsage + md5Hash = byteSource.hash(Hashing.md5()).toString(); + //noinspection UnstableApiUsage + sha256Hash = byteSource.hash(Hashing.sha256()).toString(); + } catch (Exception e) { + if (GeyserConnector.getInstance().getConfig().isDebugMode()) { + e.printStackTrace(); + } + } + + this.hashInfo = new HashInfo(md5Hash, sha256Hash); + this.ramInfo = new DumpInfo.RamInfo(); - this.userPlatforms = new Object2IntOpenHashMap(); + this.userPlatforms = new Object2IntOpenHashMap<>(); for (GeyserSession session : GeyserConnector.getInstance().getPlayers()) { DeviceOS device = session.getClientData().getDeviceOS(); userPlatforms.put(device, userPlatforms.getOrDefault(device, 0) + 1); @@ -106,6 +132,13 @@ public class DumpInfo { } } + @AllArgsConstructor + @Getter + public static class HashInfo { + private final String md5Hash; + private final String sha256Hash; + } + @Getter public static class NetworkInfo { From 1301cd9c3062238bac408304138337a214aafd0b Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 30 Apr 2021 18:35:45 -0400 Subject: [PATCH 11/11] Fix interact entity packet (#2179) For the InteractAt type of packet of the PlayerInteractEntityPacket, Java sends a diff between the entity's current position and the hit result of its cursor. Bedrock sends the full vector location in its interaction. Therefore, we just do the diff ourselves. This fixes taking armor off of armor stands. --- .../bedrock/BedrockInventoryTransactionTranslator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java index df9c2bda7..99d2de0b4 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/BedrockInventoryTransactionTranslator.java @@ -321,7 +321,7 @@ public class BedrockInventoryTransactionTranslator extends PacketTranslator