From a73444b0e1b84cdbaeb048e9f82f2ee2348430b1 Mon Sep 17 00:00:00 2001 From: Aikar Date: Thu, 28 Mar 2019 22:06:35 -0400 Subject: [PATCH] Reuse buffers for chunk compression to optimize memory use Instead of allocating a buffer for every chunk compression, reuse the same 64k sized buffer. Also stopped doing dynamic compression levels. It wasn't helping enough. This will improve memory usage and zlib performance of chunk compression. --- ...0181-PlayerDeathEvent-getItemsToKeep.patch | 26 ++++++-- ...412-Allow-Saving-of-Oversized-Chunks.patch | 65 ++++++++++--------- 2 files changed, 57 insertions(+), 34 deletions(-) diff --git a/Spigot-API-Patches/0181-PlayerDeathEvent-getItemsToKeep.patch b/Spigot-API-Patches/0181-PlayerDeathEvent-getItemsToKeep.patch index e6ccbb43e..7c0e2f94d 100644 --- a/Spigot-API-Patches/0181-PlayerDeathEvent-getItemsToKeep.patch +++ b/Spigot-API-Patches/0181-PlayerDeathEvent-getItemsToKeep.patch @@ -1,4 +1,4 @@ -From 9552cb45732ed2d11f179cdf766996d3c0843001 Mon Sep 17 00:00:00 2001 +From f3ebc45b923294990f9db2b1514d34d7c9a4840f Mon Sep 17 00:00:00 2001 From: Aikar Date: Mon, 11 Mar 2013 20:04:34 -0400 Subject: [PATCH] PlayerDeathEvent#getItemsToKeep @@ -8,10 +8,10 @@ Exposes a mutable array on items a player should keep on death Example Usage: https://gist.github.com/aikar/5bb202de6057a051a950ce1f29feb0b4 diff --git a/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java b/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java -index 5b0ef1eb1..b30818177 100644 +index 5b0ef1eb..af6070d1 100644 --- a/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java +++ b/src/main/java/org/bukkit/event/entity/PlayerDeathEvent.java -@@ -18,6 +18,22 @@ public class PlayerDeathEvent extends EntityDeathEvent { +@@ -18,6 +18,40 @@ public class PlayerDeathEvent extends EntityDeathEvent { private boolean keepLevel = false; private boolean keepInventory = false; @@ -19,9 +19,27 @@ index 5b0ef1eb1..b30818177 100644 + private List itemsToKeep = new java.util.ArrayList<>(); + + /** -+ * A mutable collection to add items that the player should keep on death (Similar to KeepInventory game rule) ++ * A mutable collection to add items that the player should retain in their inventory on death (Similar to KeepInventory game rule) + * + * You MUST remove the item from the .getDrops() collection too or it will duplicate! ++ *
++     *    \@EventHandler(ignoreCancelled = true)
++     *     public void onPlayerDeath(PlayerDeathEvent event) {
++     *         for (Iterator iterator = event.getDrops().iterator(); iterator.hasNext(); ) {
++     *             ItemStack drop = iterator.next();
++     *             List lore = drop.getLore();
++     *             if (lore != null && !lore.isEmpty()) {
++     *                 if (lore.get(0).contains("(SOULBOUND)")) {
++     *                     iterator.remove();
++     *                     event.getItemsToKeep().add(drop);
++     *                 }
++     *             }
++     *         }
++     *     }
++     * 
++ * ++ * Adding an item to this list that the player did not previously have will give them the item on death. ++ * An example case could be a "Note" that "You died at X/Y/Z coordinates" + * + * @return The list to hold items to keep + */ diff --git a/Spigot-Server-Patches/0412-Allow-Saving-of-Oversized-Chunks.patch b/Spigot-Server-Patches/0412-Allow-Saving-of-Oversized-Chunks.patch index e63875f91..e8e8711d8 100644 --- a/Spigot-Server-Patches/0412-Allow-Saving-of-Oversized-Chunks.patch +++ b/Spigot-Server-Patches/0412-Allow-Saving-of-Oversized-Chunks.patch @@ -1,4 +1,4 @@ -From b676fa2fa828bd7dbaa734620bcf463b8bfcea11 Mon Sep 17 00:00:00 2001 +From a159e031449910796e17ce6bf645972011605b65 Mon Sep 17 00:00:00 2001 From: Aikar Date: Fri, 15 Feb 2019 01:08:19 -0500 Subject: [PATCH] Allow Saving of Oversized Chunks @@ -51,7 +51,7 @@ index 12268f87b..e1f7e06ab 100644 a((NBTBase) nbttagcompound, dataoutput); } diff --git a/src/main/java/net/minecraft/server/RegionFile.java b/src/main/java/net/minecraft/server/RegionFile.java -index c20511588..d148ce497 100644 +index c20511588..82f7af46f 100644 --- a/src/main/java/net/minecraft/server/RegionFile.java +++ b/src/main/java/net/minecraft/server/RegionFile.java @@ -78,6 +78,7 @@ public class RegionFile { @@ -96,7 +96,7 @@ index c20511588..d148ce497 100644 if (k1 >= 256) { // Spigot start - if (!ENABLE_EXTENDED_SAVE) return; -+ if (!USE_SPIGOT_OVERSIZED_METHOD) throw new ChunkTooLargeException(i, j, k1); // Paper - throw error instead ++ if (!USE_SPIGOT_OVERSIZED_METHOD && !RegionFileCache.isOverzealous()) throw new ChunkTooLargeException(i, j, k1); // Paper - throw error instead org.bukkit.Bukkit.getLogger().log(java.util.logging.Level.WARNING,"Large Chunk Detected: ({0}, {1}) Size: {2} {3}", new Object[]{i, j, k1, this.b}); + if (!ENABLE_EXTENDED_SAVE) return; // Spigot end @@ -212,7 +212,7 @@ index c20511588..d148ce497 100644 // Paper end class ChunkBuffer extends ByteArrayOutputStream { -@@ -387,8 +492,40 @@ public class RegionFile { +@@ -387,8 +492,35 @@ public class RegionFile { this.c = j; } @@ -227,36 +227,31 @@ index c20511588..d148ce497 100644 + int length = out.size(); + + RegionFile.this.a(this.b, this.c, bytes, length); // Paper - change to bytes/length -+ // Paper end + } + } + ++ private static final byte[] compressionBuffer = new byte[1024 * 64]; // 64k fits most standard chunks input size even, ideally 1 pass through zlib ++ private static final java.util.zip.Deflater deflater = new java.util.zip.Deflater(); ++ // since file IO is single threaded, no benefit to using per-region file buffers/synchronization, we can change that later if it becomes viable. + private static DirectByteArrayOutputStream compressData(byte[] buf, int length) throws IOException { -+ final java.util.zip.Deflater deflater; -+ if (length > 1024 * 512) { -+ deflater = new java.util.zip.Deflater(9); -+ } else if (length > 1024 * 128) { -+ deflater = new java.util.zip.Deflater(8); -+ } else { -+ deflater = new java.util.zip.Deflater(6); -+ } ++ synchronized (deflater) { ++ deflater.setInput(buf, 0, length); ++ deflater.finish(); + -+ -+ deflater.setInput(buf, 0, length); -+ deflater.finish(); -+ -+ DirectByteArrayOutputStream out = new DirectByteArrayOutputStream(length); -+ byte[] buffer = new byte[1024 * (length > 1024 * 124 ? 32 : 16)]; -+ while (!deflater.finished()) { -+ out.write(buffer, 0, deflater.deflate(buffer)); ++ DirectByteArrayOutputStream out = new DirectByteArrayOutputStream(length); ++ while (!deflater.finished()) { ++ out.write(compressionBuffer, 0, deflater.deflate(compressionBuffer)); ++ } ++ out.close(); ++ deflater.reset(); ++ return out; } -+ out.close(); -+ deflater.end(); -+ return out; } ++ // Paper end ++ } diff --git a/src/main/java/net/minecraft/server/RegionFileCache.java b/src/main/java/net/minecraft/server/RegionFileCache.java -index 8c8b7cbab..a17e76d83 100644 +index 8c8b7cbab..17e76815a 100644 --- a/src/main/java/net/minecraft/server/RegionFileCache.java +++ b/src/main/java/net/minecraft/server/RegionFileCache.java @@ -16,6 +16,7 @@ public class RegionFileCache { @@ -267,7 +262,7 @@ index 8c8b7cbab..a17e76d83 100644 public static synchronized RegionFile a(File file, int i, int j) { File file1 = new File(file, "region"); File file2 = new File(file1, "r." + (i >> 5) + "." + (j >> 5) + ".mca"); -@@ -83,6 +84,125 @@ public class RegionFileCache { +@@ -83,6 +84,135 @@ public class RegionFileCache { public static synchronized boolean hasRegionFile(File file, int i, int j) { return RegionFileCache.cache.containsKey(getRegionFileName(file, i, j)); } @@ -276,7 +271,8 @@ index 8c8b7cbab..a17e76d83 100644 + } + + private static final int DEFAULT_SIZE_THRESHOLD = 1024 * 8; -+ private static final int OVERZEALOUS_THRESHOLD = 1024 * 2; ++ private static final int OVERZEALOUS_TOTAL_THRESHOLD = 1024 * 64; ++ private static final int OVERZEALOUS_THRESHOLD = 1024; + private static int SIZE_THRESHOLD = DEFAULT_SIZE_THRESHOLD; + private static void resetFilterThresholds() { + SIZE_THRESHOLD = Math.max(1024 * 4, Integer.getInteger("Paper.FilterThreshhold", DEFAULT_SIZE_THRESHOLD)); @@ -284,6 +280,11 @@ index 8c8b7cbab..a17e76d83 100644 + static { + resetFilterThresholds(); + } ++ ++ static boolean isOverzealous() { ++ return SIZE_THRESHOLD == OVERZEALOUS_THRESHOLD; ++ } ++ + private static void writeRegion(File file, int x, int z, NBTTagCompound nbttagcompound) throws IOException { + RegionFile regionfile = getRegionFile(file, x, z); + @@ -333,11 +334,15 @@ index 8c8b7cbab..a17e76d83 100644 + private static void filterChunkList(NBTTagCompound level, NBTTagCompound extra, String key) { + NBTTagList list = level.getList(key, 10); + NBTTagList newList = extra.getList(key, 10); ++ int totalSize = 0; + for (Iterator iterator = list.list.iterator(); iterator.hasNext(); ) { + NBTBase object = iterator.next(); -+ if (getNBTSize(object) > SIZE_THRESHOLD) { ++ int nbtSize = getNBTSize(object); ++ if (nbtSize > SIZE_THRESHOLD || (SIZE_THRESHOLD == OVERZEALOUS_THRESHOLD && totalSize > OVERZEALOUS_TOTAL_THRESHOLD)) { + newList.add(object); + iterator.remove(); ++ } else { ++ totalSize += nbtSize; + } + } + level.set(key, list); @@ -393,7 +398,7 @@ index 8c8b7cbab..a17e76d83 100644 // Paper End public static synchronized void a() { -@@ -108,6 +228,12 @@ public class RegionFileCache { +@@ -108,6 +238,12 @@ public class RegionFileCache { // CraftBukkit start - call sites hoisted for synchronization public static NBTTagCompound read(File file, int i, int j) throws IOException { // Paper - remove synchronization RegionFile regionfile = a(file, i, j); @@ -406,7 +411,7 @@ index 8c8b7cbab..a17e76d83 100644 DataInputStream datainputstream = regionfile.a(i & 31, j & 31); -@@ -121,11 +247,14 @@ public class RegionFileCache { +@@ -121,11 +257,14 @@ public class RegionFileCache { @Nullable public static void write(File file, int i, int j, NBTTagCompound nbttagcompound) throws IOException { int attempts = 0; Exception laste = null; while (attempts++ < 5) { try { // Paper