From 5aaf6c5cbf4efa8eb99710ffdd0957e3877b2952 Mon Sep 17 00:00:00 2001 From: Sergey Shatunov Date: Wed, 10 Feb 2016 19:17:47 +0700 Subject: [PATCH] ChunkManager implementation ChunkManager optimize chunks handling in small and big worlds Small worlds gets benefits with using int-object map instead long-object (in area -32768...32767 by x and z) Large worlds gets benefits by using efficient map by Colt project, which a little (3-6%) slower in writing, but greatly faster (400-500% from Java's HashMap and 150-180% from Trove's maps) on reading. --- build.gradle | 1 + .../network/NetHandlerPlayServer.java.patch | 2 +- .../management/PlayerManager.java.patch | 2 +- .../minecraft/world/WorldServer.java.patch | 77 +++++++++-------- .../world/gen/ChunkProviderServer.java.patch | 86 ++++++++++--------- .../common/chunkio/ChunkIOProvider.java.patch | 2 +- src/main/java/kcauldron/ChunkManager.java | 73 ++++++++++++++++ .../wrapper/VanillaChunkHashMap.java | 33 +++---- .../cauldron/CauldronHooks.java | 2 +- .../cauldron/command/CauldronCommand.java | 2 +- .../org/bukkit/craftbukkit/CraftWorld.java | 29 ++++--- .../java/org/spigotmc/WatchdogThread.java | 4 +- 12 files changed, 202 insertions(+), 111 deletions(-) create mode 100644 src/main/java/kcauldron/ChunkManager.java diff --git a/build.gradle b/build.gradle index 6a9b72c..ac57329 100644 --- a/build.gradle +++ b/build.gradle @@ -144,6 +144,7 @@ dependencies { libraries 'net.minecraft:server:1.7.10' libraries 'pw.prok:KImagine:0.2.0@jar' libraries 'org.apache.httpcomponents:httpclient:4.4.1' + libraries 'colt:colt:1.2.0' } packageUniversal { diff --git a/patches/net/minecraft/network/NetHandlerPlayServer.java.patch b/patches/net/minecraft/network/NetHandlerPlayServer.java.patch index 5af476d..fac2409 100644 --- a/patches/net/minecraft/network/NetHandlerPlayServer.java.patch +++ b/patches/net/minecraft/network/NetHandlerPlayServer.java.patch @@ -1804,7 +1804,7 @@ { packetbuffer = new PacketBuffer(Unpooled.wrappedBuffer(p_147349_1_.func_149558_e())); @@ -1093,16 +2398,18 @@ - + { if (itemstack.getItem() == Items.writable_book && itemstack.getItem() == itemstack1.getItem()) { - itemstack1.setTagInfo("pages", itemstack.getTagCompound().getTagList("pages", 8)); diff --git a/patches/net/minecraft/server/management/PlayerManager.java.patch b/patches/net/minecraft/server/management/PlayerManager.java.patch index fde6e37..024fee1 100644 --- a/patches/net/minecraft/server/management/PlayerManager.java.patch +++ b/patches/net/minecraft/server/management/PlayerManager.java.patch @@ -73,7 +73,7 @@ if (this.players.isEmpty()) { -+ if (this.theWorldServer.loadedEntityList.size() == 0 || this.theWorldServer.theChunkProviderServer.loadedChunkHashMap_KC.size() == 0) ++ if (this.theWorldServer.loadedEntityList.size() == 0 || this.theWorldServer.theChunkProviderServer.chunkManager.size() == 0) + { + return; // CraftBukkit - Only do unload when we go from non-empty to empty + } diff --git a/patches/net/minecraft/world/WorldServer.java.patch b/patches/net/minecraft/world/WorldServer.java.patch index 1adcb53..b5d1bd6 100644 --- a/patches/net/minecraft/world/WorldServer.java.patch +++ b/patches/net/minecraft/world/WorldServer.java.patch @@ -69,7 +69,12 @@ this.mcServer = p_i45284_1_; this.theEntityTracker = new EntityTracker(this); this.thePlayerManager = new PlayerManager(this); -@@ -106,12 +129,12 @@ +@@ -103,15 +126,17 @@ + { + this.entityIdMap = new IntHashMap(); + } ++ ++ if (this.blockUpdatesTracker == null) this.blockUpdatesTracker = new kcauldron.BlockUpdatesTracker(this); if (this.pendingTickListEntriesHashSet == null) { @@ -84,7 +89,7 @@ } this.worldTeleporter = new Teleporter(this); -@@ -124,6 +147,47 @@ +@@ -124,6 +149,49 @@ this.mapStorage.setData("scoreboard", scoreboardsavedata); } @@ -108,6 +113,8 @@ + { + this.entityIdMap = new IntHashMap(); + } ++ ++ if (this.blockUpdatesTracker == null) this.blockUpdatesTracker = new kcauldron.BlockUpdatesTracker(this); + + if (this.pendingTickListEntriesHashSet == null) + { @@ -132,7 +139,7 @@ if (!(this instanceof WorldServerMulti)) //Forge: We fix the global mapStorage, which causes us to share scoreboards early. So don't associate the save data with the temporary scoreboard { scoreboardsavedata.func_96499_a(this.worldScoreboard); -@@ -132,6 +196,31 @@ +@@ -132,6 +200,32 @@ DimensionManager.setWorld(p_i45284_4_, this); } @@ -146,6 +153,7 @@ + this.theEntityTracker = null; + this.thePlayerManager = null; + this.worldTeleporter = null; ++ this.blockUpdatesTracker = null; + } + + private boolean canSpawn(int x, int z) @@ -164,7 +172,7 @@ public void tick() { super.tick(); -@@ -155,12 +244,19 @@ +@@ -155,12 +249,19 @@ } this.theProfiler.startSection("mobSpawner"); @@ -187,7 +195,7 @@ this.theProfiler.endStartSection("chunkSource"); this.chunkProvider.unloadQueuedChunks(); int j = this.calculateSkylightSubtracted(1.0F); -@@ -170,30 +266,47 @@ +@@ -170,30 +271,47 @@ this.skylightSubtracted = j; } @@ -236,7 +244,7 @@ } public BiomeGenBase.SpawnListEntry spawnRandomCreature(EnumCreatureType p_73057_1_, int p_73057_2_, int p_73057_3_, int p_73057_4_) -@@ -212,7 +325,7 @@ +@@ -212,7 +330,7 @@ { EntityPlayer entityplayer = (EntityPlayer)iterator.next(); @@ -245,7 +253,7 @@ { this.allPlayersSleeping = false; break; -@@ -240,7 +353,25 @@ +@@ -240,7 +358,25 @@ private void resetRainAndThunder() { @@ -272,7 +280,7 @@ } public boolean areAllPlayersAsleep() -@@ -248,19 +379,26 @@ +@@ -248,19 +384,26 @@ if (this.allPlayersSleeping && !this.isRemote) { Iterator iterator = this.playerEntities.iterator(); @@ -302,7 +310,7 @@ return false; } else -@@ -302,15 +440,29 @@ +@@ -302,15 +445,29 @@ super.func_147456_g(); int i = 0; int j = 0; @@ -338,7 +346,7 @@ this.func_147467_a(k, l, chunk); this.theProfiler.endStartSection("tickChunk"); chunk.func_150804_b(false); -@@ -346,12 +498,32 @@ +@@ -346,12 +503,32 @@ if (this.isBlockFreezableNaturally(j1 + k, l1 - 1, k1 + l)) { @@ -373,7 +381,7 @@ } if (this.isRaining()) -@@ -388,6 +560,7 @@ +@@ -388,6 +565,7 @@ if (block.getTickRandomly()) { ++i; @@ -381,7 +389,7 @@ block.updateTick(this, j2 + k, l2 + extendedblockstorage.getYLocation(), k2 + l, this.rand); } } -@@ -396,12 +569,19 @@ +@@ -396,12 +574,19 @@ this.theProfiler.endSection(); } @@ -403,7 +411,7 @@ } public void scheduleBlockUpdate(int p_147464_1_, int p_147464_2_, int p_147464_3_, Block p_147464_4_, int p_147464_5_) -@@ -411,70 +591,29 @@ +@@ -411,70 +596,29 @@ public void scheduleBlockUpdateWithPriority(int p_147454_1_, int p_147454_2_, int p_147454_3_, Block p_147454_4_, int p_147454_5_, int p_147454_6_) { @@ -484,7 +492,7 @@ { if (this.updateEntityTick++ >= 1200) { -@@ -487,6 +626,7 @@ +@@ -487,6 +631,7 @@ } super.updateEntities(); @@ -492,7 +500,7 @@ } public void resetUpdateEntityTick() -@@ -496,33 +636,40 @@ +@@ -496,33 +641,40 @@ public boolean tickUpdates(boolean p_72955_1_) { @@ -547,7 +555,7 @@ this.pendingTickListEntriesThisTick.add(nextticklistentry); } -@@ -532,22 +679,22 @@ +@@ -532,22 +684,22 @@ while (iterator.hasNext()) { @@ -575,7 +583,7 @@ } catch (Throwable throwable1) { -@@ -557,27 +704,27 @@ +@@ -557,27 +709,27 @@ try { @@ -607,7 +615,7 @@ } } -@@ -596,7 +743,7 @@ +@@ -596,7 +748,7 @@ if (i1 == 0) { @@ -616,7 +624,7 @@ } else { -@@ -610,13 +757,13 @@ +@@ -610,13 +762,13 @@ while (iterator.hasNext()) { @@ -633,7 +641,7 @@ iterator.remove(); } -@@ -651,7 +798,37 @@ +@@ -651,7 +803,37 @@ protected IChunkProvider createChunkProvider() { IChunkLoader ichunkloader = this.saveHandler.getChunkLoader(this.provider); @@ -672,7 +680,7 @@ return this.theChunkProviderServer; } -@@ -659,29 +836,31 @@ +@@ -659,29 +841,31 @@ { ArrayList arraylist = new ArrayList(); @@ -719,26 +727,27 @@ return arraylist; } -@@ -702,14 +881,14 @@ +@@ -701,15 +885,17 @@ + { this.entityIdMap = new IntHashMap(); } ++ ++ if (blockUpdatesTracker == null) blockUpdatesTracker = new kcauldron.BlockUpdatesTracker(this); -- if (this.pendingTickListEntriesHashSet == null) -+ if (this.pendingTickListEntriesHashSet == null && blockUpdatesTracker != null) + if (this.pendingTickListEntriesHashSet == null) { - this.pendingTickListEntriesHashSet = new HashSet(); + this.pendingTickListEntriesHashSet = blockUpdatesTracker.hashSet; } -- if (this.pendingTickListEntriesTreeSet == null) -+ if (this.pendingTickListEntriesTreeSet == null && blockUpdatesTracker != null) + if (this.pendingTickListEntriesTreeSet == null) { - this.pendingTickListEntriesTreeSet = new TreeSet(); + this.pendingTickListEntriesTreeSet = blockUpdatesTracker.treeSet; } this.createSpawnPosition(p_72963_1_); -@@ -733,7 +912,28 @@ +@@ -733,7 +919,28 @@ int i = 0; int j = this.provider.getAverageGroundLevel(); int k = 0; @@ -767,7 +776,7 @@ if (chunkposition != null) { i = chunkposition.chunkPosX; -@@ -876,6 +1076,20 @@ +@@ -876,6 +1083,20 @@ public boolean addWeatherEffect(Entity p_72942_1_) { @@ -788,7 +797,7 @@ if (super.addWeatherEffect(p_72942_1_)) { this.mcServer.getConfigurationManager().sendToAllNear(p_72942_1_.posX, p_72942_1_.posY, p_72942_1_.posZ, 512.0D, this.provider.dimensionId, new S2CPacketSpawnGlobalEntity(p_72942_1_)); -@@ -894,13 +1108,23 @@ +@@ -894,13 +1115,23 @@ public Explosion newExplosion(Entity p_72885_1_, double p_72885_2_, double p_72885_4_, double p_72885_6_, float p_72885_8_, boolean p_72885_9_, boolean p_72885_10_) { @@ -813,7 +822,7 @@ if (!p_72885_10_) { explosion.affectedBlockPositions.clear(); -@@ -977,7 +1201,7 @@ +@@ -977,7 +1208,7 @@ { boolean flag = this.isRaining(); super.updateWeather(); @@ -822,7 +831,7 @@ if (this.prevRainingStrength != this.rainingStrength) { this.mcServer.getConfigurationManager().sendPacketToAllPlayersInDimension(new S2BPacketChangeGameState(7, this.rainingStrength), this.provider.dimensionId); -@@ -988,10 +1212,6 @@ +@@ -988,10 +1219,6 @@ this.mcServer.getConfigurationManager().sendPacketToAllPlayersInDimension(new S2BPacketChangeGameState(8, this.thunderingStrength), this.provider.dimensionId); } @@ -833,7 +842,7 @@ if (flag != this.isRaining()) { if (flag) -@@ -1006,6 +1226,33 @@ +@@ -1006,6 +1233,33 @@ this.mcServer.getConfigurationManager().sendPacketToAllPlayersInDimension(new S2BPacketChangeGameState(7, this.rainingStrength), this.provider.dimensionId); this.mcServer.getConfigurationManager().sendPacketToAllPlayersInDimension(new S2BPacketChangeGameState(8, this.thunderingStrength), this.provider.dimensionId); } @@ -867,7 +876,7 @@ } protected int func_152379_p() -@@ -1069,4 +1316,54 @@ +@@ -1069,4 +1323,54 @@ this(); } } @@ -920,5 +929,5 @@ + // CraftBukkit end + + // KCauldron start -+ public final kcauldron.BlockUpdatesTracker blockUpdatesTracker = new kcauldron.BlockUpdatesTracker(this); ++ public kcauldron.BlockUpdatesTracker blockUpdatesTracker; } diff --git a/patches/net/minecraft/world/gen/ChunkProviderServer.java.patch b/patches/net/minecraft/world/gen/ChunkProviderServer.java.patch index 2ef541c..350f029 100644 --- a/patches/net/minecraft/world/gen/ChunkProviderServer.java.patch +++ b/patches/net/minecraft/world/gen/ChunkProviderServer.java.patch @@ -67,11 +67,11 @@ - 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 TLongObjectMap loadedChunkHashMap_KC = new TLongObjectHashMap(); ++ public kcauldron.ChunkManager chunkManager = new kcauldron.ChunkManager(); + public List loadedChunks = new ArrayList(); // Cauldron - vanilla compatibility public WorldServer worldObj; private Set loadingChunks = com.google.common.collect.Sets.newHashSet(); -+ public LongHashMap loadedChunkHashMap = new kcauldron.wrapper.VanillaChunkHashMap(loadedChunkHashMap_KC); // KCauldron - vanilla/mystcraft compatibility ++ public LongHashMap loadedChunkHashMap = new kcauldron.wrapper.VanillaChunkHashMap(chunkManager); // KCauldron - vanilla/mystcraft compatibility private static final String __OBFID = "CL_00001436"; public ChunkProviderServer(WorldServer p_i1520_1_, IChunkLoader p_i1520_2_, IChunkProvider p_i1520_3_) @@ -85,7 +85,7 @@ public boolean chunkExists(int p_73149_1_, int p_73149_2_) { - return this.loadedChunkHashMap.containsItem(ChunkCoordIntPair.chunkXZ2Int(p_73149_1_, p_73149_2_)); -+ return this.loadedChunkHashMap_KC.containsKey(LongHash.toLong(p_73149_1_, p_73149_2_)); // CraftBukkit ++ return this.chunkManager.chunkExists(p_73149_1_, p_73149_2_); // KCauldron } - public List func_152380_a() @@ -97,7 +97,7 @@ public void unloadChunksIfNotNearSpawn(int p_73241_1_, int p_73241_2_) { + // PaperSpigot start - Asynchronous lighting updates -+ Chunk chunk = this.loadedChunkHashMap_KC.get(LongHash.toLong(p_73241_1_, p_73241_2_)); ++ Chunk chunk = this.chunkManager.getChunk(p_73241_1_, p_73241_2_); // KCauldron + if (chunk != null && chunk.worldObj.spigotConfig.useAsyncLighting && (chunk.pendingLightUpdates.get() > 0 || chunk.worldObj.getTotalWorldTime() - chunk.lightUpdateTime < 20)) { + return; + } @@ -105,7 +105,7 @@ if (this.worldObj.provider.canRespawnHere() && DimensionManager.shouldLoadSpawn(this.worldObj.provider.dimensionId)) { ChunkCoordinates chunkcoordinates = this.worldObj.getSpawnPoint(); -@@ -74,26 +117,49 @@ +@@ -74,26 +117,52 @@ int l = p_73241_2_ * 16 + 8 - chunkcoordinates.posZ; short short1 = 128; @@ -114,11 +114,11 @@ { - this.chunksToUnload.add(Long.valueOf(ChunkCoordIntPair.chunkXZ2Int(p_73241_1_, p_73241_2_))); + this.chunksToUnload.add(p_73241_1_, p_73241_2_); -+ Chunk c = this.loadedChunkHashMap_KC.get(LongHash.toLong(p_73241_1_, p_73241_2_)); ++ // Chunk c = this.loadedChunkHashMap_KC.get(LongHash.toLong(p_73241_1_, p_73241_2_)); + -+ if (c != null) ++ if (chunk != null) + { -+ c.mustSave = true; ++ chunk.mustSave = true; + } + CauldronHooks.logChunkUnload(this, p_73241_1_, p_73241_2_, "Chunk added to unload queue"); } @@ -130,11 +130,11 @@ - this.chunksToUnload.add(Long.valueOf(ChunkCoordIntPair.chunkXZ2Int(p_73241_1_, p_73241_2_))); + // CraftBukkit start + this.chunksToUnload.add(p_73241_1_, p_73241_2_); -+ Chunk c = this.loadedChunkHashMap_KC.get(LongHash.toLong(p_73241_1_, p_73241_2_)); ++ //Chunk c = this.loadedChunkHashMap_KC.get(LongHash.toLong(p_73241_1_, p_73241_2_)); + -+ if (c != null) ++ if (chunk != null) + { -+ c.mustSave = true; ++ chunk.mustSave = true; + } + CauldronHooks.logChunkUnload(this, p_73241_1_, p_73241_2_, "Chunk added to unload queue"); + // CraftBukkit end @@ -144,13 +144,16 @@ public void unloadAllChunks() { - Iterator iterator = this.loadedChunks.iterator(); -+ this.loadedChunkHashMap_KC.forEachValue(new TObjectProcedure() { ++ // KCauldron start ++ this.chunkManager.forEach(new cern.colt.function.ObjectProcedure() { + @Override -+ public boolean execute(Chunk chunk) { ++ public boolean apply(Object arg) { ++ final Chunk chunk = (Chunk) arg; + unloadChunksIfNotNearSpawn(chunk.xPosition, chunk.zPosition); -+ return true; ++ return false; + } + }); ++ // KCauldron end + } - while (iterator.hasNext()) @@ -159,11 +162,11 @@ - this.unloadChunksIfNotNearSpawn(chunk.xPosition, chunk.zPosition); - } + public Chunk getChunkIfLoaded(int x, int z) { -+ return this.loadedChunkHashMap_KC.get(LongHash.toLong(x, z)); ++ return this.chunkManager.getChunk(x, z); // KCauldron } public Chunk loadChunk(int p_73158_1_, int p_73158_2_) -@@ -103,9 +169,9 @@ +@@ -103,9 +172,9 @@ public Chunk loadChunk(int par1, int par2, Runnable runnable) { @@ -171,12 +174,12 @@ - this.chunksToUnload.remove(Long.valueOf(k)); - Chunk chunk = (Chunk)this.loadedChunkHashMap.getValueByKey(k); + this.chunksToUnload.remove(par1, par2); -+ Chunk chunk = (Chunk) this.loadedChunkHashMap_KC.get(LongHash.toLong(par1, par2)); ++ Chunk chunk = (Chunk) this.chunkManager.getChunk(par1, par2); // KCauldron + boolean newChunk = false; AnvilChunkLoader loader = null; if (this.currentChunkLoader instanceof AnvilChunkLoader) -@@ -113,6 +179,8 @@ +@@ -113,6 +182,8 @@ loader = (AnvilChunkLoader) this.currentChunkLoader; } @@ -185,7 +188,7 @@ // We can only use the queue for already generated chunks if (chunk == null && loader != null && loader.chunkExists(this.worldObj, par1, par2)) { -@@ -142,18 +210,19 @@ +@@ -142,18 +213,19 @@ public Chunk originalLoadChunk(int p_73158_1_, int p_73158_2_) { @@ -193,7 +196,7 @@ - this.chunksToUnload.remove(Long.valueOf(k)); - Chunk chunk = (Chunk)this.loadedChunkHashMap.getValueByKey(k); + this.chunksToUnload.remove(p_73158_1_, p_73158_2_); -+ Chunk chunk = (Chunk) this.loadedChunkHashMap_KC.get(LongHash.toLong(p_73158_1_, p_73158_2_)); ++ Chunk chunk = (Chunk) this.chunkManager.getChunk(p_73158_1_, p_73158_2_); // KCauldron + boolean newChunk = false; // CraftBukkit if (chunk == null) @@ -210,7 +213,7 @@ if (chunk == null) { chunk = this.safeLoadChunk(p_73158_1_, p_73158_2_); -@@ -176,18 +245,53 @@ +@@ -176,18 +248,53 @@ 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_)})); @@ -228,7 +231,7 @@ - this.loadedChunks.add(chunk); - loadingChunks.remove(k); - chunk.onChunkLoad(); -+ this.loadedChunkHashMap_KC.put(LongHash.toLong(p_73158_1_, p_73158_2_), chunk); // CraftBukkit ++ this.chunkManager.putChunk(chunk, p_73158_1_, p_73158_2_); // KCauldron + this.loadedChunks.add(chunk); // Cauldron - vanilla compatibility + loadingChunks.remove(LongHash.toLong(p_73158_1_, p_73158_2_)); // Cauldron - LongHash + @@ -269,14 +272,14 @@ } return chunk; -@@ -195,11 +299,29 @@ +@@ -195,11 +302,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; + // CraftBukkit start -+ Chunk chunk = (Chunk) this.loadedChunkHashMap_KC.get(LongHash.toLong(p_73154_1_, p_73154_2_)); ++ Chunk chunk = this.chunkManager.getChunk(p_73154_1_, p_73154_2_); // KCauldron + 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) + + if (chunk == this.defaultEmptyChunk) @@ -302,7 +305,7 @@ { if (this.currentChunkLoader == null) { -@@ -209,6 +331,7 @@ +@@ -209,6 +334,7 @@ { try { @@ -310,7 +313,7 @@ Chunk chunk = this.currentChunkLoader.loadChunk(this.worldObj, p_73239_1_, p_73239_2_); if (chunk != null) -@@ -217,8 +340,11 @@ +@@ -217,8 +343,11 @@ if (this.currentChunkProvider != null) { @@ -322,7 +325,7 @@ } return chunk; -@@ -231,7 +357,7 @@ +@@ -231,7 +360,7 @@ } } @@ -331,7 +334,7 @@ { if (this.currentChunkLoader != null) { -@@ -246,7 +372,7 @@ +@@ -246,7 +375,7 @@ } } @@ -340,7 +343,7 @@ { if (this.currentChunkLoader != null) { -@@ -254,15 +380,18 @@ +@@ -254,15 +383,18 @@ { p_73242_1_.lastSaveTime = this.worldObj.getTotalWorldTime(); this.currentChunkLoader.saveChunk(this.worldObj, p_73242_1_); @@ -360,7 +363,7 @@ } } -@@ -277,6 +406,35 @@ +@@ -277,6 +409,35 @@ if (this.currentChunkProvider != null) { this.currentChunkProvider.populate(p_73153_1_, p_73153_2_, p_73153_3_); @@ -396,7 +399,7 @@ GameRegistry.generateWorld(p_73153_2_, p_73153_3_, worldObj, currentChunkProvider, p_73153_1_); chunk.setChunkModified(); } -@@ -286,11 +444,13 @@ +@@ -286,11 +447,13 @@ public boolean saveChunks(boolean p_73151_1_, IProgressUpdate p_73151_2_) { int i = 0; @@ -413,7 +416,7 @@ if (p_73151_1_) { -@@ -325,36 +485,73 @@ +@@ -325,36 +488,73 @@ { if (!this.worldObj.levelSaving) { @@ -436,7 +439,7 @@ { - if (!this.chunksToUnload.isEmpty()) + long chunkcoordinates = this.chunksToUnload.popFirst(); -+ Chunk chunk = this.loadedChunkHashMap_KC.get(chunkcoordinates); ++ Chunk chunk = this.chunkManager.getChunk(LongHash.msw(chunkcoordinates), LongHash.lsw(chunkcoordinates)); + + if (chunk == null) { @@ -491,10 +494,10 @@ - - this.chunksToUnload.remove(olong); - this.loadedChunkHashMap.remove(olong.longValue()); -+ this.loadedChunkHashMap_KC.remove(chunkcoordinates); // CraftBukkit ++ this.chunkManager.remove(chunk.xPosition, chunk.zPosition); // KCauldron + this.loadedChunks.remove(chunk); // Cauldron - vanilla compatibility + ForgeChunkManager.putDormantChunk(chunkcoordinates, chunk); -+ if(this.loadedChunkHashMap_KC.size() == 0 && ForgeChunkManager.getPersistentChunksFor(this.worldObj).size() == 0 && !DimensionManager.shouldLoadSpawn(this.worldObj.provider.dimensionId)){ ++ if(this.chunkManager.size() == 0 && ForgeChunkManager.getPersistentChunksFor(this.worldObj).size() == 0 && !DimensionManager.shouldLoadSpawn(this.worldObj.provider.dimensionId)){ + DimensionManager.unloadWorld(this.worldObj.provider.dimensionId); + return currentChunkProvider.unloadQueuedChunks(); + } @@ -506,21 +509,21 @@ if (this.currentChunkLoader != null) { this.currentChunkLoader.chunkTick(); -@@ -371,7 +568,7 @@ +@@ -371,7 +571,7 @@ public String makeString() { - return "ServerChunkCache: " + this.loadedChunkHashMap.getNumHashElements() + " Drop: " + this.chunksToUnload.size(); -+ return "ServerChunkCache: " + this.loadedChunkHashMap_KC.size() + " Drop: " + this.chunksToUnload.size(); // Cauldron ++ return "ServerChunkCache: " + this.chunkManager.size() + " Drop: " + this.chunksToUnload.size(); // Cauldron } public List getPossibleCreatures(EnumCreatureType p_73155_1_, int p_73155_2_, int p_73155_3_, int p_73155_4_) -@@ -386,8 +583,31 @@ +@@ -386,8 +586,30 @@ public int getLoadedChunkCount() { - return this.loadedChunkHashMap.getNumHashElements(); -+ return this.loadedChunkHashMap_KC.size(); // Cauldron ++ return this.chunkManager.size(); // KCauldron } public void recreateStructures(int p_82695_1_, int p_82695_2_) {} @@ -536,9 +539,8 @@ + + public long lastAccessed(int x, int z) + { -+ long chunkHash = LongHash.toLong(x, z); -+ if (!loadedChunkHashMap_KC.containsKey(chunkHash)) return 0; -+ return loadedChunkHashMap_KC.get(chunkHash).lastAccessedTick; ++ final Chunk chunk = this.chunkManager.getChunk(x, z); // KCauldron ++ return chunk == null ? 0 : chunk.lastAccessedTick; // KCauldron + } + + /*private boolean shouldUnloadChunk(Chunk chunk) diff --git a/patches/net/minecraftforge/common/chunkio/ChunkIOProvider.java.patch b/patches/net/minecraftforge/common/chunkio/ChunkIOProvider.java.patch index 8ab053a..671377a 100644 --- a/patches/net/minecraftforge/common/chunkio/ChunkIOProvider.java.patch +++ b/patches/net/minecraftforge/common/chunkio/ChunkIOProvider.java.patch @@ -15,7 +15,7 @@ MinecraftForge.EVENT_BUS.post(new ChunkDataEvent.Load(chunk, queuedChunk.compound)); // Don't call ChunkDataEvent.Load async chunk.lastSaveTime = queuedChunk.provider.worldObj.getTotalWorldTime(); - queuedChunk.provider.loadedChunkHashMap.add(ChunkCoordIntPair.chunkXZ2Int(queuedChunk.x, queuedChunk.z), chunk); -+ queuedChunk.provider.loadedChunkHashMap_KC.put(LongHash.toLong(queuedChunk.x, queuedChunk.z), chunk); ++ queuedChunk.provider.chunkManager.putChunk(chunk, queuedChunk.x, queuedChunk.z); // KCauldron queuedChunk.provider.loadedChunks.add(chunk); chunk.onChunkLoad(); diff --git a/src/main/java/kcauldron/ChunkManager.java b/src/main/java/kcauldron/ChunkManager.java new file mode 100644 index 0000000..e82f716 --- /dev/null +++ b/src/main/java/kcauldron/ChunkManager.java @@ -0,0 +1,73 @@ +package kcauldron; + +import cern.colt.function.IntObjectProcedure; +import cern.colt.function.LongObjectProcedure; +import cern.colt.function.ObjectProcedure; +import cern.colt.map.OpenIntObjectHashMap; +import cern.colt.map.OpenLongObjectHashMap; +import net.minecraft.world.chunk.Chunk; + +public class ChunkManager { + private static final int MIN = -32768, MAX = 32767; + public OpenIntObjectHashMap loadedChunksLSB = new OpenIntObjectHashMap(); + public OpenLongObjectHashMap loadefChunksMSB = new OpenLongObjectHashMap(); + + public ChunkManager() { + } + + public Chunk getChunk(final int x, final int z) { + if (MIN <= x && MIN <= z && x <= MAX && z <= MAX) + return (Chunk) loadedChunksLSB.get(x << 16 | (z & 0xFFFF)); + else + return (Chunk) loadefChunksMSB.get(((long) x) << 32 | z); + } + + public boolean chunkExists(final int x, final int z) { + if (MIN <= x && MIN <= z && x <= MAX && z <= MAX) + return loadedChunksLSB.containsKey(x << 16 | (z & 0xFFFF)); + else + return loadefChunksMSB.containsKey(((long) x) << 32 | z); + } + + public void putChunk(Chunk chunk) { + putChunk(chunk, chunk.xPosition, chunk.zPosition); + } + + public void putChunk(Chunk chunk, final int x, final int z) { + if (MIN <= x && MIN <= z && x <= MAX && z <= MAX) + loadedChunksLSB.put(x << 16 | (z & 0xFFFF), chunk); + else + loadefChunksMSB.put(((long) x) << 32 | z, chunk); + } + + public boolean removeKey(final int x, final int z) { + if (MIN <= x && MIN <= z && x <= MAX && z <= MAX) + return loadedChunksLSB.removeKey(x << 16 | (z & 0xFFFF)); + else + return loadefChunksMSB.removeKey(((long) x) << 32 | z); + } + + public Chunk remove(final int x, final int z) { + Chunk chunk = getChunk(x, z); + return removeKey(x, z) ? chunk : null; + } + + public int size() { + return loadedChunksLSB.size() + loadefChunksMSB.size(); + } + + public void forEach(final ObjectProcedure proc) { + loadedChunksLSB.forEachPair(new IntObjectProcedure() { + @Override + public boolean apply(int arg0, Object arg1) { + return proc.apply(arg1); + } + }); + loadefChunksMSB.forEachPair(new LongObjectProcedure() { + @Override + public boolean apply(long arg0, Object arg1) { + return proc.apply(arg1); + } + }); + } +} diff --git a/src/main/java/kcauldron/wrapper/VanillaChunkHashMap.java b/src/main/java/kcauldron/wrapper/VanillaChunkHashMap.java index 3e04f1c..c85796f 100644 --- a/src/main/java/kcauldron/wrapper/VanillaChunkHashMap.java +++ b/src/main/java/kcauldron/wrapper/VanillaChunkHashMap.java @@ -1,36 +1,37 @@ package kcauldron.wrapper; -import gnu.trove.map.TLongObjectMap; +import kcauldron.ChunkManager; +import net.minecraft.util.LongHashMap; import net.minecraft.world.chunk.Chunk; -import org.bukkit.craftbukkit.util.LongHash; +public class VanillaChunkHashMap extends LongHashMap { + private ChunkManager manager; -public class VanillaChunkHashMap extends LongHashMapTrove { - public VanillaChunkHashMap(TLongObjectMap map) { - super(map); + public VanillaChunkHashMap(ChunkManager manager) { + this.manager = manager; } - private static long V2B(long key) { - return LongHash.toLong((int) (key & 0xFFFFFFFFL), (int) (key >>> 32)); - } - @Override public void add(long key, Object value) { - super.add(V2B(key), value); + manager.putChunk((Chunk) value); } - + @Override public boolean containsItem(long key) { - return super.containsItem(V2B(key)); + return getValueByKey(key) != null; } - + @Override public Object getValueByKey(long key) { - return super.getValueByKey(V2B(key)); + final int x = (int) (key >>> 32); + final int z = (int) (key); + return manager.getChunk(x, z); } - + @Override public Object remove(long key) { - return super.remove(V2B(key)); + final int x = (int) (key >>> 32); + final int z = (int) (key); + return manager.remove(x, z); } } diff --git a/src/main/java/net/minecraftforge/cauldron/CauldronHooks.java b/src/main/java/net/minecraftforge/cauldron/CauldronHooks.java index 8008d9b..e74952a 100644 --- a/src/main/java/net/minecraftforge/cauldron/CauldronHooks.java +++ b/src/main/java/net/minecraftforge/cauldron/CauldronHooks.java @@ -332,7 +332,7 @@ public class CauldronHooks writer.name("name").value(world.getWorld().getName()); writer.name("dimensionId").value(world.provider.dimensionId); writer.name("players").value(world.playerEntities.size()); - writer.name("loadedChunks").value(world.theChunkProviderServer.loadedChunkHashMap_KC.size()); + writer.name("loadedChunks").value(world.theChunkProviderServer.chunkManager.size()); writer.name("activeChunks").value(world.activeChunkSet.size()); writer.name("entities").value(world.loadedEntityList.size()); writer.name("tiles").value(world.loadedTileEntityList.size()); diff --git a/src/main/java/net/minecraftforge/cauldron/command/CauldronCommand.java b/src/main/java/net/minecraftforge/cauldron/command/CauldronCommand.java index 3442f89..337a8dd 100644 --- a/src/main/java/net/minecraftforge/cauldron/command/CauldronCommand.java +++ b/src/main/java/net/minecraftforge/cauldron/command/CauldronCommand.java @@ -110,7 +110,7 @@ public class CauldronCommand extends Command for (net.minecraft.world.WorldServer world : MinecraftServer.getServer().worlds) { sender.sendMessage(ChatColor.GOLD + "Dimension: " + ChatColor.GRAY + world.provider.dimensionId + - ChatColor.GOLD + " Loaded Chunks: " + ChatColor.GRAY + world.theChunkProviderServer.loadedChunkHashMap_KC.size() + + ChatColor.GOLD + " Loaded Chunks: " + ChatColor.GRAY + world.theChunkProviderServer.chunkManager.size() + ChatColor.GOLD + " Active Chunks: " + ChatColor.GRAY + world.activeChunkSet.size() + ChatColor.GOLD + " Entities: " + ChatColor.GRAY + world.loadedEntityList.size() + ChatColor.GOLD + " Tile Entities: " + ChatColor.GRAY + world.loadedTileEntityList.size() diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index 8e94643..d6dd160 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -138,14 +138,18 @@ public class CraftWorld implements World { } public Chunk[] getLoadedChunks() { - Object[] chunks = world.theChunkProviderServer.loadedChunkHashMap_KC.values(); - org.bukkit.Chunk[] craftChunks = new CraftChunk[chunks.length]; - - for (int i = 0; i < chunks.length; i++) { - net.minecraft.world.chunk.Chunk chunk = (net.minecraft.world.chunk.Chunk) chunks[i]; - craftChunks[i] = chunk.bukkitChunk; - } - + // KCauldron start + final kcauldron.ChunkManager chunkManager = world.theChunkProviderServer.chunkManager; + final org.bukkit.Chunk[] craftChunks = new CraftChunk[chunkManager.size()]; + chunkManager.forEach(new cern.colt.function.ObjectProcedure() { + int i = 0; + @Override + public boolean apply(Object arg) { + craftChunks[i++] = ((net.minecraft.world.chunk.Chunk) arg).bukkitChunk; + return true; + } + }); + // KCauldron end return craftChunks; } @@ -262,7 +266,7 @@ public class CraftWorld implements World { } world.theChunkProviderServer.chunksToUnload.remove(x, z); - net.minecraft.world.chunk.Chunk chunk = world.theChunkProviderServer.loadedChunkHashMap_KC.get(LongHash.toLong(x, z)); + net.minecraft.world.chunk.Chunk chunk = world.theChunkProviderServer.chunkManager.getChunk(x, z); if (chunk == null) { world.timings.syncChunkLoadTimer.startTiming(); // Spigot @@ -276,7 +280,7 @@ public class CraftWorld implements World { private void chunkLoadPostProcess(net.minecraft.world.chunk.Chunk chunk, int x, int z) { if (chunk != null) { - world.theChunkProviderServer.loadedChunkHashMap_KC.put(LongHash.toLong(x, z), chunk); + world.theChunkProviderServer.chunkManager.putChunk(chunk, x, z); // KCauldron world.theChunkProviderServer.loadedChunks.add(chunk); // Cauldron - vanilla compatibility chunk.onChunkLoad(); @@ -1394,9 +1398,10 @@ public class CraftWorld implements World { } final net.minecraft.world.gen.ChunkProviderServer cps = world.theChunkProviderServer; - cps.loadedChunkHashMap_KC.forEachValue(new TObjectProcedure() { + cps.chunkManager.forEach(new cern.colt.function.ObjectProcedure() { @Override - public boolean execute(net.minecraft.world.chunk.Chunk chunk) { + public boolean apply(Object arg) { + net.minecraft.world.chunk.Chunk chunk = (net.minecraft.world.chunk.Chunk) arg; // If in use, skip it if (isChunkInUse(chunk.xPosition, chunk.zPosition)) { return true; diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java index 4d7ee7a..4a4dde5 100644 --- a/src/main/java/org/spigotmc/WatchdogThread.java +++ b/src/main/java/org/spigotmc/WatchdogThread.java @@ -89,7 +89,7 @@ public class WatchdogThread extends Thread { log.log(Level.SEVERE, " Dimension:" + world.provider.dimensionId); log.log(Level.SEVERE, - " Loaded Chunks: " + world.theChunkProviderServer.loadedChunkHashMap_KC.size() + " Active Chunks: " + world.activeChunkSet.size() + " Loaded Chunks: " + world.theChunkProviderServer.chunkManager.size() + " Active Chunks: " + world.activeChunkSet.size() + " Entities: " + world.loadedEntityList.size() + " Tile Entities: " + world.loadedTileEntityList.size()); log.log(Level.SEVERE, " Entities Last Tick: " + world.entitiesTicked); log.log(Level.SEVERE, " Tiles Last Tick: " + world.tilesTicked); @@ -153,7 +153,7 @@ public class WatchdogThread extends Thread for (net.minecraft.world.WorldServer world : MinecraftServer.getServer().worlds) { log.log(Level.WARNING, " Dimension:" + world.provider.dimensionId); - log.log(Level.WARNING, " Loaded Chunks: " + world.theChunkProviderServer.loadedChunkHashMap_KC.size() + + log.log(Level.WARNING, " Loaded Chunks: " + world.theChunkProviderServer.chunkManager.size() + " Active Chunks: " + world.activeChunkSet.size() + " Entities: " + world.loadedEntityList.size() + " Tile Entities: " + world.loadedTileEntityList.size());