aboutsummaryrefslogtreecommitdiffhomepage
path: root/paper-server/patches/unapplied/net/minecraft/network/Connection.java.patch
diff options
context:
space:
mode:
Diffstat (limited to 'paper-server/patches/unapplied/net/minecraft/network/Connection.java.patch')
-rw-r--r--paper-server/patches/unapplied/net/minecraft/network/Connection.java.patch330
1 files changed, 330 insertions, 0 deletions
diff --git a/paper-server/patches/unapplied/net/minecraft/network/Connection.java.patch b/paper-server/patches/unapplied/net/minecraft/network/Connection.java.patch
new file mode 100644
index 0000000000..f1e414f6b1
--- /dev/null
+++ b/paper-server/patches/unapplied/net/minecraft/network/Connection.java.patch
@@ -0,0 +1,330 @@
+--- a/net/minecraft/network/Connection.java
++++ b/net/minecraft/network/Connection.java
+@@ -82,13 +82,13 @@
+ marker.add(Connection.PACKET_MARKER);
+ });
+ public static final Supplier<NioEventLoopGroup> NETWORK_WORKER_GROUP = Suppliers.memoize(() -> {
+- return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).build());
++ return new NioEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Client IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
+ });
+ public static final Supplier<EpollEventLoopGroup> NETWORK_EPOLL_WORKER_GROUP = Suppliers.memoize(() -> {
+- return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Client IO #%d").setDaemon(true).build());
++ return new EpollEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Epoll Client IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
+ });
+ public static final Supplier<DefaultEventLoopGroup> LOCAL_WORKER_GROUP = Suppliers.memoize(() -> {
+- return new DefaultEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).build());
++ return new DefaultEventLoopGroup(0, (new ThreadFactoryBuilder()).setNameFormat("Netty Local Client IO #%d").setDaemon(true).setUncaughtExceptionHandler(new net.minecraft.DefaultUncaughtExceptionHandlerWithName(LOGGER)).build()); // Paper
+ });
+ private static final ProtocolInfo<ServerHandshakePacketListener> INITIAL_PROTOCOL = HandshakeProtocols.SERVERBOUND;
+ private final PacketFlow receiving;
+@@ -96,6 +96,11 @@
+ private final Queue<Consumer<Connection>> pendingActions = Queues.newConcurrentLinkedQueue();
+ public Channel channel;
+ public SocketAddress address;
++ // Spigot Start
++ public java.util.UUID spoofedUUID;
++ public com.mojang.authlib.properties.Property[] spoofedProfile;
++ public boolean preparing = true;
++ // Spigot End
+ @Nullable
+ private volatile PacketListener disconnectListener;
+ @Nullable
+@@ -114,7 +119,42 @@
+ private volatile DisconnectionDetails delayedDisconnect;
+ @Nullable
+ BandwidthDebugMonitor bandwidthDebugMonitor;
++ public String hostname = ""; // CraftBukkit - add field
++ // Paper start - NetworkClient implementation
++ public int protocolVersion;
++ public java.net.InetSocketAddress virtualHost;
++ private static boolean enableExplicitFlush = Boolean.getBoolean("paper.explicit-flush"); // Paper - Disable explicit network manager flushing
++ // Paper end
+
++ // Paper start - add utility methods
++ public final net.minecraft.server.level.ServerPlayer getPlayer() {
++ if (this.packetListener instanceof net.minecraft.server.network.ServerGamePacketListenerImpl impl) {
++ return impl.player;
++ } else if (this.packetListener instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl impl) {
++ org.bukkit.craftbukkit.entity.CraftPlayer player = impl.getCraftPlayer();
++ return player == null ? null : player.getHandle();
++ }
++ return null;
++ }
++ // Paper end - add utility methods
++ // Paper start - packet limiter
++ protected final Object PACKET_LIMIT_LOCK = new Object();
++ protected final @Nullable io.papermc.paper.util.IntervalledCounter allPacketCounts = io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.allPackets.isEnabled() ? new io.papermc.paper.util.IntervalledCounter(
++ (long)(io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.allPackets.interval() * 1.0e9)
++ ) : null;
++ protected final java.util.Map<Class<? extends net.minecraft.network.protocol.Packet<?>>, io.papermc.paper.util.IntervalledCounter> packetSpecificLimits = new java.util.HashMap<>();
++
++ private boolean stopReadingPackets;
++ private void killForPacketSpam() {
++ this.sendPacket(new ClientboundDisconnectPacket(io.papermc.paper.adventure.PaperAdventure.asVanilla(io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.kickMessage)), PacketSendListener.thenRun(() -> {
++ this.disconnect(io.papermc.paper.adventure.PaperAdventure.asVanilla(io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.kickMessage));
++ }), true);
++ this.setReadOnly();
++ this.stopReadingPackets = true;
++ }
++ // Paper end - packet limiter
++ @Nullable public SocketAddress haProxyAddress; // Paper - Add API to get player's proxy address
++
+ public Connection(PacketFlow side) {
+ this.receiving = side;
+ }
+@@ -123,6 +163,9 @@
+ super.channelActive(channelhandlercontext);
+ this.channel = channelhandlercontext.channel();
+ this.address = this.channel.remoteAddress();
++ // Spigot Start
++ this.preparing = false;
++ // Spigot End
+ if (this.delayedDisconnect != null) {
+ this.disconnect(this.delayedDisconnect);
+ }
+@@ -134,6 +177,21 @@
+ }
+
+ public void exceptionCaught(ChannelHandlerContext channelhandlercontext, Throwable throwable) {
++ // Paper start - Handle large packets disconnecting client
++ if (throwable instanceof io.netty.handler.codec.EncoderException && throwable.getCause() instanceof PacketEncoder.PacketTooLargeException packetTooLargeException) {
++ final Packet<?> packet = packetTooLargeException.getPacket();
++ if (packet.packetTooLarge(this)) {
++ ProtocolSwapHandler.handleOutboundTerminalPacket(channelhandlercontext, packet);
++ return;
++ } else if (packet.isSkippable()) {
++ Connection.LOGGER.debug("Skipping packet due to errors", throwable.getCause());
++ ProtocolSwapHandler.handleOutboundTerminalPacket(channelhandlercontext, packet);
++ return;
++ } else {
++ throwable = throwable.getCause();
++ }
++ }
++ // Paper end - Handle large packets disconnecting client
+ if (throwable instanceof SkipPacketException) {
+ Connection.LOGGER.debug("Skipping packet due to errors", throwable.getCause());
+ } else {
+@@ -141,8 +199,10 @@
+
+ this.handlingFault = true;
+ if (this.channel.isOpen()) {
++ net.minecraft.server.level.ServerPlayer player = this.getPlayer(); // Paper - Add API for quit reason
+ if (throwable instanceof TimeoutException) {
+ Connection.LOGGER.debug("Timeout", throwable);
++ if (player != null) player.quitReason = org.bukkit.event.player.PlayerQuitEvent.QuitReason.TIMED_OUT; // Paper - Add API for quit reason
+ this.disconnect((Component) Component.translatable("disconnect.timeout"));
+ } else {
+ MutableComponent ichatmutablecomponent = Component.translatable("disconnect.genericReason", "Internal Exception: " + String.valueOf(throwable));
+@@ -155,9 +215,11 @@
+ disconnectiondetails = new DisconnectionDetails(ichatmutablecomponent);
+ }
+
++ if (player != null) player.quitReason = org.bukkit.event.player.PlayerQuitEvent.QuitReason.ERRONEOUS_STATE; // Paper - Add API for quit reason
+ if (flag) {
+ Connection.LOGGER.debug("Failed to sent packet", throwable);
+- if (this.getSending() == PacketFlow.CLIENTBOUND) {
++ boolean doesDisconnectExist = this.packetListener.protocol() != ConnectionProtocol.STATUS && this.packetListener.protocol() != ConnectionProtocol.HANDSHAKING; // Paper
++ if (this.getSending() == PacketFlow.CLIENTBOUND && doesDisconnectExist) { // Paper
+ Packet<?> packet = this.sendLoginDisconnect ? new ClientboundLoginDisconnectPacket(ichatmutablecomponent) : new ClientboundDisconnectPacket(ichatmutablecomponent);
+
+ this.send((Packet) packet, PacketSendListener.thenRun(() -> {
+@@ -176,6 +238,7 @@
+
+ }
+ }
++ if (net.minecraft.server.MinecraftServer.getServer().isDebugging()) io.papermc.paper.util.TraceUtil.printStackTrace(throwable); // Spigot // Paper
+ }
+
+ protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet<?> packet) {
+@@ -185,11 +248,61 @@
+ if (packetlistener == null) {
+ throw new IllegalStateException("Received a packet before the packet listener was initialized");
+ } else {
++ // Paper start - packet limiter
++ if (this.stopReadingPackets) {
++ return;
++ }
++ if (this.allPacketCounts != null ||
++ io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.overrides.containsKey(packet.getClass())) {
++ long time = System.nanoTime();
++ synchronized (PACKET_LIMIT_LOCK) {
++ if (this.allPacketCounts != null) {
++ this.allPacketCounts.updateAndAdd(1, time);
++ if (this.allPacketCounts.getRate() >= io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.allPackets.maxPacketRate()) {
++ this.killForPacketSpam();
++ return;
++ }
++ }
++
++ for (Class<?> check = packet.getClass(); check != Object.class; check = check.getSuperclass()) {
++ io.papermc.paper.configuration.GlobalConfiguration.PacketLimiter.PacketLimit packetSpecificLimit =
++ io.papermc.paper.configuration.GlobalConfiguration.get().packetLimiter.overrides.get(check);
++ if (packetSpecificLimit == null || !packetSpecificLimit.isEnabled()) {
++ continue;
++ }
++ io.papermc.paper.util.IntervalledCounter counter = this.packetSpecificLimits.computeIfAbsent((Class)check, (clazz) -> {
++ return new io.papermc.paper.util.IntervalledCounter((long)(packetSpecificLimit.interval() * 1.0e9));
++ });
++ counter.updateAndAdd(1, time);
++ if (counter.getRate() >= packetSpecificLimit.maxPacketRate()) {
++ switch (packetSpecificLimit.action()) {
++ case DROP:
++ return;
++ case KICK:
++ String deobfedPacketName = io.papermc.paper.util.ObfHelper.INSTANCE.deobfClassName(check.getName());
++
++ String playerName;
++ if (this.packetListener instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl impl) {
++ playerName = impl.getOwner().getName();
++ } else {
++ playerName = this.getLoggableAddress(net.minecraft.server.MinecraftServer.getServer().logIPs());
++ }
++
++ Connection.LOGGER.warn("{} kicked for packet spamming: {}", playerName, deobfedPacketName.substring(deobfedPacketName.lastIndexOf(".") + 1));
++ this.killForPacketSpam();
++ return;
++ }
++ }
++ }
++ }
++ }
++ // Paper end - packet limiter
+ if (packetlistener.shouldHandleMessage(packet)) {
+ try {
+ Connection.genericsFtw(packet, packetlistener);
+ } catch (RunningOnDifferentThreadException cancelledpackethandleexception) {
+ ;
++ } catch (io.papermc.paper.util.ServerStopRejectedExecutionException ignored) { // Paper - do not prematurely disconnect players on stop
+ } catch (RejectedExecutionException rejectedexecutionexception) {
+ this.disconnect((Component) Component.translatable("multiplayer.disconnect.server_shutdown"));
+ } catch (ClassCastException classcastexception) {
+@@ -205,7 +318,7 @@
+ }
+
+ private static <T extends PacketListener> void genericsFtw(Packet<T> packet, PacketListener listener) {
+- packet.handle(listener);
++ packet.handle((T) listener); // CraftBukkit - decompile error
+ }
+
+ private void validateListener(ProtocolInfo<?> state, PacketListener listener) {
+@@ -418,12 +531,26 @@
+ }
+ }
+
++ private static final int MAX_PER_TICK = io.papermc.paper.configuration.GlobalConfiguration.get().misc.maxJoinsPerTick; // Paper - Buffer joins to world
++ private static int joinAttemptsThisTick; // Paper - Buffer joins to world
++ private static int currTick; // Paper - Buffer joins to world
+ public void tick() {
+ this.flushQueue();
++ // Paper start - Buffer joins to world
++ if (Connection.currTick != net.minecraft.server.MinecraftServer.currentTick) {
++ Connection.currTick = net.minecraft.server.MinecraftServer.currentTick;
++ Connection.joinAttemptsThisTick = 0;
++ }
++ // Paper end - Buffer joins to world
+ PacketListener packetlistener = this.packetListener;
+
+ if (packetlistener instanceof TickablePacketListener tickablepacketlistener) {
++ // Paper start - Buffer joins to world
++ if (!(this.packetListener instanceof net.minecraft.server.network.ServerLoginPacketListenerImpl loginPacketListener)
++ || loginPacketListener.state != net.minecraft.server.network.ServerLoginPacketListenerImpl.State.VERIFYING
++ || Connection.joinAttemptsThisTick++ < MAX_PER_TICK) {
+ tickablepacketlistener.tick();
++ } // Paper end - Buffer joins to world
+ }
+
+ if (!this.isConnected() && !this.disconnectionHandled) {
+@@ -431,7 +558,7 @@
+ }
+
+ if (this.channel != null) {
+- this.channel.flush();
++ if (enableExplicitFlush) this.channel.eventLoop().execute(() -> this.channel.flush()); // Paper - Disable explicit network manager flushing; we don't need to explicit flush here, but allow opt in incase issues are found to a better version
+ }
+
+ if (this.tickCount++ % 20 == 0) {
+@@ -464,12 +591,15 @@
+ }
+
+ public void disconnect(DisconnectionDetails disconnectionInfo) {
++ // Spigot Start
++ this.preparing = false;
++ // Spigot End
+ if (this.channel == null) {
+ this.delayedDisconnect = disconnectionInfo;
+ }
+
+ if (this.isConnected()) {
+- this.channel.close().awaitUninterruptibly();
++ this.channel.close(); // We can't wait as this may be called from an event loop.
+ this.disconnectionDetails = disconnectionInfo;
+ }
+
+@@ -537,7 +667,7 @@
+ }
+
+ public void configurePacketHandler(ChannelPipeline pipeline) {
+- pipeline.addLast("hackfix", new ChannelOutboundHandlerAdapter(this) {
++ pipeline.addLast("hackfix", new ChannelOutboundHandlerAdapter() { // CraftBukkit - decompile error
+ public void write(ChannelHandlerContext channelhandlercontext, Object object, ChannelPromise channelpromise) throws Exception {
+ super.write(channelhandlercontext, object, channelpromise);
+ }
+@@ -613,6 +743,14 @@
+
+ }
+
++ // Paper start - add proper async disconnect
++ public void enableAutoRead() {
++ if (this.channel != null) {
++ this.channel.config().setAutoRead(true);
++ }
++ }
++ // Paper end - add proper async disconnect
++
+ public void setupCompression(int compressionThreshold, boolean rejectsBadPackets) {
+ if (compressionThreshold >= 0) {
+ ChannelHandler channelhandler = this.channel.pipeline().get("decompress");
+@@ -633,6 +771,7 @@
+ } else {
+ this.channel.pipeline().addAfter("prepender", "compress", new CompressionEncoder(compressionThreshold));
+ }
++ this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_THRESHOLD_SET); // Paper - Add Channel initialization listeners
+ } else {
+ if (this.channel.pipeline().get("decompress") instanceof CompressionDecoder) {
+ this.channel.pipeline().remove("decompress");
+@@ -641,6 +780,7 @@
+ if (this.channel.pipeline().get("compress") instanceof CompressionEncoder) {
+ this.channel.pipeline().remove("compress");
+ }
++ this.channel.pipeline().fireUserEventTriggered(io.papermc.paper.network.ConnectionEvent.COMPRESSION_DISABLED); // Paper - Add Channel initialization listeners
+ }
+
+ }
+@@ -661,6 +801,27 @@
+
+ packetlistener1.onDisconnect(disconnectiondetails);
+ }
++ this.pendingActions.clear(); // Free up packet queue.
++ // Paper start - Add PlayerConnectionCloseEvent
++ final PacketListener packetListener = this.getPacketListener();
++ if (packetListener instanceof net.minecraft.server.network.ServerCommonPacketListenerImpl commonPacketListener) {
++ /* Player was logged in, either game listener or configuration listener */
++ final com.mojang.authlib.GameProfile profile = commonPacketListener.getOwner();
++ new com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent(profile.getId(),
++ profile.getName(), ((InetSocketAddress) this.address).getAddress(), false).callEvent();
++ } else if (packetListener instanceof net.minecraft.server.network.ServerLoginPacketListenerImpl loginListener) {
++ /* Player is login stage */
++ switch (loginListener.state) {
++ case VERIFYING:
++ case WAITING_FOR_DUPE_DISCONNECT:
++ case PROTOCOL_SWITCHING:
++ case ACCEPTED:
++ final com.mojang.authlib.GameProfile profile = loginListener.authenticatedProfile; /* Should be non-null at this stage */
++ new com.destroystokyo.paper.event.player.PlayerConnectionCloseEvent(profile.getId(), profile.getName(),
++ ((InetSocketAddress) this.address).getAddress(), false).callEvent();
++ }
++ }
++ // Paper end - Add PlayerConnectionCloseEvent
+
+ }
+ }