diff --git a/api/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java b/api/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java index 13fd60407..8d695bf02 100644 --- a/api/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java +++ b/api/src/main/java/org/geysermc/geyser/api/connection/GeyserConnection.java @@ -25,11 +25,31 @@ package org.geysermc.geyser.api.connection; +import org.checkerframework.checker.index.qual.NonNegative; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.geysermc.api.connection.Connection; import org.geysermc.geyser.api.command.CommandSource; +import org.geysermc.geyser.api.entity.type.GeyserEntity; +import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; + +import java.util.concurrent.CompletableFuture; /** * Represents a player connection used in Geyser. */ public interface GeyserConnection extends Connection, CommandSource { + /** + * @param javaId the Java entity ID to look up. + * @return a {@link GeyserEntity} if present in this connection's entity tracker. + */ + @NonNull + CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId); + + /** + * + * @param emoter the player entity emoting. + * @param emoteId the emote ID to send to the client. + */ + void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId); } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaServerDataTranslator.java b/api/src/main/java/org/geysermc/geyser/api/entity/type/GeyserEntity.java similarity index 54% rename from core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaServerDataTranslator.java rename to api/src/main/java/org/geysermc/geyser/api/entity/type/GeyserEntity.java index 9484b99d5..02acb4e21 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaServerDataTranslator.java +++ b/api/src/main/java/org/geysermc/geyser/api/entity/type/GeyserEntity.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019-2022 GeyserMC. http://geysermc.org + * Copyright (c) 2019-2023 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 @@ -23,22 +23,18 @@ * @link https://github.com/GeyserMC/Geyser */ -package org.geysermc.geyser.translator.protocol.java; +package org.geysermc.geyser.api.entity.type; -import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundServerDataPacket; -import org.geysermc.geyser.api.util.TriState; -import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.translator.protocol.PacketTranslator; -import org.geysermc.geyser.translator.protocol.Translator; +import org.checkerframework.checker.index.qual.NonNegative; -@Translator(packet = ClientboundServerDataPacket.class) -public class JavaServerDataTranslator extends PacketTranslator { - - @Override - public void translate(GeyserSession session, ClientboundServerDataPacket packet) { - // We only want to warn about chat maybe not working once - if (packet.isEnforcesSecureChat() && session.getWorldCache().getChatWarningSent() == TriState.NOT_SET) { - session.getWorldCache().setChatWarningSent(TriState.FALSE); - } - } +/** + * Represents a unique instance of an entity. Each {@link org.geysermc.geyser.api.connection.GeyserConnection} + * have their own sets of entities - no two instances will share the same GeyserEntity instance. + */ +public interface GeyserEntity { + /** + * @return the entity ID that the server has assigned to this entity. + */ + @NonNegative + int javaId(); } diff --git a/api/src/main/java/org/geysermc/geyser/api/entity/type/player/GeyserPlayerEntity.java b/api/src/main/java/org/geysermc/geyser/api/entity/type/player/GeyserPlayerEntity.java new file mode 100644 index 000000000..da2e28609 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/entity/type/player/GeyserPlayerEntity.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2019-2023 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.api.entity.type.player; + +import org.geysermc.geyser.api.entity.type.GeyserEntity; + +public interface GeyserPlayerEntity extends GeyserEntity { +} diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java index 228274859..e0f8c5a49 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/Entity.java @@ -46,6 +46,7 @@ import org.cloudburstmc.protocol.bedrock.packet.MoveEntityAbsolutePacket; import org.cloudburstmc.protocol.bedrock.packet.MoveEntityDeltaPacket; import org.cloudburstmc.protocol.bedrock.packet.RemoveEntityPacket; import org.cloudburstmc.protocol.bedrock.packet.SetEntityDataPacket; +import org.geysermc.geyser.api.entity.type.GeyserEntity; import org.geysermc.geyser.entity.EntityDefinition; import org.geysermc.geyser.entity.GeyserDirtyMetadata; import org.geysermc.geyser.session.GeyserSession; @@ -63,7 +64,7 @@ import java.util.UUID; @Getter @Setter -public class Entity { +public class Entity implements GeyserEntity { protected final GeyserSession session; protected int entityId; @@ -509,6 +510,11 @@ public class Entity { } } + @Override + public int javaId() { + return entityId; + } + public boolean isAlive() { return this.valid; } diff --git a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java index 81f556e2b..e80418744 100644 --- a/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java +++ b/core/src/main/java/org/geysermc/geyser/entity/type/player/PlayerEntity.java @@ -44,6 +44,7 @@ import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes; import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag; import org.cloudburstmc.protocol.bedrock.data.entity.EntityLinkData; import org.cloudburstmc.protocol.bedrock.packet.*; +import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; import org.geysermc.geyser.entity.EntityDefinitions; import org.geysermc.geyser.entity.type.Entity; import org.geysermc.geyser.entity.type.LivingEntity; @@ -64,7 +65,7 @@ import java.util.UUID; import java.util.concurrent.TimeUnit; @Getter @Setter -public class PlayerEntity extends LivingEntity { +public class PlayerEntity extends LivingEntity implements GeyserPlayerEntity { public static final float SNEAKING_POSE_HEIGHT = 1.5f; protected static final List BASE_ABILITY_LAYER; diff --git a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java index 8ffae1b70..e841dd43c 100644 --- a/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java +++ b/core/src/main/java/org/geysermc/geyser/session/GeyserSession.java @@ -63,8 +63,6 @@ import com.github.steveice10.packetlib.tcp.TcpSession; import com.nimbusds.jwt.SignedJWT; import io.netty.channel.Channel; import io.netty.channel.EventLoop; -import it.unimi.dsi.fastutil.ints.Int2IntMap; -import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; @@ -75,10 +73,12 @@ import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import lombok.AccessLevel; import lombok.Getter; -import lombok.NonNull; import lombok.Setter; import lombok.experimental.Accessors; +import org.checkerframework.checker.index.qual.NonNegative; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; import org.checkerframework.common.value.qual.IntRange; import org.cloudburstmc.math.vector.*; import org.cloudburstmc.nbt.NbtMap; @@ -102,6 +102,8 @@ import org.geysermc.floodgate.util.BedrockData; import org.geysermc.geyser.Constants; import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.api.connection.GeyserConnection; +import org.geysermc.geyser.api.entity.type.GeyserEntity; +import org.geysermc.geyser.api.entity.type.player.GeyserPlayerEntity; import org.geysermc.geyser.api.network.AuthType; import org.geysermc.geyser.api.network.RemoteServer; import org.geysermc.geyser.command.GeyserCommandSource; @@ -1921,6 +1923,26 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource { return true; } + @Override + public @NonNull CompletableFuture<@Nullable GeyserEntity> entityByJavaId(@NonNegative int javaId) { + CompletableFuture future = new CompletableFuture<>(); + ensureInEventLoop(() -> future.complete(this.entityCache.getEntityByJavaId(javaId))); + return future; + } + + @Override + public void showEmote(@NonNull GeyserPlayerEntity emoter, @NonNull String emoteId) { + Entity entity = (Entity) emoter; + if (entity.getSession() != this) { + throw new IllegalStateException("Given entity must be from this session!"); + } + + EmotePacket packet = new EmotePacket(); + packet.setEmoteId(emoteId); + packet.setRuntimeEntityId(entity.getGeyserId()); + sendUpstreamPacket(packet); + } + public void addCommandEnum(String name, String enums) { softEnumPacket(name, SoftEnumUpdateType.ADD, enums); } diff --git a/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java b/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java index 0d63208de..dd166358d 100644 --- a/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java +++ b/core/src/main/java/org/geysermc/geyser/session/cache/WorldCache.java @@ -26,15 +26,13 @@ package org.geysermc.geyser.session.cache; import com.github.steveice10.mc.protocol.data.game.setting.Difficulty; -import org.cloudburstmc.math.vector.Vector3i; -import org.cloudburstmc.protocol.bedrock.packet.SetTitlePacket; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntMaps; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import lombok.Getter; import lombok.Setter; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.geysermc.geyser.api.util.TriState; +import org.cloudburstmc.math.vector.Vector3i; +import org.cloudburstmc.protocol.bedrock.packet.SetTitlePacket; import org.geysermc.geyser.scoreboard.Scoreboard; import org.geysermc.geyser.scoreboard.ScoreboardUpdater.ScoreboardSession; import org.geysermc.geyser.session.GeyserSession; @@ -63,17 +61,6 @@ public final class WorldCache { private int currentSequence; private final Object2IntMap unverifiedPredictions = new Object2IntOpenHashMap<>(1); - /** - *
    - *
  • NOT_SET = not yet triggered
  • - *
  • FALSE = enforce-secure-profile is true but player hasn't chatted yet
  • - *
  • TRUE = enforce-secure-profile is enabled, and player has chatted and they have seen our message.
  • - *
