From c16c80948c982466b30920df11731d5f69166635 Mon Sep 17 00:00:00 2001 From: Biquaternions Date: Sun, 25 Jan 2026 18:42:50 -0500 Subject: [PATCH] Fix custom MOTD not being sent after a player joins the server --- .../server/PaperServerListPingEvent.java | 16 +++++++++--- .../event/server/ServerListPingEvent.java | 16 ++++++------ .../server/players/PlayerList.java.patch | 11 ++++++-- .../network/PaperLegacyStatusClient.java | 2 +- .../network/PaperServerListPingEventImpl.java | 4 +-- .../StandardPaperServerListPingEventImpl.java | 26 +++++++++++++------ 6 files changed, 51 insertions(+), 24 deletions(-) diff --git a/paper-api/src/main/java/com/destroystokyo/paper/event/server/PaperServerListPingEvent.java b/paper-api/src/main/java/com/destroystokyo/paper/event/server/PaperServerListPingEvent.java index 15b12abe17da..3a576941ae73 100644 --- a/paper-api/src/main/java/com/destroystokyo/paper/event/server/PaperServerListPingEvent.java +++ b/paper-api/src/main/java/com/destroystokyo/paper/event/server/PaperServerListPingEvent.java @@ -31,6 +31,16 @@ /** * Extended version of {@link ServerListPingEvent} that allows full control * of the response sent to the client. + *

+ * This event will sometimes fire synchronously, depending on how it was + * triggered. + *

+ * If a request is done from outside the server, via the Minecraft client + * server menu or external tools, this event will be asynchronous. + * If a player joins the server, this event will be synchronous. + *

+ * Care should be taken to check {@link #isAsynchronous()} and treat the event + * appropriately. */ public class PaperServerListPingEvent extends ServerListPingEvent implements Cancellable { @@ -56,9 +66,9 @@ public class PaperServerListPingEvent extends ServerListPingEvent implements Can private Object[] players; @ApiStatus.Internal - public PaperServerListPingEvent(@NotNull StatusClient client, @NotNull net.kyori.adventure.text.Component motd, int numPlayers, int maxPlayers, - @NotNull String version, int protocolVersion, @Nullable CachedServerIcon favicon) { - super("", client.getAddress().getAddress(), motd, numPlayers, maxPlayers); + public PaperServerListPingEvent(final boolean async, @NotNull StatusClient client, @NotNull net.kyori.adventure.text.Component motd, int numPlayers, + int maxPlayers, @NotNull String version, int protocolVersion, @Nullable CachedServerIcon favicon) { + super(async, "", client.getAddress().getAddress(), motd, numPlayers, maxPlayers); this.client = client; this.numPlayers = numPlayers; this.version = version; diff --git a/paper-api/src/main/java/org/bukkit/event/server/ServerListPingEvent.java b/paper-api/src/main/java/org/bukkit/event/server/ServerListPingEvent.java index fdc39724354f..45aa876dfce1 100644 --- a/paper-api/src/main/java/org/bukkit/event/server/ServerListPingEvent.java +++ b/paper-api/src/main/java/org/bukkit/event/server/ServerListPingEvent.java @@ -35,8 +35,8 @@ public class ServerListPingEvent extends ServerEvent implements Iterable @ApiStatus.Internal @Deprecated(forRemoval = true) - public ServerListPingEvent(@NotNull final String hostname, @NotNull final InetAddress address, @NotNull final String motd, final int numPlayers, final int maxPlayers) { - super(true); + public ServerListPingEvent(final boolean async, @NotNull final String hostname, @NotNull final InetAddress address, @NotNull final String motd, final int numPlayers, final int maxPlayers) { + super(async); Preconditions.checkArgument(numPlayers >= 0, "Cannot have negative number of players online", numPlayers); this.hostname = hostname; this.address = address; @@ -47,8 +47,8 @@ public ServerListPingEvent(@NotNull final String hostname, @NotNull final InetAd @ApiStatus.Internal @Deprecated(forRemoval = true) - protected ServerListPingEvent(@NotNull final String hostname, @NotNull final InetAddress address, @NotNull final String motd, final int maxPlayers) { - super(true); + protected ServerListPingEvent(final boolean async, @NotNull final String hostname, @NotNull final InetAddress address, @NotNull final String motd, final int maxPlayers) { + super(async); this.numPlayers = MAGIC_PLAYER_COUNT; this.hostname = hostname; this.address = address; @@ -58,13 +58,13 @@ protected ServerListPingEvent(@NotNull final String hostname, @NotNull final Ine @ApiStatus.Internal @Deprecated(forRemoval = true) - public ServerListPingEvent(@NotNull final InetAddress address, @NotNull final Component motd, final int numPlayers, final int maxPlayers) { - this("", address, motd, numPlayers, maxPlayers); + public ServerListPingEvent(final boolean async, @NotNull final InetAddress address, @NotNull final Component motd, final int numPlayers, final int maxPlayers) { + this(async, "", address, motd, numPlayers, maxPlayers); } @ApiStatus.Internal - public ServerListPingEvent(@NotNull final String hostname, @NotNull final InetAddress address, @NotNull final Component motd, final int numPlayers, final int maxPlayers) { - super(true); + public ServerListPingEvent(final boolean async, @NotNull final String hostname, @NotNull final InetAddress address, @NotNull final Component motd, final int numPlayers, final int maxPlayers) { + super(async); this.hostname = hostname; this.address = address; this.motd = motd; diff --git a/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch b/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch index 7ba449bd2628..5ccf096d56af 100644 --- a/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/players/PlayerList.java.patch @@ -86,7 +86,7 @@ serverGamePacketListenerImpl.send(new ClientboundChangeDifficultyPacket(levelData.getDifficulty(), levelData.isDifficultyLocked())); serverGamePacketListenerImpl.send(new ClientboundPlayerAbilitiesPacket(player.getAbilities())); serverGamePacketListenerImpl.send(new ClientboundSetHeldSlotPacket(player.getInventory().getSelectedSlot())); -@@ -203,24 +_,129 @@ +@@ -203,24 +_,135 @@ mutableComponent = Component.translatable("multiplayer.player.joined.renamed", player.getDisplayName(), string); } @@ -97,7 +97,14 @@ serverGamePacketListenerImpl.teleport(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot()); ServerStatus status = this.server.getStatus(); if (status != null && !cookie.transferred()) { - player.sendServerStatus(status); +- player.sendServerStatus(status); ++ // Paper start - Fire PaperServerListPingEvent when the player joins ++ // player.sendServerStatus(status); ++ status = com.destroystokyo.paper.network.StandardPaperServerListPingEventImpl.getEventResponse(this.server, player.connection.connection, false); ++ if (status != null) { ++ player.sendServerStatus(status); ++ } ++ // Paper end - Fire PaperServerListPingEvent when the player joins } - player.connection.send(ClientboundPlayerInfoUpdatePacket.createPlayerInitializing(this.players)); diff --git a/paper-server/src/main/java/com/destroystokyo/paper/network/PaperLegacyStatusClient.java b/paper-server/src/main/java/com/destroystokyo/paper/network/PaperLegacyStatusClient.java index cc54b1c20798..fdb44713a432 100644 --- a/paper-server/src/main/java/com/destroystokyo/paper/network/PaperLegacyStatusClient.java +++ b/paper-server/src/main/java/com/destroystokyo/paper/network/PaperLegacyStatusClient.java @@ -47,7 +47,7 @@ public static PaperServerListPingEvent processRequest(MinecraftServer server, InetSocketAddress address, int protocolVersion, @Nullable InetSocketAddress virtualHost) { PaperServerListPingEvent event = new PaperServerListPingEventImpl(server, - new PaperLegacyStatusClient(address, protocolVersion, virtualHost), Byte.MAX_VALUE, null); + new PaperLegacyStatusClient(address, protocolVersion, virtualHost), Byte.MAX_VALUE, null, true); server.server.getPluginManager().callEvent(event); if (event.isCancelled()) { diff --git a/paper-server/src/main/java/com/destroystokyo/paper/network/PaperServerListPingEventImpl.java b/paper-server/src/main/java/com/destroystokyo/paper/network/PaperServerListPingEventImpl.java index 6ed2114f577c..cb6ee6155081 100644 --- a/paper-server/src/main/java/com/destroystokyo/paper/network/PaperServerListPingEventImpl.java +++ b/paper-server/src/main/java/com/destroystokyo/paper/network/PaperServerListPingEventImpl.java @@ -12,8 +12,8 @@ class PaperServerListPingEventImpl extends PaperServerListPingEvent { private final MinecraftServer server; - PaperServerListPingEventImpl(MinecraftServer server, StatusClient client, int protocolVersion, @Nullable CachedServerIcon icon) { - super(client, server.motd(), server.getPlayerCount(), server.getMaxPlayers(), + PaperServerListPingEventImpl(MinecraftServer server, StatusClient client, int protocolVersion, @Nullable CachedServerIcon icon, boolean async) { + super(async, client, server.motd(), server.getPlayerCount(), server.getMaxPlayers(), server.getServerModName() + ' ' + server.getServerVersion(), protocolVersion, icon); this.server = server; } diff --git a/paper-server/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java b/paper-server/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java index b40b79beb311..5bd854dfd057 100644 --- a/paper-server/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java +++ b/paper-server/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java @@ -20,8 +20,8 @@ public final class StandardPaperServerListPingEventImpl extends PaperServerListP private List originalSample; - private StandardPaperServerListPingEventImpl(MinecraftServer server, Connection networkManager, ServerStatus ping) { - super(server, new PaperStatusClient(networkManager), ping.version().map(ServerStatus.Version::protocol).orElse(-1), server.server.getServerIcon()); + private StandardPaperServerListPingEventImpl(MinecraftServer server, Connection networkManager, ServerStatus ping, boolean async) { + super(server, new PaperStatusClient(networkManager), ping.version().map(ServerStatus.Version::protocol).orElse(-1), server.server.getServerIcon(), async); this.originalSample = ping.players().map(ServerStatus.Players::sample).orElse(null); // GH-1473 - pre-tick race condition NPE } @@ -64,13 +64,24 @@ private List getPlayerSampleHandle() { } public static void processRequest(MinecraftServer server, Connection networkManager) { - StandardPaperServerListPingEventImpl event = new StandardPaperServerListPingEventImpl(server, networkManager, server.getStatus()); + ServerStatus ping = getEventResponse(server, networkManager, true); + + if (ping == null) { + networkManager.disconnect((Component) null); + return; + } + + // Send response + networkManager.send(new ClientboundStatusResponsePacket(ping)); + } + + public static ServerStatus getEventResponse(MinecraftServer server, Connection networkManager, boolean async) { + StandardPaperServerListPingEventImpl event = new StandardPaperServerListPingEventImpl(server, networkManager, server.getStatus(), async); server.server.getPluginManager().callEvent(event); // Close connection immediately if event is cancelled if (event.isCancelled()) { - networkManager.disconnect((Component) null); - return; + return null; } // Setup response @@ -96,10 +107,9 @@ public static void processRequest(MinecraftServer server, Connection networkMana } else { favicon = Optional.empty(); } - final ServerStatus ping = new ServerStatus(description, players, Optional.of(version), favicon, server.enforceSecureProfile()); - // Send response - networkManager.send(new ClientboundStatusResponsePacket(ping)); + // Return response + return new ServerStatus(description, players, Optional.of(version), favicon, server.enforceSecureProfile()); } }