From f4341b1ee6b7384fbf2f860fc23f900f4f12dc63 Mon Sep 17 00:00:00 2001 From: Ethan Date: Mon, 22 Jul 2024 20:01:34 +0800 Subject: [PATCH] test --- .../geyser/api/bedrock/gui/GuiData.java | 70 +++++++++ .../geyser/api/bedrock/gui/GuiElement.java | 60 ++++++++ .../mod/mixin/server/BlockPlaceMixin.java | 79 +++++++++++ .../geyser/impl/gui/GeyserGuiData.java | 133 ++++++++++++++++++ .../geyser/item/components/Rarity.java | 51 +++++++ .../item/type/BedrockRequiresTagItem.java | 36 +++++ ...vaFinishConfigurationPacketTranslator.java | 49 +++++++ 7 files changed, 478 insertions(+) create mode 100644 api/src/main/java/org/geysermc/geyser/api/bedrock/gui/GuiData.java create mode 100644 api/src/main/java/org/geysermc/geyser/api/bedrock/gui/GuiElement.java create mode 100644 bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/server/BlockPlaceMixin.java create mode 100644 core/src/main/java/org/geysermc/geyser/impl/gui/GeyserGuiData.java create mode 100644 core/src/main/java/org/geysermc/geyser/item/components/Rarity.java create mode 100644 core/src/main/java/org/geysermc/geyser/item/type/BedrockRequiresTagItem.java create mode 100644 core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaFinishConfigurationPacketTranslator.java diff --git a/api/src/main/java/org/geysermc/geyser/api/bedrock/gui/GuiData.java b/api/src/main/java/org/geysermc/geyser/api/bedrock/gui/GuiData.java new file mode 100644 index 000000000..706a29d79 --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/bedrock/gui/GuiData.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2024 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.bedrock.gui; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; + +import java.util.Set; + +public interface GuiData { + /** + * Hides a {@link GuiElement} on the client's side. + * + * @param element the {@link GuiElement} to hide + */ + void hideElement(@NonNull GuiElement... element); + + /** + * Resets a {@link GuiElement} on the client's side. + * This makes the client decide on its own - e.g. based on client settings - + * whether to show or hide the gui element. + *

+ * If no elements are specified, this will reset all currently hidden elements + * + * @param element the {@link GuiElement} to reset + */ + void resetElement(@NonNull GuiElement @Nullable ... element); + + /** + * Determines whether a {@link GuiElement} is currently hidden. + * + * @param element the {@link GuiElement} to check + */ + boolean isHudElementHidden(@NonNull GuiElement element); + + /** + * Returns the currently hidden {@link GuiElement}s. + * + * @return an unmodifiable view of all currently hidden {@link GuiElement}s + */ + @NonNull Set hiddenElements(); + + /** + * Sends a notification toast to the client. + */ + void sendToast(@NonNull String title, @NonNull String content); +} diff --git a/api/src/main/java/org/geysermc/geyser/api/bedrock/gui/GuiElement.java b/api/src/main/java/org/geysermc/geyser/api/bedrock/gui/GuiElement.java new file mode 100644 index 000000000..c90c917ba --- /dev/null +++ b/api/src/main/java/org/geysermc/geyser/api/bedrock/gui/GuiElement.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024 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.bedrock.gui; + +/** + * Represent GUI elements on the players HUD display. + * These can be hidden using {@link GuiData#hideElement(GuiElement...)}, + * and one can reset their visibility using {@link GuiData#resetElement(GuiElement...)}. + */ +public class GuiElement { + public static final GuiElement PAPER_DOLL = new GuiElement(0); + public static final GuiElement ARMOR = new GuiElement(1); + public static final GuiElement TOOL_TIPS = new GuiElement(2); + public static final GuiElement TOUCH_CONTROLS = new GuiElement(3); + public static final GuiElement CROSSHAIR = new GuiElement(4); + public static final GuiElement HOTBAR = new GuiElement(5); + public static final GuiElement HEALTH = new GuiElement(6); + public static final GuiElement PROGRESS_BAR = new GuiElement(7); + public static final GuiElement FOOD_BAR = new GuiElement(8); + public static final GuiElement AIR_BUBBLES_BAR = new GuiElement(9); + public static final GuiElement VEHICLE_HEALTH = new GuiElement(10); + public static final GuiElement EFFECTS_BAR = new GuiElement(11); + public static final GuiElement ITEM_TEXT_POPUP = new GuiElement(12); + + public GuiElement(int id) { + this.id = id; + } + + private final int id; + + /** + * Internal use only; don't depend on these values being consistent. + */ + public int id() { + return this.id; + } +} diff --git a/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/server/BlockPlaceMixin.java b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/server/BlockPlaceMixin.java new file mode 100644 index 000000000..98620588e --- /dev/null +++ b/bootstrap/mod/src/main/java/org/geysermc/geyser/platform/mod/mixin/server/BlockPlaceMixin.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024 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.platform.mod.mixin.server; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockState; +import org.cloudburstmc.math.vector.Vector3f; +import org.cloudburstmc.protocol.bedrock.data.SoundEvent; +import org.cloudburstmc.protocol.bedrock.packet.LevelSoundEventPacket; +import org.geysermc.geyser.GeyserImpl; +import org.geysermc.geyser.session.GeyserSession; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(BlockItem.class) +public class BlockPlaceMixin { + + @Inject(method = "place", locals = LocalCapture.CAPTURE_FAILSOFT, at = @At(value = "INVOKE", target = "Lnet/minecraft/world/level/Level;playSound(Lnet/minecraft/world/entity/player/Player;Lnet/minecraft/core/BlockPos;Lnet/minecraft/sounds/SoundEvent;Lnet/minecraft/sounds/SoundSource;FF)V")) + private void geyser$hijackPlaySound(BlockPlaceContext blockPlaceContext, CallbackInfoReturnable cir, BlockPlaceContext blockPlaceContext2, BlockState blockState, BlockPos blockPos, Level level, Player player, ItemStack itemStack, BlockState blockState2, SoundType soundType) { + if (player == null) { + return; + } + + GeyserSession session = GeyserImpl.getInstance().connectionByUuid(player.getUUID()); + if (session == null) { + return; + } + + Vector3f position = Vector3f.from( + blockPos.getX(), + blockPos.getY(), + blockPos.getZ() + ); + + LevelSoundEventPacket placeBlockSoundPacket = new LevelSoundEventPacket(); + placeBlockSoundPacket.setSound(SoundEvent.PLACE); + placeBlockSoundPacket.setPosition(position); + placeBlockSoundPacket.setBabySound(false); + placeBlockSoundPacket.setExtraData(session.getBlockMappings().getBedrockBlockId(Block.BLOCK_STATE_REGISTRY.getId(blockState2))); + placeBlockSoundPacket.setIdentifier(":"); + session.sendUpstreamPacket(placeBlockSoundPacket); + session.setLastBlockPlacePosition(null); + session.setLastBlockPlaced(null); + } +} diff --git a/core/src/main/java/org/geysermc/geyser/impl/gui/GeyserGuiData.java b/core/src/main/java/org/geysermc/geyser/impl/gui/GeyserGuiData.java new file mode 100644 index 000000000..42b2c44b0 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/impl/gui/GeyserGuiData.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2024 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.impl.gui; + +import org.checkerframework.checker.nullness.qual.NonNull; +import org.checkerframework.checker.nullness.qual.Nullable; +import org.cloudburstmc.protocol.bedrock.data.HudElement; +import org.cloudburstmc.protocol.bedrock.data.HudVisibility; +import org.cloudburstmc.protocol.bedrock.packet.SetHudPacket; +import org.cloudburstmc.protocol.bedrock.packet.ToastRequestPacket; +import org.geysermc.geyser.api.bedrock.gui.GuiData; +import org.geysermc.geyser.api.bedrock.gui.GuiElement; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +public class GeyserGuiData implements GuiData { + private static final HudElement[] HUD_ELEMENT_VALUES = HudElement.values(); + private static final Set ALL_HUD_ELEMENTS = Set.of(HUD_ELEMENT_VALUES); + + /** + * An array of elements to hide when the player is in spectator mode. + * Helps with tidying up the GUI; Java-style. + */ + private static final GuiElement[] SPECTATOR_HIDDEN_ELEMENTS = { + GuiElement.AIR_BUBBLES_BAR, + GuiElement.ARMOR, + GuiElement.HEALTH, + GuiElement.FOOD_BAR, + GuiElement.PROGRESS_BAR, + GuiElement.TOOL_TIPS + }; + + private final GeyserSession session; + + public GeyserGuiData(GeyserSession session) { + this.session = session; + } + + /** + * All currently hidden HUD elements + */ + private final Set hiddenHudElements = new HashSet<>(); + + @Override + public void hideElement(@NotNull @NonNull GuiElement... element) { + + } + + @Override + public void resetElement(GuiElement... elements) { + SetHudPacket packet = new SetHudPacket(); + packet.setVisibility(HudVisibility.RESET); + Set elementSet = packet.getElements(); + + if (elements != null && elements.length != 0) { + for (GuiElement element : elements) { + this.hiddenHudElements.remove(element); + elementSet.add(HUD_ELEMENT_VALUES[element.id()]); + } + } else { + this.hiddenHudElements.clear(); + elementSet.addAll(ALL_HUD_ELEMENTS); + } + + session.sendUpstreamPacket(packet); + } + + @Override + public boolean isHudElementHidden(@NonNull GuiElement element) { + Objects.requireNonNull(element); + return this.hiddenHudElements.contains(element); + } + + @Override + public @NonNull Set hiddenElements() { + return Collections.unmodifiableSet(hiddenHudElements); + } + + @Override + public void sendToast(@NonNull String title, @NonNull String content) { + ToastRequestPacket toastRequestPacket = new ToastRequestPacket(); + toastRequestPacket.setTitle(title); + toastRequestPacket.setContent(content); + session.sendUpstreamPacket(toastRequestPacket); + } + + /** + * Deals with hiding hud elements while in spectator. + * + * @param currentlySpectator whether the player is currently in spectator mode + * @param newGameMode the new GameMode to switch to + */ + public void handleGameModeChange(boolean currentlySpectator, GameMode newGameMode) { + if (newGameMode == GameMode.SPECTATOR) { + if (!currentlySpectator) { + hideElement(SPECTATOR_HIDDEN_ELEMENTS); + } + } else { + if (currentlySpectator) { + resetElement(SPECTATOR_HIDDEN_ELEMENTS); + } + } + } +} diff --git a/core/src/main/java/org/geysermc/geyser/item/components/Rarity.java b/core/src/main/java/org/geysermc/geyser/item/components/Rarity.java new file mode 100644 index 000000000..6fa74ea35 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/components/Rarity.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.item.components; + +import lombok.Getter; + +@Getter +public enum Rarity { + COMMON("common", 'f'), + UNCOMMON("uncommon", 'e'), + RARE("rare", 'b'), + EPIC("epic", 'd'); + + private final String name; + private final char color; + + Rarity(final String name, char chatColor) { + this.name = name; + this.color = chatColor; + } + + private static final Rarity[] VALUES = values(); + + public static Rarity fromId(int id) { + return VALUES.length > id ? VALUES[id] : VALUES[0]; + } + +} diff --git a/core/src/main/java/org/geysermc/geyser/item/type/BedrockRequiresTagItem.java b/core/src/main/java/org/geysermc/geyser/item/type/BedrockRequiresTagItem.java new file mode 100644 index 000000000..c41d14396 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/item/type/BedrockRequiresTagItem.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 GeyserMC. http://geysermc.org + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + * @author GeyserMC + * @link https://github.com/GeyserMC/Geyser + */ + +package org.geysermc.geyser.item.type; + +import org.checkerframework.checker.nullness.qual.Nullable; +import org.geysermc.geyser.session.GeyserSession; +import org.geysermc.geyser.translator.item.BedrockItemBuilder; +import org.geysermc.mcprotocollib.protocol.data.game.item.component.DataComponents; + +public interface BedrockRequiresTagItem { + + void addRequiredNbt(GeyserSession session, @Nullable DataComponents components, BedrockItemBuilder builder); +} diff --git a/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaFinishConfigurationPacketTranslator.java b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaFinishConfigurationPacketTranslator.java new file mode 100644 index 000000000..8ade4a1f0 --- /dev/null +++ b/core/src/main/java/org/geysermc/geyser/translator/protocol/java/JavaFinishConfigurationPacketTranslator.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 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.translator.protocol.java; + +import org.cloudburstmc.protocol.bedrock.packet.PlayerListPacket; +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; +import org.geysermc.mcprotocollib.protocol.packet.configuration.clientbound.ClientboundFinishConfigurationPacket; + +@Translator(packet = ClientboundFinishConfigurationPacket.class) +public class JavaFinishConfigurationPacketTranslator extends PacketTranslator { + + @Override + public void translate(GeyserSession session, ClientboundFinishConfigurationPacket packet) { + // Clear the player list, as on Java the player list is cleared after transitioning from config to play phase + PlayerListPacket playerListPacket = new PlayerListPacket(); + playerListPacket.setAction(PlayerListPacket.Action.REMOVE); + for (PlayerEntity otherEntity : session.getEntityCache().getAllPlayerEntities()) { + playerListPacket.getEntries().add(new PlayerListPacket.Entry(otherEntity.getTabListUuid())); + } + session.sendUpstreamPacket(playerListPacket); + session.getEntityCache().removeAllPlayerEntities(); + } +}