- */ - @Getter - @Setter - private @NonNull TriState chatWarningSent = TriState.NOT_SET; - public WorldCache(GeyserSession session) { this.session = session; this.scoreboard = new Scoreboard(session); diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java index 1f55d19bd..f696396ad 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/BedrockTextTranslator.java @@ -26,9 +26,7 @@ package org.geysermc.geyser.translator.protocol.bedrock; import org.cloudburstmc.protocol.bedrock.packet.TextPacket; -import org.geysermc.geyser.api.util.TriState; import org.geysermc.geyser.session.GeyserSession; -import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.text.MessageTranslator; @@ -49,15 +47,6 @@ public class BedrockTextTranslator extends PacketTranslator { return; } - if (session.getWorldCache().getChatWarningSent() == TriState.FALSE) { - if (Boolean.parseBoolean(System.getProperty("Geyser.PrintSecureChatInformation", "true"))) { - session.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.chat.secure_info_1", session.locale())); - session.sendMessage(GeyserLocale.getPlayerLocaleString("geyser.chat.secure_info_2", session.locale(), "https://geysermc.link/secure-chat")); - } - // Never send this message again for this session. - session.getWorldCache().setChatWarningSent(TriState.TRUE); - } - session.sendChat(message); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockEmoteTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockEmoteTranslator.java index 0d00cc8e8..f499cbe64 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockEmoteTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/bedrock/entity/player/BedrockEmoteTranslator.java @@ -29,6 +29,7 @@ import org.cloudburstmc.protocol.bedrock.packet.EmotePacket; import org.geysermc.geyser.api.event.bedrock.ClientEmoteEvent; import org.geysermc.geyser.configuration.EmoteOffhandWorkaroundOption; import org.geysermc.geyser.entity.type.Entity; +import org.geysermc.geyser.entity.type.player.PlayerEntity; import org.geysermc.geyser.session.GeyserSession; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; @@ -61,7 +62,7 @@ public class BedrockEmoteTranslator extends PacketTranslator { if (otherSession.getEventLoop().inEventLoop()) { playEmote(otherSession, javaId, packet.getEmoteId()); } else { - session.executeInEventLoop(() -> playEmote(otherSession, javaId, packet.getEmoteId())); + otherSession.executeInEventLoop(() -> playEmote(otherSession, javaId, packet.getEmoteId())); } } } @@ -69,10 +70,7 @@ public class BedrockEmoteTranslator extends PacketTranslator { private void playEmote(GeyserSession otherSession, int javaId, String emoteId) { Entity otherEntity = otherSession.getEntityCache().getEntityByJavaId(javaId); // Must be ran on same thread - if (otherEntity == null) return; - EmotePacket otherEmotePacket = new EmotePacket(); - otherEmotePacket.setEmoteId(emoteId); - otherEmotePacket.setRuntimeEntityId(otherEntity.getGeyserId()); - otherSession.sendUpstreamPacket(otherEmotePacket); + if (!(otherEntity instanceof PlayerEntity otherPlayer)) return; + otherSession.showEmote(otherPlayer, emoteId); } } diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaSystemChatTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaSystemChatTranslator.java index a8093ba65..03660b147 100644 --- a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaSystemChatTranslator.java +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaSystemChatTranslator.java @@ -26,8 +26,10 @@ package org.geysermc.geyser.translator.protocol.java; import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundSystemChatPacket; +import net.kyori.adventure.text.TranslatableComponent; import org.cloudburstmc.protocol.bedrock.packet.TextPacket; import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.text.GeyserLocale; import org.geysermc.geyser.translator.protocol.PacketTranslator; import org.geysermc.geyser.translator.protocol.Translator; import org.geysermc.geyser.translator.text.MessageTranslator; @@ -37,6 +39,15 @@ public class JavaSystemChatTranslator extends PacketTranslator