3
0

Cap chunk generation to 1 chunk per tick (configurable)

This commit is contained in:
Sergey Shatunov 2016-03-16 12:01:28 +07:00
parent 232bc98881
commit 8d2ba00f70
9 changed files with 261 additions and 60 deletions

View File

@ -11,7 +11,15 @@
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
@@ -282,6 +286,7 @@
@@ -245,6 +249,7 @@
public void onPostServerTick()
{
bus().post(new TickEvent.ServerTickEvent(Phase.END));
+ kcauldron.ChunkGenerator.INSTANCE.chunkGeneratorCycle(); // KCauldron
}
/**
@@ -282,6 +287,7 @@
{
Loader.instance().serverStarted();
sidedDelegate.allowLogins();
@ -19,7 +27,7 @@
}
public void handleServerStopping()
@@ -385,10 +390,11 @@
@@ -385,10 +391,11 @@
{
return;
}
@ -32,7 +40,7 @@
handlerSet.add(handler);
handlerToCheck = new WeakReference<SaveHandler>(handler); // for confirmBackupLevelDatUse
Map<String,NBTBase> additionalProperties = Maps.newHashMap();
@@ -496,7 +502,13 @@
@@ -496,7 +503,13 @@
public String getModName()
{
@ -47,7 +55,7 @@
modNames.add("fml");
if (!noForge)
{
@@ -540,8 +552,17 @@
@@ -540,8 +553,17 @@
bus().post(new InputEvent.KeyInputEvent());
}

View File

@ -15,40 +15,15 @@
public class BiomeDecorator
{
public World currentWorld;
@@ -61,6 +69,8 @@
@@ -61,6 +69,7 @@
public int clayPerChunk;
public int bigMushroomsPerChunk;
public boolean generateLakes;
+ private final List<Chunk> chunksToUnload = new ArrayList<Chunk>(); // Spigot
+ private final java.util.concurrent.locks.Lock decorateLock = new java.util.concurrent.locks.ReentrantLock();
private static final String __OBFID = "CL_00000164";
public BiomeDecorator()
@@ -92,12 +102,8 @@
public void decorateChunk(World p_150512_1_, Random p_150512_2_, BiomeGenBase p_150512_3_, int p_150512_4_, int p_150512_5_)
{
- if (this.currentWorld != null)
- {
- throw new RuntimeException("Already decorating!!");
- }
- else
- {
+ decorateLock.lock();
+ try {
this.currentWorld = p_150512_1_;
this.randomGenerator = p_150512_2_;
this.chunk_X = p_150512_4_;
@@ -105,6 +111,8 @@
this.genDecorations(p_150512_3_);
this.currentWorld = null;
this.randomGenerator = null;
+ } finally {
+ decorateLock.unlock();
}
}
@@ -194,7 +202,7 @@
@@ -194,7 +196,7 @@
{
k = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
@ -57,7 +32,7 @@
WorldGenerator worldgenerator = p_150513_1_.getRandomWorldGenForGrass(this.randomGenerator);
worldgenerator.generate(this.currentWorld, this.randomGenerator, k, i1, l);
}
@@ -204,7 +212,7 @@
@@ -204,7 +206,7 @@
{
k = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
@ -66,7 +41,7 @@
(new WorldGenDeadBush(Blocks.deadbush)).generate(this.currentWorld, this.randomGenerator, k, i1, l);
}
@@ -214,7 +222,7 @@
@@ -214,7 +216,7 @@
k = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
@ -75,7 +50,7 @@
{
;
}
@@ -229,7 +237,7 @@
@@ -229,7 +231,7 @@
{
k = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
@ -84,7 +59,7 @@
this.mushroomBrownGen.generate(this.currentWorld, this.randomGenerator, k, i1, l);
}
@@ -237,7 +245,7 @@
@@ -237,7 +239,7 @@
{
k = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
@ -93,7 +68,7 @@
this.mushroomRedGen.generate(this.currentWorld, this.randomGenerator, k, i1, l);
}
}
@@ -246,7 +254,7 @@
@@ -246,7 +248,7 @@
{
j = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
k = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
@ -102,7 +77,7 @@
this.mushroomBrownGen.generate(this.currentWorld, this.randomGenerator, j, l, k);
}
@@ -254,7 +262,7 @@
@@ -254,7 +256,7 @@
{
j = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
k = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
@ -111,7 +86,7 @@
this.mushroomRedGen.generate(this.currentWorld, this.randomGenerator, j, l, k);
}
@@ -263,7 +271,7 @@
@@ -263,7 +265,7 @@
{
k = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
@ -120,7 +95,7 @@
this.reedGen.generate(this.currentWorld, this.randomGenerator, k, i1, l);
}
@@ -271,7 +279,7 @@
@@ -271,7 +273,7 @@
{
k = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
@ -129,7 +104,7 @@
this.reedGen.generate(this.currentWorld, this.randomGenerator, k, i1, l);
}
@@ -280,7 +288,7 @@
@@ -280,7 +282,7 @@
{
j = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
k = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
@ -138,7 +113,7 @@
(new WorldGenPumpkin()).generate(this.currentWorld, this.randomGenerator, j, l, k);
}
@@ -289,7 +297,7 @@
@@ -289,7 +291,7 @@
{
k = this.chunk_X + this.randomGenerator.nextInt(16) + 8;
l = this.chunk_Z + this.randomGenerator.nextInt(16) + 8;
@ -147,7 +122,7 @@
this.cactusGen.generate(this.currentWorld, this.randomGenerator, k, i1, l);
}
@@ -313,6 +321,7 @@
@@ -313,6 +315,7 @@
}
}
@ -155,7 +130,7 @@
MinecraftForge.EVENT_BUS.post(new DecorateBiomeEvent.Post(currentWorld, randomGenerator, chunk_X, chunk_Z));
}
@@ -360,6 +369,31 @@
@@ -360,6 +363,31 @@
MinecraftForge.ORE_GEN_BUS.post(new OreGenEvent.Post(currentWorld, randomGenerator, chunk_X, chunk_Z));
}

View File

@ -188,13 +188,27 @@
// We can only use the queue for already generated chunks
if (chunk == null && loader != null && loader.chunkExists(this.worldObj, par1, par2))
{
@@ -142,18 +213,19 @@
@@ -128,6 +199,12 @@
}
else if (chunk == null)
{
+ // KCauldron start
+ if (runnable != null) {
+ kcauldron.ChunkGenerator.INSTANCE.queueChunkGeneration(this, par1, par2, new net.minecraftforge.common.chunkio.ChunkIOExecutor.RunnableCallback(runnable));
+ return null;
+ }
+ // KCauldron end
chunk = this.originalLoadChunk(par1, par2);
}
@@ -142,18 +219,20 @@
public Chunk originalLoadChunk(int p_73158_1_, int p_73158_2_)
{
- long k = ChunkCoordIntPair.chunkXZ2Int(p_73158_1_, p_73158_2_);
- this.chunksToUnload.remove(Long.valueOf(k));
- Chunk chunk = (Chunk)this.loadedChunkHashMap.getValueByKey(k);
+ generatorLock.lock(); try { // KCauldron
+ this.chunksToUnload.remove(p_73158_1_, p_73158_2_);
+ Chunk chunk = (Chunk) this.chunkManager.getChunk(p_73158_1_, p_73158_2_); // KCauldron
+ boolean newChunk = false; // CraftBukkit
@ -213,7 +227,7 @@
if (chunk == null)
{
chunk = this.safeLoadChunk(p_73158_1_, p_73158_2_);
@@ -176,18 +248,53 @@
@@ -176,30 +255,84 @@
CrashReport crashreport = CrashReport.makeCrashReport(throwable, "Exception generating new chunk");
CrashReportCategory crashreportcategory = crashreport.makeCategory("Chunk to be generated");
crashreportcategory.addCrashSection("Location", String.format("%d,%d", new Object[] {Integer.valueOf(p_73158_1_), Integer.valueOf(p_73158_2_)}));
@ -272,7 +286,8 @@
}
return chunk;
@@ -195,11 +302,29 @@
+ } finally { generatorLock.unlock(); } // KCauldron
}
public Chunk provideChunk(int p_73154_1_, int p_73154_2_)
{
@ -305,7 +320,7 @@
{
if (this.currentChunkLoader == null)
{
@@ -209,6 +334,7 @@
@@ -209,6 +342,7 @@
{
try
{
@ -313,7 +328,7 @@
Chunk chunk = this.currentChunkLoader.loadChunk(this.worldObj, p_73239_1_, p_73239_2_);
if (chunk != null)
@@ -217,8 +343,11 @@
@@ -217,8 +351,11 @@
if (this.currentChunkProvider != null)
{
@ -325,7 +340,7 @@
}
return chunk;
@@ -231,7 +360,7 @@
@@ -231,7 +368,7 @@
}
}
@ -334,7 +349,7 @@
{
if (this.currentChunkLoader != null)
{
@@ -246,7 +375,7 @@
@@ -246,7 +383,7 @@
}
}
@ -343,7 +358,7 @@
{
if (this.currentChunkLoader != null)
{
@@ -254,15 +383,18 @@
@@ -254,15 +391,18 @@
{
p_73242_1_.lastSaveTime = this.worldObj.getTotalWorldTime();
this.currentChunkLoader.saveChunk(this.worldObj, p_73242_1_);
@ -363,7 +378,7 @@
}
}
@@ -277,6 +409,35 @@
@@ -277,6 +417,35 @@
if (this.currentChunkProvider != null)
{
this.currentChunkProvider.populate(p_73153_1_, p_73153_2_, p_73153_3_);
@ -399,7 +414,7 @@
GameRegistry.generateWorld(p_73153_2_, p_73153_3_, worldObj, currentChunkProvider, p_73153_1_);
chunk.setChunkModified();
}
@@ -286,11 +447,13 @@
@@ -286,11 +455,13 @@
public boolean saveChunks(boolean p_73151_1_, IProgressUpdate p_73151_2_)
{
int i = 0;
@ -416,7 +431,7 @@
if (p_73151_1_)
{
@@ -325,36 +488,73 @@
@@ -325,36 +496,73 @@
{
if (!this.worldObj.levelSaving)
{
@ -509,7 +524,7 @@
if (this.currentChunkLoader != null)
{
this.currentChunkLoader.chunkTick();
@@ -371,7 +571,7 @@
@@ -371,7 +579,7 @@
public String makeString()
{
@ -518,7 +533,7 @@
}
public List getPossibleCreatures(EnumCreatureType p_73155_1_, int p_73155_2_, int p_73155_3_, int p_73155_4_)
@@ -386,8 +586,30 @@
@@ -386,8 +594,53 @@
public int getLoadedChunkCount()
{
@ -549,4 +564,27 @@
+ return MinecraftServer.getServer().getTickCounter() - chunk.lastAccessedTick > CauldronConfig.chunkGCGracePeriod.getValue();
+ }*/
+ // Cauldron end
+ // KCauldron start
+ private final java.util.concurrent.locks.Lock generatorLock = new java.util.concurrent.locks.ReentrantLock();
+
+ public boolean loadAsync(int x, int z, boolean generateOnRequest, kcauldron.ChunkCallback callback) {
+ Chunk chunk = getChunkIfLoaded(x, z);
+ if (chunk != null) {
+ callback.onChunkLoaded(chunk);
+ } else if (((net.minecraft.world.chunk.storage.AnvilChunkLoader) currentChunkLoader).chunkExists(worldObj, x, z)) {
+ net.minecraftforge.common.chunkio.ChunkIOExecutor.queueChunkLoad(this.worldObj,
+ (net.minecraft.world.chunk.storage.AnvilChunkLoader) currentChunkLoader, this, x, z, callback);
+ } else if (generateOnRequest) {
+ callback.onChunkLoaded(originalLoadChunk(x, z));
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ public void loadAsync(int x, int z, kcauldron.ChunkCallback callback) {
+ if (!loadAsync(x, z, false, callback))
+ kcauldron.ChunkGenerator.INSTANCE.queueChunkGeneration(this, x, z, callback);
+ }
+ // KCauldron end
}

View File

@ -1,6 +1,25 @@
--- ../src-base/minecraft/net/minecraftforge/common/chunkio/ChunkIOExecutor.java
+++ ../src-work/minecraft/net/minecraftforge/common/chunkio/ChunkIOExecutor.java
@@ -22,7 +22,7 @@
@@ -6,27 +6,57 @@
static final int BASE_THREADS = 1;
static final int PLAYERS_PER_THREAD = 50;
- private static final AsynchronousExecutor<QueuedChunk, net.minecraft.world.chunk.Chunk, Runnable, RuntimeException> instance = new AsynchronousExecutor<QueuedChunk, net.minecraft.world.chunk.Chunk, Runnable, RuntimeException>(new ChunkIOProvider(), BASE_THREADS);
+ private static final AsynchronousExecutor<QueuedChunk, net.minecraft.world.chunk.Chunk, kcauldron.ChunkCallback, RuntimeException> instance = new AsynchronousExecutor<QueuedChunk, net.minecraft.world.chunk.Chunk, kcauldron.ChunkCallback, RuntimeException>(new ChunkIOProvider(), BASE_THREADS); // KCauldron
public static net.minecraft.world.chunk.Chunk syncChunkLoad(net.minecraft.world.World world, net.minecraft.world.chunk.storage.AnvilChunkLoader loader, net.minecraft.world.gen.ChunkProviderServer provider, int x, int z) {
return instance.getSkipQueue(new QueuedChunk(x, z, loader, world, provider));
}
public static void queueChunkLoad(net.minecraft.world.World world, net.minecraft.world.chunk.storage.AnvilChunkLoader loader, net.minecraft.world.gen.ChunkProviderServer provider, int x, int z, Runnable runnable) {
- instance.add(new QueuedChunk(x, z, loader, world, provider), runnable);
+ instance.add(new QueuedChunk(x, z, loader, world, provider), new RunnableCallback(runnable)); // KCauldron
}
// Abuses the fact that hashCode and equals for QueuedChunk only use world and coords
public static void dropQueuedChunkLoad(net.minecraft.world.World world, int x, int z, Runnable runnable) {
- instance.drop(new QueuedChunk(x, z, null, world, null), runnable);
+ instance.drop(new QueuedChunk(x, z, null, world, null), new RunnableCallback(runnable));
}
public static void adjustPoolSize(int players) {
@ -9,3 +28,37 @@
instance.setActiveThreads(size);
}
public static void tick() {
instance.finishActive();
}
+
+ // KCauldron start
+ public static void queueChunkLoad(net.minecraft.world.World world,
+ net.minecraft.world.chunk.storage.AnvilChunkLoader loader,
+ net.minecraft.world.gen.ChunkProviderServer provider, int x, int z, kcauldron.ChunkCallback runnable) {
+ instance.add(new QueuedChunk(x, z, loader, world, provider), runnable);
+ }
+
+ public static final class RunnableCallback implements kcauldron.ChunkCallback {
+ private final Runnable runnable;
+
+ public RunnableCallback(Runnable runnable) {
+ this.runnable = runnable;
+ }
+
+ @Override
+ public void onChunkLoaded(net.minecraft.world.chunk.Chunk chunk) {
+ runnable.run();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+ Runnable runnable = obj instanceof Runnable ? (Runnable) obj
+ : obj instanceof RunnableCallback ? ((RunnableCallback) obj).runnable : null;
+ return this.runnable == runnable;
+ }
+ }
+ // KCauldron end
}

