01a13871de
Patch documentation to come Issues with the old system that are fixed now: - World generation does not scale with cpu cores effectively. - Relies on the main thread for scheduling and maintaining chunk state, dropping chunk load/generate rates at lower tps. - Unreliable prioritisation of chunk gen/load calls that block the main thread. - Shutdown logic is utterly unreliable, as it has to wait for all chunks to unload - is it guaranteed that the chunk system is in a state on shutdown that it can reliably do this? Watchdog shutdown also typically failed due to thread checks, which is now resolved. - Saving of data is not unified (i.e can save chunk data without saving entity data, poses problems for desync if shutdown is really abnormal. - Entities are not loaded with chunks. This caused quite a bit of headache for Chunk#getEntities API, but now the new chunk system loads entities with chunks so that they are ready whenever the chunk loads in. Effectively brings the behavior back to 1.16 era, but still storing entities in their own separate regionfiles. The above list is not complete. The patch documentation will complete it. New chunk system hard relies on starlight and dataconverter, and most importantly the new concurrent utilities in ConcurrentUtil. Some of the old async chunk i/o interface (i.e the old file io thread reroutes _some_ calls to the new file io thread) is kept for plugin compat reasons. It will be removed in the next major version of minecraft. The old legacy chunk system patches have been moved to the removed folder in case we need them again.
169 Zeilen
6.3 KiB
Diff
169 Zeilen
6.3 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Minecrell <minecrell@minecrell.net>
|
|
Date: Wed, 11 Oct 2017 18:22:50 +0200
|
|
Subject: [PATCH] Make legacy ping handler more reliable
|
|
|
|
The Minecraft server often fails to respond to old ("legacy") pings
|
|
from old Minecraft versions using the protocol used before the switch
|
|
to Netty in Minecraft 1.7.
|
|
|
|
Due to packet fragmentation[1], we might not have all needed bytes
|
|
available when the LegacyPingHandler is called. In this case, it will
|
|
run into an error, remove the handler and continue using the modern
|
|
protocol.
|
|
|
|
This is unlikely to happen for the first two revisions of the legacy
|
|
ping protocol (used in Minecraft 1.5.x and older) since the request
|
|
consists of only one or two bytes, but happens frequently for the
|
|
last/third revision introduced in Minecraft 1.6.
|
|
|
|
It has much larger, variable packet sizes due to the inclusion of
|
|
the virtual host (the hostname/port used to connect to the server).
|
|
|
|
The solution[2] is simple: If we find more than two matching bytes,
|
|
we buffer the remaining bytes until we have enough to fully read and
|
|
respond to the request.
|
|
|
|
[1]: https://netty.io/wiki/user-guide-for-4.x.html#wiki-h3-11
|
|
[2]: https://netty.io/wiki/user-guide-for-4.x.html#wiki-h4-13
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/network/LegacyQueryHandler.java b/src/main/java/net/minecraft/server/network/LegacyQueryHandler.java
|
|
index 56f0090bbb8513e56ca5f0ed8b5292939f25953b..e35f4121280ca81dc78121d9aa65e9b7d0c41932 100644
|
|
--- a/src/main/java/net/minecraft/server/network/LegacyQueryHandler.java
|
|
+++ b/src/main/java/net/minecraft/server/network/LegacyQueryHandler.java
|
|
@@ -17,6 +17,7 @@ public class LegacyQueryHandler extends ChannelInboundHandlerAdapter {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
public static final int FAKE_PROTOCOL_VERSION = 127;
|
|
private final ServerConnectionListener serverConnectionListener;
|
|
+ private ByteBuf buf; // Paper
|
|
|
|
public LegacyQueryHandler(ServerConnectionListener networkIo) {
|
|
this.serverConnectionListener = networkIo;
|
|
@@ -25,6 +26,16 @@ public class LegacyQueryHandler extends ChannelInboundHandlerAdapter {
|
|
public void channelRead(ChannelHandlerContext channelhandlercontext, Object object) {
|
|
ByteBuf bytebuf = (ByteBuf) object;
|
|
|
|
+ // Paper start - Make legacy ping handler more reliable
|
|
+ if (this.buf != null) {
|
|
+ try {
|
|
+ readLegacy1_6(channelhandlercontext, bytebuf);
|
|
+ } finally {
|
|
+ bytebuf.release();
|
|
+ }
|
|
+ return;
|
|
+ }
|
|
+ // Paper end
|
|
bytebuf.markReaderIndex();
|
|
boolean flag = true;
|
|
|
|
@@ -55,6 +66,10 @@ public class LegacyQueryHandler extends ChannelInboundHandlerAdapter {
|
|
this.sendFlushAndClose(channelhandlercontext, this.createReply(s));
|
|
break;
|
|
default:
|
|
+ // Paper start - Replace with improved version below
|
|
+ if (bytebuf.readUnsignedByte() != 0x01 || bytebuf.readUnsignedByte() != 0xFA) return;
|
|
+ readLegacy1_6(channelhandlercontext, bytebuf);
|
|
+ /*
|
|
boolean flag1 = bytebuf.readUnsignedByte() == 1;
|
|
|
|
flag1 &= bytebuf.readUnsignedByte() == 250;
|
|
@@ -78,6 +93,7 @@ public class LegacyQueryHandler extends ChannelInboundHandlerAdapter {
|
|
} finally {
|
|
bytebuf1.release();
|
|
}
|
|
+ */ // Paper end - Replace with improved version below
|
|
}
|
|
|
|
bytebuf.release();
|
|
@@ -95,6 +111,90 @@ public class LegacyQueryHandler extends ChannelInboundHandlerAdapter {
|
|
|
|
}
|
|
|
|
+ // Paper start
|
|
+ private static String readLegacyString(ByteBuf buf) {
|
|
+ int size = buf.readShort() * Character.BYTES;
|
|
+ if (!buf.isReadable(size)) {
|
|
+ return null;
|
|
+ }
|
|
+
|
|
+ String result = buf.toString(buf.readerIndex(), size, StandardCharsets.UTF_16BE);
|
|
+ buf.skipBytes(size); // toString doesn't increase readerIndex automatically
|
|
+ return result;
|
|
+ }
|
|
+
|
|
+ private void readLegacy1_6(ChannelHandlerContext ctx, ByteBuf part) {
|
|
+ ByteBuf buf = this.buf;
|
|
+
|
|
+ if (buf == null) {
|
|
+ this.buf = buf = ctx.alloc().buffer();
|
|
+ buf.markReaderIndex();
|
|
+ } else {
|
|
+ buf.resetReaderIndex();
|
|
+ }
|
|
+
|
|
+ buf.writeBytes(part);
|
|
+
|
|
+ if (!buf.isReadable(Short.BYTES + Short.BYTES + Byte.BYTES + Short.BYTES + Integer.BYTES)) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ String s = readLegacyString(buf);
|
|
+ if (s == null) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!s.equals("MC|PingHost")) {
|
|
+ removeHandler(ctx);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!buf.isReadable(Short.BYTES) || !buf.isReadable(buf.readShort())) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ MinecraftServer server = this.serverConnectionListener.getServer();
|
|
+ int protocolVersion = buf.readByte();
|
|
+ String host = readLegacyString(buf);
|
|
+ if (host == null) {
|
|
+ removeHandler(ctx);
|
|
+ return;
|
|
+ }
|
|
+ int port = buf.readInt();
|
|
+
|
|
+ if (buf.isReadable()) {
|
|
+ removeHandler(ctx);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ buf.release();
|
|
+ this.buf = null;
|
|
+
|
|
+ LOGGER.debug("Ping: (1.6) from {}", ctx.channel().remoteAddress());
|
|
+
|
|
+ String response = String.format("\u00a71\u0000%d\u0000%s\u0000%s\u0000%d\u0000%d",
|
|
+ Byte.MAX_VALUE, server.getServerVersion(), server.getMotd(), server.getPlayerCount(), server.getMaxPlayers());
|
|
+ this.sendFlushAndClose(ctx, this.createReply(response));
|
|
+ }
|
|
+
|
|
+ private void removeHandler(ChannelHandlerContext ctx) {
|
|
+ ByteBuf buf = this.buf;
|
|
+ this.buf = null;
|
|
+
|
|
+ buf.resetReaderIndex();
|
|
+ ctx.pipeline().remove(this);
|
|
+ ctx.fireChannelRead(buf);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public void handlerRemoved(ChannelHandlerContext ctx) {
|
|
+ if (this.buf != null) {
|
|
+ this.buf.release();
|
|
+ this.buf = null;
|
|
+ }
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
private void sendFlushAndClose(ChannelHandlerContext ctx, ByteBuf buf) {
|
|
ctx.pipeline().firstContext().writeAndFlush(buf).addListener(ChannelFutureListener.CLOSE);
|
|
}
|