3
0

ChunkManager implementation

ChunkManager optimize chunks handling in small and big worlds

Small worlds gets benefits with using int-object map instead long-object (in area -32768...32767 by x and z)

Large worlds gets benefits by using efficient map by Colt project, which a little (3-6%) slower in writing, but greatly faster
(400-500% from Java's HashMap and 150-180% from Trove's maps) on reading.
This commit is contained in:
Sergey Shatunov
2016-02-10 19:17:47 +07:00
parent 6f447f2804
commit 5aaf6c5cbf
12 changed files with 202 additions and 111 deletions

View File

@ -0,0 +1,73 @@
package kcauldron;
import cern.colt.function.IntObjectProcedure;
import cern.colt.function.LongObjectProcedure;
import cern.colt.function.ObjectProcedure;
import cern.colt.map.OpenIntObjectHashMap;
import cern.colt.map.OpenLongObjectHashMap;
import net.minecraft.world.chunk.Chunk;
public class ChunkManager {
private static final int MIN = -32768, MAX = 32767;
public OpenIntObjectHashMap loadedChunksLSB = new OpenIntObjectHashMap();
public OpenLongObjectHashMap loadefChunksMSB = new OpenLongObjectHashMap();
public ChunkManager() {
}
public Chunk getChunk(final int x, final int z) {
if (MIN <= x && MIN <= z && x <= MAX && z <= MAX)
return (Chunk) loadedChunksLSB.get(x << 16 | (z & 0xFFFF));
else
return (Chunk) loadefChunksMSB.get(((long) x) << 32 | z);
}
public boolean chunkExists(final int x, final int z) {
if (MIN <= x && MIN <= z && x <= MAX && z <= MAX)
return loadedChunksLSB.containsKey(x << 16 | (z & 0xFFFF));
else
return loadefChunksMSB.containsKey(((long) x) << 32 | z);
}
public void putChunk(Chunk chunk) {
putChunk(chunk, chunk.xPosition, chunk.zPosition);
}
public void putChunk(Chunk chunk, final int x, final int z) {
if (MIN <= x && MIN <= z && x <= MAX && z <= MAX)
loadedChunksLSB.put(x << 16 | (z & 0xFFFF), chunk);
else
loadefChunksMSB.put(((long) x) << 32 | z, chunk);
}
public boolean removeKey(final int x, final int z) {
if (MIN <= x && MIN <= z && x <= MAX && z <= MAX)
return loadedChunksLSB.removeKey(x << 16 | (z & 0xFFFF));
else
return loadefChunksMSB.removeKey(((long) x) << 32 | z);
}
public Chunk remove(final int x, final int z) {
Chunk chunk = getChunk(x, z);
return removeKey(x, z) ? chunk : null;
}
public int size() {
return loadedChunksLSB.size() + loadefChunksMSB.size();
}
public void forEach(final ObjectProcedure proc) {
loadedChunksLSB.forEachPair(new IntObjectProcedure() {
@Override
public boolean apply(int arg0, Object arg1) {
return proc.apply(arg1);
}
});
loadefChunksMSB.forEachPair(new LongObjectProcedure() {
@Override
public boolean apply(long arg0, Object arg1) {
return proc.apply(arg1);
}
});
}
}

View File