View File

@ -1,16 +1,18 @@
--- ../src-base/minecraft/net/minecraftforge/common/chunkio/ChunkIOProvider.java
+++ ../src-work/minecraft/net/minecraftforge/common/chunkio/ChunkIOProvider.java
@@ -9,6 +9,9 @@
@@ -9,7 +9,10 @@
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
-class ChunkIOProvider implements AsynchronousExecutor.CallBackProvider<QueuedChunk, net.minecraft.world.chunk.Chunk, Runnable, RuntimeException> {
+import org.bukkit.Server;
+import org.bukkit.craftbukkit.util.LongHash;
+
class ChunkIOProvider implements AsynchronousExecutor.CallBackProvider<QueuedChunk, net.minecraft.world.chunk.Chunk, Runnable, RuntimeException> {
+class ChunkIOProvider implements AsynchronousExecutor.CallBackProvider<QueuedChunk, net.minecraft.world.chunk.Chunk, kcauldron.ChunkCallback, RuntimeException> {
private final AtomicInteger threadNumber = new AtomicInteger(1);
@@ -41,14 +44,36 @@
// async stuff
@@ -41,19 +44,41 @@
queuedChunk.loader.loadEntities(queuedChunk.world, queuedChunk.compound.getCompoundTag("Level"), chunk);
MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Load(chunk, queuedChunk.compound)); // Don't call ChunkDataEvent.Load async
chunk.lastSaveTime = queuedChunk.provider.worldObj.getTotalWorldTime();
@ -48,3 +50,10 @@
chunk.populateChunk(queuedChunk.provider, queuedChunk.provider, queuedChunk.x, queuedChunk.z);
}
- public void callStage3(QueuedChunk queuedChunk, net.minecraft.world.chunk.Chunk chunk, Runnable runnable) throws RuntimeException {
- runnable.run();
+ public void callStage3(QueuedChunk queuedChunk, net.minecraft.world.chunk.Chunk chunk, kcauldron.ChunkCallback callback) throws RuntimeException {
+ callback.onChunkLoaded(chunk);
}
public Thread newThread(Runnable runnable) {

View File

@ -0,0 +1,13 @@
--- ../src-base/minecraft/net/minecraftforge/common/util/AsynchronousExecutor.java
+++ ../src-work/minecraft/net/minecraftforge/common/util/AsynchronousExecutor.java
@@ -268,8 +268,8 @@
if (task == null) {
// Print debug info for QueuedChunk and avoid crash
//throw new IllegalStateException("Unknown " + parameter);
- FMLLog.info("Unknown %s", parameter);
- FMLLog.info("This should not happen. Please report this error to Forge.");
+ //FMLLog.info("Unknown %s", parameter); // KCauldron
+ //FMLLog.info("This should not happen. Please report this error to Forge."); // KCauldron
return false;
}
if (!task.callbacks.remove(callback)) {

View File

@ -0,0 +1,7 @@
package kcauldron;
import net.minecraft.world.chunk.Chunk;
public interface ChunkCallback {
void onChunkLoaded(Chunk chunk);
}

View File

@ -0,0 +1,96 @@
package kcauldron;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import com.google.common.collect.Lists;
import com.google.common.collect.Queues;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.LongHashMap;
import net.minecraft.world.ChunkCoordIntPair;
import net.minecraft.world.chunk.Chunk;
import net.minecraft.world.gen.ChunkProviderServer;
public enum ChunkGenerator {
INSTANCE;
private final Queue<QueuedChunk> queue = Queues.newArrayDeque();
private final LongHashMap map = new LongHashMap();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
public void queueChunkGeneration(ChunkProviderServer provider, int cx, int cz, ChunkCallback callback) {
long key = ChunkCoordIntPair.chunkXZ2Int(cx, cz);
QueuedChunk chunk;
lock.readLock().lock();
try {
chunk = (QueuedChunk) map.getValueByKey(key);
} finally {
lock.readLock().unlock();
}
if (chunk != null) {
chunk.callbacks.add(callback);
} else {
chunk = new QueuedChunk(provider, cx, cz, callback);
lock.writeLock().lock();
try {
map.add(key, chunk);
queue.add(chunk);
} finally {
lock.writeLock().unlock();
}
}
}
public void chunkGeneratorCycle() {
int max = MinecraftServer.kcauldronConfig.commonMaxChunkGenPerTick.getValue();
lock.writeLock().lock();
try {
for (int i = 0; i < max && internalGenerate(); i++)
;
} finally {
lock.writeLock().unlock();
}
}
public boolean generate() {
lock.writeLock().lock();
try {
return internalGenerate();
} finally {
lock.writeLock().unlock();
}
}
private boolean internalGenerate() {
for (QueuedChunk chunk; (chunk = queue.poll()) != null;) {
map.remove(ChunkCoordIntPair.chunkXZ2Int(chunk.cx, chunk.cz));
if (chunk.provider.loadAsync(chunk.cx, chunk.cz, false, chunk))
continue;
chunk.onChunkLoaded(chunk.provider.originalLoadChunk(chunk.cx, chunk.cz));
return true;
}
return false;
}
private static class QueuedChunk implements ChunkCallback {
public final ChunkProviderServer provider;
public final int cx;
public final int cz;
public final List<ChunkCallback> callbacks = Lists.newArrayListWithCapacity(1);
public QueuedChunk(ChunkProviderServer provider, int cx, int cz, ChunkCallback callback) {
this.provider = provider;
this.cx = cx;
this.cz = cz;
this.callbacks.add(callback);
}
@Override
public void onChunkLoaded(Chunk chunk) {
for (ChunkCallback cb : callbacks)
cb.onChunkLoaded(chunk);
}
}
}

View File

@ -36,6 +36,8 @@ public class KCauldronConfig extends ConfigBase {
"common.fastLeavesDecay.minTickTime", 5, "Minimal amount of tick between block updates");
public IntSetting commonFastLeavesDecayMaxTickTime = new IntSetting(this,
"common.fastLeavesDecay.maxTickTime", 10, "Minimal amount of tick between block updates");
public IntSetting commonMaxChunkGenPerTick = new IntSetting(this,
"common.maxChunkGenPerTick", 1, "How many chunks generate during tick");
public BoolSetting experimentalTileEntityListRecreation = new BoolSetting(this,
"experimental.tileEntityListRecreation", false, "EXPERIMENTAL! Recreate list of TE each tick.");