server/network

This commit is contained in:
Lulu13022002
2024-12-14 17:00:39 +01:00
parent 41494c70b0
commit bfcb0e71c9
12 changed files with 1555 additions and 1847 deletions

View File

@@ -1,105 +1,117 @@
--- a/net/minecraft/server/network/LegacyQueryHandler.java --- a/net/minecraft/server/network/LegacyQueryHandler.java
+++ b/net/minecraft/server/network/LegacyQueryHandler.java +++ b/net/minecraft/server/network/LegacyQueryHandler.java
@@ -15,6 +15,7 @@ @@ -14,6 +_,7 @@
public class LegacyQueryHandler extends ChannelInboundHandlerAdapter {
private static final Logger LOGGER = LogUtils.getLogger(); private static final Logger LOGGER = LogUtils.getLogger();
private final ServerInfo server; private final ServerInfo server;
+ private ByteBuf buf; // Paper + private ByteBuf buf; // Paper
public LegacyQueryHandler(ServerInfo server) { public LegacyQueryHandler(ServerInfo server) {
this.server = server; this.server = server;
@@ -23,6 +24,16 @@ @@ -22,6 +_,17 @@
public void channelRead(ChannelHandlerContext channelhandlercontext, Object object) { @Override
ByteBuf bytebuf = (ByteBuf) object; public void channelRead(ChannelHandlerContext context, Object message) {
ByteBuf byteBuf = (ByteBuf)message;
+ // Paper start - Make legacy ping handler more reliable + // Paper start - Make legacy ping handler more reliable
+ if (this.buf != null) { + if (this.buf != null) {
+ try { + try {
+ readLegacy1_6(channelhandlercontext, bytebuf); + readLegacy1_6(context, byteBuf);
+ } finally { + } finally {
+ bytebuf.release(); + byteBuf.release();
+ } + }
+ return; + return;
+ } + }
+ // Paper end + // Paper end - Make legacy ping handler more reliable
bytebuf.markReaderIndex(); +
byteBuf.markReaderIndex();
boolean flag = true; boolean flag = true;
@@ -34,11 +45,23 @@ @@ -33,9 +_,21 @@
SocketAddress socketaddress = channelhandlercontext.channel().remoteAddress(); SocketAddress socketAddress = context.channel().remoteAddress();
int i = bytebuf.readableBytes(); int i = byteBuf.readableBytes();
- String s; + String string = null; // Paper
+ String s = null; // Paper + // org.bukkit.event.server.ServerListPingEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callServerListPingEvent(socketAddress, this.server.getMotd(), this.server.getPlayerCount(), this.server.getMaxPlayers()); // CraftBukkit // Paper
+ // org.bukkit.event.server.ServerListPingEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callServerListPingEvent(socketaddress, this.server.getMotd(), this.server.getPlayerCount(), this.server.getMaxPlayers()); // CraftBukkit // Paper
+ com.destroystokyo.paper.event.server.PaperServerListPingEvent event; // Paper + com.destroystokyo.paper.event.server.PaperServerListPingEvent event; // Paper
if (i == 0) { if (i == 0) {
- LegacyQueryHandler.LOGGER.debug("Ping: (<1.3.x) from {}", socketaddress); - LOGGER.debug("Ping: (<1.3.x) from {}", socketAddress);
- s = LegacyQueryHandler.createVersion0Response(this.server); - String string = createVersion0Response(this.server);
+ LegacyQueryHandler.LOGGER.debug("Ping: (<1.3.x) from {}", net.minecraft.server.MinecraftServer.getServer().logIPs() ? socketaddress: "<ip address withheld>"); // Paper - Respect logIPs option + LOGGER.debug("Ping: (<1.3.x) from {}", net.minecraft.server.MinecraftServer.getServer().logIPs() ? socketAddress: "<ip address withheld>"); // Paper - Respect logIPs option
+
+ // Paper start - Call PaperServerListPingEvent and use results + // Paper start - Call PaperServerListPingEvent and use results
+ event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest(net.minecraft.server.MinecraftServer.getServer(), (java.net.InetSocketAddress) socketaddress, 39, null); + event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest(net.minecraft.server.MinecraftServer.getServer(), (java.net.InetSocketAddress) socketAddress, 39, null);
+ if (event == null) { + if (event == null) {
+ channelhandlercontext.close(); + context.close();
+ bytebuf.release(); + byteBuf.release();
+ flag = false; + flag = false;
+ return; + return;
+ } + }
+ s = String.format(Locale.ROOT, "%s\u00a7%d\u00a7%d", com.destroystokyo.paper.network.PaperLegacyStatusClient.getUnformattedMotd(event), event.getNumPlayers(), event.getMaxPlayers()); + string = String.format(Locale.ROOT, "%s\u00a7%d\u00a7%d", com.destroystokyo.paper.network.PaperLegacyStatusClient.getUnformattedMotd(event), event.getNumPlayers(), event.getMaxPlayers());
+ // Paper end + // Paper end - Call PaperServerListPingEvent and use results
LegacyQueryHandler.sendFlushAndClose(channelhandlercontext, LegacyQueryHandler.createLegacyDisconnectPacket(channelhandlercontext.alloc(), s)); sendFlushAndClose(context, createLegacyDisconnectPacket(context.alloc(), string));
} else { } else {
if (bytebuf.readUnsignedByte() != 1) { if (byteBuf.readUnsignedByte() != 1) {
@@ -46,16 +69,35 @@ @@ -43,16 +_,35 @@
} }
if (bytebuf.isReadable()) { if (byteBuf.isReadable()) {
- if (!LegacyQueryHandler.readCustomPayloadPacket(bytebuf)) { - if (!readCustomPayloadPacket(byteBuf)) {
- return;
+ // Paper start - Replace with improved version below + // Paper start - Replace with improved version below
+ if (bytebuf.readUnsignedByte() != 250) { + if (byteBuf.readUnsignedByte() != LegacyProtocolUtils.CUSTOM_PAYLOAD_PACKET_ID) {
+ s = this.readLegacy1_6(channelhandlercontext, bytebuf); + string = this.readLegacy1_6(context, byteBuf);
+ if (s == null) { + if (string == null) {
+ return; + return;
+ } + }
} + }
- + // if (!readCustomPayloadPacket(byteBuf)) {
- LegacyQueryHandler.LOGGER.debug("Ping: (1.6) from {}", socketaddress);
+ // if (!LegacyQueryHandler.readCustomPayloadPacket(bytebuf)) {
+ // return; + // return;
+ // } + // }
+ // +
+ // LegacyQueryHandler.LOGGER.debug("Ping: (1.6) from {}", socketaddress); + // LOGGER.debug("Ping: (1.6) from {}", socketAddress);
+ // Paper end + // Paper end - Replace with improved version below
} else { + } else {
- LegacyQueryHandler.LOGGER.debug("Ping: (1.4-1.5.x) from {}", socketaddress); + LOGGER.debug("Ping: (1.4-1.5.x) from {}", net.minecraft.server.MinecraftServer.getServer().logIPs() ? socketAddress : "<ip address withheld>"); // Paper - Respect logIPs option
+ LegacyQueryHandler.LOGGER.debug("Ping: (1.4-1.5.x) from {}", net.minecraft.server.MinecraftServer.getServer().logIPs() ? socketaddress: "<ip address withheld>"); // Paper - Respect logIPs option + }
} +
+ if (string == null) {
- s = LegacyQueryHandler.createVersion1Response(this.server);
+ if (s == null) {
+ // Paper start - Call PaperServerListPingEvent and use results + // Paper start - Call PaperServerListPingEvent and use results
+ event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest(net.minecraft.server.MinecraftServer.getServer(), (java.net.InetSocketAddress) socketaddress, 127, null); // Paper + event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest(net.minecraft.server.MinecraftServer.getServer(), (java.net.InetSocketAddress) socketAddress, 127, null); // Paper
+ if (event == null) { + if (event == null) {
+ channelhandlercontext.close(); + context.close();
+ bytebuf.release(); + byteBuf.release();
+ flag = false; + flag = false;
+ return; return;
+ } }
+ s = String.format(Locale.ROOT, "\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d", new Object[] { event.getProtocolVersion(), this.server.getServerVersion(), event.getMotd(), event.getNumPlayers(), event.getMaxPlayers()}); // CraftBukkit -
+ // Paper end - LOGGER.debug("Ping: (1.6) from {}", socketAddress);
+ } - } else {
LegacyQueryHandler.sendFlushAndClose(channelhandlercontext, LegacyQueryHandler.createLegacyDisconnectPacket(channelhandlercontext.alloc(), s)); - LOGGER.debug("Ping: (1.4-1.5.x) from {}", socketAddress);
+ string = String.format(Locale.ROOT, "\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d", event.getProtocolVersion(), this.server.getServerVersion(), event.getMotd(), event.getNumPlayers(), event.getMaxPlayers()); // CraftBukkit
+ // Paper end - Call PaperServerListPingEvent and use results
}
-
- String string = createVersion1Response(this.server);
sendFlushAndClose(context, createLegacyDisconnectPacket(context.alloc(), string));
} }
@@ -106,14 +148,110 @@ @@ -95,21 +_,97 @@
} }
} }
- private static String createVersion0Response(ServerInfo server) { - private static String createVersion0Response(ServerInfo server) {
- return String.format(Locale.ROOT, "%s\u00a7%d\u00a7%d", server.getMotd(), server.getPlayerCount(), server.getMaxPlayers()); - return String.format(Locale.ROOT, "%s§%d§%d", server.getMotd(), server.getPlayerCount(), server.getMaxPlayers());
- }
-
- private static String createVersion1Response(ServerInfo server) {
- return String.format(
- Locale.ROOT,
- "§1\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d",
- 127,
- server.getServerVersion(),
- server.getMotd(),
- server.getPlayerCount(),
- server.getMaxPlayers()
- );
- }
+ // Paper start + // Paper start
+ private static String readLegacyString(ByteBuf buf) { + private static String readLegacyString(ByteBuf buf) {
+ int size = buf.readShort() * Character.BYTES; + int size = buf.readShort() * Character.BYTES;
@@ -110,10 +122,8 @@
+ String result = buf.toString(buf.readerIndex(), size, java.nio.charset.StandardCharsets.UTF_16BE); + String result = buf.toString(buf.readerIndex(), size, java.nio.charset.StandardCharsets.UTF_16BE);
+ buf.skipBytes(size); // toString doesn't increase readerIndex automatically + buf.skipBytes(size); // toString doesn't increase readerIndex automatically
+ return result; + return result;
} + }
+
- private static String createVersion1Response(ServerInfo server) {
- return String.format(Locale.ROOT, "\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d", 127, server.getServerVersion(), server.getMotd(), server.getPlayerCount(), server.getMaxPlayers());
+ private String readLegacy1_6(ChannelHandlerContext ctx, ByteBuf part) { + private String readLegacy1_6(ChannelHandlerContext ctx, ByteBuf part) {
+ ByteBuf buf = this.buf; + ByteBuf buf = this.buf;
+ +
@@ -130,12 +140,12 @@
+ return null; + return null;
+ } + }
+ +
+ String s = readLegacyString(buf); + String string = readLegacyString(buf);
+ if (s == null) { + if (string == null) {
+ return null; + return null;
+ } + }
+ +
+ if (!s.equals("MC|PingHost")) { + if (!string.equals(LegacyProtocolUtils.CUSTOM_PAYLOAD_PACKET_PING_CHANNEL)) {
+ removeHandler(ctx); + removeHandler(ctx);
+ return null; + return null;
+ } + }
@@ -144,15 +154,14 @@
+ return null; + return null;
+ } + }
+ +
+ net.minecraft.server.MinecraftServer server = net.minecraft.server.MinecraftServer.getServer();
+ int protocolVersion = buf.readByte(); + int protocolVersion = buf.readByte();
+ String host = readLegacyString(buf); + String host = readLegacyString(buf);
+ if (host == null) { + if (host == null) {
+ removeHandler(ctx); + removeHandler(ctx);
+ return null; + return null;
+ } + }
+ int port = buf.readInt();
+ +
+ int port = buf.readInt();
+ if (buf.isReadable()) { + if (buf.isReadable()) {
+ removeHandler(ctx); + removeHandler(ctx);
+ return null; + return null;
@@ -163,6 +172,7 @@
+ +
+ LOGGER.debug("Ping: (1.6) from {}", net.minecraft.server.MinecraftServer.getServer().logIPs() ? ctx.channel().remoteAddress(): "<ip address withheld>"); // Paper - Respect logIPs option + LOGGER.debug("Ping: (1.6) from {}", net.minecraft.server.MinecraftServer.getServer().logIPs() ? ctx.channel().remoteAddress(): "<ip address withheld>"); // Paper - Respect logIPs option
+ +
+ net.minecraft.server.MinecraftServer server = net.minecraft.server.MinecraftServer.getServer();
+ java.net.InetSocketAddress virtualHost = com.destroystokyo.paper.network.PaperNetworkClient.prepareVirtualHost(host, port); + java.net.InetSocketAddress virtualHost = com.destroystokyo.paper.network.PaperNetworkClient.prepareVirtualHost(host, port);
+ com.destroystokyo.paper.event.server.PaperServerListPingEvent event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest( + com.destroystokyo.paper.event.server.PaperServerListPingEvent event = com.destroystokyo.paper.network.PaperLegacyStatusClient.processRequest(
+ server, (java.net.InetSocketAddress) ctx.channel().remoteAddress(), protocolVersion, virtualHost); + server, (java.net.InetSocketAddress) ctx.channel().remoteAddress(), protocolVersion, virtualHost);
@@ -174,8 +184,8 @@
+ String response = String.format("\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d", event.getProtocolVersion(), event.getVersion(), + String response = String.format("\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d", event.getProtocolVersion(), event.getVersion(),
+ com.destroystokyo.paper.network.PaperLegacyStatusClient.getMotd(event), event.getNumPlayers(), event.getMaxPlayers()); + com.destroystokyo.paper.network.PaperLegacyStatusClient.getMotd(event), event.getNumPlayers(), event.getMaxPlayers());
+ return response; + return response;
} + }
+
+ private void removeHandler(ChannelHandlerContext ctx) { + private void removeHandler(ChannelHandlerContext ctx) {
+ ByteBuf buf = this.buf; + ByteBuf buf = this.buf;
+ this.buf = null; + this.buf = null;
@@ -193,19 +203,6 @@
+ } + }
+ } + }
+ // Paper end + // Paper end
+
+ // CraftBukkit start private static void sendFlushAndClose(ChannelHandlerContext context, ByteBuf buffer) {
+ private static String createVersion0Response(ServerInfo serverinfo, org.bukkit.event.server.ServerListPingEvent event) { context.pipeline().firstContext().writeAndFlush(buffer).addListener(ChannelFutureListener.CLOSE);
+ return String.format(Locale.ROOT, "%s\u00a7%d\u00a7%d", event.getMotd(), event.getNumPlayers(), event.getMaxPlayers());
+ // CraftBukkit end
+ }
+
+ // CraftBukkit start
+ private static String createVersion1Response(ServerInfo serverinfo, org.bukkit.event.server.ServerListPingEvent event) {
+ return String.format(Locale.ROOT, "\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d", 127, serverinfo.getServerVersion(), event.getMotd(), event.getNumPlayers(), event.getMaxPlayers());
+ // CraftBukkit end
+ }
+
private static void sendFlushAndClose(ChannelHandlerContext context, ByteBuf buf) {
context.pipeline().firstContext().writeAndFlush(buf).addListener(ChannelFutureListener.CLOSE);
}

View File

@@ -1,26 +1,26 @@
--- a/net/minecraft/server/network/PlayerChunkSender.java --- a/net/minecraft/server/network/PlayerChunkSender.java
+++ b/net/minecraft/server/network/PlayerChunkSender.java +++ b/net/minecraft/server/network/PlayerChunkSender.java
@@ -44,6 +44,11 @@ @@ -44,6 +_,11 @@
public void dropChunk(ServerPlayer player, ChunkPos pos) { public void dropChunk(ServerPlayer player, ChunkPos chunkPos) {
if (!this.pendingChunks.remove(pos.toLong()) && player.isAlive()) { if (!this.pendingChunks.remove(chunkPos.toLong()) && player.isAlive()) {
player.connection.send(new ClientboundForgetLevelChunkPacket(pos)); player.connection.send(new ClientboundForgetLevelChunkPacket(chunkPos));
+ // Paper start - PlayerChunkUnloadEvent + // Paper start - PlayerChunkUnloadEvent
+ if (io.papermc.paper.event.packet.PlayerChunkUnloadEvent.getHandlerList().getRegisteredListeners().length > 0) { + if (io.papermc.paper.event.packet.PlayerChunkUnloadEvent.getHandlerList().getRegisteredListeners().length > 0) {
+ new io.papermc.paper.event.packet.PlayerChunkUnloadEvent(player.getBukkitEntity().getWorld().getChunkAt(pos.longKey), player.getBukkitEntity()).callEvent(); + new io.papermc.paper.event.packet.PlayerChunkUnloadEvent(player.getBukkitEntity().getWorld().getChunkAt(chunkPos.longKey), player.getBukkitEntity()).callEvent();
+ } + }
+ // Paper end - PlayerChunkUnloadEvent + // Paper end - PlayerChunkUnloadEvent
} }
} }
@@ -75,6 +80,11 @@ @@ -75,6 +_,11 @@
private static void sendChunk(ServerGamePacketListenerImpl handler, ServerLevel world, LevelChunk chunk) { private static void sendChunk(ServerGamePacketListenerImpl packetListener, ServerLevel level, LevelChunk chunk) {
handler.send(new ClientboundLevelChunkWithLightPacket(chunk, world.getLightEngine(), null, null)); packetListener.send(new ClientboundLevelChunkWithLightPacket(chunk, level.getLightEngine(), null, null));
+ // Paper start - PlayerChunkLoadEvent + // Paper start - PlayerChunkLoadEvent
+ if (io.papermc.paper.event.packet.PlayerChunkLoadEvent.getHandlerList().getRegisteredListeners().length > 0) { + if (io.papermc.paper.event.packet.PlayerChunkLoadEvent.getHandlerList().getRegisteredListeners().length > 0) {
+ new io.papermc.paper.event.packet.PlayerChunkLoadEvent(new org.bukkit.craftbukkit.CraftChunk(chunk), handler.getPlayer().getBukkitEntity()).callEvent(); + new io.papermc.paper.event.packet.PlayerChunkLoadEvent(new org.bukkit.craftbukkit.CraftChunk(chunk), packetListener.getPlayer().getBukkitEntity()).callEvent();
+ } + }
+ // Paper end - PlayerChunkLoadEvent + // Paper end - PlayerChunkLoadEvent
ChunkPos chunkPos = chunk.getPos(); ChunkPos pos = chunk.getPos();
DebugPackets.sendPoiPacketsForChunk(world, chunkPos); DebugPackets.sendPoiPacketsForChunk(level, pos);
} }

View File

@@ -1,46 +1,69 @@
--- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java --- a/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerCommonPacketListenerImpl.java
@@ -4,11 +4,13 @@ @@ -27,30 +_,82 @@
import com.mojang.logging.LogUtils;
import java.util.Objects;
import javax.annotation.Nullable;
+import net.minecraft.ChatFormatting;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.Util;
import net.minecraft.network.Connection;
+import net.minecraft.network.ConnectionProtocol;
import net.minecraft.network.DisconnectionDetails;
import net.minecraft.network.PacketSendListener;
import net.minecraft.network.chat.Component;
@@ -22,39 +24,88 @@
import net.minecraft.network.protocol.common.ServerboundPongPacket;
import net.minecraft.network.protocol.common.ServerboundResourcePackPacket;
import net.minecraft.network.protocol.cookie.ServerboundCookieResponsePacket;
+import net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket;
+import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ClientInformation;
+import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.VisibleForDebug;
import net.minecraft.util.profiling.Profiler; import net.minecraft.util.profiling.Profiler;
import net.minecraft.util.thread.BlockableEventLoop;
import org.slf4j.Logger; import org.slf4j.Logger;
-public abstract class ServerCommonPacketListenerImpl implements ServerCommonPacketListener { -public abstract class ServerCommonPacketListenerImpl implements ServerCommonPacketListener {
+// CraftBukkit start +// CraftBukkit start
+import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBuf;
+import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutionException;
+import net.minecraft.network.ConnectionProtocol;
+import net.minecraft.network.protocol.common.custom.DiscardedPayload; +import net.minecraft.network.protocol.common.custom.DiscardedPayload;
+import net.minecraft.network.protocol.game.ClientboundSetDefaultSpawnPositionPacket;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.server.level.ServerPlayer;
+import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.entity.CraftPlayer;
+import org.bukkit.craftbukkit.util.CraftChatMessage;
+import org.bukkit.craftbukkit.util.CraftLocation; +import org.bukkit.craftbukkit.util.CraftLocation;
+import org.bukkit.craftbukkit.util.Waitable; +import org.bukkit.craftbukkit.util.Waitable;
+import org.bukkit.event.player.PlayerKickEvent; +import org.bukkit.event.player.PlayerKickEvent;
+import org.bukkit.event.player.PlayerResourcePackStatusEvent; +import org.bukkit.event.player.PlayerResourcePackStatusEvent;
+
+public abstract class ServerCommonPacketListenerImpl implements ServerCommonPacketListener, CraftPlayer.TransferCookieConnection { +public abstract class ServerCommonPacketListenerImpl implements ServerCommonPacketListener, CraftPlayer.TransferCookieConnection {
+ // CraftBukkit end
private static final Logger LOGGER = LogUtils.getLogger();
public static final int LATENCY_CHECK_INTERVAL = 15000;
private static final int CLOSED_LISTENER_TIMEOUT = 15000;
private static final Component TIMEOUT_DISCONNECTION_MESSAGE = Component.translatable("disconnect.timeout");
static final Component DISCONNECT_UNEXPECTED_QUERY = Component.translatable("multiplayer.disconnect.unexpected_query_response");
protected final MinecraftServer server;
- protected final Connection connection;
+ public final Connection connection; // Paper
private final boolean transferred;
- private long keepAliveTime;
+ private long keepAliveTime = Util.getMillis(); // Paper
private boolean keepAlivePending;
private long keepAliveChallenge;
private long closedListenerTime;
private boolean closed = false;
private int latency;
private volatile boolean suspendFlushingOnServerThread = false;
+ // CraftBukkit start
+ protected final ServerPlayer player;
+ protected final org.bukkit.craftbukkit.CraftServer cserver;
+ public boolean processedDisconnect;
+ // CraftBukkit end
+ public final java.util.Map<java.util.UUID, net.kyori.adventure.resource.ResourcePackCallback> packCallbacks = new java.util.concurrent.ConcurrentHashMap<>(); // Paper - adventure resource pack callbacks
+ private static final long KEEPALIVE_LIMIT = Long.getLong("paper.playerconnection.keepalive", 30) * 1000; // Paper - provide property to set keepalive limit
+ protected static final ResourceLocation MINECRAFT_BRAND = ResourceLocation.withDefaultNamespace("brand"); // Paper - Brand support
- public ServerCommonPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie) {
+ public ServerCommonPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie, ServerPlayer player) { // CraftBukkit
this.server = server;
this.connection = connection;
this.keepAliveTime = Util.getMillis();
this.latency = cookie.latency();
this.transferred = cookie.transferred();
- }
+ // CraftBukkit start - add fields and methods
+ this.player = player;
+ this.player.transferCookieConnection = this;
+ this.cserver = server.server;
+ }
+
+ public CraftPlayer getCraftPlayer() {
+ return this.player == null ? null : this.player.getBukkitEntity();
+ }
+ +
+ @Override + @Override
+ public boolean isTransferred() { + public boolean isTransferred() {
@@ -62,97 +85,45 @@
+ this.disconnect(reason, cause); // Paper - kick event causes + this.disconnect(reason, cause); // Paper - kick event causes
+ } + }
+ // CraftBukkit end + // CraftBukkit end
private static final Logger LOGGER = LogUtils.getLogger();
public static final int LATENCY_CHECK_INTERVAL = 15000;
private static final int CLOSED_LISTENER_TIMEOUT = 15000;
private static final Component TIMEOUT_DISCONNECTION_MESSAGE = Component.translatable("disconnect.timeout");
static final Component DISCONNECT_UNEXPECTED_QUERY = Component.translatable("multiplayer.disconnect.unexpected_query_response");
protected final MinecraftServer server;
- protected final Connection connection;
+ public final Connection connection; // Paper
private final boolean transferred;
- private long keepAliveTime;
+ private long keepAliveTime = Util.getMillis(); // Paper
private boolean keepAlivePending;
private long keepAliveChallenge;
private long closedListenerTime;
private boolean closed = false;
private int latency;
private volatile boolean suspendFlushingOnServerThread = false;
+ public final java.util.Map<java.util.UUID, net.kyori.adventure.resource.ResourcePackCallback> packCallbacks = new java.util.concurrent.ConcurrentHashMap<>(); // Paper - adventure resource pack callbacks
+ private static final long KEEPALIVE_LIMIT = Long.getLong("paper.playerconnection.keepalive", 30) * 1000; // Paper - provide property to set keepalive limit
+ protected static final ResourceLocation MINECRAFT_BRAND = ResourceLocation.withDefaultNamespace("brand"); // Paper - Brand support
- public ServerCommonPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie clientData) {
- this.server = server;
- this.connection = connection;
+ public ServerCommonPacketListenerImpl(MinecraftServer minecraftserver, Connection networkmanager, CommonListenerCookie commonlistenercookie, ServerPlayer player) { // CraftBukkit
+ this.server = minecraftserver;
+ this.connection = networkmanager;
this.keepAliveTime = Util.getMillis();
- this.latency = clientData.latency();
- this.transferred = clientData.transferred();
+ this.latency = commonlistenercookie.latency();
+ this.transferred = commonlistenercookie.transferred();
+ // CraftBukkit start - add fields and methods
+ this.player = player;
+ this.player.transferCookieConnection = this;
+ this.cserver = minecraftserver.server;
}
+ protected final ServerPlayer player;
+ protected final org.bukkit.craftbukkit.CraftServer cserver;
+ public boolean processedDisconnect;
+ public CraftPlayer getCraftPlayer() {
+ return (this.player == null) ? null : (CraftPlayer) this.player.getBukkitEntity();
+ // CraftBukkit end
+ }
+
private void close() { private void close() {
if (!this.closed) { if (!this.closed) {
this.closedListenerTime = Util.getMillis(); @@ -61,6 +_,12 @@
@@ -65,6 +116,11 @@
@Override @Override
public void onDisconnect(DisconnectionDetails info) { public void onDisconnect(DisconnectionDetails details) {
+ // Paper start - Fix kick event leave message not being sent + // Paper start - Fix kick event leave message not being sent
+ this.onDisconnect(info, null); + this.onDisconnect(details, null);
+ } + }
+
+ public void onDisconnect(DisconnectionDetails info, @Nullable net.kyori.adventure.text.Component quitMessage) { + public void onDisconnect(DisconnectionDetails info, @Nullable net.kyori.adventure.text.Component quitMessage) {
+ // Paper end - Fix kick event leave message not being sent + // Paper end - Fix kick event leave message not being sent
if (this.isSingleplayerOwner()) { if (this.isSingleplayerOwner()) {
ServerCommonPacketListenerImpl.LOGGER.info("Stopping singleplayer server as player logged out"); LOGGER.info("Stopping singleplayer server as player logged out");
this.server.halt(false); this.server.halt(false);
@@ -80,13 +136,14 @@ @@ -80,7 +_,7 @@
@Override
public void handleKeepAlive(ServerboundKeepAlivePacket packet) {
+ //PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); // CraftBukkit // Paper - handle ServerboundKeepAlivePacket async
if (this.keepAlivePending && packet.getId() == this.keepAliveChallenge) {
int i = (int) (Util.getMillis() - this.keepAliveTime);
this.latency = (this.latency * 3 + i) / 4; this.latency = (this.latency * 3 + i) / 4;
this.keepAlivePending = false; this.keepAlivePending = false;
} else if (!this.isSingleplayerOwner()) { } else if (!this.isSingleplayerOwner()) {
- this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE); - this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE);
+ this.disconnectAsync(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - add proper async disconnect + this.disconnectAsync(TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - add proper async disconnect
}
} }
@@ -88,37 +_,124 @@
public void handlePong(ServerboundPongPacket packet) {
} }
@@ -94,38 +151,127 @@
@Override
public void handlePong(ServerboundPongPacket packet) {}
+ // CraftBukkit start + private static final ResourceLocation CUSTOM_REGISTER = ResourceLocation.withDefaultNamespace("register"); // CraftBukkit
+ private static final ResourceLocation CUSTOM_REGISTER = ResourceLocation.withDefaultNamespace("register"); + private static final ResourceLocation CUSTOM_UNREGISTER = ResourceLocation.withDefaultNamespace("unregister"); // CraftBukkit
+ private static final ResourceLocation CUSTOM_UNREGISTER = ResourceLocation.withDefaultNamespace("unregister");
+ +
@Override @Override
- public void handleCustomPayload(ServerboundCustomPayloadPacket packet) {} public void handleCustomPayload(ServerboundCustomPayloadPacket packet) {
+ public void handleCustomPayload(ServerboundCustomPayloadPacket packet) { - }
+ // CraftBukkit start
+ // Paper start - Brand support + // Paper start - Brand support
+ if (packet.payload() instanceof net.minecraft.network.protocol.common.custom.BrandPayload brandPayload) { + if (packet.payload() instanceof net.minecraft.network.protocol.common.custom.BrandPayload(String brand)) {
+ this.player.clientBrandName = brandPayload.brand(); + this.player.clientBrandName = brand;
+ } + }
+ // Paper end - Brand support + // Paper end - Brand support
+ if (!(packet.payload() instanceof DiscardedPayload)) { + if (!(packet.payload() instanceof DiscardedPayload)) {
@@ -161,7 +132,7 @@
+ PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel()); + PacketUtils.ensureRunningOnSameThread(packet, this, this.player.serverLevel());
+ ResourceLocation identifier = packet.payload().type().id(); + ResourceLocation identifier = packet.payload().type().id();
+ ByteBuf payload = ((DiscardedPayload)packet.payload()).data(); + ByteBuf payload = ((DiscardedPayload)packet.payload()).data();
+
+ if (identifier.equals(ServerCommonPacketListenerImpl.CUSTOM_REGISTER)) { + if (identifier.equals(ServerCommonPacketListenerImpl.CUSTOM_REGISTER)) {
+ try { + try {
+ String channels = payload.toString(com.google.common.base.Charsets.UTF_8); + String channels = payload.toString(com.google.common.base.Charsets.UTF_8);
@@ -169,7 +140,7 @@
+ this.getCraftPlayer().addChannel(channel); + this.getCraftPlayer().addChannel(channel);
+ } + }
+ } catch (Exception ex) { + } catch (Exception ex) {
+ ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t register custom payload", ex); + ServerGamePacketListenerImpl.LOGGER.error("Couldn't register custom payload", ex);
+ this.disconnect(Component.literal("Invalid payload REGISTER!"), PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause + this.disconnect(Component.literal("Invalid payload REGISTER!"), PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause
+ } + }
+ } else if (identifier.equals(ServerCommonPacketListenerImpl.CUSTOM_UNREGISTER)) { + } else if (identifier.equals(ServerCommonPacketListenerImpl.CUSTOM_UNREGISTER)) {
@@ -179,7 +150,7 @@
+ this.getCraftPlayer().removeChannel(channel); + this.getCraftPlayer().removeChannel(channel);
+ } + }
+ } catch (Exception ex) { + } catch (Exception ex) {
+ ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t unregister custom payload", ex); + ServerGamePacketListenerImpl.LOGGER.error("Couldn't unregister custom payload", ex);
+ this.disconnect(Component.literal("Invalid payload UNREGISTER!"), PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause + this.disconnect(Component.literal("Invalid payload UNREGISTER!"), PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause
+ } + }
+ } else { + } else {
@@ -197,26 +168,26 @@
+ // Paper end - Brand support + // Paper end - Brand support
+ this.cserver.getMessenger().dispatchIncomingMessage(this.player.getBukkitEntity(), identifier.toString(), data); + this.cserver.getMessenger().dispatchIncomingMessage(this.player.getBukkitEntity(), identifier.toString(), data);
+ } catch (Exception ex) { + } catch (Exception ex) {
+ ServerGamePacketListenerImpl.LOGGER.error("Couldn\'t dispatch custom payload", ex); + ServerGamePacketListenerImpl.LOGGER.error("Couldn't dispatch custom payload", ex);
+ this.disconnect(Component.literal("Invalid custom payload!"), PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause + this.disconnect(Component.literal("Invalid custom payload!"), PlayerKickEvent.Cause.INVALID_PAYLOAD); // Paper - kick event cause
+ } + }
+ } + }
+
+ } + }
+ +
+ public final boolean isDisconnected() { + public final boolean isDisconnected() {
+ return (!this.player.joining && !this.connection.isConnected()) || this.processedDisconnect; // Paper - Fix duplication bugs + return (!this.player.joining && !this.connection.isConnected()) || this.processedDisconnect; // Paper - Fix duplication bugs
+ } + }
+ // CraftBukkit end + // CraftBukkit end
+
@Override @Override
public void handleResourcePackResponse(ServerboundResourcePackPacket packet) { public void handleResourcePackResponse(ServerboundResourcePackPacket packet) {
PacketUtils.ensureRunningOnSameThread(packet, this, (BlockableEventLoop) this.server); PacketUtils.ensureRunningOnSameThread(packet, this, this.server);
if (packet.action() == ServerboundResourcePackPacket.Action.DECLINED && this.server.isResourcePackRequired()) { if (packet.action() == ServerboundResourcePackPacket.Action.DECLINED && this.server.isResourcePackRequired()) {
ServerCommonPacketListenerImpl.LOGGER.info("Disconnecting {} due to resource pack {} rejection", this.playerProfile().getName(), packet.id()); LOGGER.info("Disconnecting {} due to resource pack {} rejection", this.playerProfile().getName(), packet.id());
- this.disconnect((Component) Component.translatable("multiplayer.requiredTexturePrompt.disconnect")); - this.disconnect(Component.translatable("multiplayer.requiredTexturePrompt.disconnect"));
+ this.disconnect((Component) Component.translatable("multiplayer.requiredTexturePrompt.disconnect"), PlayerKickEvent.Cause.RESOURCE_PACK_REJECTION); // Paper - kick event cause - }
} + this.disconnect(Component.translatable("multiplayer.requiredTexturePrompt.disconnect"), PlayerKickEvent.Cause.RESOURCE_PACK_REJECTION); // Paper - kick event cause
+ }
+ // Paper start - adventure pack callbacks + // Paper start - adventure pack callbacks
+ // call the callbacks before the previously-existing event so the event has final say + // call the callbacks before the previously-existing event so the event has final say
+ final net.kyori.adventure.resource.ResourcePackCallback callback; + final net.kyori.adventure.resource.ResourcePackCallback callback;
@@ -231,43 +202,41 @@
+ // Paper end + // Paper end
+ // Paper start - store last pack status + // Paper start - store last pack status
+ PlayerResourcePackStatusEvent.Status packStatus = PlayerResourcePackStatusEvent.Status.values()[packet.action().ordinal()]; + PlayerResourcePackStatusEvent.Status packStatus = PlayerResourcePackStatusEvent.Status.values()[packet.action().ordinal()];
+ player.getBukkitEntity().resourcePackStatus = packStatus; + this.player.getBukkitEntity().resourcePackStatus = packStatus;
+ this.cserver.getPluginManager().callEvent(new PlayerResourcePackStatusEvent(this.getCraftPlayer(), packet.id(), packStatus)); // CraftBukkit + this.cserver.getPluginManager().callEvent(new PlayerResourcePackStatusEvent(this.getCraftPlayer(), packet.id(), packStatus)); // CraftBukkit
+ // Paper end - store last pack status + // Paper end - store last pack status
} }
@Override @Override
public void handleCookieResponse(ServerboundCookieResponsePacket packet) { public void handleCookieResponse(ServerboundCookieResponsePacket packet) {
- this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY); - this.disconnect(DISCONNECT_UNEXPECTED_QUERY);
+ // CraftBukkit start + // CraftBukkit start
+ PacketUtils.ensureRunningOnSameThread(packet, this, (BlockableEventLoop) this.server); + PacketUtils.ensureRunningOnSameThread(packet, this, this.server);
+ if (this.player.getBukkitEntity().handleCookieResponse(packet)) { + if (this.player.getBukkitEntity().handleCookieResponse(packet)) {
+ return; + return;
+ } + }
+ // CraftBukkit end + // CraftBukkit end
+ this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY, PlayerKickEvent.Cause.INVALID_COOKIE); // Paper - kick event cause + this.disconnect(DISCONNECT_UNEXPECTED_QUERY, PlayerKickEvent.Cause.INVALID_COOKIE); // Paper - kick event cause
} }
protected void keepConnectionAlive() { protected void keepConnectionAlive() {
Profiler.get().push("keepAlive"); Profiler.get().push("keepAlive");
- long i = Util.getMillis(); - long millis = Util.getMillis();
- if (!this.isSingleplayerOwner() && millis - this.keepAliveTime >= 15000L) {
- if (this.keepAlivePending) {
- this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE);
- } else if (this.checkIfClosed(millis)) {
+ // Paper start - give clients a longer time to respond to pings as per pre 1.12.2 timings + // Paper start - give clients a longer time to respond to pings as per pre 1.12.2 timings
+ // This should effectively place the keepalive handling back to "as it was" before 1.12.2 + // This should effectively place the keepalive handling back to "as it was" before 1.12.2
+ long currentTime = Util.getMillis(); + long currentTime = Util.getMillis();
+ long elapsedTime = currentTime - this.keepAliveTime; + long elapsedTime = currentTime - this.keepAliveTime;
- if (!this.isSingleplayerOwner() && i - this.keepAliveTime >= 15000L) {
- if (this.keepAlivePending) {
- this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE);
- } else if (this.checkIfClosed(i)) {
+ if (!this.isSingleplayerOwner() && elapsedTime >= 15000L) { // Paper - use vanilla's 15000L between keep alive packets + if (!this.isSingleplayerOwner() && elapsedTime >= 15000L) { // Paper - use vanilla's 15000L between keep alive packets
+ if (this.keepAlivePending && !this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // Paper - check keepalive limit, don't fire if already disconnected + if (this.keepAlivePending && !this.processedDisconnect && elapsedTime >= KEEPALIVE_LIMIT) { // Paper - check keepalive limit, don't fire if already disconnected
+ this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause + this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause
+ } else if (this.checkIfClosed(currentTime)) { // Paper + } else if (this.checkIfClosed(currentTime)) { // Paper
this.keepAlivePending = true; this.keepAlivePending = true;
- this.keepAliveTime = i; - this.keepAliveTime = millis;
- this.keepAliveChallenge = i; - this.keepAliveChallenge = millis;
+ this.keepAliveTime = currentTime; + this.keepAliveTime = currentTime;
+ this.keepAliveChallenge = currentTime; + this.keepAliveChallenge = currentTime;
this.send(new ClientboundKeepAlivePacket(this.keepAliveChallenge)); this.send(new ClientboundKeepAlivePacket(this.keepAliveChallenge));
@@ -277,31 +246,30 @@
Profiler.get().pop(); Profiler.get().pop();
} }
@@ -133,7 +279,7 @@ @@ -126,7 +_,7 @@
private boolean checkIfClosed(long time) { private boolean checkIfClosed(long time) {
if (this.closed) { if (this.closed) {
if (time - this.closedListenerTime >= 15000L) { if (time - this.closedListenerTime >= 15000L) {
- this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE); - this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE);
+ this.disconnect(ServerCommonPacketListenerImpl.TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause + this.disconnect(TIMEOUT_DISCONNECTION_MESSAGE, PlayerKickEvent.Cause.TIMEOUT); // Paper - kick event cause
} }
return false; return false;
@@ -156,6 +302,14 @@ @@ -149,6 +_,13 @@
} }
public void send(Packet<?> packet, @Nullable PacketSendListener callbacks) { public void send(Packet<?> packet, @Nullable PacketSendListener listener) {
+ // CraftBukkit start + // CraftBukkit start
+ if (packet == null || this.processedDisconnect) { // Spigot + if (packet == null || this.processedDisconnect) { // Spigot
+ return; + return;
+ } else if (packet instanceof ClientboundSetDefaultSpawnPositionPacket) { + } else if (packet instanceof ClientboundSetDefaultSpawnPositionPacket defaultSpawnPositionPacket) {
+ ClientboundSetDefaultSpawnPositionPacket packet6 = (ClientboundSetDefaultSpawnPositionPacket) packet; + this.player.compassTarget = CraftLocation.toBukkit(defaultSpawnPositionPacket.getPos(), this.getCraftPlayer().getWorld());
+ this.player.compassTarget = CraftLocation.toBukkit(packet6.pos, this.getCraftPlayer().getWorld());
+ } + }
+ // CraftBukkit end + // CraftBukkit end
if (packet.isTerminal()) { if (packet.isTerminal()) {
this.close(); this.close();
} }
@@ -175,22 +329,109 @@ @@ -165,19 +_,108 @@
} }
} }
@@ -309,27 +277,28 @@
+ public void disconnect(final net.kyori.adventure.text.Component reason) { + public void disconnect(final net.kyori.adventure.text.Component reason) {
+ this.disconnect(reason, PlayerKickEvent.Cause.UNKNOWN); + this.disconnect(reason, PlayerKickEvent.Cause.UNKNOWN);
+ } + }
+
+ public void disconnect(final net.kyori.adventure.text.Component reason, PlayerKickEvent.Cause cause) { + public void disconnect(final net.kyori.adventure.text.Component reason, PlayerKickEvent.Cause cause) {
+ this.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(reason), cause); + this.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(reason), cause);
+ // Paper end - kick event causes
+ } + }
+ // Paper end - adventure
+ +
+ // Paper end - adventure
+ @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - kick event causes + @Deprecated @io.papermc.paper.annotation.DoNotUse // Paper - kick event causes
public void disconnect(Component reason) { public void disconnect(Component reason) {
- this.disconnect(new DisconnectionDetails(reason)); - this.disconnect(new DisconnectionDetails(reason));
- }
-
- public void disconnect(DisconnectionDetails disconnectionDetails) {
+ // Paper start - kick event causes + // Paper start - kick event causes
+ this.disconnect(reason, PlayerKickEvent.Cause.UNKNOWN); + this.disconnect(reason, PlayerKickEvent.Cause.UNKNOWN);
} + }
+
+ public void disconnect(final Component reason, PlayerKickEvent.Cause cause) { + public void disconnect(final Component reason, PlayerKickEvent.Cause cause) {
+ this.disconnect(new DisconnectionDetails(reason), cause); + this.disconnect(new DisconnectionDetails(reason), cause);
+ // Paper end - kick event causes + // Paper end - kick event causes
+ } + }
+
- public void disconnect(DisconnectionDetails disconnectionInfo) { + public void disconnect(DisconnectionDetails disconnectionDetails, PlayerKickEvent.Cause cause) { // Paper - kick event cause
- this.connection.send(new ClientboundDisconnectPacket(disconnectionInfo.reason()), PacketSendListener.thenRun(() -> {
- this.connection.disconnect(disconnectionInfo);
+ public void disconnect(DisconnectionDetails disconnectionInfo, PlayerKickEvent.Cause cause) { // Paper - kick event cause
+ // CraftBukkit start - fire PlayerKickEvent + // CraftBukkit start - fire PlayerKickEvent
+ if (this.processedDisconnect) { + if (this.processedDisconnect) {
+ return; + return;
@@ -338,7 +307,7 @@
+ Waitable waitable = new Waitable() { + Waitable waitable = new Waitable() {
+ @Override + @Override
+ protected Object evaluate() { + protected Object evaluate() {
+ ServerCommonPacketListenerImpl.this.disconnect(disconnectionInfo, cause); // Paper - kick event causes + ServerCommonPacketListenerImpl.this.disconnect(disconnectionDetails, cause); // Paper - kick event causes
+ return null; + return null;
+ } + }
+ }; + };
@@ -357,7 +326,7 @@
+ +
+ net.kyori.adventure.text.Component leaveMessage = net.kyori.adventure.text.Component.translatable("multiplayer.player.left", net.kyori.adventure.text.format.NamedTextColor.YELLOW, io.papermc.paper.configuration.GlobalConfiguration.get().messages.useDisplayNameInQuitMessage ? this.player.getBukkitEntity().displayName() : net.kyori.adventure.text.Component.text(this.player.getScoreboardName())); // Paper - Adventure + net.kyori.adventure.text.Component leaveMessage = net.kyori.adventure.text.Component.translatable("multiplayer.player.left", net.kyori.adventure.text.format.NamedTextColor.YELLOW, io.papermc.paper.configuration.GlobalConfiguration.get().messages.useDisplayNameInQuitMessage ? this.player.getBukkitEntity().displayName() : net.kyori.adventure.text.Component.text(this.player.getScoreboardName())); // Paper - Adventure
+ +
+ PlayerKickEvent event = new PlayerKickEvent(this.player.getBukkitEntity(), io.papermc.paper.adventure.PaperAdventure.asAdventure(disconnectionInfo.reason()), leaveMessage, cause); // Paper - adventure & kick event causes + PlayerKickEvent event = new PlayerKickEvent(this.player.getBukkitEntity(), io.papermc.paper.adventure.PaperAdventure.asAdventure(disconnectionDetails.reason()), leaveMessage, cause); // Paper - adventure & kick event causes
+ +
+ if (this.cserver.getServer().isRunning()) { + if (this.cserver.getServer().isRunning()) {
+ this.cserver.getPluginManager().callEvent(event); + this.cserver.getPluginManager().callEvent(event);
@@ -368,26 +337,26 @@
+ return; + return;
+ } + }
+ // Send the possibly modified leave message + // Send the possibly modified leave message
+ this.disconnect0(new DisconnectionDetails(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.reason()), disconnectionInfo.report(), disconnectionInfo.bugReportLink()), event.leaveMessage()); // Paper - Adventure & use kick event leave message + this.disconnect0(new DisconnectionDetails(io.papermc.paper.adventure.PaperAdventure.asVanilla(event.reason()), disconnectionDetails.report(), disconnectionDetails.bugReportLink()), event.leaveMessage()); // Paper - Adventure & use kick event leave message
+ } + }
+ +
+ private void disconnect0(DisconnectionDetails disconnectiondetails, @Nullable net.kyori.adventure.text.Component leaveMessage) { // Paper - use kick event leave message + private void disconnect0(DisconnectionDetails disconnectionDetails, @Nullable net.kyori.adventure.text.Component leaveMessage) { // Paper - use kick event leave message
+ // CraftBukkit end + // CraftBukkit end
+ this.player.quitReason = org.bukkit.event.player.PlayerQuitEvent.QuitReason.KICKED; // Paper - Add API for quit reason + this.player.quitReason = org.bukkit.event.player.PlayerQuitEvent.QuitReason.KICKED; // Paper - Add API for quit reason
+ this.connection.send(new ClientboundDisconnectPacket(disconnectiondetails.reason()), PacketSendListener.thenRun(() -> { this.connection
+ this.connection.disconnect(disconnectiondetails); .send(
})); new ClientboundDisconnectPacket(disconnectionDetails.reason()),
+ this.onDisconnect(disconnectiondetails, leaveMessage); // CraftBukkit - fire quit instantly // Paper - use kick event leave message PacketSendListener.thenRun(() -> this.connection.disconnect(disconnectionDetails))
this.connection.setReadOnly(); );
MinecraftServer minecraftserver = this.server; - this.connection.setReadOnly();
Connection networkmanager = this.connection; - this.server.executeBlocking(this.connection::handleDisconnection);
- }
Objects.requireNonNull(this.connection); + this.onDisconnect(disconnectionDetails, leaveMessage); // CraftBukkit - fire quit instantly // Paper - use kick event leave message
- minecraftserver.executeBlocking(networkmanager::handleDisconnection); + this.connection.setReadOnly();
+ // CraftBukkit - Don't wait + // CraftBukkit - Don't wait
+ minecraftserver.scheduleOnMain(networkmanager::handleDisconnection); // Paper + this.server.scheduleOnMain(this.connection::handleDisconnection); // Paper
} + }
+
+ // Paper start - add proper async disconnect + // Paper start - add proper async disconnect
+ public void disconnectAsync(net.kyori.adventure.text.Component reason, PlayerKickEvent.Cause cause) { + public void disconnectAsync(net.kyori.adventure.text.Component reason, PlayerKickEvent.Cause cause) {
+ this.disconnectAsync(io.papermc.paper.adventure.PaperAdventure.asVanilla(reason), cause); + this.disconnectAsync(io.papermc.paper.adventure.PaperAdventure.asVanilla(reason), cause);
@@ -402,6 +371,7 @@
+ this.disconnect(disconnectionInfo, cause); + this.disconnect(disconnectionInfo, cause);
+ return; + return;
+ } + }
+
+ this.connection.setReadOnly(); + this.connection.setReadOnly();
+ this.server.scheduleOnMain(() -> { + this.server.scheduleOnMain(() -> {
+ ServerCommonPacketListenerImpl.this.disconnect(disconnectionInfo, cause); + ServerCommonPacketListenerImpl.this.disconnect(disconnectionInfo, cause);
@@ -412,7 +382,6 @@
+ }); + });
+ } + }
+ // Paper end - add proper async disconnect + // Paper end - add proper async disconnect
+
protected boolean isSingleplayerOwner() { protected boolean isSingleplayerOwner() {
return this.server.isSingleplayerOwner(this.playerProfile()); return this.server.isSingleplayerOwner(this.playerProfile());
}

View File

@@ -0,0 +1,71 @@
--- a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
@@ -48,8 +_,10 @@
@Nullable
private SynchronizeRegistriesTask synchronizeRegistriesTask;
- public ServerConfigurationPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie) {
- super(server, connection, cookie);
+ // CraftBukkit start
+ public ServerConfigurationPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie cookie, ServerPlayer player) {
+ super(server, connection, cookie, player);
+ // CraftBukkit end
this.gameProfile = cookie.gameProfile();
this.clientInformation = cookie.clientInformation();
}
@@ -61,6 +_,10 @@
@Override
public void onDisconnect(DisconnectionDetails details) {
+ // Paper start - Debugging
+ if (this.server.isDebugging()) {
+ ServerConfigurationPacketListenerImpl.LOGGER.info("{} lost connection: {}, while in configuration phase {}", this.gameProfile, details.reason().getString(), this.currentTask != null ? this.currentTask.type().id() : "null");
+ } else // Paper end
LOGGER.info("{} lost connection: {}", this.gameProfile, details.reason().getString());
super.onDisconnect(details);
}
@@ -73,6 +_,12 @@
public void startConfiguration() {
this.send(new ClientboundCustomPayloadPacket(new BrandPayload(this.server.getServerModName())));
ServerLinks serverLinks = this.server.serverLinks();
+ // CraftBukkit start
+ org.bukkit.craftbukkit.CraftServerLinks wrapper = new org.bukkit.craftbukkit.CraftServerLinks(serverLinks);
+ org.bukkit.event.player.PlayerLinksSendEvent event = new org.bukkit.event.player.PlayerLinksSendEvent(this.player.getBukkitEntity(), wrapper);
+ this.cserver.getPluginManager().callEvent(event);
+ serverLinks = wrapper.getServerLinks();
+ // CraftBukkit end
if (!serverLinks.isEmpty()) {
this.send(new ClientboundServerLinksPacket(serverLinks.untrust()));
}
@@ -105,6 +_,7 @@
@Override
public void handleClientInformation(ServerboundClientInformationPacket packet) {
this.clientInformation = packet.information();
+ this.connection.channel.attr(io.papermc.paper.adventure.PaperAdventure.LOCALE_ATTRIBUTE).set(net.kyori.adventure.translation.Translator.parseLocale(packet.information().language())); // Paper
}
@Override
@@ -139,16 +_,21 @@
return;
}
- Component component = playerList.canPlayerLogin(this.connection.getRemoteAddress(), this.gameProfile);
+ Component component = null; // CraftBukkit - login checks already completed
if (component != null) {
this.disconnect(component);
return;
}
- ServerPlayer playerForLogin = playerList.getPlayerForLogin(this.gameProfile, this.clientInformation);
+ ServerPlayer playerForLogin = playerList.getPlayerForLogin(this.gameProfile, this.clientInformation, this.player); // CraftBukkit
playerList.placeNewPlayer(this.connection, playerForLogin, this.createCookie(this.clientInformation));
} catch (Exception var5) {
LOGGER.error("Couldn't place player in world", (Throwable)var5);
+ // Paper start - Debugging
+ if (this.server.isDebugging()) {
+ var5.printStackTrace();
+ }
+ // Paper end - Debugging
this.connection.send(new ClientboundDisconnectPacket(DISCONNECT_REASON_INVALID_DATA));
this.connection.disconnect(DISCONNECT_REASON_INVALID_DATA);
}

View File

@@ -0,0 +1,154 @@
--- a/net/minecraft/server/network/ServerConnectionListener.java
+++ b/net/minecraft/server/network/ServerConnectionListener.java
@@ -49,10 +_,10 @@
public class ServerConnectionListener {
private static final Logger LOGGER = LogUtils.getLogger();
public static final Supplier<NioEventLoopGroup> SERVER_EVENT_GROUP = Suppliers.memoize(
- () -> new NioEventLoopGroup(0, new ThreadFactoryBuilder().setNameFormat("Netty Server IO #%d").setDaemon(true).build())
+ () -> new NioEventLoopGroup(0, new ThreadFactoryBuilder().setNameFormat("Netty Server IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()) // Paper
);
public static final Supplier<EpollEventLoopGroup> SERVER_EPOLL_EVENT_GROUP = Suppliers.memoize(
- () -> new EpollEventLoopGroup(0, new ThreadFactoryBuilder().setNameFormat("Netty Epoll Server IO #%d").setDaemon(true).build())
+ () -> new EpollEventLoopGroup(0, new ThreadFactoryBuilder().setNameFormat("Netty Epoll Server IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()) // Paper
);
final MinecraftServer server;
public volatile boolean running;
@@ -64,12 +_,35 @@
this.running = true;
}
+ // Paper start - prevent blocking on adding a new connection while the server is ticking
+ private final java.util.Queue<Connection> pending = new java.util.concurrent.ConcurrentLinkedQueue<>();
+
+ private final void addPending() {
+ Connection connection;
+ while ((connection = this.pending.poll()) != null) {
+ this.connections.add(connection);
+ }
+ }
+ // Paper end - prevent blocking on adding a new connection while the server is ticking
+
public void startTcpServerListener(@Nullable InetAddress address, int port) throws IOException {
+ // Paper start - Unix domain socket support
+ this.startTcpServerListener(new java.net.InetSocketAddress(address, port));
+ }
+
+ public void startTcpServerListener(SocketAddress address) throws IOException {
+ // Paper end - Unix domain socket support
synchronized (this.channels) {
- Class<? extends ServerSocketChannel> clazz;
+ Class<? extends io.netty.channel.ServerChannel> clazz; // Paper - Unix domain socket support
EventLoopGroup eventLoopGroup;
if (Epoll.isAvailable() && this.server.isEpollEnabled()) {
+ // Paper start - Unix domain socket support
+ if (address instanceof io.netty.channel.unix.DomainSocketAddress) {
+ clazz = io.netty.channel.epoll.EpollServerDomainSocketChannel.class;
+ } else {
clazz = EpollServerSocketChannel.class;
+ }
+ // Paper end - Unix domain socket support
eventLoopGroup = SERVER_EPOLL_EVENT_GROUP.get();
LOGGER.info("Using epoll channel type");
} else {
@@ -78,6 +_,12 @@
LOGGER.info("Using default channel type");
}
+ // Paper start - Warn people with console access that HAProxy is in use.
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.proxyProtocol) {
+ LOGGER.warn("Using HAProxy, please ensure the server port is adequately firewalled.");
+ }
+ // Paper end - Warn people with console access that HAProxy is in use.
+
this.channels
.add(
new ServerBootstrap()
@@ -101,22 +_,64 @@
Connection connection = (Connection)(rateLimitPacketsPerSecond > 0
? new RateKickingConnection(rateLimitPacketsPerSecond)
: new Connection(PacketFlow.SERVERBOUND));
- ServerConnectionListener.this.connections.add(connection);
+ // ServerConnectionListener.this.connections.add(connection);
+ // Paper start - Add support for Proxy Protocol
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.proxyProtocol) {
+ channel.pipeline().addAfter("timeout", "haproxy-decoder", new io.netty.handler.codec.haproxy.HAProxyMessageDecoder());
+ channel.pipeline().addAfter("haproxy-decoder", "haproxy-handler", new ChannelInboundHandlerAdapter() {
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ if (msg instanceof io.netty.handler.codec.haproxy.HAProxyMessage message) {
+ if (message.command() == io.netty.handler.codec.haproxy.HAProxyCommand.PROXY) {
+ String realaddress = message.sourceAddress();
+ int realport = message.sourcePort();
+
+ SocketAddress socketaddr = new java.net.InetSocketAddress(realaddress, realport);
+
+ Connection connection = (Connection) channel.pipeline().get("packet_handler");
+ connection.address = socketaddr;
+ // Paper start - Add API to get player's proxy address
+ final String proxyAddress = message.destinationAddress();
+ final int proxyPort = message.destinationPort();
+
+ connection.haProxyAddress = new java.net.InetSocketAddress(proxyAddress, proxyPort);
+ // Paper end - Add API to get player's proxy address
+ }
+ } else {
+ super.channelRead(ctx, msg);
+ }
+ }
+ });
+ }
+ // Paper end - Add support for proxy protocol
+ pending.add(connection); // Paper - prevent blocking on adding a new connection while the server is ticking
connection.configurePacketHandler(channelPipeline);
connection.setListenerForServerboundHandshake(
new ServerHandshakePacketListenerImpl(ServerConnectionListener.this.server, connection)
);
+ io.papermc.paper.network.ChannelInitializeListenerHolder.callListeners(channel); // Paper - Add Channel initialization listeners
}
}
)
.group(eventLoopGroup)
- .localAddress(address, port)
+ .localAddress(address) // Paper - Unix domain socket support
+ .option(ChannelOption.AUTO_READ, false) // CraftBukkit
.bind()
.syncUninterruptibly()
);
}
}
+ // CraftBukkit start
+ public void acceptConnections() {
+ synchronized (this.channels) {
+ for (ChannelFuture future : this.channels) {
+ future.channel().config().setAutoRead(true);
+ }
+ }
+ }
+ // CraftBukkit end
+
public SocketAddress startMemoryChannel() {
ChannelFuture channelFuture;
synchronized (this.channels) {
@@ -161,6 +_,13 @@
public void tick() {
synchronized (this.connections) {
+ // Spigot start
+ this.addPending(); // Paper - prevent blocking on adding a new connection while the server is ticking
+ // This prevents players from 'gaming' the server, and strategically relogging to increase their position in the tick order
+ if (org.spigotmc.SpigotConfig.playerShuffle > 0 && MinecraftServer.currentTick % org.spigotmc.SpigotConfig.playerShuffle == 0) {
+ Collections.shuffle(this.connections);
+ }
+ // Spigot end
Iterator<Connection> iterator = this.connections.iterator();
while (iterator.hasNext()) {
@@ -180,6 +_,7 @@
connection.setReadOnly();
}
} else {
+ if (connection.preparing) continue; // Spigot - Fix a race condition where a NetworkManager could be unregistered just before connection
iterator.remove();
connection.handleDisconnection();
}

View File

@@ -1,6 +1,6 @@
--- a/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java --- a/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java +++ b/net/minecraft/server/network/ServerHandshakePacketListenerImpl.java
@@ -13,11 +13,26 @@ @@ -12,11 +_,27 @@
import net.minecraft.network.protocol.status.StatusProtocols; import net.minecraft.network.protocol.status.StatusProtocols;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
@@ -10,24 +10,25 @@
+// CraftBukkit end +// CraftBukkit end
+ +
public class ServerHandshakePacketListenerImpl implements ServerHandshakePacketListener { public class ServerHandshakePacketListenerImpl implements ServerHandshakePacketListener {
private static final Component IGNORE_STATUS_REASON = Component.translatable("disconnect.ignoring_status_request");
+ // Spigot start + // Spigot start
+ private static final com.google.gson.Gson gson = new com.google.gson.Gson(); + private static final com.google.gson.Gson gson = new com.google.gson.Gson();
+ static final java.util.regex.Pattern HOST_PATTERN = java.util.regex.Pattern.compile("[0-9a-f\\.:]{0,45}"); + static final java.util.regex.Pattern HOST_PATTERN = java.util.regex.Pattern.compile("[0-9a-f\\.:]{0,45}");
+ static final java.util.regex.Pattern PROP_PATTERN = java.util.regex.Pattern.compile("\\w{0,16}"); + static final java.util.regex.Pattern PROP_PATTERN = java.util.regex.Pattern.compile("\\w{0,16}");
+ // Spigot end + // Spigot end
+ // CraftBukkit start - add fields + // CraftBukkit start - add fields
+ private static final HashMap<InetAddress, Long> throttleTracker = new HashMap<InetAddress, Long>(); + private static final HashMap<InetAddress, Long> throttleTracker = new HashMap<>();
+ private static int throttleCounter = 0; + private static int throttleCounter = 0;
+ // CraftBukkit end + // CraftBukkit end
private static final Component IGNORE_STATUS_REASON = Component.translatable("disconnect.ignoring_status_request"); + private static final boolean BYPASS_HOSTCHECK = Boolean.getBoolean("Paper.bypassHostCheck"); // Paper
private final MinecraftServer server; private final MinecraftServer server;
private final Connection connection; private final Connection connection;
+ private static final boolean BYPASS_HOSTCHECK = Boolean.getBoolean("Paper.bypassHostCheck"); // Paper
+
public ServerHandshakePacketListenerImpl(MinecraftServer server, Connection connection) { public ServerHandshakePacketListenerImpl(MinecraftServer server, Connection connection) {
this.server = server; this.server = server;
@@ -26,6 +41,7 @@ this.connection = connection;
@@ -24,6 +_,7 @@
@Override @Override
public void handleIntention(ClientIntentionPacket packet) { public void handleIntention(ClientIntentionPacket packet) {
@@ -35,17 +36,18 @@
switch (packet.intention()) { switch (packet.intention()) {
case LOGIN: case LOGIN:
this.beginLogin(packet, false); this.beginLogin(packet, false);
@@ -55,23 +71,127 @@ @@ -50,22 +_,117 @@
throw new UnsupportedOperationException("Invalid intention " + String.valueOf(packet.intention())); default:
throw new UnsupportedOperationException("Invalid intention " + packet.intention());
} }
+
+ // Paper start - NetworkClient implementation + // Paper start - NetworkClient implementation
+ this.connection.protocolVersion = packet.protocolVersion(); + this.connection.protocolVersion = packet.protocolVersion();
+ this.connection.virtualHost = com.destroystokyo.paper.network.PaperNetworkClient.prepareVirtualHost(packet.hostName(), packet.port()); + this.connection.virtualHost = com.destroystokyo.paper.network.PaperNetworkClient.prepareVirtualHost(packet.hostName(), packet.port());
+ // Paper end + // Paper end
} }
private void beginLogin(ClientIntentionPacket packet, boolean transfer) { private void beginLogin(ClientIntentionPacket packet, boolean transferred) {
this.connection.setupOutboundProtocol(LoginProtocols.CLIENTBOUND); this.connection.setupOutboundProtocol(LoginProtocols.CLIENTBOUND);
+ // CraftBukkit start - Connection throttle + // CraftBukkit start - Connection throttle
+ try { + try {
@@ -69,13 +71,7 @@
+ ServerHandshakePacketListenerImpl.throttleCounter = 0; + ServerHandshakePacketListenerImpl.throttleCounter = 0;
+ +
+ // Cleanup stale entries + // Cleanup stale entries
+ java.util.Iterator iter = ServerHandshakePacketListenerImpl.throttleTracker.entrySet().iterator(); + ServerHandshakePacketListenerImpl.throttleTracker.values().removeIf(time -> time > connectionThrottle);
+ while (iter.hasNext()) {
+ java.util.Map.Entry<InetAddress, Long> entry = (java.util.Map.Entry) iter.next();
+ if (entry.getValue() > connectionThrottle) {
+ iter.remove();
+ }
+ }
+ } + }
+ } + }
+ } // Paper - Unix domain socket support + } // Paper - Unix domain socket support
@@ -84,24 +80,22 @@
+ } + }
+ // CraftBukkit end + // CraftBukkit end
if (packet.protocolVersion() != SharedConstants.getCurrentVersion().getProtocolVersion()) { if (packet.protocolVersion() != SharedConstants.getCurrentVersion().getProtocolVersion()) {
- MutableComponent ichatmutablecomponent; - Component component;
+ net.kyori.adventure.text.Component adventureComponent; // Paper - Fix hex colors not working in some kick messages
- if (packet.protocolVersion() < 754) { - if (packet.protocolVersion() < 754) {
- ichatmutablecomponent = Component.translatable("multiplayer.disconnect.outdated_client", SharedConstants.getCurrentVersion().getName()); - component = Component.translatable("multiplayer.disconnect.outdated_client", SharedConstants.getCurrentVersion().getName());
+ net.kyori.adventure.text.Component adventureComponent; // Paper - Fix hex colors not working in some kick messages
+ if (packet.protocolVersion() < SharedConstants.getCurrentVersion().getProtocolVersion()) { // Spigot - SPIGOT-7546: Handle version check correctly for outdated client message + if (packet.protocolVersion() < SharedConstants.getCurrentVersion().getProtocolVersion()) { // Spigot - SPIGOT-7546: Handle version check correctly for outdated client message
+ adventureComponent = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(java.text.MessageFormat.format(org.spigotmc.SpigotConfig.outdatedClientMessage.replaceAll("'", "''"), SharedConstants.getCurrentVersion().getName())); // Spigot // Paper - Fix hex colors not working in some kick messages + adventureComponent = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(java.text.MessageFormat.format(org.spigotmc.SpigotConfig.outdatedClientMessage.replaceAll("'", "''"), SharedConstants.getCurrentVersion().getName())); // Spigot // Paper - Fix hex colors not working in some kick messages
} else { } else {
- ichatmutablecomponent = Component.translatable("multiplayer.disconnect.incompatible", SharedConstants.getCurrentVersion().getName()); - component = Component.translatable("multiplayer.disconnect.incompatible", SharedConstants.getCurrentVersion().getName());
+ adventureComponent = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(java.text.MessageFormat.format(org.spigotmc.SpigotConfig.outdatedServerMessage.replaceAll("'", "''"), SharedConstants.getCurrentVersion().getName())); // Spigot // Paper - Fix hex colors not working in some kick messages + adventureComponent = net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(java.text.MessageFormat.format(org.spigotmc.SpigotConfig.outdatedServerMessage.replaceAll("'", "''"), SharedConstants.getCurrentVersion().getName())); // Spigot // Paper - Fix hex colors not working in some kick messages
} }
+ Component component = io.papermc.paper.adventure.PaperAdventure.asVanilla(adventureComponent); // Paper - Fix hex colors not working in some kick messages
+ Component ichatmutablecomponent = io.papermc.paper.adventure.PaperAdventure.asVanilla(adventureComponent); // Paper - Fix hex colors not working in some kick messages this.connection.send(new ClientboundLoginDisconnectPacket(component));
+ this.connection.disconnect(component);
this.connection.send(new ClientboundLoginDisconnectPacket(ichatmutablecomponent));
this.connection.disconnect((Component) ichatmutablecomponent);
} else { } else {
this.connection.setupInboundProtocol(LoginProtocols.SERVERBOUND, new ServerLoginPacketListenerImpl(this.server, this.connection, transfer)); this.connection.setupInboundProtocol(LoginProtocols.SERVERBOUND, new ServerLoginPacketListenerImpl(this.server, this.connection, transferred));
+ // Paper start - PlayerHandshakeEvent + // Paper start - PlayerHandshakeEvent
+ boolean proxyLogicEnabled = org.spigotmc.SpigotConfig.bungee; + boolean proxyLogicEnabled = org.spigotmc.SpigotConfig.bungee;
+ boolean handledByEvent = false; + boolean handledByEvent = false;
@@ -146,24 +140,21 @@
+ this.connection.address = new java.net.InetSocketAddress(split[1], socketAddress instanceof java.net.InetSocketAddress ? ((java.net.InetSocketAddress) socketAddress).getPort() : 0); + this.connection.address = new java.net.InetSocketAddress(split[1], socketAddress instanceof java.net.InetSocketAddress ? ((java.net.InetSocketAddress) socketAddress).getPort() : 0);
+ // Paper end - Unix domain socket support + // Paper end - Unix domain socket support
+ this.connection.spoofedUUID = com.mojang.util.UndashedUuid.fromStringLenient( split[2] ); + this.connection.spoofedUUID = com.mojang.util.UndashedUuid.fromStringLenient( split[2] );
+ } else + } else {
+ {
+ Component chatmessage = Component.literal("If you wish to use IP forwarding, please enable it in your BungeeCord config as well!"); + Component chatmessage = Component.literal("If you wish to use IP forwarding, please enable it in your BungeeCord config as well!");
+ this.connection.send(new ClientboundLoginDisconnectPacket(chatmessage)); + this.connection.send(new ClientboundLoginDisconnectPacket(chatmessage));
+ this.connection.disconnect(chatmessage); + this.connection.disconnect(chatmessage);
+ return; + return;
+ } + }
+ if ( split.length == 4 ) + if (split.length == 4) {
+ {
+ this.connection.spoofedProfile = ServerHandshakePacketListenerImpl.gson.fromJson(split[3], com.mojang.authlib.properties.Property[].class); + this.connection.spoofedProfile = ServerHandshakePacketListenerImpl.gson.fromJson(split[3], com.mojang.authlib.properties.Property[].class);
+ } + }
+ } else if ((split.length == 3 || split.length == 4) && (ServerHandshakePacketListenerImpl.HOST_PATTERN.matcher(split[1]).matches())) { + } else if ((split.length == 3 || split.length == 4) && (ServerHandshakePacketListenerImpl.HOST_PATTERN.matcher(split[1]).matches())) {
+ Component chatmessage = Component.literal("Unknown data in login hostname, did you forget to enable BungeeCord in spigot.yml?"); + Component chatmessage = Component.literal("Unknown data in login hostname, did you forget to enable BungeeCord in spigot.yml?");
+ this.connection.send(new ClientboundLoginDisconnectPacket(chatmessage)); + this.connection.send(new ClientboundLoginDisconnectPacket(chatmessage));
+ this.connection.disconnect(chatmessage); + this.connection.disconnect(chatmessage);
+ return;
+ } + }
+ // Spigot End + // Spigot End
} }
} }

View File

@@ -1,25 +1,10 @@
--- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java --- a/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java +++ b/net/minecraft/server/network/ServerLoginPacketListenerImpl.java
@@ -20,6 +20,7 @@ @@ -43,10 +_,19 @@
import net.minecraft.DefaultUncaughtExceptionHandler;
import net.minecraft.core.UUIDUtil;
import net.minecraft.network.Connection;
+import net.minecraft.network.ConnectionProtocol;
import net.minecraft.network.DisconnectionDetails;
import net.minecraft.network.PacketSendListener;
import net.minecraft.network.TickablePacketListener;
@@ -36,6 +37,7 @@
import net.minecraft.network.protocol.login.ServerboundKeyPacket;
import net.minecraft.network.protocol.login.ServerboundLoginAcknowledgedPacket;
import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.players.PlayerList;
import net.minecraft.util.Crypt;
import net.minecraft.util.CryptException;
@@ -43,11 +45,38 @@
import net.minecraft.util.StringUtil; import net.minecraft.util.StringUtil;
import org.apache.commons.lang3.Validate; import org.apache.commons.lang3.Validate;
import org.slf4j.Logger; import org.slf4j.Logger;
+// CraftBukkit start
+import net.minecraft.network.protocol.Packet; +import net.minecraft.network.protocol.Packet;
+import net.minecraft.network.protocol.PacketUtils; +import net.minecraft.network.protocol.PacketUtils;
+import org.bukkit.craftbukkit.entity.CraftPlayer; +import org.bukkit.craftbukkit.entity.CraftPlayer;
@@ -29,15 +14,40 @@
-public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, TickablePacketListener { -public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, TickablePacketListener {
+public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, TickablePacketListener, CraftPlayer.TransferCookieConnection { +public class ServerLoginPacketListenerImpl implements ServerLoginPacketListener, TickablePacketListener, CraftPlayer.TransferCookieConnection {
+ // CraftBukkit end
private static final AtomicInteger UNIQUE_THREAD_ID = new AtomicInteger(0);
static final Logger LOGGER = LogUtils.getLogger();
+ private static final java.util.concurrent.ExecutorService authenticatorPool = java.util.concurrent.Executors.newCachedThreadPool(new com.google.common.util.concurrent.ThreadFactoryBuilder().setNameFormat("User Authenticator #%d").setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER)).build()); // Paper - Cache authenticator threads
private static final int MAX_TICKS_BEFORE_LOGIN = 600;
private final byte[] challenge;
final MinecraftServer server;
@@ -56,9 +_,12 @@
@Nullable
String requestedUsername;
@Nullable
- private GameProfile authenticatedProfile;
+ public GameProfile authenticatedProfile; // Paper - public
private final String serverId = "";
private final boolean transferred;
+ private net.minecraft.server.level.ServerPlayer player; // CraftBukkit
+ public boolean iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation = false; // Paper - username validation overriding
+ private int velocityLoginMessageId = -1; // Paper - Add Velocity IP Forwarding Support
public ServerLoginPacketListenerImpl(MinecraftServer server, Connection connection, boolean transferred) {
this.server = server;
@@ -67,11 +_,48 @@
this.transferred = transferred;
}
+ // CraftBukkit start
+ @Override + @Override
+ public boolean isTransferred() { + public boolean isTransferred() {
+ return this.transferred; + return this.transferred;
+ } + }
+ +
+ @Override + @Override
+ public ConnectionProtocol getProtocol() { + public net.minecraft.network.ConnectionProtocol getProtocol() {
+ return ConnectionProtocol.LOGIN; + return net.minecraft.network.ConnectionProtocol.LOGIN;
+ } + }
+ +
+ @Override + @Override
@@ -50,81 +60,61 @@
+ this.disconnect(reason); + this.disconnect(reason);
+ } + }
+ // CraftBukkit end + // CraftBukkit end
private static final AtomicInteger UNIQUE_THREAD_ID = new AtomicInteger(0); +
static final Logger LOGGER = LogUtils.getLogger();
+ private static final java.util.concurrent.ExecutorService authenticatorPool = java.util.concurrent.Executors.newCachedThreadPool(new com.google.common.util.concurrent.ThreadFactoryBuilder().setNameFormat("User Authenticator #%d").setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER)).build()); // Paper - Cache authenticator threads
private static final int MAX_TICKS_BEFORE_LOGIN = 600;
private final byte[] challenge;
final MinecraftServer server;
@@ -57,9 +86,12 @@
@Nullable
String requestedUsername;
@Nullable
- private GameProfile authenticatedProfile;
+ public GameProfile authenticatedProfile; // Paper - public
private final String serverId;
private final boolean transferred;
+ private ServerPlayer player; // CraftBukkit
+ public boolean iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation = false; // Paper - username validation overriding
+ private int velocityLoginMessageId = -1; // Paper - Add Velocity IP Forwarding Support
public ServerLoginPacketListenerImpl(MinecraftServer server, Connection connection, boolean transferred) {
this.state = ServerLoginPacketListenerImpl.State.HELLO;
@@ -72,10 +104,24 @@
@Override @Override
public void tick() { public void tick() {
+ // Paper start - Do not allow logins while the server is shutting down + // Paper start - Do not allow logins while the server is shutting down
+ if (!MinecraftServer.getServer().isRunning()) { + if (!this.server.isRunning()) {
+ this.disconnect(org.bukkit.craftbukkit.util.CraftChatMessage.fromString(org.spigotmc.SpigotConfig.restartMessage)[0]); + this.disconnect(org.bukkit.craftbukkit.util.CraftChatMessage.fromString(org.spigotmc.SpigotConfig.restartMessage)[0]);
+ return; + return;
+ } + }
+ // Paper end - Do not allow logins while the server is shutting down + // Paper end - Do not allow logins while the server is shutting down
+
if (this.state == ServerLoginPacketListenerImpl.State.VERIFYING) { if (this.state == ServerLoginPacketListenerImpl.State.VERIFYING) {
+ if (this.connection.isConnected()) { // Paper - prevent logins to be processed even though disconnect was called + if (this.connection.isConnected()) { // Paper - prevent logins to be processed even though disconnect was called
this.verifyLoginAndFinishConnectionSetup((GameProfile) Objects.requireNonNull(this.authenticatedProfile)); this.verifyLoginAndFinishConnectionSetup(Objects.requireNonNull(this.authenticatedProfile));
- }
+ } // Paper - prevent logins to be processed even though disconnect was called + } // Paper - prevent logins to be processed even though disconnect was called
} + }
+
+ // CraftBukkit start + // CraftBukkit start
+ if (this.state == ServerLoginPacketListenerImpl.State.WAITING_FOR_COOKIES && !this.player.getBukkitEntity().isAwaitingCookies()) { + if (this.state == ServerLoginPacketListenerImpl.State.WAITING_FOR_COOKIES && !this.player.getBukkitEntity().isAwaitingCookies()) {
+ this.postCookies(this.authenticatedProfile); + this.postCookies(this.authenticatedProfile);
+ } + }
+ // CraftBukkit end + // CraftBukkit end
+
if (this.state == ServerLoginPacketListenerImpl.State.WAITING_FOR_DUPE_DISCONNECT && !this.isPlayerAlreadyInWorld((GameProfile) Objects.requireNonNull(this.authenticatedProfile))) {
this.finishLoginAndWaitForClient(this.authenticatedProfile);
}
@@ -86,6 +132,13 @@
if (this.state == ServerLoginPacketListenerImpl.State.WAITING_FOR_DUPE_DISCONNECT
&& !this.isPlayerAlreadyInWorld(Objects.requireNonNull(this.authenticatedProfile))) {
@@ -83,6 +_,13 @@
}
} }
+ // CraftBukkit start + // CraftBukkit start
+ @Deprecated + @Deprecated
+ public void disconnect(String s) { + public void disconnect(String reason) {
+ this.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(s))); // Paper - Fix hex colors not working in some kick messages + this.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(reason))); // Paper - Fix hex colors not working in some kick messages
+ } + }
+ // CraftBukkit end + // CraftBukkit end
+ +
@Override @Override
public boolean isAcceptingMessages() { public boolean isAcceptingMessages() {
return this.connection.isConnected(); return this.connection.isConnected();
@@ -120,7 +173,13 @@ @@ -115,7 +_,13 @@
@Override @Override
public void handleHello(ServerboundHelloPacket packet) { public void handleHello(ServerboundHelloPacket packet) {
Validate.validState(this.state == ServerLoginPacketListenerImpl.State.HELLO, "Unexpected hello packet", new Object[0]); Validate.validState(this.state == ServerLoginPacketListenerImpl.State.HELLO, "Unexpected hello packet");
- Validate.validState(StringUtil.isValidPlayerName(packet.name()), "Invalid characters in username", new Object[0]); - Validate.validState(StringUtil.isValidPlayerName(packet.name()), "Invalid characters in username");
+ // Paper start - Validate usernames + // Paper start - Validate usernames
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode() + if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.isProxyOnlineMode()
+ && io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.performUsernameValidation + && io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.performUsernameValidation
+ && !this.iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation) { + && !this.iKnowThisMayNotBeTheBestIdeaButPleaseDisableUsernameValidation) {
+ Validate.validState(StringUtil.isReasonablePlayerName(packet.name()), "Invalid characters in username", new Object[0]); + Validate.validState(StringUtil.isReasonablePlayerName(packet.name()), "Invalid characters in username");
+ } + }
+ // Paper end - Validate usernames + // Paper end - Validate usernames
this.requestedUsername = packet.name(); this.requestedUsername = packet.name();
GameProfile gameprofile = this.server.getSingleplayerProfile(); GameProfile singleplayerProfile = this.server.getSingleplayerProfile();
if (singleplayerProfile != null && this.requestedUsername.equalsIgnoreCase(singleplayerProfile.getName())) {
@@ -131,7 +190,36 @@ @@ -125,7 +_,32 @@
this.state = ServerLoginPacketListenerImpl.State.KEY; this.state = ServerLoginPacketListenerImpl.State.KEY;
this.connection.send(new ClientboundHelloPacket("", this.server.getKeyPair().getPublic().getEncoded(), this.challenge, true)); this.connection.send(new ClientboundHelloPacket("", this.server.getKeyPair().getPublic().getEncoded(), this.challenge, true));
} else { } else {
@@ -141,10 +131,7 @@
+ // Paper end - Add Velocity IP Forwarding Support + // Paper end - Add Velocity IP Forwarding Support
+ // CraftBukkit start + // CraftBukkit start
+ // Paper start - Cache authenticator threads + // Paper start - Cache authenticator threads
+ authenticatorPool.execute(new Runnable() { + authenticatorPool.execute(() -> {
+
+ @Override
+ public void run() {
+ try { + try {
+ GameProfile gameprofile = ServerLoginPacketListenerImpl.this.createOfflineProfile(ServerLoginPacketListenerImpl.this.requestedUsername); // Spigot + GameProfile gameprofile = ServerLoginPacketListenerImpl.this.createOfflineProfile(ServerLoginPacketListenerImpl.this.requestedUsername); // Spigot
+ +
@@ -155,23 +142,21 @@
+ ServerLoginPacketListenerImpl.this.disconnect("Failed to verify username!"); + ServerLoginPacketListenerImpl.this.disconnect("Failed to verify username!");
+ ServerLoginPacketListenerImpl.this.server.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + ServerLoginPacketListenerImpl.this.requestedUsername, ex); + ServerLoginPacketListenerImpl.this.server.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + ServerLoginPacketListenerImpl.this.requestedUsername, ex);
+ } + }
+ }
+ }); + });
+ // Paper end - Cache authenticator threads + // Paper end - Cache authenticator threads
+ // CraftBukkit end + // CraftBukkit end
} }
} }
@@ -144,10 +232,24 @@ }
@@ -137,9 +_,23 @@
private void verifyLoginAndFinishConnectionSetup(GameProfile profile) { private void verifyLoginAndFinishConnectionSetup(GameProfile profile) {
PlayerList playerlist = this.server.getPlayerList(); PlayerList playerList = this.server.getPlayerList();
- Component ichatbasecomponent = playerlist.canPlayerLogin(this.connection.getRemoteAddress(), profile); - Component component = playerList.canPlayerLogin(this.connection.getRemoteAddress(), profile);
- if (component != null) {
- this.disconnect(component);
+ // CraftBukkit start - fire PlayerLoginEvent + // CraftBukkit start - fire PlayerLoginEvent
+ this.player = playerlist.canPlayerLogin(this, profile); // CraftBukkit + this.player = playerList.canPlayerLogin(this, profile); // CraftBukkit
- if (ichatbasecomponent != null) {
- this.disconnect(ichatbasecomponent);
+ if (this.player != null) { + if (this.player != null) {
+ if (this.player.getBukkitEntity().isAwaitingCookies()) { + if (this.player.getBukkitEntity().isAwaitingCookies()) {
+ this.state = ServerLoginPacketListenerImpl.State.WAITING_FOR_COOKIES; + this.state = ServerLoginPacketListenerImpl.State.WAITING_FOR_COOKIES;
@@ -181,94 +166,88 @@
+ } + }
+ } + }
+ +
+ private void postCookies(GameProfile gameprofile) { + private void postCookies(GameProfile profile) {
+ PlayerList playerlist = this.server.getPlayerList(); + PlayerList playerList = this.server.getPlayerList();
+ +
+ if (this.player == null) { + if (this.player == null) {
+ // this.disconnect(ichatbasecomponent); + // this.disconnect(component);
+ // CraftBukkit end + // CraftBukkit end
} else { } else {
if (this.server.getCompressionThreshold() >= 0 && !this.connection.isMemoryConnection()) { if (this.server.getCompressionThreshold() >= 0 && !this.connection.isMemoryConnection()) {
this.connection.send(new ClientboundLoginCompressionPacket(this.server.getCompressionThreshold()), PacketSendListener.thenRun(() -> { this.connection
@@ -155,12 +257,12 @@ @@ -149,7 +_,7 @@
})); );
} }
- boolean flag = playerlist.disconnectAllPlayersWithProfile(profile); - boolean flag = playerList.disconnectAllPlayersWithProfile(profile);
+ boolean flag = playerlist.disconnectAllPlayersWithProfile(gameprofile, this.player); // CraftBukkit - add player reference + boolean flag = playerList.disconnectAllPlayersWithProfile(profile, this.player); // CraftBukkit - add player reference
if (flag) { if (flag) {
this.state = ServerLoginPacketListenerImpl.State.WAITING_FOR_DUPE_DISCONNECT; this.state = ServerLoginPacketListenerImpl.State.WAITING_FOR_DUPE_DISCONNECT;
} else { } else {
- this.finishLoginAndWaitForClient(profile); @@ -184,7 +_,8 @@
+ this.finishLoginAndWaitForClient(gameprofile); throw new IllegalStateException("Protocol error", var7);
}
} }
@@ -195,7 +297,8 @@ - Thread thread = new Thread("User Authenticator #" + UNIQUE_THREAD_ID.incrementAndGet()) {
throw new IllegalStateException("Protocol error", cryptographyexception);
}
- Thread thread = new Thread("User Authenticator #" + ServerLoginPacketListenerImpl.UNIQUE_THREAD_ID.incrementAndGet()) {
+ // Paper start - Cache authenticator threads + // Paper start - Cache authenticator threads
+ authenticatorPool.execute(new Runnable() { + authenticatorPool.execute(new Runnable() {
@Override
public void run() { public void run() {
String s1 = (String) Objects.requireNonNull(ServerLoginPacketListenerImpl.this.requestedUsername, "Player name not initialized"); String string1 = Objects.requireNonNull(ServerLoginPacketListenerImpl.this.requestedUsername, "Player name not initialized");
@@ -195,11 +_,17 @@
@@ -205,11 +308,17 @@ .hasJoinedServer(string1, string, this.getAddress());
if (profileresult != null) { if (profileResult != null) {
GameProfile gameprofile = profileresult.profile(); GameProfile gameProfile = profileResult.profile();
+ // CraftBukkit start - fire PlayerPreLoginEvent + // CraftBukkit start - fire PlayerPreLoginEvent
+ if (!ServerLoginPacketListenerImpl.this.connection.isConnected()) { + if (!ServerLoginPacketListenerImpl.this.connection.isConnected()) {
+ return; + return;
+ } + }
+ gameprofile = ServerLoginPacketListenerImpl.this.callPlayerPreLoginEvents(gameprofile); // Paper - Add more fields to AsyncPlayerPreLoginEvent + gameProfile = ServerLoginPacketListenerImpl.this.callPlayerPreLoginEvents(gameProfile); // Paper - Add more fields to AsyncPlayerPreLoginEvent
+ // CraftBukkit end + // CraftBukkit end
ServerLoginPacketListenerImpl.LOGGER.info("UUID of player {} is {}", gameprofile.getName(), gameprofile.getId()); ServerLoginPacketListenerImpl.LOGGER.info("UUID of player {} is {}", gameProfile.getName(), gameProfile.getId());
ServerLoginPacketListenerImpl.this.startClientVerification(gameprofile); ServerLoginPacketListenerImpl.this.startClientVerification(gameProfile);
} else if (ServerLoginPacketListenerImpl.this.server.isSingleplayer()) { } else if (ServerLoginPacketListenerImpl.this.server.isSingleplayer()) {
ServerLoginPacketListenerImpl.LOGGER.warn("Failed to verify username but will let them in anyway!"); ServerLoginPacketListenerImpl.LOGGER.warn("Failed to verify username but will let them in anyway!");
- ServerLoginPacketListenerImpl.this.startClientVerification(UUIDUtil.createOfflineProfile(s1)); - ServerLoginPacketListenerImpl.this.startClientVerification(UUIDUtil.createOfflineProfile(string1));
+ ServerLoginPacketListenerImpl.this.startClientVerification(ServerLoginPacketListenerImpl.this.createOfflineProfile(s1)); // Spigot + ServerLoginPacketListenerImpl.this.startClientVerification(ServerLoginPacketListenerImpl.this.createOfflineProfile(string1)); // Spigot
} else { } else {
ServerLoginPacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.unverified_username")); ServerLoginPacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.unverified_username"));
ServerLoginPacketListenerImpl.LOGGER.error("Username '{}' tried to join with an invalid session", s1); ServerLoginPacketListenerImpl.LOGGER.error("Username '{}' tried to join with an invalid session", string1);
@@ -217,11 +326,16 @@ @@ -207,11 +_,16 @@
} catch (AuthenticationUnavailableException authenticationunavailableexception) { } catch (AuthenticationUnavailableException var4) {
if (ServerLoginPacketListenerImpl.this.server.isSingleplayer()) { if (ServerLoginPacketListenerImpl.this.server.isSingleplayer()) {
ServerLoginPacketListenerImpl.LOGGER.warn("Authentication servers are down but will let them in anyway!"); ServerLoginPacketListenerImpl.LOGGER.warn("Authentication servers are down but will let them in anyway!");
- ServerLoginPacketListenerImpl.this.startClientVerification(UUIDUtil.createOfflineProfile(s1)); - ServerLoginPacketListenerImpl.this.startClientVerification(UUIDUtil.createOfflineProfile(string1));
+ ServerLoginPacketListenerImpl.this.startClientVerification(ServerLoginPacketListenerImpl.this.createOfflineProfile(s1)); // Spigot + ServerLoginPacketListenerImpl.this.startClientVerification(ServerLoginPacketListenerImpl.this.createOfflineProfile(string1)); // Spigot
} else { } else {
- ServerLoginPacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.authservers_down")); ServerLoginPacketListenerImpl.this.disconnect(Component.translatable("multiplayer.disconnect.authservers_down"));
+ ServerLoginPacketListenerImpl.this.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(io.papermc.paper.configuration.GlobalConfiguration.get().messages.kick.authenticationServersDown)); // Paper - Configurable kick message
ServerLoginPacketListenerImpl.LOGGER.error("Couldn't verify username because servers are unavailable"); ServerLoginPacketListenerImpl.LOGGER.error("Couldn't verify username because servers are unavailable");
} }
+ // CraftBukkit start - catch all exceptions + // CraftBukkit start - catch all exceptions
+ } catch (Exception exception) { + } catch (Exception ex) {
+ ServerLoginPacketListenerImpl.this.disconnect("Failed to verify username!"); + ServerLoginPacketListenerImpl.this.disconnect("Failed to verify username!");
+ ServerLoginPacketListenerImpl.this.server.server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + s1, exception); + ServerLoginPacketListenerImpl.LOGGER.warn("Exception verifying {}", string1, ex);
+ // CraftBukkit end + // CraftBukkit end
} }
} }
@@ -232,23 +346,118 @@
return ServerLoginPacketListenerImpl.this.server.getPreventProxyConnections() && socketaddress instanceof InetSocketAddress ? ((InetSocketAddress) socketaddress).getAddress() : null; @@ -222,24 +_,120 @@
? ((InetSocketAddress)remoteAddress).getAddress()
: null;
} }
- }; - };
- thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(LOGGER));
- thread.start();
- }
+ }); + });
+ // Paper end - Cache authenticator threads + // Paper end - Cache authenticator threads
+ } + }
+
- thread.setUncaughtExceptionHandler(new DefaultUncaughtExceptionHandler(ServerLoginPacketListenerImpl.LOGGER));
- thread.start();
+ // CraftBukkit start + // CraftBukkit start
+ private GameProfile callPlayerPreLoginEvents(GameProfile gameprofile) throws Exception { // Paper - Add more fields to AsyncPlayerPreLoginEvent + private GameProfile callPlayerPreLoginEvents(GameProfile gameprofile) throws Exception { // Paper - Add more fields to AsyncPlayerPreLoginEvent
+ // Paper start - Add Velocity IP Forwarding Support + // Paper start - Add Velocity IP Forwarding Support
+ if (ServerLoginPacketListenerImpl.this.velocityLoginMessageId == -1 && io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled) { + if (ServerLoginPacketListenerImpl.this.velocityLoginMessageId == -1 && io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled) {
+ disconnect("This server requires you to connect with Velocity."); + this.disconnect("This server requires you to connect with Velocity.");
+ return gameprofile; + return gameprofile;
+ } + }
+ // Paper end - Add Velocity IP Forwarding Support + // Paper end - Add Velocity IP Forwarding Support
@@ -294,7 +273,7 @@
+ if (asyncEvent.getResult() != PlayerPreLoginEvent.Result.ALLOWED) { + if (asyncEvent.getResult() != PlayerPreLoginEvent.Result.ALLOWED) {
+ event.disallow(asyncEvent.getResult(), asyncEvent.kickMessage()); // Paper - Adventure + event.disallow(asyncEvent.getResult(), asyncEvent.kickMessage()); // Paper - Adventure
+ } + }
+ Waitable<PlayerPreLoginEvent.Result> waitable = new Waitable<PlayerPreLoginEvent.Result>() { + Waitable<PlayerPreLoginEvent.Result> waitable = new Waitable<>() {
+ @Override + @Override
+ protected PlayerPreLoginEvent.Result evaluate() { + protected PlayerPreLoginEvent.Result evaluate() {
+ server.getPluginManager().callEvent(event); + server.getPluginManager().callEvent(event);
@@ -312,11 +291,12 @@
+ } + }
+ } + }
+ return gameprofile; // Paper - Add more fields to AsyncPlayerPreLoginEvent + return gameprofile; // Paper - Add more fields to AsyncPlayerPreLoginEvent
} + }
+ // CraftBukkit end + // CraftBukkit end
@Override @Override
public void handleCustomQueryPacket(ServerboundCustomQueryAnswerPacket packet) { public void handleCustomQueryPacket(ServerboundCustomQueryAnswerPacket packet) {
- this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY);
+ // Paper start - Add Velocity IP Forwarding Support + // Paper start - Add Velocity IP Forwarding Support
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled && packet.transactionId() == this.velocityLoginMessageId) { + if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.velocity.enabled && packet.transactionId() == this.velocityLoginMessageId) {
+ ServerboundCustomQueryAnswerPacket.QueryAnswerPayload payload = (ServerboundCustomQueryAnswerPacket.QueryAnswerPayload)packet.payload(); + ServerboundCustomQueryAnswerPacket.QueryAnswerPayload payload = (ServerboundCustomQueryAnswerPacket.QueryAnswerPayload)packet.payload();
@@ -326,7 +306,6 @@
+ } + }
+ +
+ net.minecraft.network.FriendlyByteBuf buf = payload.buffer; + net.minecraft.network.FriendlyByteBuf buf = payload.buffer;
+
+ if (!com.destroystokyo.paper.proxy.VelocityProxy.checkIntegrity(buf)) { + if (!com.destroystokyo.paper.proxy.VelocityProxy.checkIntegrity(buf)) {
+ this.disconnect("Unable to verify player details"); + this.disconnect("Unable to verify player details");
+ return; + return;
@@ -362,21 +341,24 @@
+ return; + return;
+ } + }
+ // Paper end - Add Velocity IP Forwarding Support + // Paper end - Add Velocity IP Forwarding Support
this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY); + // this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY);
} }
@Override @Override
public void handleLoginAcknowledgement(ServerboundLoginAcknowledgedPacket packet) { public void handleLoginAcknowledgement(ServerboundLoginAcknowledgedPacket packet) {
+ PacketUtils.ensureRunningOnSameThread(packet, this, this.server); // CraftBukkit + PacketUtils.ensureRunningOnSameThread(packet, this, this.server); // CraftBukkit
Validate.validState(this.state == ServerLoginPacketListenerImpl.State.PROTOCOL_SWITCHING, "Unexpected login acknowledgement packet", new Object[0]); Validate.validState(this.state == ServerLoginPacketListenerImpl.State.PROTOCOL_SWITCHING, "Unexpected login acknowledgement packet");
this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND); this.connection.setupOutboundProtocol(ConfigurationProtocols.CLIENTBOUND);
CommonListenerCookie commonlistenercookie = CommonListenerCookie.createInitial((GameProfile) Objects.requireNonNull(this.authenticatedProfile), this.transferred); CommonListenerCookie commonListenerCookie = CommonListenerCookie.createInitial(Objects.requireNonNull(this.authenticatedProfile), this.transferred);
- ServerConfigurationPacketListenerImpl serverconfigurationpacketlistenerimpl = new ServerConfigurationPacketListenerImpl(this.server, this.connection, commonlistenercookie); ServerConfigurationPacketListenerImpl serverConfigurationPacketListenerImpl = new ServerConfigurationPacketListenerImpl(
+ ServerConfigurationPacketListenerImpl serverconfigurationpacketlistenerimpl = new ServerConfigurationPacketListenerImpl(this.server, this.connection, commonlistenercookie, this.player); // CraftBukkit - this.server, this.connection, commonListenerCookie
+ this.server, this.connection, commonListenerCookie, this.player // CraftBukkit
this.connection.setupInboundProtocol(ConfigurationProtocols.SERVERBOUND, serverconfigurationpacketlistenerimpl); );
serverconfigurationpacketlistenerimpl.startConfiguration(); +
@@ -264,12 +473,44 @@ this.connection.setupInboundProtocol(ConfigurationProtocols.SERVERBOUND, serverConfigurationPacketListenerImpl);
serverConfigurationPacketListenerImpl.startConfiguration();
this.state = ServerLoginPacketListenerImpl.State.ACCEPTED;
@@ -252,8 +_,36 @@
@Override @Override
public void handleCookieResponse(ServerboundCookieResponsePacket packet) { public void handleCookieResponse(ServerboundCookieResponsePacket packet) {
@@ -388,24 +370,20 @@
+ // CraftBukkit end + // CraftBukkit end
this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY); this.disconnect(ServerCommonPacketListenerImpl.DISCONNECT_UNEXPECTED_QUERY);
} }
+
+ // Spigot start + // Spigot start
+ protected GameProfile createOfflineProfile(String s) { + protected GameProfile createOfflineProfile(String s) {
+ java.util.UUID uuid; + java.util.UUID uuid;
+ if ( this.connection.spoofedUUID != null ) + if (this.connection.spoofedUUID != null) {
+ {
+ uuid = this.connection.spoofedUUID; + uuid = this.connection.spoofedUUID;
+ } else + } else {
+ {
+ uuid = UUIDUtil.createOfflinePlayerUUID(s); + uuid = UUIDUtil.createOfflinePlayerUUID(s);
+ } + }
+ +
+ GameProfile gameProfile = new GameProfile(uuid, s); + GameProfile gameProfile = new GameProfile(uuid, s);
+ +
+ if (this.connection.spoofedProfile != null) + if (this.connection.spoofedProfile != null) {
+ { + for (com.mojang.authlib.properties.Property property : this.connection.spoofedProfile) {
+ for ( com.mojang.authlib.properties.Property property : this.connection.spoofedProfile )
+ {
+ if (!ServerHandshakePacketListenerImpl.PROP_PATTERN.matcher(property.name()).matches()) continue; + if (!ServerHandshakePacketListenerImpl.PROP_PATTERN.matcher(property.name()).matches()) continue;
+ gameProfile.getProperties().put(property.name(), property); + gameProfile.getProperties().put(property.name(), property);
+ } + }
@@ -414,11 +392,14 @@
+ return gameProfile; + return gameProfile;
+ } + }
+ // Spigot end + // Spigot end
+
public static enum State { public static enum State {
HELLO,
- HELLO, KEY, AUTHENTICATING, NEGOTIATING, VERIFYING, WAITING_FOR_DUPE_DISCONNECT, PROTOCOL_SWITCHING, ACCEPTED; @@ -261,6 +_,7 @@
+ HELLO, KEY, AUTHENTICATING, NEGOTIATING, VERIFYING, WAITING_FOR_COOKIES, WAITING_FOR_DUPE_DISCONNECT, PROTOCOL_SWITCHING, ACCEPTED; // CraftBukkit AUTHENTICATING,
NEGOTIATING,
private State() {} VERIFYING,
} + WAITING_FOR_COOKIES, // CraftBukkit
WAITING_FOR_DUPE_DISCONNECT,
PROTOCOL_SWITCHING,
ACCEPTED;

View File

@@ -0,0 +1,12 @@
--- a/net/minecraft/server/network/ServerStatusPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerStatusPacketListenerImpl.java
@@ -36,7 +_,8 @@
this.connection.disconnect(DISCONNECT_REASON);
} else {
this.hasRequestedStatus = true;
- this.connection.send(new ClientboundStatusResponsePacket(this.status));
+ // this.connection.send(new ClientboundStatusResponsePacket(this.status));
+ com.destroystokyo.paper.network.StandardPaperServerListPingEventImpl.processRequest(net.minecraft.server.MinecraftServer.getServer(), this.connection); // Paper - handle status request
}
}

View File

@@ -1,89 +0,0 @@
--- a/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerConfigurationPacketListenerImpl.java
@@ -38,6 +38,11 @@
import net.minecraft.world.flag.FeatureFlags;
import org.slf4j.Logger;
+// CraftBukkit start
+import org.bukkit.craftbukkit.CraftServerLinks;
+import org.bukkit.event.player.PlayerLinksSendEvent;
+// CraftBukkit end
+
public class ServerConfigurationPacketListenerImpl extends ServerCommonPacketListenerImpl implements ServerConfigurationPacketListener, TickablePacketListener {
private static final Logger LOGGER = LogUtils.getLogger();
@@ -50,10 +55,12 @@
@Nullable
private SynchronizeRegistriesTask synchronizeRegistriesTask;
- public ServerConfigurationPacketListenerImpl(MinecraftServer server, Connection connection, CommonListenerCookie clientData) {
- super(server, connection, clientData);
- this.gameProfile = clientData.gameProfile();
- this.clientInformation = clientData.clientInformation();
+ // CraftBukkit start
+ public ServerConfigurationPacketListenerImpl(MinecraftServer minecraftserver, Connection networkmanager, CommonListenerCookie commonlistenercookie, ServerPlayer player) {
+ super(minecraftserver, networkmanager, commonlistenercookie, player);
+ // CraftBukkit end
+ this.gameProfile = commonlistenercookie.gameProfile();
+ this.clientInformation = commonlistenercookie.clientInformation();
}
@Override
@@ -63,6 +70,10 @@
@Override
public void onDisconnect(DisconnectionDetails info) {
+ // Paper start - Debugging
+ if (net.minecraft.server.MinecraftServer.getServer().isDebugging()) {
+ ServerConfigurationPacketListenerImpl.LOGGER.info("{} lost connection: {}, while in configuration phase {}", this.gameProfile, info.reason().getString(), currentTask != null ? currentTask.type().id() : "null");
+ } else // Paper end
ServerConfigurationPacketListenerImpl.LOGGER.info("{} lost connection: {}", this.gameProfile, info.reason().getString());
super.onDisconnect(info);
}
@@ -75,6 +86,12 @@
public void startConfiguration() {
this.send(new ClientboundCustomPayloadPacket(new BrandPayload(this.server.getServerModName())));
ServerLinks serverlinks = this.server.serverLinks();
+ // CraftBukkit start
+ CraftServerLinks wrapper = new CraftServerLinks(serverlinks);
+ PlayerLinksSendEvent event = new PlayerLinksSendEvent(this.player.getBukkitEntity(), wrapper);
+ this.player.getBukkitEntity().getServer().getPluginManager().callEvent(event);
+ serverlinks = wrapper.getServerLinks();
+ // CraftBukkit end
if (!serverlinks.isEmpty()) {
this.send(new ClientboundServerLinksPacket(serverlinks.untrust()));
@@ -107,6 +124,7 @@
@Override
public void handleClientInformation(ServerboundClientInformationPacket packet) {
this.clientInformation = packet.information();
+ this.connection.channel.attr(io.papermc.paper.adventure.PaperAdventure.LOCALE_ATTRIBUTE).set(net.kyori.adventure.translation.Translator.parseLocale(packet.information().language())); // Paper
}
@Override
@@ -143,18 +161,23 @@
return;
}
- Component ichatbasecomponent = playerlist.canPlayerLogin(this.connection.getRemoteAddress(), this.gameProfile);
+ Component ichatbasecomponent = null; // CraftBukkit - login checks already completed
if (ichatbasecomponent != null) {
this.disconnect(ichatbasecomponent);
return;
}
- ServerPlayer entityplayer = playerlist.getPlayerForLogin(this.gameProfile, this.clientInformation);
+ ServerPlayer entityplayer = playerlist.getPlayerForLogin(this.gameProfile, this.clientInformation, this.player); // CraftBukkit
playerlist.placeNewPlayer(this.connection, entityplayer, this.createCookie(this.clientInformation));
} catch (Exception exception) {
ServerConfigurationPacketListenerImpl.LOGGER.error("Couldn't place player in world", exception);
+ // Paper start - Debugging
+ if (MinecraftServer.getServer().isDebugging()) {
+ exception.printStackTrace();
+ }
+ // Paper end - Debugging
this.connection.send(new ClientboundDisconnectPacket(ServerConfigurationPacketListenerImpl.DISCONNECT_REASON_INVALID_DATA));
this.connection.disconnect(ServerConfigurationPacketListenerImpl.DISCONNECT_REASON_INVALID_DATA);
}

View File

@@ -1,156 +0,0 @@
--- a/net/minecraft/server/network/ServerConnectionListener.java
+++ b/net/minecraft/server/network/ServerConnectionListener.java
@@ -52,22 +52,36 @@
private static final Logger LOGGER = LogUtils.getLogger();
public static final Supplier<NioEventLoopGroup> SERVER_EVENT_GROUP = Suppliers.memoize(() -> {
- return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Server IO #%d").setDaemon(true).build());
+ return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Server IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
});
public static final Supplier<EpollEventLoopGroup> SERVER_EPOLL_EVENT_GROUP = Suppliers.memoize(() -> {
- return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Server IO #%d").setDaemon(true).build());
+ return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Server IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
});
final MinecraftServer server;
public volatile boolean running;
private final List<ChannelFuture> channels = Collections.synchronizedList(Lists.newArrayList());
final List<Connection> connections = Collections.synchronizedList(Lists.newArrayList());
+ // Paper start - prevent blocking on adding a new connection while the server is ticking
+ private final java.util.Queue<Connection> pending = new java.util.concurrent.ConcurrentLinkedQueue<>();
+ private final void addPending() {
+ Connection connection;
+ while ((connection = pending.poll()) != null) {
+ connections.add(connection);
+ }
+ }
+ // Paper end - prevent blocking on adding a new connection while the server is ticking
public ServerConnectionListener(MinecraftServer server) {
this.server = server;
this.running = true;
}
+ // Paper start - Unix domain socket support
public void startTcpServerListener(@Nullable InetAddress address, int port) throws IOException {
+ bind(new java.net.InetSocketAddress(address, port));
+ }
+ public void bind(java.net.SocketAddress address) throws IOException {
+ // Paper end - Unix domain socket support
List list = this.channels;
synchronized (this.channels) {
@@ -75,7 +89,13 @@
EventLoopGroup eventloopgroup;
if (Epoll.isAvailable() && this.server.isEpollEnabled()) {
+ // Paper start - Unix domain socket support
+ if (address instanceof io.netty.channel.unix.DomainSocketAddress) {
+ oclass = io.netty.channel.epoll.EpollServerDomainSocketChannel.class;
+ } else {
oclass = EpollServerSocketChannel.class;
+ }
+ // Paper end - Unix domain socket support
eventloopgroup = (EventLoopGroup) ServerConnectionListener.SERVER_EPOLL_EVENT_GROUP.get();
ServerConnectionListener.LOGGER.info("Using epoll channel type");
} else {
@@ -84,6 +104,12 @@
ServerConnectionListener.LOGGER.info("Using default channel type");
}
+ // Paper start - Warn people with console access that HAProxy is in use.
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.proxyProtocol) {
+ ServerConnectionListener.LOGGER.warn("Using HAProxy, please ensure the server port is adequately firewalled.");
+ }
+ // Paper end - Warn people with console access that HAProxy is in use.
+
this.channels.add(((ServerBootstrap) ((ServerBootstrap) (new ServerBootstrap()).channel(oclass)).childHandler(new ChannelInitializer<Channel>() {
protected void initChannel(Channel channel) {
try {
@@ -100,16 +126,58 @@
Connection.configureSerialization(channelpipeline, PacketFlow.SERVERBOUND, false, (BandwidthDebugMonitor) null);
int j = ServerConnectionListener.this.server.getRateLimitPacketsPerSecond();
- Object object = j > 0 ? new RateKickingConnection(j) : new Connection(PacketFlow.SERVERBOUND);
+ Connection object = j > 0 ? new RateKickingConnection(j) : new Connection(PacketFlow.SERVERBOUND); // CraftBukkit - decompile error
- ServerConnectionListener.this.connections.add(object);
+ //ServerConnectionListener.this.connections.add(object); // Paper
+ // Paper start - Add support for Proxy Protocol
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().proxies.proxyProtocol) {
+ channel.pipeline().addAfter("timeout", "haproxy-decoder", new io.netty.handler.codec.haproxy.HAProxyMessageDecoder());
+ channel.pipeline().addAfter("haproxy-decoder", "haproxy-handler", new ChannelInboundHandlerAdapter() {
+ @Override
+ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
+ if (msg instanceof io.netty.handler.codec.haproxy.HAProxyMessage message) {
+ if (message.command() == io.netty.handler.codec.haproxy.HAProxyCommand.PROXY) {
+ String realaddress = message.sourceAddress();
+ int realport = message.sourcePort();
+
+ SocketAddress socketaddr = new java.net.InetSocketAddress(realaddress, realport);
+
+ Connection connection = (Connection) channel.pipeline().get("packet_handler");
+ connection.address = socketaddr;
+
+ // Paper start - Add API to get player's proxy address
+ final String proxyAddress = message.destinationAddress();
+ final int proxyPort = message.destinationPort();
+
+ connection.haProxyAddress = new java.net.InetSocketAddress(proxyAddress, proxyPort);
+ // Paper end - Add API to get player's proxy address
+ }
+ } else {
+ super.channelRead(ctx, msg);
+ }
+ }
+ });
+ }
+ // Paper end - Add support for proxy protocol
+ pending.add(object); // Paper - prevent blocking on adding a new connection while the server is ticking
((Connection) object).configurePacketHandler(channelpipeline);
((Connection) object).setListenerForServerboundHandshake(new ServerHandshakePacketListenerImpl(ServerConnectionListener.this.server, (Connection) object));
+ io.papermc.paper.network.ChannelInitializeListenerHolder.callListeners(channel); // Paper - Add Channel initialization listeners
}
- }).group(eventloopgroup).localAddress(address, port)).bind().syncUninterruptibly());
+ }).group(eventloopgroup).localAddress(address)).option(ChannelOption.AUTO_READ, false).bind().syncUninterruptibly()); // CraftBukkit // Paper - Unix domain socket support
}
}
+ // CraftBukkit start
+ public void acceptConnections() {
+ synchronized (this.channels) {
+ for (ChannelFuture future : this.channels) {
+ future.channel().config().setAutoRead(true);
+ }
+ }
+ }
+ // CraftBukkit end
+
public SocketAddress startMemoryChannel() {
List list = this.channels;
ChannelFuture channelfuture;
@@ -153,6 +221,14 @@
List list = this.connections;
synchronized (this.connections) {
+ // Spigot Start
+ this.addPending(); // Paper - prevent blocking on adding a new connection while the server is ticking
+ // This prevents players from 'gaming' the server, and strategically relogging to increase their position in the tick order
+ if ( org.spigotmc.SpigotConfig.playerShuffle > 0 && MinecraftServer.currentTick % org.spigotmc.SpigotConfig.playerShuffle == 0 )
+ {
+ Collections.shuffle( this.connections );
+ }
+ // Spigot End
Iterator<Connection> iterator = this.connections.iterator();
while (iterator.hasNext()) {
@@ -176,6 +252,10 @@
networkmanager.setReadOnly();
}
} else {
+ // Spigot Start
+ // Fix a race condition where a NetworkManager could be unregistered just before connection.
+ if (networkmanager.preparing) continue;
+ // Spigot End
iterator.remove();
networkmanager.handleDisconnection();
}

View File

@@ -1,137 +0,0 @@
--- a/net/minecraft/server/network/ServerStatusPacketListenerImpl.java
+++ b/net/minecraft/server/network/ServerStatusPacketListenerImpl.java
@@ -9,6 +9,19 @@
import net.minecraft.network.protocol.status.ServerStatus;
import net.minecraft.network.protocol.status.ServerStatusPacketListener;
import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket;
+// CraftBukkit start
+import com.mojang.authlib.GameProfile;
+import java.net.InetSocketAddress;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Optional;
+import net.minecraft.SharedConstants;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerPlayer;
+import org.bukkit.craftbukkit.util.CraftChatMessage;
+import org.bukkit.craftbukkit.util.CraftIconCache;
+import org.bukkit.entity.Player;
+// CraftBukkit end
public class ServerStatusPacketListenerImpl implements ServerStatusPacketListener {
@@ -36,7 +49,113 @@
this.connection.disconnect(ServerStatusPacketListenerImpl.DISCONNECT_REASON);
} else {
this.hasRequestedStatus = true;
- this.connection.send(new ClientboundStatusResponsePacket(this.status));
+ // Paper start - Replace everything
+ /*
+ // CraftBukkit start
+ // this.connection.send(new PacketStatusOutServerInfo(this.status));
+ MinecraftServer server = MinecraftServer.getServer();
+ final Object[] players = server.getPlayerList().players.toArray();
+ class ServerListPingEvent extends org.bukkit.event.server.ServerListPingEvent {
+
+ CraftIconCache icon = server.server.getServerIcon();
+
+ ServerListPingEvent() {
+ super(ServerStatusPacketListenerImpl.this.connection.hostname, ((InetSocketAddress) ServerStatusPacketListenerImpl.this.connection.getRemoteAddress()).getAddress(), server.server.motd(), server.getPlayerList().getMaxPlayers()); // Paper - Adventure
+ }
+
+ @Override
+ public void setServerIcon(org.bukkit.util.CachedServerIcon icon) {
+ if (!(icon instanceof CraftIconCache)) {
+ throw new IllegalArgumentException(icon + " was not created by " + org.bukkit.craftbukkit.CraftServer.class);
+ }
+ this.icon = (CraftIconCache) icon;
+ }
+
+ @Override
+ public Iterator<Player> iterator() throws UnsupportedOperationException {
+ return new Iterator<Player>() {
+ int i;
+ int ret = Integer.MIN_VALUE;
+ ServerPlayer player;
+
+ @Override
+ public boolean hasNext() {
+ if (this.player != null) {
+ return true;
+ }
+ final Object[] currentPlayers = players;
+ for (int length = currentPlayers.length, i = this.i; i < length; i++) {
+ final ServerPlayer player = (ServerPlayer) currentPlayers[i];
+ if (player != null) {
+ this.i = i + 1;
+ this.player = player;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Player next() {
+ if (!this.hasNext()) {
+ throw new java.util.NoSuchElementException();
+ }
+ final ServerPlayer player = this.player;
+ this.player = null;
+ this.ret = this.i - 1;
+ return player.getBukkitEntity();
+ }
+
+ @Override
+ public void remove() {
+ final Object[] currentPlayers = players;
+ final int i = this.ret;
+ if (i < 0 || currentPlayers[i] == null) {
+ throw new IllegalStateException();
+ }
+ currentPlayers[i] = null;
+ }
+ };
+ }
+ }
+
+ ServerListPingEvent event = new ServerListPingEvent();
+ server.server.getPluginManager().callEvent(event);
+
+ java.util.List<GameProfile> profiles = new java.util.ArrayList<GameProfile>(players.length);
+ for (Object player : players) {
+ if (player != null) {
+ ServerPlayer entityPlayer = ((ServerPlayer) player);
+ if (entityPlayer.allowsListing()) {
+ profiles.add(entityPlayer.getGameProfile());
+ } else {
+ profiles.add(MinecraftServer.ANONYMOUS_PLAYER_PROFILE);
+ }
+ }
+ }
+
+ // Spigot Start
+ if ( !server.hidesOnlinePlayers() && !profiles.isEmpty() )
+ {
+ java.util.Collections.shuffle( profiles ); // This sucks, its inefficient but we have no simple way of doing it differently
+ 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
+ ServerStatus.Players playerSample = new ServerStatus.Players(event.getMaxPlayers(), event.getNumPlayers(), (server.hidesOnlinePlayers()) ? Collections.emptyList() : profiles);
+
+ ServerStatus ping = new ServerStatus(
+ CraftChatMessage.fromString(event.getMotd(), true)[0],
+ Optional.of(playerSample),
+ Optional.of(new ServerStatus.Version(server.getServerModName() + " " + server.getServerVersion(), SharedConstants.getCurrentVersion().getProtocolVersion())),
+ (event.icon.value != null) ? Optional.of(new ServerStatus.Favicon(event.icon.value)) : Optional.empty(),
+ server.enforceSecureProfile()
+ );
+
+ this.connection.send(new ClientboundStatusResponsePacket(ping));
+ // CraftBukkit end
+ */
+ com.destroystokyo.paper.network.StandardPaperServerListPingEventImpl.processRequest(MinecraftServer.getServer(), this.connection);
+ // Paper end
}
}