From c3da24e911229cfcf8530507d75c719ce463b3eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=84=97=E3=84=A0=CB=8B=20=E3=84=91=E3=84=A7=CB=8A?= Date: Sun, 5 Apr 2020 14:59:10 +0800 Subject: [PATCH] Optimizations for network diff --git a/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java b/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java index a85466bc7e..324af1a12a 100644 --- a/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java +++ b/src/main/java/com/destroystokyo/paper/network/StandardPaperServerListPingEventImpl.java @@ -106,7 +106,7 @@ public final class StandardPaperServerListPingEventImpl extends PaperServerListP } // Send response - networkManager.sendPacket(new PacketStatusOutServerInfo(ping)); + networkManager.sendPacketAsync(new PacketStatusOutServerInfo(ping)); // Akarin - Async Sending packets } } diff --git a/src/main/java/net/minecraft/server/HandshakeListener.java b/src/main/java/net/minecraft/server/HandshakeListener.java index 0532f975b7..e7be1074b1 100644 --- a/src/main/java/net/minecraft/server/HandshakeListener.java +++ b/src/main/java/net/minecraft/server/HandshakeListener.java @@ -39,7 +39,7 @@ public class HandshakeListener implements PacketHandshakingInListener { if (throttleTracker.containsKey(address) && !"127.0.0.1".equals(address.getHostAddress()) && currentTime - throttleTracker.get(address) < connectionThrottle) { throttleTracker.put(address, currentTime); chatmessage = new ChatMessage(com.destroystokyo.paper.PaperConfig.connectionThrottleKickMessage); // Paper - Configurable connection throttle kick message - this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage)); + this.b.sendPacketAsync(new PacketLoginOutDisconnect(chatmessage)); // Akarin - Async Sending packets this.b.close(chatmessage); return; } @@ -66,11 +66,11 @@ public class HandshakeListener implements PacketHandshakingInListener { if (packethandshakinginsetprotocol.c() > SharedConstants.getGameVersion().getProtocolVersion()) { chatmessage = new ChatMessage( java.text.MessageFormat.format( org.spigotmc.SpigotConfig.outdatedServerMessage.replaceAll("'", "''"), SharedConstants.getGameVersion().getName() ) ); // Spigot - this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage)); + this.b.sendPacketAsync(new PacketLoginOutDisconnect(chatmessage)); this.b.close(chatmessage); } else if (packethandshakinginsetprotocol.c() < SharedConstants.getGameVersion().getProtocolVersion()) { chatmessage = new ChatMessage( java.text.MessageFormat.format( org.spigotmc.SpigotConfig.outdatedClientMessage.replaceAll("'", "''"), SharedConstants.getGameVersion().getName() ) ); // Spigot - this.b.sendPacket(new PacketLoginOutDisconnect(chatmessage)); + this.b.sendPacketAsync(new PacketLoginOutDisconnect(chatmessage)); this.b.close(chatmessage); } else { this.b.setPacketListener(new LoginListener(this.a, this.b)); diff --git a/src/main/java/net/minecraft/server/LoginListener.java b/src/main/java/net/minecraft/server/LoginListener.java index f1222fcb2b..522cfed4cb 100644 --- a/src/main/java/net/minecraft/server/LoginListener.java +++ b/src/main/java/net/minecraft/server/LoginListener.java @@ -111,6 +111,19 @@ public class LoginListener implements PacketLoginInListener { } + // Akarin Start - Async Sending packets + public void disconnectAsync(IChatBaseComponent ichatbasecomponent) { + try { + LoginListener.LOGGER.info("Disconnecting {}: {}", this.d(), ichatbasecomponent.getString()); + this.networkManager.sendPacketAsync(new PacketLoginOutDisconnect(ichatbasecomponent)); + this.networkManager.close(ichatbasecomponent); + } catch (Exception exception) { + LoginListener.LOGGER.error("Error whilst disconnecting player", exception); + } + + } + // Akarin End - Async Sending packets + // Paper start - Cache authenticator threads private static final AtomicInteger threadId = new AtomicInteger(0); private static final java.util.concurrent.ExecutorService authenticatorPool = java.util.concurrent.Executors.newCachedThreadPool( @@ -192,7 +205,7 @@ public class LoginListener implements PacketLoginInListener { this.i = packetlogininstart.b(); if (this.server.getOnlineMode() && !this.networkManager.isLocal()) { this.g = LoginListener.EnumProtocolState.KEY; - this.networkManager.sendPacket(new PacketLoginOutEncryptionBegin("", this.server.getKeyPair().getPublic(), this.e)); + this.networkManager.sendPacketAsync(new PacketLoginOutEncryptionBegin("", this.server.getKeyPair().getPublic(), this.e)); // Akarin - Async Sending packets } else { // Paper start - Velocity support if (com.destroystokyo.paper.PaperConfig.velocitySupport) { @@ -254,7 +267,7 @@ public class LoginListener implements PacketLoginInListener { LoginListener.this.i = LoginListener.this.a(gameprofile); LoginListener.this.g = LoginListener.EnumProtocolState.READY_TO_ACCEPT; } else { - LoginListener.this.disconnect(new ChatMessage("multiplayer.disconnect.unverified_username", new Object[0])); + LoginListener.this.disconnectAsync(new ChatMessage("multiplayer.disconnect.unverified_username", new Object[0])); // Akarin - Async Sending packets LoginListener.LOGGER.error("Username '{}' tried to join with an invalid session", gameprofile.getName()); } } catch (AuthenticationUnavailableException authenticationunavailableexception) { @@ -267,7 +280,7 @@ public class LoginListener implements PacketLoginInListener { if (com.destroystokyo.paper.PaperConfig.authenticationServersDownKickMessage != null) { LoginListener.this.disconnect(new ChatComponentText(com.destroystokyo.paper.PaperConfig.authenticationServersDownKickMessage)); } else // Paper end - LoginListener.this.disconnect(new ChatMessage("multiplayer.disconnect.authservers_down", new Object[0])); + LoginListener.this.disconnectAsync(new ChatMessage("multiplayer.disconnect.authservers_down", new Object[0])); // Akarin - Async Sending packets LoginListener.LOGGER.error("Couldn't verify username because servers are unavailable"); } // CraftBukkit start - catch all exceptions @@ -376,7 +389,7 @@ public class LoginListener implements PacketLoginInListener { return; } // Paper end - this.disconnect(new ChatMessage("multiplayer.disconnect.unexpected_query_response", new Object[0])); + this.disconnectAsync(new ChatMessage("multiplayer.disconnect.unexpected_query_response", new Object[0])); // Akarin - Async Sending packets } protected GameProfile a(GameProfile gameprofile) { diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java index 96a785af27..fd1d5fe3bf 100644 --- a/src/main/java/net/minecraft/server/NetworkManager.java +++ b/src/main/java/net/minecraft/server/NetworkManager.java @@ -43,6 +43,7 @@ public class NetworkManager extends SimpleChannelInboundHandler> { }); private final EnumProtocolDirection h; private final Queue packetQueue = Queues.newConcurrentLinkedQueue(); private final Queue getPacketQueue() { return this.packetQueue; } // Paper - OBFHELPER + private EnumProtocol protocol; // Akarin - avoid map lookup public Channel channel; public SocketAddress socketAddress; public void setSpoofedRemoteAddress(SocketAddress address) { this.socketAddress = address; } // Paper - OBFHELPER // Spigot Start @@ -88,6 +89,7 @@ public class NetworkManager extends SimpleChannelInboundHandler> { public void setProtocol(EnumProtocol enumprotocol) { this.channel.attr(NetworkManager.c).set(enumprotocol); + protocol = enumprotocol; // Akarin - avoid map lookup this.channel.config().setAutoRead(true); NetworkManager.LOGGER.debug("Enabled auto read"); } @@ -175,8 +177,8 @@ public class NetworkManager extends SimpleChannelInboundHandler> { private void dispatchPacket(Packet packet, @Nullable GenericFutureListener> genericFutureListener) { this.b(packet, genericFutureListener); } // Paper - OBFHELPER private void b(Packet packet, @Nullable GenericFutureListener> genericfuturelistener) { - EnumProtocol enumprotocol = EnumProtocol.a(packet); - EnumProtocol enumprotocol1 = (EnumProtocol) this.channel.attr(NetworkManager.c).get(); + EnumProtocol enumprotocol = packet.protocol();//EnumProtocol.a(packet); // Akarin - avoid map lookup + EnumProtocol enumprotocol1 = protocol;//(EnumProtocol) this.channel.attr(NetworkManager.c).get(); // Akarin - avoid map lookup ++this.q; if (enumprotocol1 != enumprotocol) { @@ -184,7 +186,7 @@ public class NetworkManager extends SimpleChannelInboundHandler> { this.channel.config().setAutoRead(false); } - if (this.channel.eventLoop().inEventLoop()) { + if (false && this.channel.eventLoop().inEventLoop()) { // Akarin - Async Sending packets if (enumprotocol != enumprotocol1) { this.setProtocol(enumprotocol); } @@ -223,30 +225,92 @@ public class NetworkManager extends SimpleChannelInboundHandler> { } - // Paper start - Async-Anti-Xray - Stop dispatching further packets and return false if the peeked packet is a chunk packet which is not ready - private boolean sendPacketQueue() { return this.o(); } // OBFHELPER // void -> boolean - private boolean o() { // void -> boolean - if (this.channel != null && this.channel.isOpen()) { - Queue queue = this.packetQueue; - - synchronized (this.packetQueue) { - while (!this.packetQueue.isEmpty()) { - NetworkManager.QueuedPacket networkmanager_queuedpacket = (NetworkManager.QueuedPacket) this.getPacketQueue().peek(); // poll -> peek - - if (networkmanager_queuedpacket != null) { // Fix NPE (Spigot bug caused by handleDisconnection()) - if (networkmanager_queuedpacket.getPacket() instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) networkmanager_queuedpacket.getPacket()).isReady()) { // Check if the peeked packet is a chunk packet which is not ready - return false; // Return false if the peeked packet is a chunk packet which is not ready - } else { - this.getPacketQueue().poll(); // poll here - this.dispatchPacket(networkmanager_queuedpacket.getPacket(), networkmanager_queuedpacket.getGenericFutureListener()); // dispatch the packet - } - } - } + // Akarin Start - Async Sending packets - multiple packets, copied from above + public void sendPacketAsync(Packet packet) { + EnumProtocol enumprotocol = packet.protocol(); + EnumProtocol enumprotocol1 = protocol; - } + ++this.q; + if (enumprotocol1 != enumprotocol) { + NetworkManager.LOGGER.debug("Disabled auto read"); + this.channel.config().setAutoRead(false); + this.setProtocol(enumprotocol); } - return true; // Return true if all packets were dispatched + ChannelFuture channelfuture = this.channel.writeAndFlush(packet); + + channelfuture.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); + + } + + public void sendPackets(Packet packet, Packet other) { + EnumProtocol enumprotocol = packet.protocol(); + + ++this.q; + if (protocol != enumprotocol) { + NetworkManager.LOGGER.debug("Disabled auto read"); + this.channel.config().setAutoRead(false); + } + + this.channel.eventLoop().execute(() -> { + if (enumprotocol != protocol) { + this.setProtocol(enumprotocol); + } + + this.channel.write(packet).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); + this.channel.write(other).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); + this.channel.flush(); + }); + } + + public void sendPackets(Packet packet, Packet ...packets) { + EnumProtocol enumprotocol = packet.protocol(); + + ++this.q; + if (protocol != enumprotocol) { + NetworkManager.LOGGER.debug("Disabled auto read"); + this.channel.config().setAutoRead(false); + } + + this.channel.eventLoop().execute(() -> { + if (enumprotocol != protocol) { + this.setProtocol(enumprotocol); + } + + this.channel.write(packet).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); + for (Packet pkt : packets) { + this.channel.write(pkt).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); + } + + this.channel.flush(); + }); + } + // Akarin End + + // Paper start - Async-Anti-Xray - Stop dispatching further packets and return false if the peeked packet is a chunk packet which is not ready + private boolean sendPacketQueue() { return this.o(); } // OBFHELPER // void -> boolean + private boolean o() { // void -> boolean + if (this.channel != null && this.channel.isOpen()) { + Queue queue = this.packetQueue; + + synchronized (this.packetQueue) { + while (!this.packetQueue.isEmpty()) { + NetworkManager.QueuedPacket networkmanager_queuedpacket = (NetworkManager.QueuedPacket) this.getPacketQueue().peek(); // poll -> peek + + if (networkmanager_queuedpacket != null) { // Fix NPE (Spigot bug caused by handleDisconnection()) + if (networkmanager_queuedpacket.getPacket() instanceof PacketPlayOutMapChunk && !((PacketPlayOutMapChunk) networkmanager_queuedpacket.getPacket()).isReady()) { // Check if the peeked packet is a chunk packet which is not ready + return false; // Return false if the peeked packet is a chunk packet which is not ready + } else { + this.getPacketQueue().poll(); // poll here + this.dispatchPacket(networkmanager_queuedpacket.getPacket(), networkmanager_queuedpacket.getGenericFutureListener()); // dispatch the packet + } + } + } + + } + } + + return true; // Return true if all packets were dispatched } // Paper end @@ -260,9 +324,9 @@ public class NetworkManager extends SimpleChannelInboundHandler> { ((PlayerConnection) this.packetListener).tick(); } - if (this.channel != null) { - if (enableExplicitFlush) this.channel.eventLoop().execute(() -> this.channel.flush()); // Paper - we don't need to explicit flush here, but allow opt in incase issues are found to a better version - } + //if (this.channel != null) { // Akarin - already did + //if (enableExplicitFlush) this.channel.eventLoop().execute(() -> this.channel.flush()); // Paper - we don't need to explicit flush here, but allow opt in incase issues are found to a better version + //} if (this.t++ % 20 == 0) { this.s = this.s * 0.75F + (float) this.q * 0.25F; diff --git a/src/main/java/net/minecraft/server/Packet.java b/src/main/java/net/minecraft/server/Packet.java index 8d0965a053..7e27cd280f 100644 --- a/src/main/java/net/minecraft/server/Packet.java +++ b/src/main/java/net/minecraft/server/Packet.java @@ -20,4 +20,10 @@ public interface Packet { default boolean a() { return false; } + + // Akarin start - add protocol + default EnumProtocol protocol() { + return EnumProtocol.PLAY; + } + // Akarin end } diff --git a/src/main/java/net/minecraft/server/PacketLoginOutCustomPayload.java b/src/main/java/net/minecraft/server/PacketLoginOutCustomPayload.java index 7eb230f1b2..d01cac0bd4 100644 --- a/src/main/java/net/minecraft/server/PacketLoginOutCustomPayload.java +++ b/src/main/java/net/minecraft/server/PacketLoginOutCustomPayload.java @@ -41,4 +41,11 @@ public class PacketLoginOutCustomPayload implements Packet public void a(PacketLoginOutListener packetloginoutlistener) { packetloginoutlistener.a(this); } + + // Akarin start - add protocol + @Override + public EnumProtocol protocol() { + return EnumProtocol.LOGIN; + } + // Akarin end } diff --git a/src/main/java/net/minecraft/server/PacketLoginOutEncryptionBegin.java b/src/main/java/net/minecraft/server/PacketLoginOutEncryptionBegin.java index b0d6342c31..eeabb54c44 100644 --- a/src/main/java/net/minecraft/server/PacketLoginOutEncryptionBegin.java +++ b/src/main/java/net/minecraft/server/PacketLoginOutEncryptionBegin.java @@ -34,4 +34,11 @@ public class PacketLoginOutEncryptionBegin implements Packet { public void a(PacketLoginOutListener packetloginoutlistener) { packetloginoutlistener.a(this); } + + // Akarin start - add protocol + @Override + public EnumProtocol protocol() { + return EnumProtocol.LOGIN; + } + // Akarin end } diff --git a/src/main/java/net/minecraft/server/PacketStatusListener.java b/src/main/java/net/minecraft/server/PacketStatusListener.java index 4bb21c48bd..06af567fc1 100644 --- a/src/main/java/net/minecraft/server/PacketStatusListener.java +++ b/src/main/java/net/minecraft/server/PacketStatusListener.java @@ -143,7 +143,7 @@ public class PacketStatusListener implements PacketStatusInListener { @Override public void a(PacketStatusInPing packetstatusinping) { - this.networkManager.sendPacket(new PacketStatusOutPong(packetstatusinping.b())); + this.networkManager.sendPacketAsync(new PacketStatusOutPong(packetstatusinping.b())); // Akarin - Async Sending packets this.networkManager.close(PacketStatusListener.a); } } diff --git a/src/main/java/net/minecraft/server/PacketStatusOutPong.java b/src/main/java/net/minecraft/server/PacketStatusOutPong.java index 94a0da87db..577c6ef9cf 100644 --- a/src/main/java/net/minecraft/server/PacketStatusOutPong.java +++ b/src/main/java/net/minecraft/server/PacketStatusOutPong.java @@ -25,4 +25,11 @@ public class PacketStatusOutPong implements Packet { public void a(PacketStatusOutListener packetstatusoutlistener) { packetstatusoutlistener.a(this); } + + // Akarin start - add protocol + @Override + public EnumProtocol protocol() { + return EnumProtocol.STATUS; + } + // Akarin end } diff --git a/src/main/java/net/minecraft/server/PacketStatusOutServerInfo.java b/src/main/java/net/minecraft/server/PacketStatusOutServerInfo.java index 50d5fb62ef..4da7a2c1a4 100644 --- a/src/main/java/net/minecraft/server/PacketStatusOutServerInfo.java +++ b/src/main/java/net/minecraft/server/PacketStatusOutServerInfo.java @@ -28,4 +28,11 @@ public class PacketStatusOutServerInfo implements Packet