From 8dd291089bfd73acfc3a170e496bf34e855b1de1 Mon Sep 17 00:00:00 2001 From: Alfie Cleveland Date: Sun, 9 Jul 2017 00:41:19 +0100 Subject: [PATCH] Reimplement FlatMap diff --git a/src/main/java/net/frozenorb/util/ChunkFlatMap.java b/src/main/java/net/frozenorb/util/ChunkFlatMap.java new file mode 100644 index 000000000..6d937ec68 --- /dev/null +++ b/src/main/java/net/frozenorb/util/ChunkFlatMap.java @@ -0,0 +1,28 @@ +package net.frozenorb.util; + +import net.minecraft.server.Chunk; + +public class ChunkFlatMap extends FlatMap { + + private Chunk lastChunk; + + @Override + public Chunk get(int x, int z) { + Chunk last = lastChunk; // have to do this to be somewhat thread-safe-ish + + if (last != null && last.locX == x && last.locZ == z) { + return last; + } + + return lastChunk = super.get(x, z); + } + + @Override + public void remove(int x, int z) { + if (lastChunk != null && lastChunk.locX == x && lastChunk.locZ == z) { + lastChunk = null; // we don't really care for thread safety here, we'd just lose a few cache hits + } + + super.remove(x, z); + } +} diff --git a/src/main/java/net/frozenorb/util/CoordinateChunkHybridMap.java b/src/main/java/net/frozenorb/util/CoordinateChunkHybridMap.java new file mode 100644 index 000000000..bd912d30b --- /dev/null +++ b/src/main/java/net/frozenorb/util/CoordinateChunkHybridMap.java @@ -0,0 +1,10 @@ +package net.frozenorb.util; + +import net.minecraft.server.Chunk; + +public class CoordinateChunkHybridMap extends CoordinateObjectHybridMap { + + public CoordinateChunkHybridMap() { + flatMap = new ChunkFlatMap(); + } +} diff --git a/src/main/java/net/frozenorb/util/CoordinateObjectHybridMap.java b/src/main/java/net/frozenorb/util/CoordinateObjectHybridMap.java new file mode 100644 index 000000000..ac015051c --- /dev/null +++ b/src/main/java/net/frozenorb/util/CoordinateObjectHybridMap.java @@ -0,0 +1,56 @@ +package net.frozenorb.util; + +import java.util.Collection; +import java.util.Collections; + +import org.bukkit.craftbukkit.util.LongHash; + +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; + +public class CoordinateObjectHybridMap { + + private Long2ObjectOpenHashMap backingMap = new Long2ObjectOpenHashMap(); + private Collection values = Collections.unmodifiableCollection(backingMap.values()); // values() is uncached and is simply a view, so we can cache it and make it externally unmodifiable + + protected FlatMap flatMap; + + public CoordinateObjectHybridMap() { + this.flatMap = new FlatMap(); + } + + public boolean contains(int x, int z) { + return get(x, z) != null; + } + + public T get(int x, int z) { + if (x * x < FlatMap.HALF_DIAMETER_SQUARED && z * z < FlatMap.HALF_DIAMETER_SQUARED) { + return flatMap.get(x, z); + } + + return backingMap.get(LongHash.toLong(x, z)); + } + + public void remove(int x, int z) { + if (x * x < FlatMap.HALF_DIAMETER_SQUARED && z * z < FlatMap.HALF_DIAMETER_SQUARED) { + flatMap.put(x, z, null); + } + + backingMap.remove(LongHash.toLong(x, z)); + } + + public void put(int x, int z, T value) { + if (x * x < FlatMap.HALF_DIAMETER_SQUARED && z * z < FlatMap.HALF_DIAMETER_SQUARED) { + flatMap.put(x, z, value); + } + + backingMap.put(LongHash.toLong(x, z), value); + } + + public int size() { + return backingMap.size(); + } + + public Collection values() { + return values; + } +} diff --git a/src/main/java/net/frozenorb/util/FlatMap.java b/src/main/java/net/frozenorb/util/FlatMap.java new file mode 100644 index 000000000..6a5c36fa2 --- /dev/null +++ b/src/main/java/net/frozenorb/util/FlatMap.java @@ -0,0 +1,30 @@ +package net.frozenorb.util; + +public class FlatMap { + + private static final int DIAMETER = 4096; + private static final int HALF_DIAMETER = DIAMETER / 2; + private final Object[] flatLookup; + + public static final int HALF_DIAMETER_SQUARED = HALF_DIAMETER * HALF_DIAMETER; + + public FlatMap() { + this.flatLookup = new Object[DIAMETER * DIAMETER]; + } + + public void put(final int msw, final int lsw, final V value) { + this.flatLookup[(msw + HALF_DIAMETER) * DIAMETER + (lsw + HALF_DIAMETER)] = value; + } + + public void remove(final int msw, final int lsw) { + this.put(msw, lsw, null); + } + + public boolean contains(final int msw, final int lsw) { + return this.get(msw, lsw) != null; + } + + public V get(final int msw, final int lsw) { + return (V) this.flatLookup[(msw + HALF_DIAMETER) * DIAMETER + (lsw + HALF_DIAMETER)]; + } +} diff --git a/src/main/java/net/minecraft/server/ChunkProviderServer.java b/src/main/java/net/minecraft/server/ChunkProviderServer.java index b4ddc3bbf..5a061b274 100644 --- a/src/main/java/net/minecraft/server/ChunkProviderServer.java +++ b/src/main/java/net/minecraft/server/ChunkProviderServer.java @@ -1,28 +1,22 @@ package net.minecraft.server; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; import java.util.Iterator; import java.util.List; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; - -import net.minecraft.util.com.google.common.collect.Lists; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - // CraftBukkit start import java.util.Random; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.bukkit.Server; import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor; import org.bukkit.craftbukkit.util.LongHash; import org.bukkit.craftbukkit.util.LongHashSet; -import org.bukkit.craftbukkit.util.LongObjectHashMap; import org.bukkit.event.world.ChunkUnloadEvent; // CraftBukkit end +import net.frozenorb.util.CoordinateChunkHybridMap; +import net.frozenorb.util.CoordinateObjectHybridMap; + public class ChunkProviderServer implements IChunkProvider { private static final Logger b = LogManager.getLogger(); @@ -32,7 +26,8 @@ public class ChunkProviderServer implements IChunkProvider { public IChunkProvider chunkProvider; private IChunkLoader f; public boolean forceChunkLoad = false; // true -> false - public LongObjectHashMap chunks = new LongObjectHashMap(); + //public LongObjectHashMap chunks = new LongObjectHashMap(); + public CoordinateObjectHybridMap chunks = new CoordinateChunkHybridMap(); // MineHQ public WorldServer world; // CraftBukkit end @@ -44,7 +39,7 @@ public class ChunkProviderServer implements IChunkProvider { } public boolean isChunkLoaded(int i, int j) { - return this.chunks.containsKey(LongHash.toLong(i, j)); // CraftBukkit + return this.chunks.contains(i, j); // CraftBukkit // MineHQ } // CraftBukkit start - Change return type to Collection and return the values of our chunk map @@ -56,7 +51,7 @@ public class ChunkProviderServer implements IChunkProvider { public void queueUnload(int i, int j) { // PaperSpigot start - Asynchronous lighting updates - Chunk chunk = this.chunks.get(LongHash.toLong(i, j)); + Chunk chunk = this.chunks.get(i, j); // MineHQ if (chunk != null && chunk.world.paperSpigotConfig.useAsyncLighting && (chunk.pendingLightUpdates.get() > 0 || chunk.world.getTime() - chunk.lightUpdateTime < 20)) { return; } @@ -82,20 +77,22 @@ public class ChunkProviderServer implements IChunkProvider { if (k < -short1 || k > short1 || l < -short1 || l > short1 || !(this.world.keepSpawnInMemory)) { // Added 'this.world.keepSpawnInMemory' this.unloadQueue.add(i, j); - Chunk c = this.chunks.get(LongHash.toLong(i, j)); - if (c != null) { - c.mustSave = true; + // MineHQ start - don't lookup twice + if (chunk != null) { + chunk.mustSave = true; } + // MineHQ end } // CraftBukkit end } else { // CraftBukkit start this.unloadQueue.add(i, j); - Chunk c = this.chunks.get(LongHash.toLong(i, j)); - if (c != null) { - c.mustSave = true; + // MineHQ start - don't lookup twice + if (chunk != null) { + chunk.mustSave = true; } + // MineHQ end // CraftBukkit end } } @@ -112,7 +109,7 @@ 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)); + return this.chunks.get(x, z); // MineHQ } public Chunk getChunkAt(int i, int j) { @@ -121,7 +118,7 @@ public class ChunkProviderServer implements IChunkProvider { public Chunk getChunkAt(int i, int j, Runnable runnable) { this.unloadQueue.remove(i, j); - Chunk chunk = this.chunks.get(LongHash.toLong(i, j)); + Chunk chunk = this.chunks.get(i, j); // MineHQ ChunkRegionLoader loader = null; if (this.f instanceof ChunkRegionLoader) { @@ -150,7 +147,7 @@ public class ChunkProviderServer implements IChunkProvider { public Chunk originalGetChunkAt(int i, int j) { this.unloadQueue.remove(i, j); - Chunk chunk = (Chunk) this.chunks.get(LongHash.toLong(i, j)); + Chunk chunk = (Chunk) this.chunks.get(i, j); // MineHQ boolean newChunk = false; if (chunk == null) { @@ -175,7 +172,7 @@ public class ChunkProviderServer implements IChunkProvider { newChunk = true; // CraftBukkit } - this.chunks.put(LongHash.toLong(i, j), chunk); // CraftBukkit + this.chunks.put(i, j, chunk); // CraftBukkit // MineHQ chunk.addEntities(); // CraftBukkit start @@ -213,7 +210,7 @@ public class ChunkProviderServer implements IChunkProvider { public Chunk getOrCreateChunk(int i, int j) { // CraftBukkit start - Chunk chunk = (Chunk) this.chunks.get(LongHash.toLong(i, j)); + Chunk chunk = (Chunk) this.chunks.get(i, j); // MineHQ chunk = chunk == null ? (!this.world.isLoading && !this.forceChunkLoad ? this.emptyChunk : this.getChunkAt(i, j)) : chunk; if (chunk == this.emptyChunk) return chunk; @@ -360,7 +357,11 @@ public class ChunkProviderServer implements IChunkProvider { Server server = this.world.getServer(); for (int i = 0; i < 100 && !this.unloadQueue.isEmpty(); i++) { long chunkcoordinates = this.unloadQueue.popFirst(); - Chunk chunk = this.chunks.get(chunkcoordinates); + // MineHQ start + int locX = LongHash.msw(chunkcoordinates); + int locZ = LongHash.lsw(chunkcoordinates); + Chunk chunk = this.chunks.get(locX, locZ); + // MineHQ end if (chunk == null) continue; ChunkUnloadEvent event = new ChunkUnloadEvent(chunk.bukkitChunk); @@ -370,7 +371,7 @@ public class ChunkProviderServer implements IChunkProvider { chunk.removeEntities(); this.saveChunk(chunk); this.saveChunkNOP(chunk); - this.chunks.remove(chunkcoordinates); // CraftBukkit + this.chunks.remove(locX, locZ); // CraftBukkit // MineHQ } // this.unloadQueue.remove(olong); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index 8f4a96fe6..7e5ae9e4e 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -232,7 +232,7 @@ public class CraftWorld implements World { } world.chunkProviderServer.unloadQueue.remove(x, z); - world.chunkProviderServer.chunks.remove(LongHash.toLong(x, z)); + world.chunkProviderServer.chunks.remove(x, z); // MineHQ return true; } @@ -290,7 +290,7 @@ public class CraftWorld implements World { } world.chunkProviderServer.unloadQueue.remove(x, z); - net.minecraft.server.Chunk chunk = world.chunkProviderServer.chunks.get(LongHash.toLong(x, z)); + net.minecraft.server.Chunk chunk = world.chunkProviderServer.chunks.get(x, z); // MineHQ if (chunk == null) { world.timings.syncChunkLoadTimer.startTiming(); // Spigot @@ -304,7 +304,7 @@ public class CraftWorld implements World { private void chunkLoadPostProcess(net.minecraft.server.Chunk chunk, int x, int z) { if (chunk != null) { - world.chunkProviderServer.chunks.put(LongHash.toLong(x, z), chunk); + world.chunkProviderServer.chunks.put(x, z, chunk); // MineHQ chunk.addEntities(); diff --git a/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java b/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java index c249e776d..8c6f2a221 100644 --- a/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java +++ b/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java @@ -36,7 +36,7 @@ class ChunkIOProvider implements AsynchronousExecutor.CallBackProvider