diff --git a/patches/cpw/mods/fml/common/FMLCommonHandler.java.patch b/patches/cpw/mods/fml/common/FMLCommonHandler.java.patch index 9a50900..fb02df6 100644 --- a/patches/cpw/mods/fml/common/FMLCommonHandler.java.patch +++ b/patches/cpw/mods/fml/common/FMLCommonHandler.java.patch @@ -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(handler); // for confirmBackupLevelDatUse Map 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()); } diff --git a/patches/net/minecraft/world/biome/BiomeDecorator.java.patch b/patches/net/minecraft/world/biome/BiomeDecorator.java.patch index 755ec82..db0829f 100644 --- a/patches/net/minecraft/world/biome/BiomeDecorator.java.patch +++ b/patches/net/minecraft/world/biome/BiomeDecorator.java.patch @@ -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 chunksToUnload = new ArrayList(); // 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)); } diff --git a/patches/net/minecraft/world/gen/ChunkProviderServer.java.patch b/patches/net/minecraft/world/gen/ChunkProviderServer.java.patch index 350f029..915b91b 100644 --- a/patches/net/minecraft/world/gen/ChunkProviderServer.java.patch +++ b/patches/net/minecraft/world/gen/ChunkProviderServer.java.patch @@ -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 } diff --git a/patches/net/minecraftforge/common/chunkio/ChunkIOExecutor.java.patch b/patches/net/minecraftforge/common/chunkio/ChunkIOExecutor.java.patch index abaf990..f913769 100644 --- a/patches/net/minecraftforge/common/chunkio/ChunkIOExecutor.java.patch +++ b/patches/net/minecraftforge/common/chunkio/ChunkIOExecutor.java.patch @@ -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 instance = new AsynchronousExecutor(new ChunkIOProvider(), BASE_THREADS); ++ private static final AsynchronousExecutor instance = new AsynchronousExecutor(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 + } diff --git a/patches/net/minecraftforge/common/chunkio/ChunkIOProvider.java.patch b/patches/net/minecraftforge/common/chunkio/ChunkIOProvider.java.patch index 671377a..80ccef8 100644 --- a/patches/net/minecraftforge/common/chunkio/ChunkIOProvider.java.patch +++ b/patches/net/minecraftforge/common/chunkio/ChunkIOProvider.java.patch @@ -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 { +import org.bukkit.Server; +import org.bukkit.craftbukkit.util.LongHash; + - class ChunkIOProvider implements AsynchronousExecutor.CallBackProvider { ++class ChunkIOProvider implements AsynchronousExecutor.CallBackProvider { 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) { diff --git a/patches/net/minecraftforge/common/util/AsynchronousExecutor.java.patch b/patches/net/minecraftforge/common/util/AsynchronousExecutor.java.patch new file mode 100644 index 0000000..3986079 --- /dev/null +++ b/patches/net/minecraftforge/common/util/AsynchronousExecutor.java.patch @@ -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)) { diff --git a/src/main/java/kcauldron/ChunkCallback.java b/src/main/java/kcauldron/ChunkCallback.java new file mode 100644 index 0000000..2be4814 --- /dev/null +++ b/src/main/java/kcauldron/ChunkCallback.java @@ -0,0 +1,7 @@ +package kcauldron; + +import net.minecraft.world.chunk.Chunk; + +public interface ChunkCallback { + void onChunkLoaded(Chunk chunk); +} diff --git a/src/main/java/kcauldron/ChunkGenerator.java b/src/main/java/kcauldron/ChunkGenerator.java new file mode 100644 index 0000000..d16faf5 --- /dev/null +++ b/src/main/java/kcauldron/ChunkGenerator.java @@ -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 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 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); + } + } +} diff --git a/src/main/java/kcauldron/KCauldronConfig.java b/src/main/java/kcauldron/KCauldronConfig.java index 05cb9af..f5e5b75 100644 --- a/src/main/java/kcauldron/KCauldronConfig.java +++ b/src/main/java/kcauldron/KCauldronConfig.java @@ -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.");