diff --git a/patches/net/minecraft/block/BlockSapling.java.patch b/patches/net/minecraft/block/BlockSapling.java.patch index 2b2011b0..80fb37dd 100644 --- a/patches/net/minecraft/block/BlockSapling.java.patch +++ b/patches/net/minecraft/block/BlockSapling.java.patch @@ -1,6 +1,15 @@ --- ../src-base/minecraft/net/minecraft/block/BlockSapling.java +++ ../src-work/minecraft/net/minecraft/block/BlockSapling.java -@@ -22,10 +22,19 @@ +@@ -4,6 +4,8 @@ + import cpw.mods.fml.relauncher.SideOnly; + import java.util.List; + import java.util.Random; ++ ++import io.github.crucible.util.WorldChangeHolder; + import net.minecraft.client.renderer.texture.IIconRegister; + import net.minecraft.creativetab.CreativeTabs; + import net.minecraft.init.Blocks; +@@ -22,10 +24,19 @@ import net.minecraft.world.gen.feature.WorldGenTrees; import net.minecraft.world.gen.feature.WorldGenerator; @@ -20,40 +29,34 @@ private static final String __OBFID = "CL_00000305"; protected BlockSapling() -@@ -41,9 +50,39 @@ +@@ -41,9 +52,32 @@ { super.updateTick(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, p_149674_5_); - if (p_149674_1_.getBlockLightValue(p_149674_2_, p_149674_3_ + 1, p_149674_4_) >= 9 && p_149674_5_.nextInt(7) == 0) + if (p_149674_1_.getBlockLightValue(p_149674_2_, p_149674_3_ + 1, p_149674_4_) >= 9 && (p_149674_5_.nextInt(Math.max(2, (int)((p_149674_1_.growthOdds / p_149674_1_.getSpigotConfig().saplingModifier * 7) + 0.5F))) == 0)) // Spigot // Cauldron { +- this.func_149879_c(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, p_149674_5_); + // Cauldron start -+ p_149674_1_.captureTreeGeneration = true; - this.func_149879_c(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, p_149674_5_); -+ p_149674_1_.captureTreeGeneration = false; -+ if (p_149674_1_.capturedBlockSnapshots.size() > 0) -+ { ++ // If there's no bukkit tree type, changes will be implicitly applied ++ try (WorldChangeHolder.CaptureContext context = p_149674_1_.worldChangeHolder.startCapture()) { ++ this.func_149879_c(p_149674_1_, p_149674_2_, p_149674_3_, p_149674_4_, p_149674_5_); ++ context.pauseCapture(); ++ + TreeType treeType = BlockSapling.treeType; + BlockSapling.treeType = null; -+ Location location = new Location(p_149674_1_.getWorld(), p_149674_2_, p_149674_3_, p_149674_4_); -+ List blocks = (List) p_149674_1_.capturedBlockSnapshots.clone(); -+ List blockstates = new java.util.ArrayList(); -+ for (net.minecraftforge.common.util.BlockSnapshot snapshot : blocks) -+ { -+ blockstates.add(new CraftBlockState(snapshot)); -+ } -+ p_149674_1_.capturedBlockSnapshots.clear(); -+ StructureGrowEvent event = null; -+ if (treeType != null) -+ { -+ event = new StructureGrowEvent(location, treeType, false, null, blockstates); ++ ++ if (treeType != null && context.hasChanges()) { ++ List blockstates = context.asBukkitBlockState(); ++ context.discardChanges(); // Changes applied through the event and bukkit BlockState ++ Location location = new Location(p_149674_1_.getWorld(), p_149674_2_, p_149674_3_, p_149674_4_); ++ ++ StructureGrowEvent event = new StructureGrowEvent(location, treeType, false, null, blockstates);; + org.bukkit.Bukkit.getPluginManager().callEvent(event); -+ } -+ if (event == null || !event.isCancelled()) -+ { -+ for (BlockState blockstate : blockstates) -+ { -+ blockstate.update(true); ++ if (!event.isCancelled()) { ++ for (BlockState blockstate : blockstates) { ++ blockstate.update(true); ++ } + } + } + } @@ -61,7 +64,7 @@ } } } -@@ -73,7 +112,20 @@ +@@ -73,7 +107,20 @@ { if (!net.minecraftforge.event.terraingen.TerrainGen.saplingGrowTree(p_149878_1_, p_149878_5_, p_149878_2_, p_149878_3_, p_149878_4_)) return; int l = p_149878_1_.getBlockMetadata(p_149878_2_, p_149878_3_, p_149878_4_) & 7; @@ -83,7 +86,7 @@ int i1 = 0; int j1 = 0; boolean flag = false; -@@ -84,6 +136,7 @@ +@@ -84,6 +131,7 @@ default: break; case 1: @@ -91,7 +94,7 @@ label78: for (i1 = 0; i1 >= -1; --i1) -@@ -108,6 +161,7 @@ +@@ -108,6 +156,7 @@ break; case 2: @@ -99,7 +102,7 @@ object = new WorldGenForest(true, false); break; case 3: -@@ -119,6 +173,7 @@ +@@ -119,6 +168,7 @@ { if (this.func_149880_a(p_149878_1_, p_149878_2_ + i1, p_149878_3_, p_149878_4_ + j1, 3) && this.func_149880_a(p_149878_1_, p_149878_2_ + i1 + 1, p_149878_3_, p_149878_4_ + j1, 3) && this.func_149880_a(p_149878_1_, p_149878_2_ + i1, p_149878_3_, p_149878_4_ + j1 + 1, 3) && this.func_149880_a(p_149878_1_, p_149878_2_ + i1 + 1, p_149878_3_, p_149878_4_ + j1 + 1, 3)) { @@ -107,7 +110,7 @@ object = new WorldGenMegaJungle(true, 10, 20, 3, 3); flag = true; break label93; -@@ -130,11 +185,13 @@ +@@ -130,11 +180,13 @@ { j1 = 0; i1 = 0; @@ -121,7 +124,7 @@ object = new WorldGenSavannaTree(true); break; case 5: -@@ -147,6 +204,7 @@ +@@ -147,6 +199,7 @@ if (this.func_149880_a(p_149878_1_, p_149878_2_ + i1, p_149878_3_, p_149878_4_ + j1, 5) && this.func_149880_a(p_149878_1_, p_149878_2_ + i1 + 1, p_149878_3_, p_149878_4_ + j1, 5) && this.func_149880_a(p_149878_1_, p_149878_2_ + i1, p_149878_3_, p_149878_4_ + j1 + 1, 5) && this.func_149880_a(p_149878_1_, p_149878_2_ + i1 + 1, p_149878_3_, p_149878_4_ + j1 + 1, 5)) { object = new WorldGenCanopyTree(true); diff --git a/patches/net/minecraft/world/World.java.patch b/patches/net/minecraft/world/World.java.patch index 5664362f..db24c2e1 100644 --- a/patches/net/minecraft/world/World.java.patch +++ b/patches/net/minecraft/world/World.java.patch @@ -16,7 +16,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; -@@ -12,12 +17,12 @@ +@@ -12,12 +17,13 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.Callable; @@ -24,6 +24,7 @@ +import java.util.concurrent.ConcurrentMap; + +import io.github.crucible.CrucibleModContainer; ++import io.github.crucible.util.WorldChangeHolder; import net.minecraft.block.Block; -import net.minecraft.block.BlockHopper; import net.minecraft.block.BlockLiquid; @@ -33,7 +34,7 @@ import net.minecraft.block.material.Material; import net.minecraft.command.IEntitySelector; import net.minecraft.crash.CrashReport; -@@ -36,7 +41,6 @@ +@@ -36,7 +42,6 @@ import net.minecraft.tileentity.TileEntity; import net.minecraft.util.AxisAlignedBB; import net.minecraft.util.ChunkCoordinates; @@ -41,7 +42,7 @@ import net.minecraft.util.Facing; import net.minecraft.util.MathHelper; import net.minecraft.util.MovingObjectPosition; -@@ -51,7 +55,6 @@ +@@ -51,7 +56,6 @@ import net.minecraft.world.storage.ISaveHandler; import net.minecraft.world.storage.MapStorage; import net.minecraft.world.storage.WorldInfo; @@ -49,7 +50,7 @@ import cpw.mods.fml.common.FMLLog; import com.google.common.collect.ImmutableSetMultimap; -@@ -60,16 +63,56 @@ +@@ -60,19 +64,59 @@ import net.minecraftforge.common.ForgeChunkManager; import net.minecraftforge.common.ForgeChunkManager.Ticket; import net.minecraftforge.common.ForgeModContainer; @@ -107,8 +108,12 @@ + public abstract class World implements IBlockAccess { - /** -@@ -82,16 +125,15 @@ +- /** ++ /** + * Used in the getEntitiesWithinAABB functions to expand the search area for entities. + * Modders should change this variable to a higher value if it is less then the radius + * of one of there entities. +@@ -82,16 +126,15 @@ public final MapStorage perWorldStorage; public boolean scheduledUpdatesAreImmediate; @@ -130,7 +135,7 @@ protected final int DIST_HASH_MAGIC = 1013904223; public float prevRainingStrength; public float rainingStrength; -@@ -99,27 +141,92 @@ +@@ -99,27 +142,93 @@ public float thunderingStrength; public int lastLightningBolt; public EnumDifficulty difficultySetting; @@ -172,14 +177,15 @@ private boolean field_147481_N; int[] lightUpdateBlockList; + // Cauldron start -+ public boolean captureTreeGeneration = false; ++ public boolean captureAndProxyBlocks = false; ++ public WorldChangeHolder worldChangeHolder = new WorldChangeHolder(this); + public ArrayList capturedItems = new ArrayList(); + public int entitiesTicked; + public int tilesTicked; + public CauldronWorldConfig cauldronConfig; + public TileEntityWorldConfig tileentityConfig; + public SushchestvoWorldConfig sushchestvoConfig; -+ ++ + public Boolean isModded = null; + // preload world crash report classes to fix NCDFE masking StackOverflow/memory error, see #721 + private static boolean preloadedCrashClasses = false; @@ -231,7 +237,7 @@ private static final String __OBFID = "CL_00000140"; public boolean restoringBlockSnapshots = false; public boolean captureBlockSnapshots = false; -@@ -166,6 +273,27 @@ +@@ -166,6 +275,27 @@ return this.provider.worldChunkMgr; } @@ -259,7 +265,7 @@ @SideOnly(Side.CLIENT) public World(ISaveHandler p_i45368_1_, String p_i45368_2_, WorldProvider p_i45368_3_, WorldSettings p_i45368_4_, Profiler p_i45368_5_) { -@@ -179,6 +307,12 @@ +@@ -179,6 +309,12 @@ this.worldInfo = new WorldInfo(p_i45368_4_, p_i45368_2_); this.provider = p_i45368_3_; perWorldStorage = new MapStorage((ISaveHandler)null); @@ -272,13 +278,13 @@ } // Broken up so that the WorldClient gets the chance to set the mapstorage object before the dimension initializes -@@ -207,8 +341,182 @@ +@@ -207,8 +343,182 @@ this.calculateInitialWeather(); } + // Changed signature - added gen and env + public World(ISaveHandler p_i45369_1_, String p_i45369_2_, WorldSettings p_i45369_3_, WorldProvider p_i45369_4_, Profiler p_i45369_5_, ChunkGenerator gen, -+ org.bukkit.World.Environment env) ++ org.bukkit.World.Environment env) + { + this.spigotConfig = new org.spigotmc.SpigotWorldConfig(p_i45369_2_); // Spigot + initLimiter(); // Spigot @@ -368,11 +374,11 @@ + // Cauldron end + this.chunkProvider = this.createChunkProvider(); + // Thermos start -+ try { -+ if(this.chunkProvider != null) -+ this.isModded = !this.chunkProvider.getClass().getPackage().getName().startsWith("net.minecraft"); } catch(Exception e) { this.isModded = false; } ++ try { ++ if(this.chunkProvider != null) ++ this.isModded = !this.chunkProvider.getClass().getPackage().getName().startsWith("net.minecraft"); } catch(Exception e) { this.isModded = false; } + // Thermos end -+ ++ + if (this instanceof WorldServer) + { + this.perWorldStorage = new MapStorage(new WorldSpecificSaveHandler((WorldServer) this, p_i45369_1_)); @@ -435,7 +441,7 @@ + initLimiter(); // Spigot + this.cauldronConfig = new CauldronWorldConfig(p_i45369_2_, MinecraftServer.getServer().cauldronConfig); + this.tileentityConfig = new TileEntityWorldConfig(p_i45369_2_, MinecraftServer.getServer().tileEntityConfig); -+ this.sushchestvoConfig = new SushchestvoWorldConfig(p_i45369_2_, MinecraftServer.getServer().sushchestvoConfig); ++ this.sushchestvoConfig = new SushchestvoWorldConfig(p_i45369_2_, MinecraftServer.getServer().sushchestvoConfig); + this.world = DimensionManager.getWorld(0).getWorld(); + this.timings = DimensionManager.getWorld(0).timings; + this.activeChunkSet_CB = new gnu.trove.map.hash.TLongShortHashMap(spigotConfig.chunksPerTick * 5, 0.7f, Long.MIN_VALUE, Short.MIN_VALUE); @@ -455,7 +461,7 @@ this.ambientTickCountdown = this.rand.nextInt(12000); this.spawnHostileMobs = true; this.spawnPeacefulMobs = true; -@@ -216,7 +524,6 @@ +@@ -216,7 +526,6 @@ this.lightUpdateBlockList = new int[32768]; this.saveHandler = p_i45369_1_; this.theProfiler = p_i45369_5_; @@ -463,7 +469,7 @@ this.worldInfo = p_i45369_1_.loadWorldInfo(); if (p_i45369_4_ != null) -@@ -235,15 +542,33 @@ +@@ -235,14 +544,32 @@ if (this.worldInfo == null) { this.worldInfo = new WorldInfo(p_i45369_3_, p_i45369_2_); @@ -488,159 +494,119 @@ + this.provider.dimensionId = providerId; + // Cauldron end this.chunkProvider = this.createChunkProvider(); -- + // Thermos start -+ try { -+ if(this.chunkProvider != null) -+ this.isModded = !this.chunkProvider.getClass().getPackage().getName().startsWith("net.minecraft"); } catch(Exception e) { this.isModded = false; } ++ try { ++ if(this.chunkProvider != null) ++ this.isModded = !this.chunkProvider.getClass().getPackage().getName().startsWith("net.minecraft"); } catch(Exception e) { this.isModded = false; } + // Thermos end -+ + if (this instanceof WorldServer) { - this.perWorldStorage = new MapStorage(new WorldSpecificSaveHandler((WorldServer)this, p_i45369_1_)); -@@ -294,6 +619,7 @@ +@@ -294,6 +621,7 @@ this.calculateInitialSkylight(); this.calculateInitialWeather(); } -+ // Cauldron end ++ // Cauldron end private static MapStorage s_mapStorage; private static ISaveHandler s_savehandler; -@@ -336,6 +662,18 @@ +@@ -336,6 +664,15 @@ public Block getBlock(int p_147439_1_, int p_147439_2_, int p_147439_3_) { + // Cauldron start - tree generation -+ if (captureTreeGeneration) ++ if (this.worldChangeHolder.isCapturing()) + { -+ for (net.minecraftforge.common.util.BlockSnapshot blocksnapshot : capturedBlockSnapshots) -+ { -+ if (blocksnapshot.x == p_147439_1_ && blocksnapshot.y == p_147439_2_ && blocksnapshot.z == p_147439_3_) -+ { -+ return blocksnapshot.replacedBlock; -+ } ++ Block block = this.worldChangeHolder.getBlock(p_147439_1_, p_147439_2_, p_147439_3_); ++ if (block != null) { ++ return block; + } + } + // Cauldron end if (p_147439_1_ >= -30000000 && p_147439_3_ >= -30000000 && p_147439_1_ < 30000000 && p_147439_3_ < 30000000 && p_147439_2_ >= 0 && p_147439_2_ < 256) { Chunk chunk = null; -@@ -376,35 +714,58 @@ - return this.checkChunksExist(p_72873_1_ - p_72873_4_, p_72873_2_ - p_72873_4_, p_72873_3_ - p_72873_4_, p_72873_1_ + p_72873_4_, p_72873_2_ + p_72873_4_, p_72873_3_ + p_72873_4_); - } - -- public boolean checkChunksExist(int p_72904_1_, int p_72904_2_, int p_72904_3_, int p_72904_4_, int p_72904_5_, int p_72904_6_) -- { -- if (p_72904_5_ >= 0 && p_72904_2_ < 256) -- { +@@ -380,23 +717,46 @@ + { + if (p_72904_5_ >= 0 && p_72904_2_ < 256) + { - p_72904_1_ >>= 4; - p_72904_3_ >>= 4; - p_72904_4_ >>= 4; - p_72904_6_ >>= 4; -+ public boolean checkChunksExist(int p_72904_1_, int p_72904_2_, int p_72904_3_, int p_72904_4_, int p_72904_5_, int p_72904_6_) -+ { -+ if (p_72904_5_ >= 0 && p_72904_2_ < 256) -+ { -+ if (!(this.chunkProvider instanceof ChunkProviderServer)) -+ { -+ p_72904_1_ >>= 4; -+ p_72904_3_ >>= 4; -+ p_72904_4_ >>= 4; -+ p_72904_6_ >>= 4; -+ -+ for (int k1 = p_72904_1_; k1 <= p_72904_4_; ++k1) -+ { -+ for (int l1 = p_72904_3_; l1 <= p_72904_6_; ++l1) -+ { -+ if (!this.chunkExists(k1, l1)) -+ { -+ return false; -+ } -+ } -+ } -+ -+ return true; -+ } -+ else -+ { -+ p_72904_1_ >>= 4; -+ p_72904_3_ >>= 4; -+ p_72904_4_ >>= 4; -+ p_72904_6_ >>= 4; -+ -+ ArrayList st = new ArrayList(); -+ st.ensureCapacity((p_72904_4_ - p_72904_1_+1) * (p_72904_6_ - p_72904_3_ + 1)); -+ -+ for (int k1 = p_72904_1_; k1 <= p_72904_4_; ++k1) -+ { -+ for (int l1 = p_72904_3_; l1 <= p_72904_6_; ++l1) -+ { -+ st.add(new int[] { k1, l1} ); -+ } -+ } -+ ChunkProviderServer cps = (ChunkProviderServer) this.chunkProvider; -+ return cps.loadedChunkHashMap_KC.rawThermos().bulkCheck(st); -+ } -+ } -+ else -+ { -+ return false; -+ } -+ } - +- - for (int k1 = p_72904_1_; k1 <= p_72904_4_; ++k1) -- { ++ if (!(this.chunkProvider instanceof ChunkProviderServer)) + { - for (int l1 = p_72904_3_; l1 <= p_72904_6_; ++l1) -- { ++ p_72904_1_ >>= 4; ++ p_72904_3_ >>= 4; ++ p_72904_4_ >>= 4; ++ p_72904_6_ >>= 4; ++ ++ for (int k1 = p_72904_1_; k1 <= p_72904_4_; ++k1) + { - if (!this.chunkExists(k1, l1)) -- { ++ for (int l1 = p_72904_3_; l1 <= p_72904_6_; ++l1) + { - return false; -- } -- } -- } -- ++ if (!this.chunkExists(k1, l1)) ++ { ++ return false; ++ } + } + } ++ ++ return true; + } ++ else ++ { ++ p_72904_1_ >>= 4; ++ p_72904_3_ >>= 4; ++ p_72904_4_ >>= 4; ++ p_72904_6_ >>= 4; + - return true; -- } -- else -- { -- return false; -- } -- } -- ++ ArrayList st = new ArrayList(); ++ st.ensureCapacity((p_72904_4_ - p_72904_1_+1) * (p_72904_6_ - p_72904_3_ + 1)); ++ ++ for (int k1 = p_72904_1_; k1 <= p_72904_4_; ++k1) ++ { ++ for (int l1 = p_72904_3_; l1 <= p_72904_6_; ++l1) ++ { ++ st.add(new int[] { k1, l1} ); ++ } ++ } ++ ChunkProviderServer cps = (ChunkProviderServer) this.chunkProvider; ++ return cps.loadedChunkHashMap_KC.rawThermos().bulkCheck(st); ++ } + } + else + { +@@ -404,7 +764,7 @@ + } + } + - protected boolean chunkExists(int p_72916_1_, int p_72916_2_) + public boolean chunkExists(int p_72916_1_, int p_72916_2_) // Cauldron - protected -> public for repackaging { return this.chunkProvider.chunkExists(p_72916_1_, p_72916_2_); } -@@ -421,6 +782,27 @@ +@@ -421,6 +781,13 @@ public boolean setBlock(int p_147465_1_, int p_147465_2_, int p_147465_3_, Block p_147465_4_, int p_147465_5_, int p_147465_6_) { + // Cauldron start - tree generation -+ if (this.captureTreeGeneration) ++ if (this.worldChangeHolder.isCapturing()) + { -+ net.minecraftforge.common.util.BlockSnapshot blocksnapshot = null; -+ -+ for (net.minecraftforge.common.util.BlockSnapshot previous : capturedBlockSnapshots) -+ { -+ if (previous.x == p_147465_1_ && previous.y == p_147465_2_ && previous.z == p_147465_3_) -+ { -+ blocksnapshot = previous; -+ break; -+ } -+ } -+ if (blocksnapshot != null) -+ { -+ capturedBlockSnapshots.remove(blocksnapshot); -+ } -+ this.capturedBlockSnapshots.add(new net.minecraftforge.common.util.BlockSnapshot(this, p_147465_1_, p_147465_2_, p_147465_3_, p_147465_4_, p_147465_5_, p_147465_6_)); ++ this.worldChangeHolder.setBlock(p_147465_1_, p_147465_2_, p_147465_3_, p_147465_4_, p_147465_5_, p_147465_6_); + return true; + } + // Cauldron end if (p_147465_1_ >= -30000000 && p_147465_3_ >= -30000000 && p_147465_1_ < 30000000 && p_147465_3_ < 30000000) { if (p_147465_2_ < 0) -@@ -448,8 +830,22 @@ +@@ -448,8 +815,22 @@ this.capturedBlockSnapshots.add(blockSnapshot); } @@ -663,7 +629,7 @@ if (!flag && blockSnapshot != null) { this.capturedBlockSnapshots.remove(blockSnapshot); -@@ -460,6 +856,7 @@ +@@ -460,6 +841,7 @@ this.func_147451_t(p_147465_1_, p_147465_2_, p_147465_3_); this.theProfiler.endSection(); @@ -671,19 +637,16 @@ if (flag && blockSnapshot == null) // Don't notify clients or update physics while capturing blockstates { // Modularize client and physic updates -@@ -496,6 +893,19 @@ +@@ -496,6 +878,16 @@ public int getBlockMetadata(int p_72805_1_, int p_72805_2_, int p_72805_3_) { + // Cauldron start - tree generation -+ if (captureTreeGeneration) ++ if (this.worldChangeHolder.isCapturing()) + { -+ for (net.minecraftforge.common.util.BlockSnapshot blocksnapshot : capturedBlockSnapshots) -+ { -+ if (blocksnapshot.x == p_72805_1_ && blocksnapshot.y == p_72805_2_ && blocksnapshot.z == p_72805_3_) -+ { -+ return blocksnapshot.meta; -+ } ++ int meta = this.worldChangeHolder.getMetadata(p_72805_1_, p_72805_2_, p_72805_3_); ++ if (meta >= 0) { ++ return meta; + } + } + // Cauldron end @@ -691,7 +654,7 @@ if (p_72805_1_ >= -30000000 && p_72805_3_ >= -30000000 && p_72805_1_ < 30000000 && p_72805_3_ < 30000000) { if (p_72805_2_ < 0) -@@ -511,7 +921,7 @@ +@@ -511,7 +903,7 @@ Chunk chunk = this.getChunkFromChunkCoords(p_72805_1_ >> 4, p_72805_3_ >> 4); p_72805_1_ &= 15; p_72805_3_ &= 15; @@ -700,7 +663,7 @@ } } else -@@ -610,6 +1020,12 @@ +@@ -610,6 +1002,12 @@ public void notifyBlockChange(int p_147444_1_, int p_147444_2_, int p_147444_3_, Block p_147444_4_) { @@ -713,7 +676,7 @@ this.notifyBlocksOfNeighborChange(p_147444_1_, p_147444_2_, p_147444_3_, p_147444_4_); } -@@ -694,6 +1110,21 @@ +@@ -694,6 +1092,21 @@ try { @@ -723,7 +686,7 @@ + if (world != null && !isProfilingWorld()) + { + BlockPhysicsEvent event = new BlockPhysicsEvent(world.getBlockAt(p_147460_1_, p_147460_2_, p_147460_3_), -+ CraftMagicNumbers.getId(p_147460_4_)); ++ CraftMagicNumbers.getId(p_147460_4_)); + this.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) @@ -735,7 +698,7 @@ block.onNeighborBlockChange(this, p_147460_1_, p_147460_2_, p_147460_3_, p_147460_4_); } catch (Throwable throwable1) -@@ -947,14 +1378,13 @@ +@@ -947,14 +1360,13 @@ { int l = p_72972_2_ >> 4; int i1 = p_72972_4_ >> 4; @@ -752,23 +715,21 @@ return chunk.getSavedLightValue(p_72972_1_, p_72972_2_ & 15, p_72972_3_, p_72972_4_ & 15); } } -@@ -1304,9 +1734,24 @@ - this.weatherEffects.add(p_72942_1_); +@@ -1305,8 +1717,23 @@ return true; } -- -+ + + public List itemStackSpawnQueue = new ArrayList(); + public boolean captureItemDrops = false; -+ ++ public boolean spawnEntityInWorld(Entity p_72838_1_) { + // CraftBukkit start - Used for entities other than creatures -+ if(p_72838_1_ instanceof EntityItem && this.captureItemDrops) -+ { -+ itemStackSpawnQueue.add((EntityItem)p_72838_1_); -+ return true; -+ } ++ if(p_72838_1_ instanceof EntityItem && this.captureItemDrops) ++ { ++ itemStackSpawnQueue.add((EntityItem)p_72838_1_); ++ return true; ++ } + return this.addEntity(p_72838_1_, SpawnReason.DEFAULT); // Set reason as DEFAULT + } + @@ -778,7 +739,7 @@ // do not drop any items while restoring blocksnapshots. Prevents dupes if (!this.isRemote && (p_72838_1_ == null || (p_72838_1_ instanceof net.minecraft.entity.item.EntityItem && this.restoringBlockSnapshots))) return false; -@@ -1319,23 +1764,99 @@ +@@ -1319,23 +1746,99 @@ flag = true; } @@ -799,9 +760,9 @@ + { + // Cauldron start - add custom entity support + boolean isAnimal = p_72838_1_ instanceof EntityAnimal || p_72838_1_ instanceof EntityWaterMob || p_72838_1_ instanceof EntityGolem -+ || p_72838_1_.isCreatureType(EnumCreatureType.creature, false); ++ || p_72838_1_.isCreatureType(EnumCreatureType.creature, false); + boolean isMonster = p_72838_1_ instanceof EntityMob || p_72838_1_ instanceof EntityGhast || p_72838_1_ instanceof EntitySlime -+ || p_72838_1_.isCreatureType(EnumCreatureType.monster, false); ++ || p_72838_1_.isCreatureType(EnumCreatureType.monster, false); + // Cauldron end + + if (spawnReason != SpawnReason.CUSTOM) @@ -880,7 +841,7 @@ return true; } } -@@ -1346,6 +1867,8 @@ +@@ -1346,6 +1849,8 @@ { ((IWorldAccess)this.worldAccesses.get(i)).onEntityCreate(p_72923_1_); } @@ -889,7 +850,7 @@ } public void onEntityRemoved(Entity p_72847_1_) -@@ -1354,6 +1877,8 @@ +@@ -1354,6 +1859,8 @@ { ((IWorldAccess)this.worldAccesses.get(i)).onEntityDestroy(p_72847_1_); } @@ -898,7 +859,7 @@ } public void removeEntity(Entity p_72900_1_) -@@ -1397,6 +1922,19 @@ +@@ -1397,6 +1904,19 @@ } this.loadedEntityList.remove(p_72973_1_); @@ -918,7 +879,7 @@ this.onEntityRemoved(p_72973_1_); } -@@ -1407,42 +1945,62 @@ +@@ -1407,42 +1927,62 @@ public List getCollidingBoundingBoxes(Entity p_72945_1_, AxisAlignedBB p_72945_2_) { @@ -992,11 +953,11 @@ + net.minecraftforge.cauldron.CauldronHooks.logEntitySize(this, p_72945_1_, list); // Cauldron add logging for entity collisions + this.collidingBoundingBoxes.ensureCapacity(list.size()); -+ ++ for (int j2 = 0; j2 < list.size(); ++j2) { AxisAlignedBB axisalignedbb1 = ((Entity)list.get(j2)).getBoundingBox(); -@@ -1797,11 +2355,22 @@ +@@ -1797,11 +2337,22 @@ Entity entity; CrashReport crashreport; CrashReportCategory crashreportcategory; @@ -1019,7 +980,7 @@ try { ++entity.ticksExisted; -@@ -1862,10 +2431,15 @@ +@@ -1862,10 +2413,15 @@ this.unloadedEntityList.clear(); this.theProfiler.endStartSection("regular"); @@ -1032,13 +993,13 @@ + TimingHistory.entityTicks += this.loadedEntityList.size(); // Paper + int entitiesThisCycle = 0; + if (tickPosition < 0) tickPosition = 0; -+ for (entityLimiter.initTick(); entitiesThisCycle < loadedEntityList.size() && (entitiesThisCycle % 10 == 0 || entityLimiter.shouldContinue()); tickPosition++, entitiesThisCycle++) { ++ for (entityLimiter.initTick(); entitiesThisCycle < loadedEntityList.size() && (entitiesThisCycle % 10 == 0 || entityLimiter.shouldContinue()); tickPosition++, entitiesThisCycle++) { + tickPosition = (tickPosition < loadedEntityList.size()) ? tickPosition : 0; + entity = (Entity)this.loadedEntityList.get(this.tickPosition); if (entity.ridingEntity != null) { -@@ -1884,7 +2458,10 @@ +@@ -1884,7 +2440,10 @@ { try { @@ -1049,14 +1010,14 @@ } catch (Throwable throwable1) { -@@ -1916,30 +2493,82 @@ +@@ -1916,30 +2475,82 @@ { this.getChunkFromChunkCoords(j, l).removeEntity(entity); } - - this.loadedEntityList.remove(i--); + if (this.tickPosition < this.loadedEntityList.size()) // KCauldron -+ this.loadedEntityList.remove(this.tickPosition--); // CraftBukkit - Use field for loop variable ++ this.loadedEntityList.remove(this.tickPosition--); // CraftBukkit - Use field for loop variable + else CrucibleModContainer.logger.warn("Entity removal desync! {}", entity); // KCauldron this.onEntityRemoved(entity); } @@ -1139,7 +1100,7 @@ crashreport = CrashReport.makeCrashReport(throwable, "Ticking block entity"); crashreportcategory = crashreport.makeCategory("Block entity being ticked"); tileentity.func_145828_a(crashreportcategory); -@@ -1955,23 +2584,13 @@ +@@ -1955,23 +2566,13 @@ } } } @@ -1168,7 +1129,7 @@ if (!this.field_147483_b.isEmpty()) { for (Object tile : field_147483_b) -@@ -1981,6 +2600,7 @@ +@@ -1981,6 +2582,7 @@ this.loadedTileEntityList.removeAll(this.field_147483_b); this.field_147483_b.clear(); } @@ -1176,7 +1137,7 @@ this.field_147481_N = false; -@@ -1992,14 +2612,14 @@ +@@ -1992,14 +2594,14 @@ { TileEntity tileentity1 = (TileEntity)this.addedTileEntityList.get(k); @@ -1193,7 +1154,7 @@ { if (this.chunkExists(tileentity1.xCoord >> 4, tileentity1.zCoord >> 4)) { -@@ -2016,17 +2636,25 @@ +@@ -2016,17 +2618,25 @@ this.addedTileEntityList.clear(); } @@ -1201,7 +1162,7 @@ + co.aikar.timings.TimingHistory.tileEntityTicks += this.tilesTicked; // Paper this.theProfiler.endSection(); this.theProfiler.endSection(); -+ ++ } public void func_147448_a(Collection p_147448_1_) @@ -1222,7 +1183,7 @@ } public void updateEntity(Entity p_72870_1_) -@@ -2036,11 +2664,16 @@ +@@ -2036,11 +2646,16 @@ public void updateEntityWithOptionalForce(Entity p_72866_1_, boolean p_72866_2_) { @@ -1242,7 +1203,7 @@ if (!canUpdate) { -@@ -2048,9 +2681,30 @@ +@@ -2048,9 +2663,30 @@ MinecraftForge.EVENT_BUS.post(event); canUpdate = event.canUpdate; } @@ -1264,7 +1225,7 @@ + { + p_72866_1_.ticksExisted++; + p_72866_1_.inactiveTick(); -+ return; ++ return; + } + // Spigot end if (canUpdate) @@ -1274,7 +1235,7 @@ p_72866_1_.lastTickPosX = p_72866_1_.posX; p_72866_1_.lastTickPosY = p_72866_1_.posY; p_72866_1_.lastTickPosZ = p_72866_1_.posZ; -@@ -2134,6 +2788,7 @@ +@@ -2134,6 +2770,7 @@ p_72866_1_.riddenByEntity = null; } } @@ -1282,7 +1243,24 @@ } } -@@ -2570,7 +3225,7 @@ +@@ -2517,6 +3154,16 @@ + int l; + TileEntity tileentity1; + ++ // Crucible start - tree generation ++ if (this.worldChangeHolder.isCapturing()) ++ { ++ tileentity = this.worldChangeHolder.getTileEntity(p_147438_1_, p_147438_2_, p_147438_3_); ++ if (tileentity != null) { ++ return tileentity; ++ } ++ } ++ // Crucible end ++ + if (this.field_147481_N) + { + for (l = 0; l < this.addedTileEntityList.size(); ++l) +@@ -2570,7 +3217,7 @@ return; } @@ -1291,7 +1269,7 @@ { if (this.field_147481_N) { -@@ -2606,12 +3261,32 @@ +@@ -2606,13 +3253,33 @@ public void removeTileEntity(int p_147475_1_, int p_147475_2_, int p_147475_3_) { Chunk chunk = getChunkFromChunkCoords(p_147475_1_ >> 4, p_147475_3_ >> 4); @@ -1313,18 +1291,20 @@ + } + }*/ + } -+ ++ if (chunk != null) chunk.removeTileEntity(p_147475_1_ & 15, p_147475_2_, p_147475_3_ & 15); func_147453_f(p_147475_1_, p_147475_2_, p_147475_3_, getBlock(p_147475_1_, p_147475_2_, p_147475_3_)); } public void func_147457_a(TileEntity p_147457_1_) { +- this.field_147483_b.add(p_147457_1_); + if(p_147457_1_ != null) - this.field_147483_b.add(p_147457_1_); ++ this.field_147483_b.add(p_147457_1_); } -@@ -2718,7 +3393,15 @@ + public boolean func_147469_q(int p_147469_1_, int p_147469_2_, int p_147469_3_) +@@ -2718,7 +3385,15 @@ if (i <= 0) { @@ -1341,7 +1321,7 @@ } } -@@ -2754,7 +3437,15 @@ +@@ -2754,7 +3429,15 @@ if (j <= 0) { @@ -1358,7 +1338,7 @@ } } -@@ -2777,8 +3468,41 @@ +@@ -2777,8 +3460,41 @@ protected void setActivePlayerChunksAndCheckLight() { this.activeChunkSet.clear(); @@ -1401,7 +1381,7 @@ int i; EntityPlayer entityplayer; int j; -@@ -2788,17 +3512,28 @@ +@@ -2788,17 +3504,28 @@ for (i = 0; i < this.playerEntities.size(); ++i) { entityplayer = (EntityPlayer)this.playerEntities.get(i); @@ -1436,7 +1416,7 @@ } this.theProfiler.endSection(); -@@ -2810,7 +3545,7 @@ +@@ -2810,7 +3537,7 @@ this.theProfiler.startSection("playerCheckLight"); @@ -1445,7 +1425,7 @@ { i = this.rand.nextInt(this.playerEntities.size()); entityplayer = (EntityPlayer)this.playerEntities.get(i); -@@ -3034,9 +3769,9 @@ +@@ -3034,9 +3761,9 @@ } } @@ -1457,12 +1437,12 @@ { return false; } -@@ -3166,6 +3901,28 @@ +@@ -3166,6 +3893,28 @@ } } + // PaperSpigot start - Asynchronous light updates -+ ++ + // Thermos avoid lighting modded chunks + if(isModded == null && this.chunkProvider != null) + { @@ -1470,10 +1450,10 @@ + } + else if(this.chunkProvider == null) + { -+ this.isModded = false; ++ this.isModded = false; + } + // Thermos end -+ ++ + if (!isModded && chunk.worldObj.spigotConfig.useAsyncLighting) { + chunk.pendingLightUpdates.decrementAndGet(); + if (neighbors != null) { @@ -1486,7 +1466,7 @@ this.theProfiler.endSection(); return true; } -@@ -3188,7 +3945,7 @@ +@@ -3188,7 +3937,7 @@ public List getEntitiesWithinAABBExcludingEntity(Entity p_94576_1_, AxisAlignedBB p_94576_2_, IEntitySelector p_94576_3_) { @@ -1495,7 +1475,7 @@ int i = MathHelper.floor_double((p_94576_2_.minX - MAX_ENTITY_RADIUS) / 16.0D); int j = MathHelper.floor_double((p_94576_2_.maxX + MAX_ENTITY_RADIUS) / 16.0D); int k = MathHelper.floor_double((p_94576_2_.minZ - MAX_ENTITY_RADIUS) / 16.0D); -@@ -3219,7 +3976,7 @@ +@@ -3219,7 +3968,7 @@ int j = MathHelper.floor_double((p_82733_2_.maxX + MAX_ENTITY_RADIUS) / 16.0D); int k = MathHelper.floor_double((p_82733_2_.minZ - MAX_ENTITY_RADIUS) / 16.0D); int l = MathHelper.floor_double((p_82733_2_.maxZ + MAX_ENTITY_RADIUS) / 16.0D); @@ -1504,7 +1484,7 @@ for (int i1 = i; i1 <= j; ++i1) { -@@ -3284,8 +4041,21 @@ +@@ -3284,8 +4033,21 @@ { Entity entity = (Entity)this.loadedEntityList.get(j); @@ -1527,7 +1507,7 @@ ++i; } } -@@ -3298,6 +4068,7 @@ +@@ -3298,6 +4060,7 @@ for (int i = 0; i < p_72868_1_.size(); ++i) { Entity entity = (Entity)p_72868_1_.get(i); @@ -1535,7 +1515,7 @@ if (!MinecraftForge.EVENT_BUS.post(new EntityJoinWorldEvent(entity, this))) { loadedEntityList.add(entity); -@@ -3314,8 +4085,17 @@ +@@ -3314,8 +4077,17 @@ public boolean canPlaceEntityOnSide(Block p_147472_1_, int p_147472_2_, int p_147472_3_, int p_147472_4_, boolean p_147472_5_, int p_147472_6_, Entity p_147472_7_, ItemStack p_147472_8_) { Block block1 = this.getBlock(p_147472_2_, p_147472_3_, p_147472_4_); @@ -1544,17 +1524,17 @@ - return axisalignedbb != null && !this.checkNoEntityCollision(axisalignedbb, p_147472_7_) ? false : (block1.getMaterial() == Material.circuits && p_147472_1_ == Blocks.anvil ? true : block1.isReplaceable(this, p_147472_2_, p_147472_3_, p_147472_4_) && p_147472_1_.canReplace(this, p_147472_2_, p_147472_3_, p_147472_4_, p_147472_6_, p_147472_8_)); + // CraftBukkit start - store default return + boolean defaultReturn = axisalignedbb != null && !this.checkNoEntityCollision(axisalignedbb, p_147472_7_) ? false -+ : (block1.getMaterial() == Material.circuits && p_147472_1_ == Blocks.anvil ? true : block1.isReplaceable(this, p_147472_2_, p_147472_3_, -+ p_147472_4_) && p_147472_1_.canReplace(this, p_147472_2_, p_147472_3_, p_147472_4_, p_147472_6_, p_147472_8_)); ++ : (block1.getMaterial() == Material.circuits && p_147472_1_ == Blocks.anvil ? true : block1.isReplaceable(this, p_147472_2_, p_147472_3_, ++ p_147472_4_) && p_147472_1_.canReplace(this, p_147472_2_, p_147472_3_, p_147472_4_, p_147472_6_, p_147472_8_)); + BlockCanBuildEvent event = new BlockCanBuildEvent(this.getWorld().getBlockAt(p_147472_2_, p_147472_3_, p_147472_4_), -+ CraftMagicNumbers.getId(p_147472_1_), defaultReturn); ++ CraftMagicNumbers.getId(p_147472_1_), defaultReturn); + this.getServer().getPluginManager().callEvent(event); + return event.isBuildable(); + // CraftBukkit end } public PathEntity getPathEntityToEntity(Entity p_72865_1_, Entity p_72865_2_, float p_72865_3_, boolean p_72865_4_, boolean p_72865_5_, boolean p_72865_6_, boolean p_72865_7_) -@@ -3464,6 +4244,12 @@ +@@ -3464,6 +4236,12 @@ for (int i = 0; i < this.playerEntities.size(); ++i) { EntityPlayer entityplayer1 = (EntityPlayer)this.playerEntities.get(i); @@ -1567,7 +1547,7 @@ double d5 = entityplayer1.getDistanceSq(p_72977_1_, p_72977_3_, p_72977_5_); if ((p_72977_7_ < 0.0D || d5 < p_72977_7_ * p_72977_7_) && (d4 == -1.0D || d5 < d4)) -@@ -3489,7 +4275,12 @@ +@@ -3489,7 +4267,12 @@ for (int i = 0; i < this.playerEntities.size(); ++i) { EntityPlayer entityplayer1 = (EntityPlayer)this.playerEntities.get(i); @@ -1581,7 +1561,7 @@ if (!entityplayer1.capabilities.disableDamage && entityplayer1.isEntityAlive()) { double d5 = entityplayer1.getDistanceSq(p_72846_1_, p_72846_3_, p_72846_5_); -@@ -3660,6 +4451,18 @@ +@@ -3660,6 +4443,18 @@ public void updateAllPlayersSleepingFlag() {} @@ -1600,7 +1580,7 @@ public float getWeightedThunderStrength(float p_72819_1_) { return (this.prevThunderingStrength + (this.thunderingStrength - this.prevThunderingStrength) * p_72819_1_) * this.getRainStrength(p_72819_1_); -@@ -3932,8 +4735,8 @@ +@@ -3932,8 +4727,8 @@ */ public void addTileEntity(TileEntity entity) { @@ -1611,7 +1591,7 @@ { dest.add(entity); } -@@ -4029,4 +4832,132 @@ +@@ -4029,4 +4824,132 @@ } return count; } @@ -1628,7 +1608,7 @@ + } + + public ConcurrentMap activity = new ConcurrentHashMap(); -+ ++ + public boolean isActiveChunk(int x, int z) + { + return activeChunkSet_CB.containsKey(chunkToKey(x, z)) || activity.containsKey(new ChunkCoordIntPair(x, z)); @@ -1649,8 +1629,8 @@ + public boolean isActiveBlockCoord(int x, int y, int z) + { + return isActiveChunk(x >> 4, z >> 4); -+ } -+ ++ } ++ + public boolean inActiveChunk(Entity entity) + { + return isActiveBlockCoord(MathHelper.floor_double(entity.posX), MathHelper.floor_double(entity.posZ)); @@ -1663,7 +1643,7 @@ + if (ItemStack.currentPlayer != null) + { + placeEvent = CraftEventFactory.callBlockPlaceEvent(this, ItemStack.currentPlayer, -+ CraftBlockState.getBlockState(this, x, y, z, 3), x, y, z); ++ CraftBlockState.getBlockState(this, x, y, z, 3), x, y, z); + } + + if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) @@ -1680,20 +1660,20 @@ + return this.spigotConfig; + } + // Cauldron end -+ ++ + // Spigot start + private void initLimiter() { + entityLimiter = new TickLimiter(spigotConfig.entityMaxTickTime); + tileLimiter = new TickLimiter(spigotConfig.tileMaxTickTime); + } + // Spigot end -+ ++ + // Cauldron start + public boolean isProfilingWorld() { + return provider.dimensionId == Integer.MIN_VALUE; // Mystcraft + } + // Cauldron end -+ ++ + public Chunk getChunkIfLoaded(int x, int z) { + return ((ChunkProviderServer) this.chunkProvider).getChunkIfLoaded(x, z); + } diff --git a/patches/net/minecraftforge/common/ForgeHooks.java.patch b/patches/net/minecraftforge/common/ForgeHooks.java.patch index bb2aeb9c..b69b4693 100644 --- a/patches/net/minecraftforge/common/ForgeHooks.java.patch +++ b/patches/net/minecraftforge/common/ForgeHooks.java.patch @@ -82,14 +82,14 @@ + if (itemstack.getItem() instanceof net.minecraft.item.ItemDye && itemstack.getItemDamage() == 15) + { + Block block = world.getBlock(x, y, z); -+ if (block != null && (block instanceof net.minecraft.block.BlockSapling || block instanceof net.minecraft.block.BlockMushroom)) ++ if ((block instanceof net.minecraft.block.BlockSapling || block instanceof net.minecraft.block.BlockMushroom)) + { -+ world.captureTreeGeneration = true; ++ world.captureAndProxyBlocks = true; + } + } + // Cauldron end + -+ if(!world.captureTreeGeneration) ++ if(!world.captureAndProxyBlocks) + world.captureItemDrops = true; // Thermos real og right here, prevent sugar cane dupe glitch amongst other issues + } @@ -100,23 +100,24 @@ world.captureBlockSnapshots = false; - - if (flag) -+ if(!world.captureTreeGeneration) // Thermos stop capturing item drops to the world ++ world.worldChangeHolder.transferToForgeBlockSnapshot(); // Crucible - Not touching this whole method now, keep compatibility. ++ if(!world.captureAndProxyBlocks) // Thermos stop capturing item drops to the world + world.captureItemDrops = false; + // Cauldron start -+ if (flag && world.captureTreeGeneration && world.capturedBlockSnapshots.size() > 0) ++ if (flag && world.captureAndProxyBlocks && !world.capturedBlockSnapshots.isEmpty()) { - // save new item data - int newMeta = itemstack.getItemDamage(); - int newSize = itemstack.stackSize; - NBTTagCompound newNBT = null; - if (itemstack.getTagCompound() != null) -+ world.captureTreeGeneration = false; ++ world.captureAndProxyBlocks = false; + Location loc = new Location(world.getWorld(), (double) x, (double) y, (double) z); + TreeType type = net.minecraft.block.BlockSapling.treeType; + net.minecraft.block.BlockSapling.treeType = null; -+ List states = new ArrayList(); ++ List states = new ArrayList<>(); + -+ for (net.minecraftforge.common.util.BlockSnapshot snapshot : (List) world.capturedBlockSnapshots.clone()) ++ for (net.minecraftforge.common.util.BlockSnapshot snapshot : new ArrayList<>(world.capturedBlockSnapshots)) { - newNBT = (NBTTagCompound)itemstack.getTagCompound().copy(); + states.add(new CraftBlockState(snapshot)); @@ -166,7 +167,7 @@ + } + else + { -+ world.captureTreeGeneration = false; // Cauldron end ++ world.captureAndProxyBlocks = false; // Cauldron end + if (flag) { - // Change the stack to its new content @@ -181,8 +182,7 @@ + newNBT = (NBTTagCompound) itemstack.getTagCompound().copy(); + } + net.minecraftforge.event.world.BlockEvent.PlaceEvent placeEvent = null; -+ List blockSnapshots = (List) world.capturedBlockSnapshots -+ .clone(); ++ List blockSnapshots = new ArrayList<>(world.capturedBlockSnapshots); + world.capturedBlockSnapshots.clear(); + + // make sure to set pre-placement item data for event diff --git a/src/main/java/io/github/crucible/util/BlockCoords.java b/src/main/java/io/github/crucible/util/BlockCoords.java index 78058466..a523b7a6 100644 --- a/src/main/java/io/github/crucible/util/BlockCoords.java +++ b/src/main/java/io/github/crucible/util/BlockCoords.java @@ -1,6 +1,15 @@ package io.github.crucible.util; public class BlockCoords { + private static final int PACKED_X_LENGTH = 26; + private static final int PACKED_Z_LENGTH = PACKED_X_LENGTH; + private static final int PACKED_Y_LENGTH = 64 - PACKED_X_LENGTH - PACKED_Z_LENGTH; + private static final long PACKED_X_MASK = (1L << PACKED_X_LENGTH) - 1L; + private static final long PACKED_Y_MASK = (1L << PACKED_Y_LENGTH) - 1L; + private static final long PACKED_Z_MASK = (1L << PACKED_Z_LENGTH) - 1L; + private static final int Z_OFFSET = PACKED_Y_LENGTH; + private static final int X_OFFSET = PACKED_Y_LENGTH + PACKED_Z_LENGTH; + public final int x, y, z; public final long key; private final int hash; @@ -11,7 +20,7 @@ public BlockCoords(int x, int y, int z) { this.z = z; key = ((long) y << 56) | (((long) z & 0xFFFFFFF) << 28) | (x & 0xFFFFFFF); - hash = (int) (key ^ (key >>> 32)); + hash = Long.hashCode(key); } public BlockCoords(BlockCoords coords) { @@ -36,4 +45,23 @@ public boolean equals(Object obj) { public int hashCode() { return hash; } + + public static int getX(long packedPos) { + return (int)(packedPos << 64 - X_OFFSET - PACKED_X_LENGTH >> 64 - PACKED_X_LENGTH); + } + + public static int getY(long packedPos) { + return (int)(packedPos << 64 - PACKED_Y_LENGTH >> 64 - PACKED_Y_LENGTH); + } + + public static int getZ(long packedPos) { + return (int)(packedPos << 64 - Z_OFFSET - PACKED_Z_LENGTH >> 64 - PACKED_Z_LENGTH); + } + + public static long asLong(int x, int y, int z) { + long packedPos = 0L; + packedPos |= ((long)x & PACKED_X_MASK) << X_OFFSET; + packedPos |= ((long) y & PACKED_Y_MASK); + return packedPos | ((long)z & PACKED_Z_MASK) << Z_OFFSET; + } } diff --git a/src/main/java/io/github/crucible/util/WorldChangeHolder.java b/src/main/java/io/github/crucible/util/WorldChangeHolder.java new file mode 100644 index 00000000..a102a9ab --- /dev/null +++ b/src/main/java/io/github/crucible/util/WorldChangeHolder.java @@ -0,0 +1,234 @@ +package io.github.crucible.util; + +import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap; +import it.unimi.dsi.fastutil.longs.Long2ObjectMap; +import net.minecraft.block.Block; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.World; +import net.minecraftforge.common.util.BlockSnapshot; +import org.bukkit.BlockChangeDelegate; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.v1_7_R4.block.CraftBlockState; + +import java.util.ArrayList; +import java.util.List; + +// Our best attempt to replace the janky cauldron tree generation capture +// There's still quite a bit of edge cases it does not cover however for tree generation it seems file +public class WorldChangeHolder { + private final World world; + private final Long2ObjectMap changes = new Long2ObjectLinkedOpenHashMap<>(); // Should we tweak the load factor? Using linked map since operation order may be important! + + public WorldChangeHolder(World world) { + this.world = world; + } + + public CaptureContext startCapture() { + if (this.world.captureAndProxyBlocks || this.world.captureBlockSnapshots) { + // I have a feeling we may catch some odd undiscovered bugs with this (possible recursion during capture) + // Future proofing if we need to make changes, we can keep the same usage but change how startCapture works internally. + throw new IllegalStateException("Capture already in progress!"); + } + + this.world.captureAndProxyBlocks = true; + return new SimpleCaptureContext(); + } + + // Should we add some safety checks here eventually? + public void setBlock(int x, int y, int z, Block block, int meta, int flags) { + this.changes.computeIfAbsent(BlockCoords.asLong(x, y, z), __ -> new BlockChange()).update(block, meta, flags); + } + + public Block getBlock(int x, int y, int z) { + BlockChange change = this.changes.get(BlockCoords.asLong(x, y, z)); + return change == null ? null : change.block; + } + + // How safe it is to assume -1 will never be a valid metadata value? + public int getMetadata(int x, int y, int z) { + BlockChange change = this.changes.get(BlockCoords.asLong(x, y, z)); + return change == null ? -1 : change.meta; + } + + public TileEntity getTileEntity(int x, int y, int z) { + BlockChange change = this.changes.get(BlockCoords.asLong(x, y, z)); + if (change == null) { + return null; + } + + if (change.tile != null) { + return change.tile; + } else { + return change.tile = change.block.createTileEntity(world, change.meta); + } + } + + public void applyChanges() { + // Sanity check just in case... + if (this.world.captureAndProxyBlocks || this.world.captureBlockSnapshots) { + throw new IllegalStateException("Trying to apply changes while still capturing blocks!!!"); + } + + for (Long2ObjectMap.Entry changeEntry : this.changes.long2ObjectEntrySet()) { + BlockChange change = changeEntry.getValue(); + long pos = changeEntry.getLongKey(); + change.ensureValid(); + this.world.setBlock(BlockCoords.getX(pos), BlockCoords.getY(pos), BlockCoords.getZ(pos), + change.block, change.meta, change.flags); + + if (change.tile != null) { + // Is it safe to use world.setTileEntity? + // I'll bite the performance cost and so something that seems safer similar to forge's block snapshot. + + TileEntity te = this.world.getTileEntity(BlockCoords.getX(pos), BlockCoords.getY(pos), BlockCoords.getZ(pos)); + NBTTagCompound data = new NBTTagCompound(); + change.tile.writeToNBT(data); + te.readFromNBT(data); + } + } + + this.changes.clear(); + } + + public void discardChanges() { + this.changes.clear(); + } + + public boolean isCapturing() { + return this.world.captureAndProxyBlocks; + } + + /** + * @deprecated Currently this method acts as a glue to keep compat with forge events in ForgeHooks, should be replaced eventually. + */ + @Deprecated + public void transferToForgeBlockSnapshot() { + for (Long2ObjectMap.Entry changeEntry : WorldChangeHolder.this.changes.long2ObjectEntrySet()) { + BlockChange change = changeEntry.getValue(); + long pos = changeEntry.getLongKey(); + NBTTagCompound nbt = null; + if (change.tile != null) { + nbt = new NBTTagCompound(); + change.tile.writeToNBT(nbt); + } + this.world.capturedBlockSnapshots.add(new BlockSnapshot(this.world, BlockCoords.getX(pos), BlockCoords.getY(pos), BlockCoords.getZ(pos), + change.block, change.meta, nbt)); + + } + + this.discardChanges(); + } + + public static class BlockChange { + private TileEntity tile; + private Block block; + private int meta; + private int flags; + + public void update(Block block, int meta, int flags) { + if (this.block != block || this.meta != meta) { + // We probably should check if the tile would be valid during meta changes in case a single tile supports multiple metas + this.tile = null; + } + this.block = block; + this.meta = meta; + this.flags = flags; + } + + public void ensureValid() { + if (this.block == null) { + throw new IllegalStateException("Block change was not properly initialized! Block is null"); + } + } + + @Override + public String toString() { + return "BlockChange{" + + "tile=" + tile + + ", block=" + block + + ", meta=" + meta + + ", flags=" + flags + + '}'; + } + } + + public interface CaptureContext extends AutoCloseable { + void close(); + void discardChanges(); + void notifyDelegate(BlockChangeDelegate delegate); + void pauseCapture(); + void resumeCapture(); + boolean hasChanges(); + List asBukkitBlockState(); + } + + private class SimpleCaptureContext implements CaptureContext { + private boolean discard; + private BlockChangeDelegate delegate; + + @Override + public void close() { + WorldChangeHolder.this.world.captureAndProxyBlocks = false; + + if (this.delegate != null) { + for (Long2ObjectMap.Entry changeEntry : WorldChangeHolder.this.changes.long2ObjectEntrySet()) { + BlockChange change = changeEntry.getValue(); + long pos = changeEntry.getLongKey(); + //noinspection deprecation + delegate.setTypeIdAndData(BlockCoords.getX(pos), BlockCoords.getY(pos), BlockCoords.getZ(pos), + Block.getIdFromBlock(change.block), change.meta); + } + + } + + if (this.discard) { + WorldChangeHolder.this.discardChanges(); + } else { + WorldChangeHolder.this.applyChanges(); + } + + if (!WorldChangeHolder.this.world.capturedBlockSnapshots.isEmpty()) { + throw new IllegalStateException("capturedBlockSnapshots is not empty! This is a bug!"); + } + } + + @Override + public void discardChanges() { + this.discard = true; + } + + @Override + public void notifyDelegate(BlockChangeDelegate delegate) { + this.delegate = delegate; + } + + @Override + public void pauseCapture() { + WorldChangeHolder.this.world.captureAndProxyBlocks = false; + } + + @Override + public void resumeCapture() { + WorldChangeHolder.this.world.captureAndProxyBlocks = true; + } + + @Override + public boolean hasChanges() { + return !WorldChangeHolder.this.changes.isEmpty(); + } + + @Override + public List asBukkitBlockState() { + List blocks = new ArrayList<>(WorldChangeHolder.this.changes.size()); + for (Long2ObjectMap.Entry changeEntry : WorldChangeHolder.this.changes.long2ObjectEntrySet()) { + BlockChange change = changeEntry.getValue(); + long pos = changeEntry.getLongKey(); + blocks.add(new CraftBlockState(WorldChangeHolder.this.world, BlockCoords.getX(pos), BlockCoords.getY(pos), BlockCoords.getZ(pos), + change.block, (byte) change.meta, change.tile)); + + } + return blocks; + } + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/v1_7_R4/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/v1_7_R4/CraftWorld.java index 4bf0179e..a2707f86 100644 --- a/src/main/java/org/bukkit/craftbukkit/v1_7_R4/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/v1_7_R4/CraftWorld.java @@ -1,6 +1,7 @@ package org.bukkit.craftbukkit.v1_7_R4; import cpw.mods.fml.common.registry.EntityRegistry; +import io.github.crucible.util.WorldChangeHolder; import net.minecraft.entity.EntityLiving; import net.minecraft.world.WorldServer; import net.minecraftforge.common.util.BlockSnapshot; @@ -518,28 +519,11 @@ public boolean generateTree(Location loc, TreeType type, BlockChangeDelegate del break; } - world.captureTreeGeneration = true; - world.captureBlockSnapshots = true; - boolean grownTree = gen.generate(world, rand, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ()); - world.captureBlockSnapshots = false; - world.captureTreeGeneration = false; - if (grownTree) { // Copy block data to delegate - for (BlockSnapshot blocksnapshot : world.capturedBlockSnapshots) { - int x = blocksnapshot.x; - int y = blocksnapshot.y; - int z = blocksnapshot.z; - net.minecraft.block.Block oldBlock = world.getBlock(x, y, z); - int newId = net.minecraft.block.Block.getIdFromBlock(blocksnapshot.replacedBlock); - int data = blocksnapshot.meta; - int flag = blocksnapshot.flag; - delegate.setTypeIdAndData(x, y, z, newId, data); - net.minecraft.block.Block newBlock = world.getBlock(x, y, z); - world.markAndNotifyBlock(x, y, z, null, oldBlock, newBlock, flag); + try (WorldChangeHolder.CaptureContext context = world.worldChangeHolder.startCapture()) { + if (gen.generate(world, rand, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ())) { + context.notifyDelegate(delegate); + return true; } - world.capturedBlockSnapshots.clear(); - return true; - } else { - world.capturedBlockSnapshots.clear(); return false; } } diff --git a/src/main/java/org/bukkit/craftbukkit/v1_7_R4/block/CraftBlockState.java b/src/main/java/org/bukkit/craftbukkit/v1_7_R4/block/CraftBlockState.java index febf431d..c9a1a682 100644 --- a/src/main/java/org/bukkit/craftbukkit/v1_7_R4/block/CraftBlockState.java +++ b/src/main/java/org/bukkit/craftbukkit/v1_7_R4/block/CraftBlockState.java @@ -2,6 +2,7 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.WorldServer; import net.minecraftforge.common.util.BlockSnapshot; import org.bukkit.Chunk; import org.bukkit.Location; @@ -75,6 +76,24 @@ public CraftBlockState(BlockSnapshot blocksnapshot) { this.createData((byte) blocksnapshot.meta); } + public CraftBlockState(net.minecraft.world.World world, int x, int y, int z, net.minecraft.block.Block block, byte meta, TileEntity te) { + this.world = world.getWorld(); + this.x = x; + this.y = y; + this.z = z; + this.type = net.minecraft.block.Block.getIdFromBlock(block); + this.light = (byte) block.getLightValue(); + this.chunk = (CraftChunk) this.world.getBlockAt(this.x, this.y, this.z).getChunk(); + this.flag = 3; + if (te != null) { + this.nbt = new NBTTagCompound(); + te.writeToNBT(this.nbt); + } else { + this.nbt = null; + } + this.createData(meta); + } + public static CraftBlockState getBlockState(net.minecraft.world.World world, int x, int y, int z) { return new CraftBlockState(world.getWorld().getBlockAt(x, y, z)); }