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:
73
src/main/java/kcauldron/ChunkManager.java
Normal file
73
src/main/java/kcauldron/ChunkManager.java
Normal 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
|
Reference in New Issue
Block a user