diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java index ade0c35bc..d770e4e6e 100644 --- a/src/main/java/net/minecraft/server/Chunk.java +++ b/src/main/java/net/minecraft/server/Chunk.java @@ -41,6 +41,34 @@ public class Chunk { public long s; private int x; + // CraftBukkit start - Neighbor loaded cache for chunk lighting and entity ticking + private int neighbors = 0x1 << 12; + + public boolean areNeighborsLoaded(final int radius) { + switch(radius) { + case 2: + return this.neighbors == Integer.MAX_VALUE >> 6; + case 1: + final int mask = + // x z offset x z offset x z offset + ( 0x1 << (1 * 5 + 1 + 12) ) | ( 0x1 << (0 * 5 + 1 + 12) ) | ( 0x1 << (-1 * 5 + 1 + 12) ) | + ( 0x1 << (1 * 5 + 0 + 12) ) | ( 0x1 << (0 * 5 + 0 + 12) ) | ( 0x1 << (-1 * 5 + 0 + 12) ) | + ( 0x1 << (1 * 5 + -1 + 12) ) | ( 0x1 << (0 * 5 + -1 + 12) ) | ( 0x1 << (-1 * 5 + -1 + 12) ); + return (this.neighbors & mask) == mask; + default: + throw new UnsupportedOperationException(String.valueOf(radius)); + } + } + + public void setNeighborLoaded(final int x, final int z) { + this.neighbors |= 0x1 << (x * 5 + 12 + z); + } + + public void setNeighborUnloaded(final int x, final int z) { + this.neighbors &= ~(0x1 << (x * 5 + 12 + z)); + } + // CraftBukkit end + public Chunk(World world, int i, int j) { this.sections = new ChunkSection[16]; this.v = new byte[256]; diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java index 1b22934d7..8cc2efd46 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -94,6 +94,10 @@ public class ChunkProviderServer implements IChunkProvider { } // CraftBukkit start - Add async variant, provide compatibility + public Chunk getChunkIfLoaded(int x, int z) { + return this.chunks.get(LongHash.toLong(x, z)); + } + public Chunk getChunkAt(int i, int j) { return getChunkAt(i, j, null); } @@ -166,6 +170,21 @@ public class ChunkProviderServer implements IChunkProvider { */ server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(chunk.bukkitChunk, newChunk)); } + + // Update neighbor counts + for (int x = -2; x < 3; x++) { + for (int z = -2; z < 3; z++) { + if (x == 0 && z == 0) { + continue; + } + + Chunk neighbor = this.getChunkIfLoaded(chunk.locX + x, chunk.locZ + z); + if (neighbor != null) { + neighbor.setNeighborLoaded(-x, -z); + chunk.setNeighborLoaded(x, z); + } + } + } // CraftBukkit end chunk.a(this, this, i, j); } @@ -327,6 +346,21 @@ public class ChunkProviderServer implements IChunkProvider { // this.unloadQueue.remove(olong); // this.chunks.remove(olong.longValue()); + + // Update neighbor counts + for (int x = -2; x < 3; x++) { + for (int z = -2; z < 3; z++) { + if (x == 0 && z == 0) { + continue; + } + + Chunk neighbor = this.getChunkIfLoaded(chunk.locX + x, chunk.locZ + z); + if (neighbor != null) { + neighbor.setNeighborUnloaded(-x, -z); + chunk.setNeighborUnloaded(x, z); + } + } + } } } // CraftBukkit end @@ -357,8 +391,8 @@ public class ChunkProviderServer implements IChunkProvider { } public int getLoadedChunks() { - // CraftBukkit - this.chunks.count() -> .values().size() - return this.chunks.values().size(); + // CraftBukkit - this.chunks.count() -> this.chunks.size() + return this.chunks.size(); } public void recreateStructures(int i, int j) {} diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java index af7069cb5..eb5da2afa 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java @@ -115,6 +115,10 @@ public abstract class World implements IBlockAccess { return (CraftServer) Bukkit.getServer(); } + public Chunk getChunkIfLoaded(int x, int z) { + return ((ChunkProviderServer) this.chunkProvider).getChunkIfLoaded(x, z); + } + // Changed signature - added gen and env public World(IDataManager idatamanager, String s, WorldSettings worldsettings, WorldProvider worldprovider, MethodProfiler methodprofiler, ChunkGenerator gen, org.bukkit.World.Environment env) { this.generator = gen; @@ -1396,7 +1400,10 @@ public abstract class World implements IBlockAccess { int j = MathHelper.floor(entity.locZ); byte b0 = 32; - if (!flag || this.b(i - b0, 0, j - b0, i + b0, 0, j + b0)) { + // CraftBukkit start - Use neighbor cache instead of looking up + Chunk startingChunk = this.getChunkIfLoaded(i >> 4, j >> 4); + if (!flag || (startingChunk != null && startingChunk.areNeighborsLoaded(2)) /* this.b(i - b0, 0, j - b0, i + b0, 0, j + b0) */) { + // CraftBukkit end entity.S = entity.locX; entity.T = entity.locY; entity.U = entity.locZ; @@ -2165,7 +2172,10 @@ public abstract class World implements IBlockAccess { } public boolean c(EnumSkyBlock enumskyblock, int i, int j, int k) { - if (!this.areChunksLoaded(i, j, k, 17)) { + // CraftBukkit start - Use neighbor cache instead of looking up + Chunk chunk = this.getChunkIfLoaded(i >> 4, k >> 4); + if (chunk == null || !chunk.areNeighborsLoaded(1) /* !this.areChunksLoaded(i, j, k, 17)*/) { + // CraftBukkit end return false; } else { int l = 0; diff --git a/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java b/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java index 8feadd112..1440349b9 100644 --- a/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java +++ b/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java @@ -28,7 +28,7 @@ class ChunkIOProvider implements AsynchronousExecutor.CallBackProvider