From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Minecrell Date: Wed, 11 Oct 2017 15:56:26 +0200 Subject: [PATCH] Implement extended PaperServerListPingEvent diff --git a/src/main/java/com/destroystokyo/paper/network/PaperServerListPingEventImpl.java b/src/main/java/com/destroystokyo/paper/network/PaperServerListPingEventImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..4ecd0c5bbea55f68549c85aa27e80e2c7e6265d4 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/network/PaperServerListPingEventImpl.java @@ -0,0 +1,31 @@ +package com.destroystokyo.paper.network; + +import com.destroystokyo.paper.event.server.PaperServerListPingEvent; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import org.bukkit.entity.Player; +import org.bukkit.util.CachedServerIcon; + +import javax.annotation.Nullable; + +class PaperServerListPingEventImpl extends PaperServerListPingEvent { + + private final MinecraftServer server; + + PaperServerListPingEventImpl(MinecraftServer server, StatusClient client, int protocolVersion, @Nullable CachedServerIcon icon) { + super(client, server.getMotd(), server.getPlayerCount(), server.getMaxPlayers(), + server.getServerModName() + ' ' + server.getServerVersion(), protocolVersion, icon); + this.server = server; + } + + @Override + protected final Object[] getOnlinePlayers() { + return this.server.getPlayerList().players.toArray(); + } + + @Override + protected final Player getBukkitPlayer(Object player) { + return ((ServerPlayer) player).getBukkitEntity(); + } + +} diff --git a/src/main/java/com/destroystokyo/paper/network/PaperStatusClient.java b/src/main/java/com/destroystokyo/paper/network/PaperStatusClient.java new file mode 100644 index 0000000000000000000000000000000000000000..d926ad804355ee2fdc5910b2505e8671602acdab --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/network/PaperStatusClient.java @@ -0,0 +1,11 @@ +package com.destroystokyo.paper.network; + +import net.minecraft.network.Connection; + +class PaperStatusClient extends PaperNetworkClient implements StatusClient { + + PaperStatusClient(Connection networkManager) { + super(networkManager); + } + +} diff --git a/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java b/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..4c2351b03b58511b80017b58ee9b20ab5193adc9 --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java @@ -0,0 +1,110 @@ +package com.destroystokyo.paper.network; + +import com.destroystokyo.paper.profile.CraftPlayerProfile; +import com.destroystokyo.paper.profile.PlayerProfile; +import com.google.common.base.MoreObjects; +import com.google.common.base.Strings; +import com.mojang.authlib.GameProfile; +import io.papermc.paper.adventure.AdventureComponent; +import java.util.List; +import java.util.UUID; +import javax.annotation.Nonnull; +import net.minecraft.network.Connection; +import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; +import net.minecraft.network.protocol.status.ServerStatus; +import net.minecraft.server.MinecraftServer; + +public final class StandardPaperServerListPingEventImpl extends PaperServerListPingEventImpl { + + private static final GameProfile[] EMPTY_PROFILES = new GameProfile[0]; + private static final UUID FAKE_UUID = new UUID(0, 0); + + private GameProfile[] originalSample; + + private StandardPaperServerListPingEventImpl(MinecraftServer server, Connection networkManager, ServerStatus ping) { + super(server, new PaperStatusClient(networkManager), ping.getVersion() != null ? ping.getVersion().getProtocol() : -1, server.server.getServerIcon()); + this.originalSample = ping.getPlayers() == null ? null : ping.getPlayers().getSample(); // GH-1473 - pre-tick race condition NPE + } + + @Nonnull + @Override + public List getPlayerSample() { + List sample = super.getPlayerSample(); + + if (this.originalSample != null) { + for (GameProfile profile : this.originalSample) { + sample.add(CraftPlayerProfile.asBukkitCopy(profile)); + } + this.originalSample = null; + } + + return sample; + } + + private GameProfile[] getPlayerSampleHandle() { + if (this.originalSample != null) { + return this.originalSample; + } + + List entries = super.getPlayerSample(); + if (entries.isEmpty()) { + return EMPTY_PROFILES; + } + + GameProfile[] profiles = new GameProfile[entries.size()]; + for (int i = 0; i < profiles.length; i++) { + /* + * Avoid null UUIDs/names since that will make the response invalid + * on the client. + * Instead, fall back to a fake/empty UUID and an empty string as name. + * This can be used to create custom lines in the player list that do not + * refer to a specific player. + */ + + PlayerProfile profile = entries.get(i); + if (profile.getId() != null && profile.getName() != null) { + profiles[i] = CraftPlayerProfile.asAuthlib(profile); + } else { + profiles[i] = new GameProfile(MoreObjects.firstNonNull(profile.getId(), FAKE_UUID), Strings.nullToEmpty(profile.getName())); + } + } + + return profiles; + } + + @SuppressWarnings("deprecation") + public static void processRequest(MinecraftServer server, Connection networkManager) { + StandardPaperServerListPingEventImpl event = new StandardPaperServerListPingEventImpl(server, networkManager, server.getStatus()); + server.server.getPluginManager().callEvent(event); + + // Close connection immediately if event is cancelled + if (event.isCancelled()) { + networkManager.disconnect(null); + return; + } + + // Setup response + ServerStatus ping = new ServerStatus(); + + // Description + ping.setDescription(new AdventureComponent(event.motd())); + + // Players + if (!event.shouldHidePlayers()) { + ping.setPlayers(new ServerStatus.Players(event.getMaxPlayers(), event.getNumPlayers())); + ping.getPlayers().setSample(event.getPlayerSampleHandle()); + } + + // Version + ping.setVersion(new ServerStatus.Version(event.getVersion(), event.getProtocolVersion())); + + // Favicon + if (event.getServerIcon() != null) { + ping.setFavicon(event.getServerIcon().getData()); + } + + // Send response + networkManager.send(new ClientboundStatusResponsePacket(ping)); + } + +} diff --git a/src/main/java/net/minecraft/network/protocol/status/ClientboundStatusResponsePacket.java b/src/main/java/net/minecraft/network/protocol/status/ClientboundStatusResponsePacket.java index b985d238eadf857602636ba5e5c277d4b1992d35..5b2081f920304244df96de78b2c66cf8a49a5b85 100644 --- a/src/main/java/net/minecraft/network/protocol/status/ClientboundStatusResponsePacket.java +++ b/src/main/java/net/minecraft/network/protocol/status/ClientboundStatusResponsePacket.java @@ -2,6 +2,7 @@ package net.minecraft.network.protocol.status; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import io.papermc.paper.adventure.AdventureComponent; // Paper import java.io.IOException; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.chat.Component; @@ -12,7 +13,9 @@ import net.minecraft.util.LowerCaseEnumTypeAdapterFactory; public class ClientboundStatusResponsePacket implements Packet { - private static final Gson GSON = (new GsonBuilder()).registerTypeAdapter(ServerStatus.Version.class, new ServerStatus.Version.Serializer()).registerTypeAdapter(ServerStatus.Players.class, new ServerStatus.Players.Serializer()).registerTypeAdapter(ServerStatus.class, new ServerStatus.Serializer()).registerTypeHierarchyAdapter(Component.class, new Component.Serializer()).registerTypeHierarchyAdapter(Style.class, new Style.Serializer()).registerTypeAdapterFactory(new LowerCaseEnumTypeAdapterFactory()).create(); + private static final Gson GSON = (new GsonBuilder()).registerTypeAdapter(ServerStatus.Version.class, new ServerStatus.Version.Serializer()).registerTypeAdapter(ServerStatus.Players.class, new ServerStatus.Players.Serializer()).registerTypeAdapter(ServerStatus.class, new ServerStatus.Serializer()).registerTypeHierarchyAdapter(Component.class, new Component.Serializer()).registerTypeHierarchyAdapter(Style.class, new Style.Serializer()).registerTypeAdapterFactory(new LowerCaseEnumTypeAdapterFactory()) + .registerTypeAdapter(AdventureComponent.class, new AdventureComponent.Serializer()) + .create(); private ServerStatus status; public ClientboundStatusResponsePacket() {} diff --git a/src/main/java/net/minecraft/network/protocol/status/ServerStatus.java b/src/main/java/net/minecraft/network/protocol/status/ServerStatus.java index d6be3dd6cfed3f65325398fc33663cb251f87ac7..31d45cd635eae2ff406cb0441f2cd2aee833b945 100644 --- a/src/main/java/net/minecraft/network/protocol/status/ServerStatus.java +++ b/src/main/java/net/minecraft/network/protocol/status/ServerStatus.java @@ -31,6 +31,7 @@ public class ServerStatus { this.description = description; } + public Players getPlayers() { return getPlayers(); } // Paper - OBFHELPER public ServerStatus.Players getPlayers() { return this.players; } @@ -162,10 +163,12 @@ public class ServerStatus { return this.numPlayers; } + public GameProfile[] getSample() { return getSample(); } // Paper - OBFHELPER public GameProfile[] getSample() { return this.sample; } + public void setSample(GameProfile[] sample) { setSample(sample); } // Paper - OBFHELPER public void setSample(GameProfile[] sample) { this.sample = sample; } diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index 4b3341877629c7065496fb0f0b4d509f5a48db6d..d34da1eb172a7dcda564680afecf3dc145bf09f3 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -2,6 +2,9 @@ package net.minecraft.server; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; +import co.aikar.timings.Timings; +import com.destroystokyo.paper.event.server.PaperServerListPingEvent; +import com.google.common.base.Stopwatch; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; @@ -1238,7 +1241,7 @@ public abstract class MinecraftServer extends ReentrantBlockableEventLoop= 5000000000L) { this.lastServerStatus = i; this.status.setPlayers(new ServerStatus.Players(this.getMaxPlayers(), this.getPlayerCount())); - GameProfile[] agameprofile = new GameProfile[Math.min(this.getPlayerCount(), 12)]; + GameProfile[] agameprofile = new GameProfile[Math.min(this.getPlayerCount(), org.spigotmc.SpigotConfig.playerSample)]; // Paper int j = Mth.nextInt(this.random, 0, this.getPlayerCount() - agameprofile.length); for (int k = 0; k < agameprofile.length; ++k) { diff --git a/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java b/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java index 223df8d27c2ff1cbff634bca3dbde5cc24de7f98..f74e3cbdff8c2d83809f04f42717501d7b1a1ed2 100644 --- a/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java +++ b/src/main/java/net/minecraft/server/network/ServerStatusPacketListenerImpl.java @@ -1,7 +1,7 @@ package net.minecraft.server.network; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerPlayer; + // CraftBukkit start import com.mojang.authlib.GameProfile; import java.net.InetSocketAddress; @@ -11,8 +11,6 @@ import net.minecraft.network.Connection; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.network.protocol.status.ClientboundPongResponsePacket; -import net.minecraft.network.protocol.status.ClientboundStatusResponsePacket; -import net.minecraft.network.protocol.status.ServerStatus; import net.minecraft.network.protocol.status.ServerStatusPacketListener; import net.minecraft.network.protocol.status.ServerboundPingRequestPacket; import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket; @@ -47,15 +45,17 @@ public class ServerStatusPacketListenerImpl implements ServerStatusPacketListene this.connection.disconnect(ServerStatusPacketListenerImpl.DISCONNECT_REASON); } else { this.hasRequestedStatus = true; + // Paper start - Replace everything + /* // CraftBukkit start // this.networkManager.sendPacket(new PacketStatusOutServerInfo(this.minecraftServer.getServerPing())); - final Object[] players = server.getPlayerList().players.toArray(); + final Object[] players = minecraftServer.getPlayerList().players.toArray(); class ServerListPingEvent extends org.bukkit.event.server.ServerListPingEvent { - CraftIconCache icon = server.server.getServerIcon(); + CraftIconCache icon = minecraftServer.server.getServerIcon(); ServerListPingEvent() { - super(((InetSocketAddress) connection.getRemoteAddress()).getAddress(), server.server.motd(), server.getPlayerList().getMaxPlayers()); // Paper - Adventure + super(((InetSocketAddress) networkManager.getSocketAddress()).getAddress(), minecraftServer.server.motd(), minecraftServer.getPlayerList().getMaxPlayers()); // Paper - Adventure } @Override @@ -71,7 +71,7 @@ public class ServerStatusPacketListenerImpl implements ServerStatusPacketListene return new Iterator() { int i; int ret = Integer.MIN_VALUE; - ServerPlayer player; + EntityPlayer player; @Override public boolean hasNext() { @@ -80,7 +80,7 @@ public class ServerStatusPacketListenerImpl implements ServerStatusPacketListene } final Object[] currentPlayers = players; for (int length = currentPlayers.length, i = this.i; i < length; i++) { - final ServerPlayer player = (ServerPlayer) currentPlayers[i]; + final EntityPlayer player = (EntityPlayer) currentPlayers[i]; if (player != null) { this.i = i + 1; this.player = player; @@ -95,7 +95,7 @@ public class ServerStatusPacketListenerImpl implements ServerStatusPacketListene if (!hasNext()) { throw new java.util.NoSuchElementException(); } - final ServerPlayer player = this.player; + final EntityPlayer player = this.player; this.player = null; this.ret = this.i - 1; return player.getBukkitEntity(); @@ -115,16 +115,16 @@ public class ServerStatusPacketListenerImpl implements ServerStatusPacketListene } ServerListPingEvent event = new ServerListPingEvent(); - this.server.server.getPluginManager().callEvent(event); + this.minecraftServer.server.getPluginManager().callEvent(event); java.util.List profiles = new java.util.ArrayList(players.length); for (Object player : players) { if (player != null) { - profiles.add(((ServerPlayer) player).getGameProfile()); + profiles.add(((EntityPlayer) player).getProfile()); } } - ServerStatus.Players playerSample = new ServerStatus.Players(event.getMaxPlayers(), profiles.size()); + ServerPing.ServerPingPlayerSample playerSample = new ServerPing.ServerPingPlayerSample(event.getMaxPlayers(), profiles.size()); // Spigot Start if ( !profiles.isEmpty() ) { @@ -132,16 +132,19 @@ public class ServerStatusPacketListenerImpl implements ServerStatusPacketListene profiles = profiles.subList( 0, Math.min( profiles.size(), org.spigotmc.SpigotConfig.playerSample ) ); // Cap the sample to n (or less) displayed players, ie: Vanilla behaviour } // Spigot End - playerSample.setSample(profiles.toArray(new GameProfile[profiles.size()])); + playerSample.a(profiles.toArray(new GameProfile[profiles.size()])); - ServerStatus ping = new ServerStatus(); + ServerPing ping = new ServerPing(); ping.setFavicon(event.icon.value); - ping.setDescription(CraftChatMessage.fromString(event.getMotd(), true)[0]); - ping.setPlayers(playerSample); - int version = SharedConstants.getCurrentVersion().getProtocolVersion(); - ping.setVersion(new ServerStatus.Version(server.getServerModName() + " " + server.getServerVersion(), version)); - - this.connection.send(new ClientboundStatusResponsePacket(ping)); + ping.setMOTD(CraftChatMessage.fromString(event.getMotd(), true)[0]); + ping.setPlayerSample(playerSample); + int version = SharedConstants.getGameVersion().getProtocolVersion(); + ping.setServerInfo(new ServerPing.ServerData(minecraftServer.getServerModName() + " " + minecraftServer.getVersion(), version)); + + this.networkManager.sendPacket(new PacketStatusOutServerInfo(ping)); + */ + com.destroystokyo.paper.network.StandardPaperServerListPingEventImpl.processRequest(this.server, this.connection); + // Paper end } // CraftBukkit end } diff --git a/src/main/java/org/spigotmc/SpigotConfig.java b/src/main/java/org/spigotmc/SpigotConfig.java index 3981ba5957fdc2929d54515f2b98bb7a4611e938..652b820a4c0bbf7b6bbb8200927a663665583606 100644 --- a/src/main/java/org/spigotmc/SpigotConfig.java +++ b/src/main/java/org/spigotmc/SpigotConfig.java @@ -289,7 +289,7 @@ public class SpigotConfig public static int playerSample; private static void playerSample() { - playerSample = getInt( "settings.sample-count", 12 ); + playerSample = Math.max( getInt( "settings.sample-count", 12 ), 0 ); // Paper - Avoid negative counts Bukkit.getLogger().log( Level.INFO, "Server Ping Player Sample Count: {0}", playerSample ); // Paper - Use logger }