From 75eb280f0ee40352910bf8148337e866adfe957a Mon Sep 17 00:00:00 2001 From: Prototik Date: Mon, 8 Jun 2015 22:59:49 +0700 Subject: [PATCH] Use concurent list of chunks in chunkproviderserver (fix #34) --- .../world/gen/ChunkProviderServer.java.patch | 104 +++++++++++------- .../org/bukkit/craftbukkit/CraftWorld.java | 36 +++--- 2 files changed, 89 insertions(+), 51 deletions(-) diff --git a/patches/net/minecraft/world/gen/ChunkProviderServer.java.patch b/patches/net/minecraft/world/gen/ChunkProviderServer.java.patch index de143f9..4f7a7f9 100644 --- a/patches/net/minecraft/world/gen/ChunkProviderServer.java.patch +++ b/patches/net/minecraft/world/gen/ChunkProviderServer.java.patch @@ -1,17 +1,50 @@ --- ../src-base/minecraft/net/minecraft/world/gen/ChunkProviderServer.java +++ ../src-work/minecraft/net/minecraft/world/gen/ChunkProviderServer.java -@@ -32,23 +32,42 @@ +@@ -1,7 +1,13 @@ + package net.minecraft.world.gen; + import com.google.common.collect.Lists; ++ + import cpw.mods.fml.common.registry.GameRegistry; ++import gnu.trove.impl.sync.TSynchronizedLongObjectMap; ++import gnu.trove.map.TLongObjectMap; ++import gnu.trove.map.hash.TLongObjectHashMap; ++import gnu.trove.procedure.TObjectProcedure; ++ + import java.io.IOException; + import java.util.ArrayList; + import java.util.Collections; +@@ -9,6 +15,7 @@ + import java.util.List; + import java.util.Set; + import java.util.concurrent.ConcurrentHashMap; ++ + import net.minecraft.crash.CrashReport; + import net.minecraft.crash.CrashReportCategory; + import net.minecraft.entity.EnumCreatureType; +@@ -33,22 +40,52 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; + ++ ++ ++ ++ +// CraftBukkit start +import java.util.Random; ++ +import net.minecraft.block.BlockSand; ++ +import org.bukkit.Server; +import org.bukkit.craftbukkit.util.LongHash; +import org.bukkit.craftbukkit.util.LongHashSet; +import org.bukkit.craftbukkit.util.LongObjectHashMap; +import org.bukkit.event.world.ChunkUnloadEvent; ++ ++ ++ ++ ++ +// CraftBukkit end +// Cauldron start +import cpw.mods.fml.common.FMLCommonHandler; @@ -19,7 +52,7 @@ +import net.minecraftforge.cauldron.configuration.CauldronConfig; +import net.minecraftforge.cauldron.CauldronHooks; +// Cauldron end - ++ public class ChunkProviderServer implements IChunkProvider { private static final Logger logger = LogManager.getLogger(); @@ -34,13 +67,12 @@ - public List loadedChunks = new ArrayList(); + public boolean loadChunkOnProvideRequest = MinecraftServer.getServer().cauldronConfig.loadChunkOnRequest.getValue(); // Cauldron - if true, allows mods to force load chunks. to disable, set load-chunk-on-request in cauldron.yml to false + public int initialTick; // Cauldron counter to keep track of when this loader was created -+ public LongObjectHashMap loadedChunkHashMap = new LongObjectHashMap(); ++ public TLongObjectMap loadedChunkHashMap = new TSynchronizedLongObjectMap(new TLongObjectHashMap()); + public List loadedChunks = new ArrayList(); // Cauldron - vanilla compatibility public WorldServer worldObj; private Set loadingChunks = com.google.common.collect.Sets.newHashSet(); + public LongHashMap loadedChunkHashMap_vanilla = new LongHashMap(); // Cauldron - vanilla/mystcraft compatibility private static final String __OBFID = "CL_00001436"; -+ private boolean mUnloading; public ChunkProviderServer(WorldServer p_i1520_1_, IChunkLoader p_i1520_2_, IChunkProvider p_i1520_3_) { @@ -48,7 +80,7 @@ this.defaultEmptyChunk = new EmptyChunk(p_i1520_1_, 0, 0); this.worldObj = p_i1520_1_; this.currentChunkLoader = p_i1520_2_; -@@ -57,10 +76,10 @@ +@@ -57,10 +94,10 @@ public boolean chunkExists(int p_73149_1_, int p_73149_2_) { @@ -61,7 +93,7 @@ { return this.loadedChunks; } -@@ -74,26 +93,47 @@ +@@ -74,26 +111,45 @@ int l = p_73241_2_ * 16 + 8 - chunkcoordinates.posZ; short short1 = 128; @@ -100,33 +132,36 @@ public void unloadAllChunks() { - Iterator iterator = this.loadedChunks.iterator(); -+ mUnloading = true; -+ Iterator iterator = this.loadedChunkHashMap.values().iterator(); // CraftBukkit - - while (iterator.hasNext()) - { - Chunk chunk = (Chunk)iterator.next(); - this.unloadChunksIfNotNearSpawn(chunk.xPosition, chunk.zPosition); - } -+ mUnloading = false; +- +- while (iterator.hasNext()) +- { +- Chunk chunk = (Chunk)iterator.next(); +- this.unloadChunksIfNotNearSpawn(chunk.xPosition, chunk.zPosition); +- } ++ this.loadedChunkHashMap.forEachValue(new TObjectProcedure() { ++ @Override ++ public boolean execute(Chunk chunk) { ++ unloadChunksIfNotNearSpawn(chunk.xPosition, chunk.zPosition); ++ return true; ++ } ++ }); } public Chunk loadChunk(int p_73158_1_, int p_73158_2_) -@@ -103,9 +143,10 @@ +@@ -103,9 +159,9 @@ public Chunk loadChunk(int par1, int par2, Runnable runnable) { - long k = ChunkCoordIntPair.chunkXZ2Int(par1, par2); - this.chunksToUnload.remove(Long.valueOf(k)); - Chunk chunk = (Chunk)this.loadedChunkHashMap.getValueByKey(k); -+ if (mUnloading) return null; + this.chunksToUnload.remove(par1, par2); + Chunk chunk = (Chunk) this.loadedChunkHashMap.get(LongHash.toLong(par1, par2)); + boolean newChunk = false; AnvilChunkLoader loader = null; if (this.currentChunkLoader instanceof AnvilChunkLoader) -@@ -113,6 +154,8 @@ +@@ -113,6 +169,8 @@ loader = (AnvilChunkLoader) this.currentChunkLoader; } @@ -135,14 +170,13 @@ // We can only use the queue for already generated chunks if (chunk == null && loader != null && loader.chunkExists(this.worldObj, par1, par2)) { -@@ -142,18 +185,20 @@ +@@ -142,18 +200,19 @@ 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); -+ if (mUnloading) return null; + this.chunksToUnload.remove(p_73158_1_, p_73158_2_); + Chunk chunk = (Chunk) this.loadedChunkHashMap.get(LongHash.toLong(p_73158_1_, p_73158_2_)); + boolean newChunk = false; // CraftBukkit @@ -161,7 +195,7 @@ if (chunk == null) { chunk = this.safeLoadChunk(p_73158_1_, p_73158_2_); -@@ -176,18 +221,40 @@ +@@ -176,18 +235,40 @@ 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_)})); @@ -207,13 +241,12 @@ } return chunk; -@@ -195,13 +262,32 @@ +@@ -195,11 +276,29 @@ public Chunk provideChunk(int p_73154_1_, int p_73154_2_) { - Chunk chunk = (Chunk)this.loadedChunkHashMap.getValueByKey(ChunkCoordIntPair.chunkXZ2Int(p_73154_1_, p_73154_2_)); - return chunk == null ? (!this.worldObj.findingSpawnPoint && !this.loadChunkOnProvideRequest ? this.defaultEmptyChunk : this.loadChunk(p_73154_1_, p_73154_2_)) : chunk; -+ if (mUnloading) return null; + // CraftBukkit start + Chunk chunk = (Chunk) this.loadedChunkHashMap.get(LongHash.toLong(p_73154_1_, p_73154_2_)); + chunk = chunk == null ? (shouldLoadChunk() ? this.loadChunk(p_73154_1_, p_73154_2_) : this.defaultEmptyChunk) : chunk; // Cauldron handle forge server tick events and load the chunk within 5 seconds of the world being loaded (for chunk loaders) @@ -239,12 +272,9 @@ - private Chunk safeLoadChunk(int p_73239_1_, int p_73239_2_) + public Chunk safeLoadChunk(int p_73239_1_, int p_73239_2_) // CraftBukkit - private -> public { -- if (this.currentChunkLoader == null) -+ if (mUnloading || this.currentChunkLoader == null) + if (this.currentChunkLoader == null) { - return null; - } -@@ -209,6 +295,7 @@ +@@ -209,6 +308,7 @@ { try { @@ -252,7 +282,7 @@ Chunk chunk = this.currentChunkLoader.loadChunk(this.worldObj, p_73239_1_, p_73239_2_); if (chunk != null) -@@ -217,8 +304,11 @@ +@@ -217,8 +317,11 @@ if (this.currentChunkProvider != null) { @@ -264,7 +294,7 @@ } return chunk; -@@ -231,7 +321,7 @@ +@@ -231,7 +334,7 @@ } } @@ -273,7 +303,7 @@ { if (this.currentChunkLoader != null) { -@@ -246,7 +336,7 @@ +@@ -246,7 +349,7 @@ } } @@ -282,7 +312,7 @@ { if (this.currentChunkLoader != null) { -@@ -254,15 +344,18 @@ +@@ -254,15 +357,18 @@ { p_73242_1_.lastSaveTime = this.worldObj.getTotalWorldTime(); this.currentChunkLoader.saveChunk(this.worldObj, p_73242_1_); @@ -302,7 +332,7 @@ } } -@@ -277,6 +370,35 @@ +@@ -277,6 +383,35 @@ if (this.currentChunkProvider != null) { this.currentChunkProvider.populate(p_73153_1_, p_73153_2_, p_73153_3_); @@ -338,7 +368,7 @@ GameRegistry.generateWorld(p_73153_2_, p_73153_3_, worldObj, currentChunkProvider, p_73153_1_); chunk.setChunkModified(); } -@@ -286,11 +408,13 @@ +@@ -286,11 +421,13 @@ public boolean saveChunks(boolean p_73151_1_, IProgressUpdate p_73151_2_) { int i = 0; @@ -355,7 +385,7 @@ if (p_73151_1_) { -@@ -325,36 +449,61 @@ +@@ -325,36 +462,61 @@ { if (!this.worldObj.levelSaving) { @@ -437,7 +467,7 @@ if (this.currentChunkLoader != null) { this.currentChunkLoader.chunkTick(); -@@ -371,7 +520,7 @@ +@@ -371,7 +533,7 @@ public String makeString() { @@ -446,7 +476,7 @@ } public List getPossibleCreatures(EnumCreatureType p_73155_1_, int p_73155_2_, int p_73155_3_, int p_73155_4_) -@@ -386,8 +535,31 @@ +@@ -386,8 +548,31 @@ public int getLoadedChunkCount() { diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index e7903ed..4e440cb 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -1,5 +1,7 @@ package org.bukkit.craftbukkit; +import gnu.trove.procedure.TObjectProcedure; + import java.io.File; import java.util.ArrayList; import java.util.Collection; @@ -11,6 +13,7 @@ import java.util.UUID; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.IEntityLivingData; +import net.minecraft.world.ChunkCoordIntPair; import net.minecraftforge.common.util.BlockSnapshot; import org.apache.commons.lang.Validate; @@ -135,7 +138,7 @@ public class CraftWorld implements World { } public Chunk[] getLoadedChunks() { - Object[] chunks = world.theChunkProviderServer.loadedChunkHashMap.values().toArray(); + Object[] chunks = world.theChunkProviderServer.loadedChunkHashMap.values(); org.bukkit.Chunk[] craftChunks = new CraftChunk[chunks.length]; for (int i = 0; i < chunks.length; i++) { @@ -275,6 +278,7 @@ public class CraftWorld implements World { if (chunk != null) { world.theChunkProviderServer.loadedChunkHashMap.put(LongHash.toLong(x, z), chunk); world.theChunkProviderServer.loadedChunks.add(chunk); // Cauldron - vanilla compatibility + world.theChunkProviderServer.loadedChunkHashMap_vanilla.add(ChunkCoordIntPair.chunkXZ2Int(x, z), chunk); chunk.onChunkLoad(); @@ -1390,21 +1394,25 @@ public class CraftWorld implements World { return; } - net.minecraft.world.gen.ChunkProviderServer cps = world.theChunkProviderServer; - for (net.minecraft.world.chunk.Chunk chunk : cps.loadedChunkHashMap.values()) { - // If in use, skip it - if (isChunkInUse(chunk.xPosition, chunk.zPosition)) { - continue; - } + final net.minecraft.world.gen.ChunkProviderServer cps = world.theChunkProviderServer; + cps.loadedChunkHashMap.forEachValue(new TObjectProcedure() { + @Override + public boolean execute(net.minecraft.world.chunk.Chunk chunk) { + // If in use, skip it + if (isChunkInUse(chunk.xPosition, chunk.zPosition)) { + return true; + } - // Already unloading? - if (cps.chunksToUnload.contains(chunk.xPosition, chunk.zPosition)) { - continue; - } + // Already unloading? + if (cps.chunksToUnload.contains(chunk.xPosition, chunk.zPosition)) { + return true; + } - // Add unload request - cps.unloadChunksIfNotNearSpawn(chunk.xPosition, chunk.zPosition); - } + // Add unload request + cps.unloadChunksIfNotNearSpawn(chunk.xPosition, chunk.zPosition); + return true; + } + }); } // Spigot start