From ae95189944826b8e9e1569a35bbdc920727d9dde Mon Sep 17 00:00:00 2001 From: Aikar <aikar@aikar.co> Date: Mon, 21 Mar 2016 22:51:14 -0400 Subject: [PATCH] Another attempt at unload queue, including EAR improvements. should be fully working now as I pretty much fell back to existing methods so anything touching the unloadQueue set should behave correctly. And maintained NMS Reflection safe change too --- .../Optimize-Chunk-Unload-Queue.patch | 165 +++++++++++++----- 1 file changed, 119 insertions(+), 46 deletions(-) rename disabled-patches/0090-Optimize-Chunk-Unload-Queue.patch => Spigot-Server-Patches/Optimize-Chunk-Unload-Queue.patch (75%) diff --git a/disabled-patches/0090-Optimize-Chunk-Unload-Queue.patch b/Spigot-Server-Patches/Optimize-Chunk-Unload-Queue.patch similarity index 75% rename from disabled-patches/0090-Optimize-Chunk-Unload-Queue.patch rename to Spigot-Server-Patches/Optimize-Chunk-Unload-Queue.patch index 3ef81dcc08..9a44a83bad 100644 --- a/disabled-patches/0090-Optimize-Chunk-Unload-Queue.patch +++ b/Spigot-Server-Patches/Optimize-Chunk-Unload-Queue.patch @@ -20,6 +20,8 @@ We mark the chunk as active in many places that notify it is still being used, s when the chunk unload queue reaches that chunk, and sees the chunk became active again, it will skip it and move to next. +Also optimize EAR to use these methods. + diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/Chunk.java @@ -33,22 +35,6 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 // Paper end // CraftBukkit start - Neighbor loaded cache for chunk lighting and entity ticking -@@ -0,0 +0,0 @@ public class Chunk { - } - - public void addEntities() { -+ isChunkActive = true; // Paper - this.i = true; - this.world.b(this.tileEntities.values()); - -@@ -0,0 +0,0 @@ public class Chunk { - } - - public void removeEntities() { -+ isChunkActive = false; // Paper - this.i = false; - Iterator iterator = this.tileEntities.values().iterator(); - diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -63,45 +49,65 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 private final IChunkLoader chunkLoader; public LongObjectHashMap<Chunk> chunks = new LongObjectHashMap<Chunk>(); // CraftBukkit @@ -0,0 +0,0 @@ public class ChunkProviderServer implements IChunkProvider { + return (chunk == null) ? getChunkAt(x, z) : chunk; + } - // CraftBukkit start - Add async variant, provide compatibility - public Chunk getOrCreateChunkFast(int x, int z) { -- Chunk chunk = chunks.get(LongHash.toLong(x, z)); -- return (chunk == null) ? getChunkAt(x, z) : chunk; -+ return getChunkAt(x, z); // Paper -+ } -+ +- public Chunk getChunkIfLoaded(int x, int z) { +- return chunks.get(LongHash.toLong(x, z)); + // Paper start + public Chunk getLoadedChunkAtWithoutMarkingActive(int i, int j) { + return chunks.get(LongHash.toLong(i, j)); } -- public Chunk getChunkIfLoaded(int x, int z) { -- return chunks.get(LongHash.toLong(x, z)); + // getChunkIfLoaded -> getChunkIfActive + // this is only used by CraftBukkit now, and plugins shouldnt mark things active + public Chunk getChunkIfActive(int x, int z) { + Chunk chunk = chunks.get(LongHash.toLong(x, z)); + return (chunk != null && chunk.isChunkActive) ? chunk : null; - } ++ } + // Paper end - ++ public Chunk getLoadedChunkAt(int i, int j) { Chunk chunk = chunks.get(LongHash.toLong(i, j)); // CraftBukkit - this.unloadQueue.remove(i, j); // CraftBukkit -+ if (chunk != null) { chunk.isChunkActive = true; }// Paper +- this.unloadQueue.remove(i, j); // CraftBukkit ++ //this.unloadQueue.remove(i, j); // CraftBukkit // Paper ++ markChunkActive(chunk); // Paper return chunk; } @@ -0,0 +0,0 @@ public class ChunkProviderServer implements IChunkProvider { - runnable.run(); + // CraftBukkit end } -+ chunk.isChunkActive = true; // Paper ++ markChunkActive(chunk); // Paper return chunk; } +@@ -0,0 +0,0 @@ public class ChunkProviderServer implements IChunkProvider { + } + + public Chunk getChunkAt(int i, int j, Runnable runnable) { +- unloadQueue.remove(i, j); ++ //unloadQueue.remove(i, j); // Paper + Chunk chunk = chunks.get(LongHash.toLong(i, j)); + ChunkRegionLoader loader = null; + +@@ -0,0 +0,0 @@ public class ChunkProviderServer implements IChunkProvider { + if (runnable != null) { + runnable.run(); + } ++ markChunkActive(chunk); // Paper + + return chunk; + } + + public Chunk originalGetChunkAt(int i, int j) { +- this.unloadQueue.remove(i, j); ++ //this.unloadQueue.remove(i, j); // Paper + Chunk chunk = this.chunks.get(LongHash.toLong(i, j)); + boolean newChunk = false; + // CraftBukkit end @@ -0,0 +0,0 @@ public class ChunkProviderServer implements IChunkProvider { continue; } @@ -111,6 +117,14 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 if (neighbor != null) { neighbor.setNeighborLoaded(-x, -z); chunk.setNeighborLoaded(x, z); +@@ -0,0 +0,0 @@ public class ChunkProviderServer implements IChunkProvider { + chunk.loadNearby(this, this.chunkGenerator); + world.timings.syncChunkLoadTimer.stopTiming(); // Spigot + } ++ markChunkActive(chunk); // Paper + + return chunk; + } @@ -0,0 +0,0 @@ public class ChunkProviderServer implements IChunkProvider { if (!this.world.savingDisabled) { // CraftBukkit start @@ -148,19 +162,66 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } + + // Paper start -+ public class ChunkUnloadQueue extends java.util.LinkedList<Chunk> { -+ public void remove(int x, int z) { -+ // nothing! Just to reduce diff ++ public static void markChunkActive(Chunk chunk) { ++ if (chunk != null) { ++ chunk.isChunkActive = true; + } -+ public void add(int x, int z) { ++ } ++ public class ChunkUnloadQueue extends LongHashSet { // Provide compat with NMS plugins ++ private java.util.LinkedList<Chunk> unloadQueue = new java.util.LinkedList<Chunk>(); ++ ++ @Override ++ public boolean isEmpty() { ++ return unloadQueue.isEmpty(); ++ } ++ ++ @Override ++ public boolean contains(long value) { ++ throw new UnsupportedOperationException("contains on unload queue"); ++ } ++ ++ @Override ++ public boolean add(long value) { ++ throw new UnsupportedOperationException("add on unload queue"); ++ } ++ ++ @Override ++ public boolean remove(long value) { ++ throw new UnsupportedOperationException("remove on unload queue"); ++ } ++ ++ @Override ++ public int size() { ++ return unloadQueue.size(); ++ } ++ ++ @Override ++ public Iterator iterator() { ++ return unloadQueue.iterator(); ++ } ++ ++ @Override ++ public boolean contains(int x, int z) { ++ final Chunk chunk = chunks.get(LongHash.toLong(x, z)); ++ return (chunk != null && chunk.isInUnloadQueue); ++ } ++ ++ public void remove(int x, int z) { ++ final Chunk chunk = chunks.get(LongHash.toLong(x, z)); ++ if (chunk != null) { ++ chunk.isChunkActive = true; ++ } ++ } ++ public boolean add(int x, int z) { + final Chunk chunk = chunks.get(LongHash.toLong(x, z)); + if (chunk != null) { + chunk.isChunkActive = false; + if (!chunk.isInUnloadQueue) { + chunk.isInUnloadQueue = true; -+ add(chunk); ++ return unloadQueue.add(chunk); + } + } ++ return false; + } + } + // Paper end @@ -183,7 +244,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java @@ -0,0 +0,0 @@ public abstract class World implements IBlockAccess { - } + // Paper end public Chunk getChunkIfLoaded(int x, int z) { - return ((ChunkProviderServer) this.chunkProvider).getChunkIfLoaded(x, z); @@ -222,14 +283,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 neighbor.setNeighborUnloaded(-xx, -zz); chunk.setNeighborUnloaded(xx, zz); @@ -0,0 +0,0 @@ public class CraftWorld implements World { - world.timings.syncChunkLoadTimer.startTiming(); // Spigot - chunk = world.getChunkProviderServer().getOrLoadChunkAt(x, z); - world.timings.syncChunkLoadTimer.stopTiming(); // Spigot -- } -+ } else { chunk.isChunkActive = true; } // Paper - return chunk != null; - } + // Use the default variant of loadChunk when generate == true. + return world.getChunkProviderServer().getChunkAt(x, z) != null; + } ++ if (true) { return world.getChunkProviderServer().getOrLoadChunkAt(x, z) != null; } // Paper + world.getChunkProviderServer().unloadQueue.remove(x, z); + net.minecraft.server.Chunk chunk = world.getChunkProviderServer().chunks.get(LongHash.toLong(x, z)); @@ -0,0 +0,0 @@ public class CraftWorld implements World { continue; } @@ -244,7 +304,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 // Already unloading? - if (cps.unloadQueue.contains(chunk.locX, chunk.locZ)) { -+ if (!chunk.isChunkActive) { // Paper ++ if (chunk.isInUnloadQueue) { // Paper continue; } @@ -265,12 +325,25 @@ diff --git a/src/main/java/org/spigotmc/ActivationRange.java b/src/main/java/org index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/spigotmc/ActivationRange.java +++ b/src/main/java/org/spigotmc/ActivationRange.java +@@ -0,0 +0,0 @@ public class ActivationRange + { + for ( int j1 = k; j1 <= l; ++j1 ) + { +- if ( world.getWorld().isChunkLoaded( i1, j1 ) ) +- { +- activateChunkEntities( world.getChunkAt( i1, j1 ) ); ++ Chunk chunk = world.getChunkIfActive(i1, j1); // Paper ++ if (chunk != null) { // Paper ++ activateChunkEntities( chunk ); // Paper + } + } + } @@ -0,0 +0,0 @@ public class ActivationRange int x = MathHelper.floor( entity.locX ); int z = MathHelper.floor( entity.locZ ); // Make sure not on edge of unloaded chunk - Chunk chunk = entity.world.getChunkIfLoaded( x >> 4, z >> 4 ); -+ Chunk chunk = entity.world.getChunkIfActive( x >> 4, z >> 4 ); // Paper ++ Chunk chunk = isActive ? entity.world.getChunkIfActive( x >> 4, z >> 4 ) : null; // Paper if ( isActive && !( chunk != null && chunk.areNeighborsLoaded( 1 ) ) ) { isActive = false;