diff --git a/Spigot-Server-Patches/0004-MC-Utils.patch b/Spigot-Server-Patches/0004-MC-Utils.patch index ce97c2d283..fe5191d05c 100644 --- a/Spigot-Server-Patches/0004-MC-Utils.patch +++ b/Spigot-Server-Patches/0004-MC-Utils.patch @@ -2077,10 +2077,10 @@ index 0000000000000000000000000000000000000000..e51104e65a07b6ea7bbbcbb6afb066ef +} diff --git a/src/main/java/com/destroystokyo/paper/util/pooled/PooledObjects.java b/src/main/java/com/destroystokyo/paper/util/pooled/PooledObjects.java new file mode 100644 -index 0000000000000000000000000000000000000000..d4325f1f1152e34f82046dfe24cbe505f6e316ae +index 0000000000000000000000000000000000000000..e272b512520486cf7d46fe4e1021ca148d4cf74f --- /dev/null +++ b/src/main/java/com/destroystokyo/paper/util/pooled/PooledObjects.java -@@ -0,0 +1,185 @@ +@@ -0,0 +1,174 @@ +package com.destroystokyo.paper.util.pooled; + +import org.apache.commons.lang3.mutable.MutableInt; @@ -2090,18 +2090,7 @@ index 0000000000000000000000000000000000000000..d4325f1f1152e34f82046dfe24cbe505 + +public final class PooledObjects { + -+ public static final PooledObjects POOLED_MUTABLE_INTEGERS = new PooledObjects<>(new PooledObjectHandler() { -+ @Override -+ public MutableInt createNew() { -+ return new MutableInt(); -+ } -+ -+ @Override -+ public void onAcquire(final MutableInt value) {} -+ -+ @Override -+ public void onRelease(final MutableInt value) {} -+ }, 200, -1); ++ public static final PooledObjects POOLED_MUTABLE_INTEGERS = new PooledObjects<>(MutableInt::new, 200, -1); + + private final PooledObjectHandler handler; + private final int maxPoolSize; @@ -2171,16 +2160,16 @@ index 0000000000000000000000000000000000000000..d4325f1f1152e34f82046dfe24cbe505 + } + + /** This object is restricted from interacting with any pool */ -+ static interface PooledObjectHandler { ++ public static interface PooledObjectHandler { + + /** + * Must return a non-null object + */ + E createNew(); + -+ void onAcquire(final E value); ++ default void onAcquire(final E value) {} + -+ void onRelease(final E value); ++ default void onRelease(final E value) {} + } + + protected static class IsolatedPool { diff --git a/Spigot-Server-Patches/0346-Optimize-Network-Manager-and-add-advanced-packet-sup.patch b/Spigot-Server-Patches/0346-Optimize-Network-Manager-and-add-advanced-packet-sup.patch index c8f735c387..75299848c6 100644 --- a/Spigot-Server-Patches/0346-Optimize-Network-Manager-and-add-advanced-packet-sup.patch +++ b/Spigot-Server-Patches/0346-Optimize-Network-Manager-and-add-advanced-packet-sup.patch @@ -4,7 +4,8 @@ Date: Wed, 6 May 2020 04:53:35 -0400 Subject: [PATCH] Optimize Network Manager and add advanced packet support Adds ability for 1 packet to bundle other packets to follow it -adds ability for a packet to delay sending more packets until a state is ready. +Adds ability for a packet to delay sending more packets until a state is ready. +Adds ability to clean up a packet when it is finished (not sent, or finished encoding), such as freeing buffers Removes synchronization from sending packets Removes processing packet queue off of main thread @@ -23,7 +24,7 @@ This should solve some deadlock risks Part of this commit was authored by: Spottedleaf diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java -index b1dededc15cce686ead74a99bee64c89ac1de22c..f81014973a4cf50c1320f82c22ed1c19fd7d6ac9 100644 +index b1dededc15cce686ead74a99bee64c89ac1de22c..d24d7df807287c51a8dd5ecf4f7c842b7cdf3976 100644 --- a/src/main/java/net/minecraft/server/NetworkManager.java +++ b/src/main/java/net/minecraft/server/NetworkManager.java @@ -64,6 +64,10 @@ public class NetworkManager extends SimpleChannelInboundHandler> { @@ -45,7 +46,7 @@ index b1dededc15cce686ead74a99bee64c89ac1de22c..f81014973a4cf50c1320f82c22ed1c19 this.channel.attr(NetworkManager.c).set(enumprotocol); this.channel.config().setAutoRead(true); NetworkManager.LOGGER.debug("Enabled auto read"); -@@ -158,19 +163,72 @@ public class NetworkManager extends SimpleChannelInboundHandler> { +@@ -158,19 +163,75 @@ public class NetworkManager extends SimpleChannelInboundHandler> { NetworkManager.LOGGER.debug("Set listener of {} to {}", this, packetlistener); this.packetListener = packetlistener; } @@ -93,7 +94,10 @@ index b1dededc15cce686ead74a99bee64c89ac1de22c..f81014973a4cf50c1320f82c22ed1c19 - this.packetQueue.add(new NetworkManager.QueuedPacket(packet, genericfuturelistener)); + // Paper start - handle oversized packets better + boolean connected = this.isConnected(); -+ if (!connected && !preparing) return; // Do nothing ++ if (!connected && !preparing) { ++ packet.onPacketDone(); ++ return; // Do nothing ++ } + if (connected && (InnerUtil.canSendImmediate(this, packet) || ( + MCUtil.isMainThread() && packet.isReady() && this.packetQueue.isEmpty() && + (packet.getExtraPackets() == null || packet.getExtraPackets().isEmpty()) @@ -109,13 +113,13 @@ index b1dededc15cce686ead74a99bee64c89ac1de22c..f81014973a4cf50c1320f82c22ed1c19 + } else { + java.util.List packets = new java.util.ArrayList<>(1 + extraPackets.size()); + packets.add(new NetworkManager.QueuedPacket(packet, null)); // delay the future listener until the end of the extra packets - ++ + for (int i = 0, len = extraPackets.size(); i < len;) { + Packet extra = extraPackets.get(i); + boolean end = ++i == len; + packets.add(new NetworkManager.QueuedPacket(extra, end ? genericfuturelistener : null)); // append listener to the end + } -+ + + this.packetQueue.addAll(packets); // atomic + } + this.sendPacketQueue(); @@ -123,7 +127,7 @@ index b1dededc15cce686ead74a99bee64c89ac1de22c..f81014973a4cf50c1320f82c22ed1c19 } private void dispatchPacket(Packet packet, @Nullable GenericFutureListener> genericFutureListener) { this.b(packet, genericFutureListener); } // Paper - OBFHELPER -@@ -214,21 +272,46 @@ public class NetworkManager extends SimpleChannelInboundHandler> { +@@ -214,21 +275,46 @@ public class NetworkManager extends SimpleChannelInboundHandler> { } @@ -180,27 +184,54 @@ index b1dededc15cce686ead74a99bee64c89ac1de22c..f81014973a4cf50c1320f82c22ed1c19 public void a() { this.o(); -@@ -260,6 +343,7 @@ public class NetworkManager extends SimpleChannelInboundHandler> { +@@ -257,9 +343,11 @@ public class NetworkManager extends SimpleChannelInboundHandler> { + return this.socketAddress; + } + ++ public void clearPacketQueue() { QueuedPacket packet; while ((packet = packetQueue.poll()) != null) packet.getPacket().onPacketDone(); } // Paper public void close(IChatBaseComponent ichatbasecomponent) { // Spigot Start this.preparing = false; -+ this.packetQueue.clear(); // Paper - just incase its closed before we ever get to the main thread to do this ++ clearPacketQueue(); // Paper // Spigot End if (this.channel.isOpen()) { this.channel.close(); // We can't wait as this may be called from an event loop. +@@ -335,7 +423,7 @@ public class NetworkManager extends SimpleChannelInboundHandler> { + } else if (this.i() != null) { + this.i().a(new ChatMessage("multiplayer.disconnect.generic", new Object[0])); + } +- this.packetQueue.clear(); // Free up packet queue. ++ clearPacketQueue(); // Paper + // Paper start - Add PlayerConnectionCloseEvent + final PacketListener packetListener = this.i(); + if (packetListener instanceof PlayerConnection) { diff --git a/src/main/java/net/minecraft/server/Packet.java b/src/main/java/net/minecraft/server/Packet.java -index 2d8e6a2f4a0c3c5d74a647d7164b0028781d3bf5..45142ed9d2440e21dd1ff1a32a12759fea4dbcb4 100644 +index 2d8e6a2f4a0c3c5d74a647d7164b0028781d3bf5..df1b4877b1560f982a1fcaf98404c8fe73e29973 100644 --- a/src/main/java/net/minecraft/server/Packet.java +++ b/src/main/java/net/minecraft/server/Packet.java -@@ -11,6 +11,8 @@ public interface Packet { +@@ -11,6 +11,9 @@ public interface Packet { void a(T t0); // Paper start ++ default void onPacketDone() {} + default boolean isReady() { return true; } + default java.util.List getExtraPackets() { return null; } default boolean packetTooLarge(NetworkManager manager) { return false; } +diff --git a/src/main/java/net/minecraft/server/PacketEncoder.java b/src/main/java/net/minecraft/server/PacketEncoder.java +index b0cfef52cbb5e23beae528668e4e98cedecf603c..f46d028016a425a29674e768ae9310c825c088f2 100644 +--- a/src/main/java/net/minecraft/server/PacketEncoder.java ++++ b/src/main/java/net/minecraft/server/PacketEncoder.java +@@ -48,7 +48,7 @@ public class PacketEncoder extends MessageToByteEncoder> { + } else { + throw throwable; + } +- } ++ } finally { try { packet.onPacketDone(); } catch (Exception e) { e.printStackTrace(); } ; } // Paper + + // Paper start + int packetLength = bytebuf.readableBytes(); diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java index e148940ab3721cff27cf791c159c11b9b94191e4..e917d37382dab70ed9e6b62decf1557c33b26065 100644 --- a/src/main/java/net/minecraft/server/PlayerList.java diff --git a/Spigot-Server-Patches/0507-Optimize-NibbleArray-to-use-pooled-buffers.patch b/Spigot-Server-Patches/0507-Optimize-NibbleArray-to-use-pooled-buffers.patch new file mode 100644 index 0000000000..a7df49c7a8 --- /dev/null +++ b/Spigot-Server-Patches/0507-Optimize-NibbleArray-to-use-pooled-buffers.patch @@ -0,0 +1,215 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Wed, 6 May 2020 23:30:30 -0400 +Subject: [PATCH] Optimize NibbleArray to use pooled buffers + +Massively reduces memory allocation of 2048 byte buffers by using +an object pool for these. + +diff --git a/src/main/java/net/minecraft/server/ChunkRegionLoader.java b/src/main/java/net/minecraft/server/ChunkRegionLoader.java +index fa893b14bcef9bab6891dea2c4375b09d74ac038..738f5b975697cd83f1ff35eb0d2d8619ee8d39e4 100644 +--- a/src/main/java/net/minecraft/server/ChunkRegionLoader.java ++++ b/src/main/java/net/minecraft/server/ChunkRegionLoader.java +@@ -387,11 +387,11 @@ public class ChunkRegionLoader { + } + + if (nibblearray != null && !nibblearray.c()) { +- nbttagcompound2.setByteArray("BlockLight", nibblearray.asBytes()); ++ nbttagcompound2.setByteArray("BlockLight", nibblearray.getIfSet()); // Paper + } + + if (nibblearray1 != null && !nibblearray1.c()) { +- nbttagcompound2.setByteArray("SkyLight", nibblearray1.asBytes()); ++ nbttagcompound2.setByteArray("SkyLight", nibblearray1.getIfSet()); // Paper + } + + nbttaglist.add(nbttagcompound2); +diff --git a/src/main/java/net/minecraft/server/LightEngineStorageSky.java b/src/main/java/net/minecraft/server/LightEngineStorageSky.java +index 06bc8371fe9de4d23fdd47e5a3919541bb399fd8..bf37e4ec1f3f4f73c27e1eecffa96423f683a10b 100644 +--- a/src/main/java/net/minecraft/server/LightEngineStorageSky.java ++++ b/src/main/java/net/minecraft/server/LightEngineStorageSky.java +@@ -166,7 +166,7 @@ public class LightEngineStorageSky extends LightEngineStorage BYTE_2048 = new PooledObjects<>(() -> new byte[2048], 16384, 1); ++ public static void releaseBytes(byte[] bytes) { ++ if (bytes != EMPTY_NIBBLE) { ++ System.arraycopy(EMPTY_NIBBLE, 0, bytes, 0, 2048); ++ BYTE_2048.release(bytes); ++ } ++ } ++ ++ public byte[] getIfSet() { ++ return this.a != null ? this.a : EMPTY_NIBBLE; ++ } ++ public byte[] getCloneIfSet() { ++ if (a == null) { ++ return EMPTY_NIBBLE; ++ } ++ byte[] ret = BYTE_2048.acquire(); ++ System.arraycopy(getIfSet(), 0, ret, 0, 2048); ++ return ret; ++ } ++ ++ @Override ++ protected void finalize() throws Throwable { ++ try { ++ if (this.a != null) { ++ releaseBytes(this.a); ++ this.a = null; ++ } ++ } finally { ++ super.finalize(); ++ } ++ } ++ // Paper end ++ @Nullable protected byte[] a; ++ + + public NibbleArray() {} + + public NibbleArray(byte[] abyte) { ++ // Paper start ++ this(abyte, false); ++ } ++ public NibbleArray(byte[] abyte, boolean isSafe) { + this.a = abyte; ++ if (!isSafe) this.a = getCloneIfSet(); // Paper - clone for safety ++ // Paper end + if (abyte.length != 2048) { + throw (IllegalArgumentException) SystemUtils.c(new IllegalArgumentException("ChunkNibbleArrays should be 2048 bytes not: " + abyte.length)); + } +@@ -44,7 +86,7 @@ public class NibbleArray { + + public void a(int i, int j) { // PAIL: private -> public + if (this.a == null) { +- this.a = new byte[2048]; ++ this.a = BYTE_2048.acquire(); // Paper + } + + int k = this.d(i); +@@ -65,7 +107,7 @@ public class NibbleArray { + + public byte[] asBytes() { + if (this.a == null) { +- this.a = new byte[2048]; ++ this.a = BYTE_2048.acquire(); // Paper + } + + return this.a; +@@ -73,7 +115,7 @@ public class NibbleArray { + + public NibbleArray copy() { return this.b(); } // Paper - OBFHELPER + public NibbleArray b() { +- return this.a == null ? new NibbleArray() : new NibbleArray((byte[]) this.a.clone()); ++ return this.a == null ? new NibbleArray() : new NibbleArray(this.a); // Paper - clone in ctor + } + + public String toString() { +diff --git a/src/main/java/net/minecraft/server/NibbleArrayFlat.java b/src/main/java/net/minecraft/server/NibbleArrayFlat.java +index 67c960292db9d99ac85b5d0dda50ae48ef942c1b..f7641156beea365a91a935667abf8c9539896dc0 100644 +--- a/src/main/java/net/minecraft/server/NibbleArrayFlat.java ++++ b/src/main/java/net/minecraft/server/NibbleArrayFlat.java +@@ -18,7 +18,7 @@ public class NibbleArrayFlat extends NibbleArray { + + @Override + public byte[] asBytes() { +- byte[] abyte = new byte[2048]; ++ byte[] abyte = BYTE_2048.acquire(); // Paper + + for (int i = 0; i < 16; ++i) { + System.arraycopy(this.a, 0, abyte, i * 128, 128); +diff --git a/src/main/java/net/minecraft/server/PacketPlayOutLightUpdate.java b/src/main/java/net/minecraft/server/PacketPlayOutLightUpdate.java +index cd1ad45469aa163b9bc41774ae80adfa617fd97b..d6560f4693869a6638963867f7ebc63bf80d534e 100644 +--- a/src/main/java/net/minecraft/server/PacketPlayOutLightUpdate.java ++++ b/src/main/java/net/minecraft/server/PacketPlayOutLightUpdate.java +@@ -17,6 +17,15 @@ public class PacketPlayOutLightUpdate implements Packet { + private List h; + + public PacketPlayOutLightUpdate() {} ++ // Paper start ++ private final java.util.List usedBytes = new java.util.ArrayList<>(); ++ ++ @Override ++ public void onPacketDone() { ++ usedBytes.forEach(NibbleArray::releaseBytes); ++ usedBytes.clear(); ++ } ++ // Paper end + + public PacketPlayOutLightUpdate(ChunkCoordIntPair chunkcoordintpair, LightEngine lightengine) { + this.a = chunkcoordintpair.x; +@@ -24,6 +33,7 @@ public class PacketPlayOutLightUpdate implements Packet { + this.g = Lists.newArrayList(); + this.h = Lists.newArrayList(); + ++ byte[] lastBytes; // Paper + for (int i = 0; i < 18; ++i) { + NibbleArray nibblearray = lightengine.a(EnumSkyBlock.SKY).a(SectionPosition.a(chunkcoordintpair, -1 + i)); + NibbleArray nibblearray1 = lightengine.a(EnumSkyBlock.BLOCK).a(SectionPosition.a(chunkcoordintpair, -1 + i)); +@@ -33,7 +43,7 @@ public class PacketPlayOutLightUpdate implements Packet { + this.e |= 1 << i; + } else { + this.c |= 1 << i; +- this.g.add(nibblearray.asBytes().clone()); ++ this.g.add(lastBytes = nibblearray.getCloneIfSet()); usedBytes.add(lastBytes); // Paper + } + } + +@@ -42,7 +52,7 @@ public class PacketPlayOutLightUpdate implements Packet { + this.f |= 1 << i; + } else { + this.d |= 1 << i; +- this.h.add(nibblearray1.asBytes().clone()); ++ this.h.add(lastBytes = nibblearray1.getCloneIfSet()); usedBytes.add(lastBytes); // Paper + } + } + } +@@ -57,13 +67,14 @@ public class PacketPlayOutLightUpdate implements Packet { + this.g = Lists.newArrayList(); + this.h = Lists.newArrayList(); + ++ byte[] lastBytes; // Paper + for (int k = 0; k < 18; ++k) { + NibbleArray nibblearray; + + if ((this.c & 1 << k) != 0) { + nibblearray = lightengine.a(EnumSkyBlock.SKY).a(SectionPosition.a(chunkcoordintpair, -1 + k)); + if (nibblearray != null && !nibblearray.c()) { +- this.g.add(nibblearray.asBytes().clone()); ++ this.g.add(lastBytes = nibblearray.getCloneIfSet()); usedBytes.add(lastBytes); // Paper + } else { + this.c &= ~(1 << k); + if (nibblearray != null) { +@@ -75,7 +86,7 @@ public class PacketPlayOutLightUpdate implements Packet { + if ((this.d & 1 << k) != 0) { + nibblearray = lightengine.a(EnumSkyBlock.BLOCK).a(SectionPosition.a(chunkcoordintpair, -1 + k)); + if (nibblearray != null && !nibblearray.c()) { +- this.h.add(nibblearray.asBytes().clone()); ++ this.h.add(lastBytes = nibblearray.getCloneIfSet()); usedBytes.add(lastBytes); // Paper + } else { + this.d &= ~(1 << k); + if (nibblearray != null) {