@ -1,36 +1,37 @@
package kcauldron.wrapper;
import gnu.trove.map.TLongObjectMap;
import kcauldron.ChunkManager;
import net.minecraft.util.LongHashMap;
import net.minecraft.world.chunk.Chunk;
import org.bukkit.craftbukkit.util.LongHash;
public class VanillaChunkHashMap extends LongHashMap {
private ChunkManager manager;
public class VanillaChunkHashMap extends LongHashMapTrove<Chunk> {
public VanillaChunkHashMap(TLongObjectMap<Chunk> map) {
super(map);
public VanillaChunkHashMap(ChunkManager manager) {
this.manager = manager;
}
private static long V2B(long key) {
return LongHash.toLong((int) (key & 0xFFFFFFFFL), (int) (key >>> 32));
}
@Override
public void add(long key, Object value) {
super.add(V2B(key), value);
manager.putChunk((Chunk) value);
}
@Override
public boolean containsItem(long key) {
return super.containsItem(V2B(key));
return getValueByKey(key) != null;
}
@Override
public Object getValueByKey(long key) {
return super.getValueByKey(V2B(key));
final int x = (int) (key >>> 32);
final int z = (int) (key);
return manager.getChunk(x, z);
}
@Override
public Object remove(long key) {
return super.remove(V2B(key));
final int x = (int) (key >>> 32);
final int z = (int) (key);
return manager.remove(x, z);
}
}

View File

@ -332,7 +332,7 @@ public class CauldronHooks
writer.name("name").value(world.getWorld().getName());
writer.name("dimensionId").value(world.provider.dimensionId);
writer.name("players").value(world.playerEntities.size());
writer.name("loadedChunks").value(world.theChunkProviderServer.loadedChunkHashMap_KC.size());
writer.name("loadedChunks").value(world.theChunkProviderServer.chunkManager.size());
writer.name("activeChunks").value(world.activeChunkSet.size());
writer.name("entities").value(world.loadedEntityList.size());
writer.name("tiles").value(world.loadedTileEntityList.size());

View File

@ -110,7 +110,7 @@ public class CauldronCommand extends Command
for (net.minecraft.world.WorldServer world : MinecraftServer.getServer().worlds)
{
sender.sendMessage(ChatColor.GOLD + "Dimension: " + ChatColor.GRAY + world.provider.dimensionId +
ChatColor.GOLD + " Loaded Chunks: " + ChatColor.GRAY + world.theChunkProviderServer.loadedChunkHashMap_KC.size() +
ChatColor.GOLD + " Loaded Chunks: " + ChatColor.GRAY + world.theChunkProviderServer.chunkManager.size() +
ChatColor.GOLD + " Active Chunks: " + ChatColor.GRAY + world.activeChunkSet.size() +
ChatColor.GOLD + " Entities: " + ChatColor.GRAY + world.loadedEntityList.size() +
ChatColor.GOLD + " Tile Entities: " + ChatColor.GRAY + world.loadedTileEntityList.size()

View File

@ -138,14 +138,18 @@ public class CraftWorld implements World {
}
public Chunk[] getLoadedChunks() {
Object[] chunks = world.theChunkProviderServer.loadedChunkHashMap_KC.values();
org.bukkit.Chunk[] craftChunks = new CraftChunk[chunks.length];
for (int i = 0; i < chunks.length; i++) {
net.minecraft.world.chunk.Chunk chunk = (net.minecraft.world.chunk.Chunk) chunks[i];
craftChunks[i] = chunk.bukkitChunk;
}
// KCauldron start
final kcauldron.ChunkManager chunkManager = world.theChunkProviderServer.chunkManager;
final org.bukkit.Chunk[] craftChunks = new CraftChunk[chunkManager.size()];
chunkManager.forEach(new cern.colt.function.ObjectProcedure() {
int i = 0;
@Override
public boolean apply(Object arg) {
craftChunks[i++] = ((net.minecraft.world.chunk.Chunk) arg).bukkitChunk;
return true;
}
});
// KCauldron end
return craftChunks;
}
@ -262,7 +266,7 @@ public class CraftWorld implements World {
}
world.theChunkProviderServer.chunksToUnload.remove(x, z);
net.minecraft.world.chunk.Chunk chunk = world.theChunkProviderServer.loadedChunkHashMap_KC.get(LongHash.toLong(x, z));
net.minecraft.world.chunk.Chunk chunk = world.theChunkProviderServer.chunkManager.getChunk(x, z);
if (chunk == null) {
world.timings.syncChunkLoadTimer.startTiming(); // Spigot
@ -276,7 +280,7 @@ public class CraftWorld implements World {
private void chunkLoadPostProcess(net.minecraft.world.chunk.Chunk chunk, int x, int z) {
if (chunk != null) {
world.theChunkProviderServer.loadedChunkHashMap_KC.put(LongHash.toLong(x, z), chunk);
world.theChunkProviderServer.chunkManager.putChunk(chunk, x, z); // KCauldron
world.theChunkProviderServer.loadedChunks.add(chunk); // Cauldron - vanilla compatibility
chunk.onChunkLoad();
@ -1394,9 +1398,10 @@ public class CraftWorld implements World {
}
final net.minecraft.world.gen.ChunkProviderServer cps = world.theChunkProviderServer;
cps.loadedChunkHashMap_KC.forEachValue(new TObjectProcedure<net.minecraft.world.chunk.Chunk>() {
cps.chunkManager.forEach(new cern.colt.function.ObjectProcedure() {
@Override
public boolean execute(net.minecraft.world.chunk.Chunk chunk) {
public boolean apply(Object arg) {
net.minecraft.world.chunk.Chunk chunk = (net.minecraft.world.chunk.Chunk) arg;
// If in use, skip it
if (isChunkInUse(chunk.xPosition, chunk.zPosition)) {
return true;

View File

@ -89,7 +89,7 @@ public class WatchdogThread extends Thread
{
log.log(Level.SEVERE, " Dimension:" + world.provider.dimensionId);
log.log(Level.SEVERE,
" Loaded Chunks: " + world.theChunkProviderServer.loadedChunkHashMap_KC.size() + " Active Chunks: " + world.activeChunkSet.size()
" Loaded Chunks: " + world.theChunkProviderServer.chunkManager.size() + " Active Chunks: " + world.activeChunkSet.size()
+ " Entities: " + world.loadedEntityList.size() + " Tile Entities: " + world.loadedTileEntityList.size());
log.log(Level.SEVERE, " Entities Last Tick: " + world.entitiesTicked);
log.log(Level.SEVERE, " Tiles Last Tick: " + world.tilesTicked);
@ -153,7 +153,7 @@ public class WatchdogThread extends Thread
for (net.minecraft.world.WorldServer world : MinecraftServer.getServer().worlds)
{
log.log(Level.WARNING, " Dimension:" + world.provider.dimensionId);
log.log(Level.WARNING, " Loaded Chunks: " + world.theChunkProviderServer.loadedChunkHashMap_KC.size() +
log.log(Level.WARNING, " Loaded Chunks: " + world.theChunkProviderServer.chunkManager.size() +
" Active Chunks: " + world.activeChunkSet.size() +
" Entities: " + world.loadedEntityList.size() +
" Tile Entities: " + world.loadedTileEntityList.size());