diff --git a/nms-patches/BlockSapling.patch b/nms-patches/BlockSapling.patch index d5895cf306..83620a555d 100644 --- a/nms-patches/BlockSapling.patch +++ b/nms-patches/BlockSapling.patch @@ -1,12 +1,10 @@ --- a/net/minecraft/server/BlockSapling.java +++ b/net/minecraft/server/BlockSapling.java -@@ -2,11 +2,21 @@ +@@ -2,11 +2,19 @@ import java.util.Random; +// CraftBukkit start -+import java.util.List; -+ +import org.bukkit.Location; +import org.bukkit.TreeType; +import org.bukkit.block.BlockState; @@ -22,7 +20,7 @@ protected BlockSapling(WorldGenTreeProvider worldgentreeprovider, Block.Info block_info) { super(block_info); -@@ -23,7 +33,30 @@ +@@ -23,7 +31,30 @@ public void tick(IBlockData iblockdata, WorldServer worldserver, BlockPosition blockposition, Random random) { super.tick(iblockdata, worldserver, blockposition, random); if (worldserver.getLightLevel(blockposition.up()) >= 9 && random.nextInt(7) == 0) { @@ -36,7 +34,7 @@ + TreeType treeType = BlockSapling.treeType; + BlockSapling.treeType = null; + Location location = new Location(worldserver.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ()); -+ List blocks = (List) worldserver.capturedBlockStates.clone(); ++ java.util.List blocks = new java.util.ArrayList<>(worldserver.capturedBlockStates.values()); + worldserver.capturedBlockStates.clear(); + StructureGrowEvent event = null; + if (treeType != null) { diff --git a/nms-patches/IDispenseBehavior.patch b/nms-patches/IDispenseBehavior.patch index 21a0db3591..c51176a961 100644 --- a/nms-patches/IDispenseBehavior.patch +++ b/nms-patches/IDispenseBehavior.patch @@ -405,7 +405,7 @@ + TreeType treeType = BlockSapling.treeType; + BlockSapling.treeType = null; + Location location = new Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ()); -+ List blocks = (List) world.capturedBlockStates.clone(); ++ List blocks = new java.util.ArrayList<>(world.capturedBlockStates.values()); + world.capturedBlockStates.clear(); + StructureGrowEvent structureEvent = null; + if (treeType != null) { diff --git a/nms-patches/ItemStack.patch b/nms-patches/ItemStack.patch index cb4f74213b..dc8d08deab 100644 --- a/nms-patches/ItemStack.patch +++ b/nms-patches/ItemStack.patch @@ -108,7 +108,7 @@ + Location location = new Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ()); + TreeType treeType = BlockSapling.treeType; + BlockSapling.treeType = null; -+ List blocks = (List) world.capturedBlockStates.clone(); ++ List blocks = new java.util.ArrayList<>(world.capturedBlockStates.values()); + world.capturedBlockStates.clear(); + StructureGrowEvent structureEvent = null; + if (treeType != null) { @@ -139,7 +139,7 @@ if (entityhuman != null && enuminteractionresult == EnumInteractionResult.SUCCESS) { - entityhuman.b(StatisticList.ITEM_USED.b(item)); + org.bukkit.event.block.BlockPlaceEvent placeEvent = null; -+ List blocks = (List) world.capturedBlockStates.clone(); ++ List blocks = new java.util.ArrayList<>(world.capturedBlockStates.values()); + world.capturedBlockStates.clear(); + if (blocks.size() > 1) { + placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockMultiPlaceEvent(world, entityhuman, enumhand, blocks, blockposition.getX(), blockposition.getY(), blockposition.getZ()); diff --git a/nms-patches/World.patch b/nms-patches/World.patch index 0625ec541e..fa0ca962d3 100644 --- a/nms-patches/World.patch +++ b/nms-patches/World.patch @@ -1,29 +1,24 @@ --- a/net/minecraft/server/World.java +++ b/net/minecraft/server/World.java -@@ -14,6 +14,22 @@ +@@ -14,6 +14,17 @@ import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.util.Supplier; +// CraftBukkit start -+import com.google.common.collect.Maps; -+import java.util.ArrayList; ++import java.util.HashMap; +import java.util.Map; +import org.bukkit.Bukkit; -+import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.CraftServer; +import org.bukkit.craftbukkit.CraftWorld; -+import org.bukkit.craftbukkit.block.CraftBlockState; ++import org.bukkit.craftbukkit.block.CapturedBlockState; +import org.bukkit.craftbukkit.block.data.CraftBlockData; -+import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.block.BlockPhysicsEvent; -+import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; -+import org.bukkit.event.weather.LightningStrikeEvent; +// CraftBukkit end + public abstract class World implements GeneratorAccess, AutoCloseable { protected static final Logger LOGGER = LogManager.getLogger(); -@@ -40,7 +56,51 @@ +@@ -40,7 +51,39 @@ private final WorldBorder worldBorder; private final BiomeManager biomeManager; @@ -36,20 +31,8 @@ + + public boolean captureBlockStates = false; + public boolean captureTreeGeneration = false; -+ public ArrayList capturedBlockStates = new ArrayList() { -+ @Override -+ public boolean add(CraftBlockState blockState) { -+ Iterator blockStateIterator = this.iterator(); -+ while (blockStateIterator.hasNext()) { -+ BlockState blockState1 = blockStateIterator.next(); -+ if (blockState1.getLocation().equals(blockState.getLocation())) { -+ return false; -+ } -+ } -+ -+ return super.add(blockState); -+ } -+ }; ++ public Map capturedBlockStates = new HashMap<>(); ++ public Map capturedTileEntities = new HashMap<>(); + public List captureDrops; + public long ticksPerAnimalSpawns; + public long ticksPerMonsterSpawns; @@ -76,7 +59,7 @@ this.methodProfiler = gameprofilerfiller; this.worldData = worlddata; this.worldProvider = dimensionmanager.getWorldProvider(this); -@@ -49,6 +109,35 @@ +@@ -49,6 +92,35 @@ this.worldBorder = this.worldProvider.getWorldBorder(); this.serverThread = Thread.currentThread(); this.biomeManager = new BiomeManager(this, flag ? worlddata.getSeed() : WorldData.c(worlddata.getSeed()), dimensionmanager.getGenLayerZoomer()); @@ -112,44 +95,34 @@ } @Override -@@ -105,6 +194,26 @@ +@@ -105,6 +177,17 @@ @Override public boolean setTypeAndData(BlockPosition blockposition, IBlockData iblockdata, int i) { + // CraftBukkit start - tree generation + if (this.captureTreeGeneration) { -+ CraftBlockState blockstate = null; -+ Iterator it = capturedBlockStates.iterator(); -+ while (it.hasNext()) { -+ CraftBlockState previous = it.next(); -+ if (previous.getPosition().equals(blockposition)) { -+ blockstate = previous; -+ it.remove(); -+ break; -+ } -+ } ++ CapturedBlockState blockstate = capturedBlockStates.get(blockposition); + if (blockstate == null) { -+ blockstate = org.bukkit.craftbukkit.block.CraftBlockState.getBlockState(this, blockposition, i); ++ blockstate = CapturedBlockState.getTreeBlockState(this, blockposition, i); ++ this.capturedBlockStates.put(blockposition.immutableCopy(), blockstate); + } + blockstate.setData(iblockdata); -+ this.capturedBlockStates.add(blockstate); + return true; + } + // CraftBukkit end if (isOutsideWorld(blockposition)) { return false; } else if (!this.isClientSide && this.worldData.getType() == WorldType.DEBUG_ALL_BLOCK_STATES) { -@@ -112,9 +221,23 @@ +@@ -112,9 +195,22 @@ } else { Chunk chunk = this.getChunkAtWorldCoords(blockposition); Block block = iblockdata.getBlock(); - IBlockData iblockdata1 = chunk.setType(blockposition, iblockdata, (i & 64) != 0); + + // CraftBukkit start - capture blockstates -+ CraftBlockState blockstate = null; -+ if (this.captureBlockStates) { -+ blockstate = org.bukkit.craftbukkit.block.CraftBlockState.getBlockState(this, blockposition, i); -+ this.capturedBlockStates.add(blockstate); ++ if (this.captureBlockStates && !this.capturedBlockStates.containsKey(blockposition)) { ++ CapturedBlockState blockstate = CapturedBlockState.getBlockState(this, blockposition, i); ++ this.capturedBlockStates.put(blockposition.immutableCopy(), blockstate); + } + // CraftBukkit end + @@ -158,13 +131,13 @@ if (iblockdata1 == null) { + // CraftBukkit start - remove blockstate if failed + if (this.captureBlockStates) { -+ this.capturedBlockStates.remove(blockstate); ++ this.capturedBlockStates.remove(blockposition); + } + // CraftBukkit end return false; } else { IBlockData iblockdata2 = this.getType(blockposition); -@@ -125,6 +248,7 @@ +@@ -125,6 +221,7 @@ this.methodProfiler.exit(); } @@ -172,7 +145,7 @@ if (iblockdata2 == iblockdata) { if (iblockdata1 != iblockdata2) { this.b(blockposition, iblockdata1, iblockdata2); -@@ -151,12 +275,65 @@ +@@ -151,12 +248,65 @@ this.a(blockposition, iblockdata1, iblockdata2); } @@ -238,7 +211,7 @@ public void a(BlockPosition blockposition, IBlockData iblockdata, IBlockData iblockdata1) {} @Override -@@ -195,6 +372,11 @@ +@@ -195,6 +345,11 @@ @Override public void update(BlockPosition blockposition, Block block) { if (this.worldData.getType() != WorldType.DEBUG_ALL_BLOCK_STATES) { @@ -250,7 +223,7 @@ this.applyPhysics(blockposition, block); } -@@ -243,6 +425,17 @@ +@@ -243,6 +398,17 @@ IBlockData iblockdata = this.getType(blockposition); try { @@ -268,25 +241,22 @@ iblockdata.doPhysics(this, blockposition, block, blockposition1, false); } catch (Throwable throwable) { CrashReport crashreport = CrashReport.a(throwable, "Exception while updating neighbours"); -@@ -285,6 +478,17 @@ +@@ -285,6 +451,14 @@ @Override public IBlockData getType(BlockPosition blockposition) { + // CraftBukkit start - tree generation + if (captureTreeGeneration) { -+ Iterator it = capturedBlockStates.iterator(); -+ while (it.hasNext()) { -+ CraftBlockState previous = it.next(); -+ if (previous.getPosition().equals(blockposition)) { -+ return previous.getHandle(); -+ } ++ CapturedBlockState previous = capturedBlockStates.get(blockposition); ++ if (previous != null) { ++ return previous.getHandle(); + } + } + // CraftBukkit end if (isOutsideWorld(blockposition)) { return Blocks.VOID_AIR.getBlockData(); } else { -@@ -306,11 +510,11 @@ +@@ -306,11 +480,11 @@ } public boolean isDay() { @@ -300,7 +270,7 @@ } @Override -@@ -432,9 +636,11 @@ +@@ -432,9 +606,11 @@ TileEntity tileentity1 = (TileEntity) this.tileEntityListPending.get(i); if (!tileentity1.isRemoved()) { @@ -312,7 +282,7 @@ if (this.isLoaded(tileentity1.getPosition())) { Chunk chunk = this.getChunkAtWorldCoords(tileentity1.getPosition()); -@@ -442,6 +648,12 @@ +@@ -442,6 +618,12 @@ chunk.setTileEntity(tileentity1.getPosition(), tileentity1); this.notify(tileentity1.getPosition(), iblockdata, iblockdata, 3); @@ -325,7 +295,7 @@ } } } -@@ -606,12 +818,26 @@ +@@ -606,12 +788,25 @@ @Nullable @Override @@ -333,7 +303,6 @@ public TileEntity getTileEntity(BlockPosition blockposition) { + return getTileEntity(blockposition, true); + } -+ public Map capturedTileEntities = Maps.newHashMap(); + + @Nullable + protected TileEntity getTileEntity(BlockPosition blockposition, boolean validate) { @@ -352,21 +321,21 @@ TileEntity tileentity = null; if (this.tickingTileEntities) { -@@ -646,6 +872,13 @@ +@@ -646,6 +841,13 @@ public void setTileEntity(BlockPosition blockposition, @Nullable TileEntity tileentity) { if (!isOutsideWorld(blockposition)) { if (tileentity != null && !tileentity.isRemoved()) { + // CraftBukkit start + if (captureBlockStates) { + tileentity.setLocation(this, blockposition); -+ capturedTileEntities.put(blockposition, tileentity); ++ capturedTileEntities.put(blockposition.immutableCopy(), tileentity); + return; + } + // CraftBukkit end if (this.tickingTileEntities) { tileentity.setLocation(this, blockposition); Iterator iterator = this.tileEntityListPending.iterator(); -@@ -670,7 +903,7 @@ +@@ -670,7 +872,7 @@ } public void removeTileEntity(BlockPosition blockposition) { diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index b3f175268b..b38a9c5c7b 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -741,7 +741,7 @@ public class CraftWorld implements World { world.captureBlockStates = false; world.captureTreeGeneration = false; if (grownTree) { // Copy block data to delegate - for (BlockState blockstate : world.capturedBlockStates) { + for (BlockState blockstate : world.capturedBlockStates.values()) { BlockPosition position = ((CraftBlockState) blockstate).getPosition(); net.minecraft.server.IBlockData oldBlock = world.getType(position); int flag = ((CraftBlockState) blockstate).getFlag(); diff --git a/src/main/java/org/bukkit/craftbukkit/block/CapturedBlockState.java b/src/main/java/org/bukkit/craftbukkit/block/CapturedBlockState.java new file mode 100644 index 0000000000..27bd1916e0 --- /dev/null +++ b/src/main/java/org/bukkit/craftbukkit/block/CapturedBlockState.java @@ -0,0 +1,60 @@ +package org.bukkit.craftbukkit.block; + +import java.util.Random; +import net.minecraft.server.BlockPosition; +import net.minecraft.server.EntityBee; +import net.minecraft.server.EntityTypes; +import net.minecraft.server.GeneratorAccess; +import net.minecraft.server.TileEntity; +import net.minecraft.server.TileEntityBeehive; +import net.minecraft.server.World; +import org.bukkit.Material; +import org.bukkit.block.Block; + +public final class CapturedBlockState extends CraftBlockState { + + private final boolean treeBlock; + + public CapturedBlockState(Block block, int flag, boolean treeBlock) { + super(block, flag); + + this.treeBlock = treeBlock; + } + + @Override + public boolean update(boolean force, boolean applyPhysics) { + boolean result = super.update(force, applyPhysics); + + // SPIGOT-5537: Horrible hack to manually add bees given World.captureTreeGeneration does not support tiles + if (this.treeBlock && getType() == Material.BEE_NEST) { + GeneratorAccess generatoraccess = this.world.getHandle(); + BlockPosition blockposition1 = this.getPosition(); + Random random = generatoraccess.getRandom(); + + // Begin copied block from WorldGenFeatureTreeBeehive + TileEntity tileentity = generatoraccess.getTileEntity(blockposition1); + + if (tileentity instanceof TileEntityBeehive) { + TileEntityBeehive tileentitybeehive = (TileEntityBeehive) tileentity; + int j = 2 + random.nextInt(2); + + for (int k = 0; k < j; ++k) { + EntityBee entitybee = new EntityBee(EntityTypes.BEE, generatoraccess.getMinecraftWorld()); + + tileentitybeehive.a(entitybee, false, random.nextInt(599)); + } + } + // End copied block + } + + return result; + } + + public static CapturedBlockState getBlockState(World world, BlockPosition pos, int flag) { + return new CapturedBlockState(world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()), flag, false); + } + + public static CapturedBlockState getTreeBlockState(World world, BlockPosition pos, int flag) { + return new CapturedBlockState(world.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()), flag, true); + } +} diff --git a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java index 0cb5582b19..d3017db1bd 100644 --- a/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java +++ b/src/main/java/org/bukkit/craftbukkit/block/CraftBlockState.java @@ -22,7 +22,7 @@ import org.bukkit.metadata.MetadataValue; import org.bukkit.plugin.Plugin; public class CraftBlockState implements BlockState { - private final CraftWorld world; + protected final CraftWorld world; private final CraftChunk chunk; private final BlockPosition position; protected IBlockData data;