From f3273ca0c9f81821ecbf9d97cb9e8f45b1e5003c Mon Sep 17 00:00:00 2001 From: Ben Staddon Date: Mon, 2 Oct 2017 23:17:37 +0100 Subject: [PATCH] Store entity data for dormant chunks using NBT (#4162) --- .../net/minecraft/entity/Entity.java.patch | 3 +- .../chunk/storage/AnvilChunkLoader.java.patch | 9 +- .../world/gen/ChunkProviderServer.java.patch | 8 +- .../common/ForgeChunkManager.java | 91 ++++++++++++++----- 4 files changed, 79 insertions(+), 32 deletions(-) diff --git a/patches/minecraft/net/minecraft/entity/Entity.java.patch b/patches/minecraft/net/minecraft/entity/Entity.java.patch index a235e29e44..cec1c3ac39 100644 --- a/patches/minecraft/net/minecraft/entity/Entity.java.patch +++ b/patches/minecraft/net/minecraft/entity/Entity.java.patch @@ -155,7 +155,7 @@ } public boolean func_174816_a(Explosion p_174816_1_, World p_174816_2_, BlockPos p_174816_3_, IBlockState p_174816_4_, float p_174816_5_) -@@ -2901,6 +2928,184 @@ +@@ -2901,6 +2928,185 @@ EnchantmentHelper.func_151385_b(p_174815_1_, p_174815_2_); } @@ -248,6 +248,7 @@ + /** + * Reset the entity ID to a new value. Not to be used from Mod code + */ ++ @Deprecated // TODO: remove (1.13?) + public final void resetEntityId() + { + this.field_145783_c = field_70152_a++; diff --git a/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch b/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch index 0c72c7d85e..cc7cd64243 100644 --- a/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch +++ b/patches/minecraft/net/minecraft/world/chunk/storage/AnvilChunkLoader.java.patch @@ -90,11 +90,12 @@ nbttagcompound.func_74768_a("DataVersion", 1343); + net.minecraftforge.fml.common.FMLCommonHandler.instance().getDataFixer().writeVersionData(nbttagcompound); this.func_75820_a(p_75816_2_, p_75816_1_, nbttagcompound1); ++ net.minecraftforge.common.ForgeChunkManager.storeChunkNBT(p_75816_2_, nbttagcompound1); + net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.event.world.ChunkDataEvent.Save(p_75816_2_, nbttagcompound)); this.func_75824_a(p_75816_2_.func_76632_l(), nbttagcompound); } catch (Exception exception) -@@ -305,11 +353,19 @@ +@@ -305,11 +354,19 @@ { NBTTagCompound nbttagcompound2 = new NBTTagCompound(); @@ -114,7 +115,7 @@ } } -@@ -318,8 +374,16 @@ +@@ -318,8 +375,16 @@ for (TileEntity tileentity : p_75820_1_.func_177434_r().values()) { @@ -131,7 +132,7 @@ } p_75820_3_.func_74782_a("TileEntities", nbttaglist2); -@@ -388,6 +452,12 @@ +@@ -388,6 +453,12 @@ chunk.func_76616_a(p_75823_2_.func_74770_j("Biomes")); } @@ -144,7 +145,7 @@ NBTTagList nbttaglist1 = p_75823_2_.func_150295_c("Entities", 10); for (int j1 = 0; j1 < nbttaglist1.func_74745_c(); ++j1) -@@ -431,8 +501,6 @@ +@@ -431,8 +502,6 @@ p_75823_1_.func_180497_b(new BlockPos(nbttagcompound3.func_74762_e("x"), nbttagcompound3.func_74762_e("y"), nbttagcompound3.func_74762_e("z")), block, nbttagcompound3.func_74762_e("t"), nbttagcompound3.func_74762_e("p")); } } diff --git a/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.java.patch b/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.java.patch index 7834557fd8..67dfb4c5ce 100644 --- a/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.java.patch +++ b/patches/minecraft/net/minecraft/world/gen/ChunkProviderServer.java.patch @@ -72,11 +72,15 @@ Iterator iterator = this.field_73248_b.iterator(); for (int i = 0; i < 100 && iterator.hasNext(); iterator.remove()) -@@ -238,6 +271,11 @@ +@@ -234,10 +267,15 @@ + if (chunk != null && chunk.field_189550_d) + { + chunk.func_76623_d(); ++ net.minecraftforge.common.ForgeChunkManager.putDormantChunk(ChunkPos.func_77272_a(chunk.field_76635_g, chunk.field_76647_h), chunk); + this.func_73242_b(chunk); this.func_73243_a(chunk); this.field_73244_f.remove(olong); ++i; -+ net.minecraftforge.common.ForgeChunkManager.putDormantChunk(ChunkPos.func_77272_a(chunk.field_76635_g, chunk.field_76647_h), chunk); + if (field_73244_f.size() == 0 && net.minecraftforge.common.ForgeChunkManager.getPersistentChunksFor(this.field_73251_h).size() == 0 && !this.field_73251_h.field_73011_w.func_186058_p().shouldLoadSpawn()){ + net.minecraftforge.common.DimensionManager.unloadWorld(this.field_73251_h.field_73011_w.getDimension()); + break; diff --git a/src/main/java/net/minecraftforge/common/ForgeChunkManager.java b/src/main/java/net/minecraftforge/common/ForgeChunkManager.java index 135389953f..6c2b9ac23f 100644 --- a/src/main/java/net/minecraftforge/common/ForgeChunkManager.java +++ b/src/main/java/net/minecraftforge/common/ForgeChunkManager.java @@ -36,12 +36,14 @@ import net.minecraft.entity.Entity; import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; +import net.minecraft.tileentity.TileEntity; import net.minecraft.util.ClassInheritanceMultiMap; import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.MathHelper; import net.minecraft.world.World; import net.minecraft.world.WorldServer; import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.storage.AnvilChunkLoader; import net.minecraftforge.common.config.ConfigCategory; import net.minecraftforge.common.config.Configuration; import net.minecraftforge.common.config.Property; @@ -52,9 +54,6 @@ import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.ModContainer; import net.minecraftforge.fml.common.eventhandler.Event; -import org.apache.logging.log4j.Level; - -import java.util.function.Function; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.collect.ArrayListMultimap; @@ -65,7 +64,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableListMultimap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSetMultimap; -import com.google.common.collect.Iterators; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.MapMaker; @@ -108,7 +106,7 @@ public class ForgeChunkManager private static Map> forcedChunks = new MapMaker().weakKeys().makeMap(); private static BiMap pendingEntities = HashBiMap.create(); - private static Map> dormantChunkCache = new MapMaker().weakKeys().makeMap(); + private static Map> dormantChunkCache = new MapMaker().weakKeys().makeMap(); private static File cfgFile; private static Configuration config; @@ -125,6 +123,18 @@ public class ForgeChunkManager MOD_PROP_ORDER.add("maximumChunksPerTicket"); } + private static class ChunkEntry + { + public final Chunk chunk; + public final NBTTagCompound nbt; + + public ChunkEntry(Chunk chunk) + { + this.chunk = chunk; + this.nbt = new NBTTagCompound(); + } + } + public static Iterator getPersistentChunksIterableFor(final World world, Iterator chunkIterator) { final ImmutableSetMultimap persistentChunksFor = getPersistentChunksFor(world); @@ -452,7 +462,7 @@ public class ForgeChunkManager ArrayListMultimap newTickets = ArrayListMultimap.create(); tickets.put(world, newTickets); - forcedChunks.put(world, ImmutableSetMultimap.of()); + forcedChunks.put(world, ImmutableSetMultimap.of()); if (!(world instanceof WorldServer)) { @@ -461,7 +471,7 @@ public class ForgeChunkManager if (dormantChunkCacheSize != 0) { // only put into cache if we're using dormant chunk caching - dormantChunkCache.put(world, CacheBuilder.newBuilder().maximumSize(dormantChunkCacheSize).build()); + dormantChunkCache.put(world, CacheBuilder.newBuilder().maximumSize(dormantChunkCacheSize).build()); } WorldServer worldServer = (WorldServer) world; File chunkDir = worldServer.getChunkSaveLocation(); @@ -517,7 +527,7 @@ public class ForgeChunkManager tick.player = ticket.getString("Player"); if (!playerLoadedTickets.containsKey(tick.modId)) { - playerLoadedTickets.put(modId, ArrayListMultimap.create()); + playerLoadedTickets.put(modId, ArrayListMultimap.create()); } playerLoadedTickets.get(tick.modId).put(tick.player, tick); } @@ -875,7 +885,7 @@ public class ForgeChunkManager */ public static ImmutableSetMultimap getPersistentChunksFor(World world) { - return forcedChunks.containsKey(world) ? forcedChunks.get(world) : ImmutableSetMultimap.of(); + return forcedChunks.containsKey(world) ? forcedChunks.get(world) : ImmutableSetMultimap.of(); } static void saveWorld(World world) @@ -957,10 +967,32 @@ public class ForgeChunkManager public static void putDormantChunk(long coords, Chunk chunk) { if (dormantChunkCacheSize == 0) return; // Skip if we're not dormant caching chunks - Cache cache = dormantChunkCache.get(chunk.getWorld()); + Cache cache = dormantChunkCache.get(chunk.getWorld()); if (cache != null) { - cache.put(coords, chunk); + cache.put(coords, new ChunkEntry(chunk)); + } + } + + public static void storeChunkNBT(Chunk chunk, NBTTagCompound nbt) + { + if (dormantChunkCacheSize == 0) return; + + Cache cache = dormantChunkCache.get(chunk.getWorld()); + if (cache == null) return; + + ChunkEntry entry = cache.getIfPresent(ChunkPos.asLong(chunk.x, chunk.z)); + if (entry != null) + { + entry.nbt.setTag("Entities", nbt.getTagList("Entities", Constants.NBT.TAG_COMPOUND)); + entry.nbt.setTag("TileEntities", nbt.getTagList("TileEntities", Constants.NBT.TAG_COMPOUND)); + + ClassInheritanceMultiMap[] entityLists = chunk.getEntityLists(); + for (int i = 0; i < entityLists.length; ++i) + { + entityLists[i] = new ClassInheritanceMultiMap<>(Entity.class); + } + chunk.getTileEntityMap().clear(); } } @@ -968,24 +1000,33 @@ public class ForgeChunkManager public static Chunk fetchDormantChunk(long coords, World world) { if (dormantChunkCacheSize == 0) return null; // Don't bother with maps at all if its never gonna get a response - Cache cache = dormantChunkCache.get(world); - if (cache == null) + + Cache cache = dormantChunkCache.get(world); + if (cache == null) return null; + + ChunkEntry entry = cache.getIfPresent(coords); + if (entry == null) return null; + + loadChunkEntities(entry.chunk, entry.nbt, world); + + return entry.chunk; + } + + private static void loadChunkEntities(Chunk chunk, NBTTagCompound nbt, World world) + { + NBTTagList entities = nbt.getTagList("Entities", Constants.NBT.TAG_COMPOUND); + for (int i = 0; i < entities.tagCount(); ++i) { - return null; + AnvilChunkLoader.readChunkEntity(entities.getCompoundTagAt(i), world, chunk); + chunk.setHasEntities(true); } - Chunk chunk = cache.getIfPresent(coords); - if (chunk != null) + + NBTTagList tileEntities = nbt.getTagList("TileEntities", Constants.NBT.TAG_COMPOUND); + for (int i = 0; i < tileEntities.tagCount(); ++i) { - for (ClassInheritanceMultiMap eList : chunk.getEntityLists()) - { - Iterator itr = eList.iterator(); - while (itr.hasNext()) - { - (itr.next()).resetEntityId(); - } - } + TileEntity tileEntity = TileEntity.create(world, tileEntities.getCompoundTagAt(i)); + if (tileEntity != null) chunk.addTileEntity(tileEntity); } - return chunk; } static void captureConfig(File configDir)