af480b8b95
Whilst the new behaviour was technically correct as it prevented the possibility of the chunk tick list actually increasing over time, it introduced a few issues, namely the fact that it slowed growth to unreasonable levels, and interfered with the values which server admins have finally tuned, and come to enjoy over the last few years. If it is absolutely essential that growth be halted and ticking reduced as much as possible, the config option is there for power users. If we wish to 'fix' this by default in the future, a new chunk ticking algorithm, which actually has meaningful config options should be designed.
189 lines
8.5 KiB
Diff
189 lines
8.5 KiB
Diff
From 11f449c45b4450a0c4641fe4e8de3b1abe1a1429 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
|
|
|
|
|
|
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
|
|
index cd529ec..53ab411 100644
|
|
--- a/src/main/java/net/minecraft/server/World.java
|
|
+++ b/src/main/java/net/minecraft/server/World.java
|
|
@@ -62,7 +62,7 @@ public abstract class World implements IBlockAccess {
|
|
public Scoreboard scoreboard = new Scoreboard(); // CraftBukkit - protected -> public
|
|
public boolean isStatic;
|
|
// CraftBukkit start - public, longhashset
|
|
- protected LongHashSet chunkTickList = new LongHashSet();
|
|
+ // protected LongHashSet chunkTickList = new LongHashSet(); // Spigot
|
|
private int L;
|
|
public boolean allowMonsters;
|
|
public boolean allowAnimals;
|
|
@@ -76,6 +76,30 @@ public abstract class World implements IBlockAccess {
|
|
private boolean N;
|
|
int[] I;
|
|
|
|
+ // 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 BiomeBase getBiome(int i, int j) {
|
|
if (this.isLoaded(i, 0, j)) {
|
|
Chunk chunk = this.getChunkAtWorldCoords(i, j);
|
|
@@ -125,6 +149,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.L = this.random.nextInt(12000);
|
|
this.allowMonsters = true;
|
|
@@ -1903,24 +1932,44 @@ public abstract class World implements IBlockAccess {
|
|
int j;
|
|
int k;
|
|
|
|
+ // 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);
|
|
byte b0 = 7;
|
|
|
|
- for (int l = -b0; l <= b0; ++l) {
|
|
- for (int i1 = -b0; i1 <= b0; ++i1) {
|
|
- // CraftBukkit start - Don't tick chunks queued for unload
|
|
- ChunkProviderServer chunkProviderServer = ((WorldServer) entityhuman.world).chunkProviderServer;
|
|
- if (chunkProviderServer.unloadQueue.contains(l + j, i1 + k)) {
|
|
- continue;
|
|
- }
|
|
- // CraftBukkit end
|
|
-
|
|
- this.chunkTickList.add(org.bukkit.craftbukkit.util.LongHash.toLong(l + j, i1 + k)); // CraftBukkit
|
|
+ // 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.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 b9b967f..3a8856d 100644
|
|
--- a/src/main/java/net/minecraft/server/WorldServer.java
|
|
+++ b/src/main/java/net/minecraft/server/WorldServer.java
|
|
@@ -306,10 +306,20 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate
|
|
// CraftBukkit start
|
|
// Iterator iterator = this.chunkTickList.iterator();
|
|
|
|
- for (long chunkCoord : this.chunkTickList.popAll()) {
|
|
+ // 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.isChunkLoaded( chunkX, chunkZ ) ) || ( this.chunkProviderServer.unloadQueue.contains( chunkX, chunkZ ) ) )
|
|
+ {
|
|
+ iter.remove();
|
|
+ continue;
|
|
+ }
|
|
+ // Spigot end
|
|
// ChunkCoordIntPair chunkcoordintpair = (ChunkCoordIntPair) iterator.next();
|
|
- int chunkX = LongHash.msw(chunkCoord);
|
|
- int chunkZ = LongHash.lsw(chunkCoord);
|
|
int k = chunkX * 16;
|
|
int l = chunkZ * 16;
|
|
|
|
@@ -400,6 +410,7 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate
|
|
|
|
if (block.isTicking()) {
|
|
++i;
|
|
+ this.growthOdds = (iter.value() < 1) ? this.modifiedOdds : 100; // Spigot - grow fast if no players are in this chunk (value = player count)
|
|
block.a(this, k2 + k, i3 + chunksection.getYPosition(), l2 + l, this.random);
|
|
}
|
|
}
|
|
@@ -408,6 +419,12 @@ public class WorldServer extends World implements org.bukkit.BlockChangeDelegate
|
|
|
|
this.methodProfiler.b();
|
|
}
|
|
+ // Spigot Start
|
|
+ if ( spigotConfig.clearChunksOnTick )
|
|
+ {
|
|
+ chunkTickList.clear();
|
|
+ }
|
|
+ // Spigot End
|
|
}
|
|
|
|
public boolean a(int i, int j, int k, Block block) {
|
|
diff --git a/src/main/java/org/spigotmc/SpigotWorldConfig.java b/src/main/java/org/spigotmc/SpigotWorldConfig.java
|
|
index 961ddb4..6ba7f5c 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: " + false );
|
|
+ }
|
|
}
|
|
--
|
|
1.8.3.2
|
|
|