Paper/CraftBukkit-Patches/0006-Better-Chunk-Tick-Sele...

197 lines
8.8 KiB
Diff

From 0e407f08a361f677443120db69572f7c73268167 Mon Sep 17 00:00:00 2001
From: md_5 <md_5@live.com.au>
Date: Tue, 11 Jun 2013 12:56:02 +1000
Subject: [PATCH] Better Chunk Tick Selection
An optimized chunk ticking algorithm which better selects chunks around players which are active on the server.
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
index e8c86d2..4d630b1 100644
--- a/src/main/java/net/minecraft/server/World.java
+++ b/src/main/java/net/minecraft/server/World.java
@@ -64,7 +64,7 @@ public abstract class World implements IBlockAccess {
public Scoreboard scoreboard = new Scoreboard();
public final boolean isStatic;
// CraftBukkit - longhashset
- protected LongHashSet chunkTickList = new LongHashSet();
+ // protected LongHashSet chunkTickList = new LongHashSet(); // Spigot
private int K;
public boolean allowMonsters;
public boolean allowAnimals;
@@ -85,6 +85,31 @@ public abstract class World implements IBlockAccess {
public long ticksPerMonsterSpawns;
public boolean populating;
private int tickPosition;
+
+ // Spigot start
+ protected final gnu.trove.map.hash.TLongShortHashMap chunkTickList;
+ protected float growthOdds = 100;
+ protected float modifiedOdds = 100;
+ private final byte chunkTickRadius;
+
+ public static long chunkToKey(int x, int z)
+ {
+ long k = ( ( ( (long) x ) & 0xFFFF0000L ) << 16 ) | ( ( ( (long) x ) & 0x0000FFFFL ) << 0 );
+ k |= ( ( ( (long) z ) & 0xFFFF0000L ) << 32 ) | ( ( ( (long) z ) & 0x0000FFFFL ) << 16 );
+ return k;
+ }
+
+ public static int keyToX(long k)
+ {
+ return (int) ( ( ( k >> 16 ) & 0xFFFF0000 ) | ( k & 0x0000FFFF ) );
+ }
+
+ public static int keyToZ(long k)
+ {
+ return (int) ( ( ( k >> 32 ) & 0xFFFF0000L ) | ( ( k >> 16 ) & 0x0000FFFF ) );
+ }
+ // Spigot end
+
public final org.spigotmc.SpigotWorldConfig spigotConfig; // Spigot
public CraftWorld getWorld() {
@@ -106,6 +131,11 @@ public abstract class World implements IBlockAccess {
this.ticksPerAnimalSpawns = this.getServer().getTicksPerAnimalSpawns(); // CraftBukkit
this.ticksPerMonsterSpawns = this.getServer().getTicksPerMonsterSpawns(); // CraftBukkit
// CraftBukkit end
+ // Spigot start
+ this.chunkTickRadius = (byte) ( ( this.getServer().getViewDistance() < 7 ) ? this.getServer().getViewDistance() : 7 );
+ this.chunkTickList = new gnu.trove.map.hash.TLongShortHashMap( spigotConfig.chunksPerTick * 5, 0.7f, Long.MIN_VALUE, Short.MIN_VALUE );
+ this.chunkTickList.setAutoCompactionFactor( 0 );
+ // Spigot end
this.K = this.random.nextInt(12000);
this.allowMonsters = true;
@@ -1884,17 +1914,44 @@ public abstract class World implements IBlockAccess {
int k;
int l;
+ // Spigot start
+ int optimalChunks = spigotConfig.chunksPerTick;
+ // Quick conditions to allow us to exist early
+ if ( optimalChunks <= 0 || players.isEmpty() )
+ {
+ return;
+ }
+ // Keep chunks with growth inside of the optimal chunk range
+ int chunksPerPlayer = Math.min( 200, Math.max( 1, (int) ( ( ( optimalChunks - players.size() ) / (double) players.size() ) + 0.5 ) ) );
+ int randRange = 3 + chunksPerPlayer / 30;
+ // Limit to normal tick radius - including view distance
+ randRange = ( randRange > chunkTickRadius ) ? chunkTickRadius : randRange;
+ // odds of growth happening vs growth happening in vanilla
+ this.growthOdds = this.modifiedOdds = Math.max( 35, Math.min( 100, ( ( chunksPerPlayer + 1 ) * 100F ) / 15F ) );
+ // Spigot end
for (i = 0; i < this.players.size(); ++i) {
entityhuman = (EntityHuman) this.players.get(i);
j = MathHelper.floor(entityhuman.locX / 16.0D);
k = MathHelper.floor(entityhuman.locZ / 16.0D);
l = this.q();
- for (int i1 = -l; i1 <= l; ++i1) {
- for (int j1 = -l; j1 <= l; ++j1) {
- this.chunkTickList.add(org.bukkit.craftbukkit.util.LongHash.toLong(i1 + j, j1 + k));
+ // Spigot start - Always update the chunk the player is on
+ long key = chunkToKey( j, k );
+ int existingPlayers = Math.max( 0, chunkTickList.get( key ) ); // filter out -1
+ chunkTickList.put( key, (short) ( existingPlayers + 1 ) );
+
+ // Check and see if we update the chunks surrounding the player this tick
+ for ( int chunk = 0; chunk < chunksPerPlayer; chunk++ )
+ {
+ int dx = ( random.nextBoolean() ? 1 : -1 ) * random.nextInt( randRange );
+ int dz = ( random.nextBoolean() ? 1 : -1 ) * random.nextInt( randRange );
+ long hash = chunkToKey( dx + j, dz + k );
+ if ( !chunkTickList.contains( hash ) && this.chunkProvider.isChunkLoaded(dx + j, dz + k ) )
+ {
+ chunkTickList.put( hash, (short) -1 ); // no players
}
}
+ // Spigot End
}
this.methodProfiler.b();
diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java
index 1a6024f..93e5a14 100644
--- a/src/main/java/net/minecraft/server/WorldServer.java
+++ b/src/main/java/net/minecraft/server/WorldServer.java
@@ -344,12 +344,15 @@ public class WorldServer extends World implements IAsyncTaskHandler {
protected void h() {
super.h();
if (this.worldData.getType() == WorldType.DEBUG_ALL_BLOCK_STATES) {
- Iterator iterator = this.chunkTickList.iterator();
+ // Spigot start
+ gnu.trove.iterator.TLongShortIterator iterator = this.chunkTickList.iterator();
while (iterator.hasNext()) {
- ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair) iterator.next();
+ iterator.advance();
+ long chunkCoord = iterator.key();
- this.getChunkAt(chunkcoordintpair.x, chunkcoordintpair.z).b(false);
+ this.getChunkAt(World.keyToX( chunkCoord ), World.keyToZ( chunkCoord )).b(false);
+ // Spigot end
}
} else {
@@ -361,9 +364,21 @@ public class WorldServer extends World implements IAsyncTaskHandler {
// ChunkCoordIntPair chunkcoordintpair1 = (ChunkCoordIntPair) iterator1.next();
// int k = chunkcoordintpair1.x * 16;
// int l = chunkcoordintpair1.z * 16;
- for (long chunkCoord : chunkTickList.popAll()) {
- int chunkX = LongHash.msw(chunkCoord);
- int chunkZ = LongHash.lsw(chunkCoord);
+ // Spigot start
+ for (gnu.trove.iterator.TLongShortIterator iter = chunkTickList.iterator(); iter.hasNext(); )
+ {
+ iter.advance();
+ long chunkCoord = iter.key();
+ int chunkX = World.keyToX( chunkCoord );
+ int chunkZ = World.keyToZ( chunkCoord );
+ // If unloaded, or in procedd of being unloaded, drop it
+ if ( ( !this.chunkProvider.isChunkLoaded( chunkX, chunkZ ) ) || ( this.chunkProviderServer.unloadQueue.contains( chunkX, chunkZ ) ) )
+ {
+ iter.remove();
+ continue;
+ }
+ // Spigot end
+ // ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair) iterator.next();
int k = chunkX * 16;
int l = chunkZ * 16;
@@ -458,6 +473,12 @@ public class WorldServer extends World implements IAsyncTaskHandler {
}
}
+ // Spigot Start
+ if ( spigotConfig.clearChunksOnTick )
+ {
+ chunkTickList.clear();
+ }
+ // Spigot End
}
protected BlockPosition a(BlockPosition blockposition) {
diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java
index 961ddb4..90a227f 100644
--- a/src/main/java/org/spigotmc/SpigotWorldConfig.java
+++ b/src/main/java/org/spigotmc/SpigotWorldConfig.java
@@ -68,4 +68,15 @@ public class SpigotWorldConfig
config.addDefault( "world-settings.default." + path, def );
return config.getString( "world-settings." + worldName + "." + path, config.getString( "world-settings.default." + path ) );
}
+
+ public int chunksPerTick;
+ public boolean clearChunksOnTick;
+ private void chunksPerTick()
+ {
+ chunksPerTick = getInt( "chunks-per-tick", 650 );
+ log( "Chunks to Grow per Tick: " + chunksPerTick );
+
+ clearChunksOnTick = getBoolean( "clear-tick-list", false );
+ log( "Clear tick list: " + clearChunksOnTick );
+ }
}
--
2.1.0