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