From 899630cfb2091206f8399ef6adec3708711381b9 Mon Sep 17 00:00:00 2001 From: Spottedleaf Date: Sun, 14 Jun 2020 00:34:47 -0700 Subject: [PATCH] Tuinity - Piston pushable TileEntities --- .../java/net/minecraft/server/BlockChest.java | 2 +- .../net/minecraft/server/BlockPiston.java | 30 ++++++++- .../minecraft/server/BlockPistonMoving.java | 7 ++- src/main/java/net/minecraft/server/Chunk.java | 11 ++++ .../java/net/minecraft/server/MCUtil.java | 14 +++++ .../java/net/minecraft/server/TileEntity.java | 49 ++++++++++++++- .../minecraft/server/TileEntityBeacon.java | 20 +++++- .../minecraft/server/TileEntityBeehive.java | 7 +++ .../server/TileEntityBrewingStand.java | 21 ++++++- .../net/minecraft/server/TileEntityChest.java | 16 +++++ .../minecraft/server/TileEntityConduit.java | 21 ++++++- .../minecraft/server/TileEntityFurnace.java | 26 ++++++-- .../minecraft/server/TileEntityJukeBox.java | 7 +++ .../minecraft/server/TileEntityLectern.java | 23 ++++++- .../minecraft/server/TileEntityPiston.java | 63 +++++++++++++++++-- src/main/java/net/minecraft/server/World.java | 13 +++- .../java/net/pl3x/purpur/PurpurConfig.java | 5 ++ 17 files changed, 308 insertions(+), 27 deletions(-) diff --git a/src/main/java/net/minecraft/server/BlockChest.java b/src/main/java/net/minecraft/server/BlockChest.java index 72fb92f7c3..e9d1847fd5 100644 --- a/src/main/java/net/minecraft/server/BlockChest.java +++ b/src/main/java/net/minecraft/server/BlockChest.java @@ -10,7 +10,7 @@ import javax.annotation.Nullable; public class BlockChest extends BlockChestAbstract implements IBlockWaterlogged { public static final BlockStateDirection FACING = BlockFacingHorizontal.FACING; - public static final BlockStateEnum c = BlockProperties.ay; + public static final BlockStateEnum c = BlockProperties.ay; public static final BlockStateEnum getChestTypeEnum() { return BlockChest.c; } // Purpur - OBFHELPER public static final BlockStateBoolean d = BlockProperties.C; protected static final VoxelShape e = Block.a(1.0D, 0.0D, 0.0D, 15.0D, 14.0D, 15.0D); protected static final VoxelShape f = Block.a(1.0D, 0.0D, 1.0D, 15.0D, 14.0D, 16.0D); diff --git a/src/main/java/net/minecraft/server/BlockPiston.java b/src/main/java/net/minecraft/server/BlockPiston.java index 0b7bed5e92..33b25d599e 100644 --- a/src/main/java/net/minecraft/server/BlockPiston.java +++ b/src/main/java/net/minecraft/server/BlockPiston.java @@ -280,7 +280,10 @@ public class BlockPiston extends BlockDirectional { return false; } - return !block.isTileEntity(); + // Purpur start - pushable TE's + TileEntity tileEntity = world.getTileEntity(blockposition); + return !block.isTileEntity() || (tileEntity != null && tileEntity.isPushable()); + // Purpur end - pushable TE's } else { return false; } @@ -392,8 +395,29 @@ public class BlockPiston extends BlockDirectional { map.remove(blockposition3); world.setTypeAndData(blockposition3, (IBlockData) Blocks.MOVING_PISTON.getBlockData().set(BlockPiston.FACING, enumdirection), 68); iblockdata1 = world.getType(oldPos); map.replace(oldPos, iblockdata1); // Purpur - fix piston physics inconsistency - move after the physics update - world.setTileEntity(blockposition3, BlockPistonMoving.a(iblockdata1, enumdirection, flag, false)); // Paper - fix piston physics inconsistency - world.setTypeAndData(oldPos, Blocks.AIR.getBlockData(), 68); // Purpur - set air to prevent later physics updates from seeing this block + // Purpur start - pushable TE's + TileEntity tileEntity = world.getTileEntity(oldPos); + if (tileEntity != null) { + if (!tileEntity.isPushable()) { + tileEntity = null; + } else { + // ensure the death of world tied state + if (tileEntity instanceof IInventory) { + MCUtil.closeInventory((IInventory)tileEntity, org.bukkit.event.inventory.InventoryCloseEvent.Reason.CANT_USE); + } + if (tileEntity instanceof TileEntityLectern) { + MCUtil.closeInventory(((TileEntityLectern)tileEntity).inventory, org.bukkit.event.inventory.InventoryCloseEvent.Reason.CANT_USE); + } + // now copy + tileEntity = tileEntity.createCopyForPush((WorldServer)world, oldPos, blockposition3, iblockdata1); + } + } + // removing the old pos TE is an ugly hack to prevent containers from dropping items + // when they're moved. + world.removeTileEntity(oldPos); + world.setTileEntity(blockposition3, BlockPistonMoving.createPistonTile(iblockdata1, enumdirection, flag, false, tileEntity)); // Paper - fix piston physics inconsistency + world.setTypeAndData(oldPos, Blocks.AIR.getBlockData(), 2 | 16 | 1024); // Paper - set air to prevent later physics updates from seeing this block + // Purpur end - pushable TE's --j; aiblockdata[j] = iblockdata1; } diff --git a/src/main/java/net/minecraft/server/BlockPistonMoving.java b/src/main/java/net/minecraft/server/BlockPistonMoving.java index 809ee9f9a6..93dfd0fa8d 100644 --- a/src/main/java/net/minecraft/server/BlockPistonMoving.java +++ b/src/main/java/net/minecraft/server/BlockPistonMoving.java @@ -21,7 +21,12 @@ public class BlockPistonMoving extends BlockTileEntity { } public static TileEntity a(IBlockData iblockdata, EnumDirection enumdirection, boolean flag, boolean flag1) { - return new TileEntityPiston(iblockdata, enumdirection, flag, flag1); + // Purpur start - add tileEntity parameter + return createPistonTile(iblockdata, enumdirection, flag, flag1, null); + } + public static TileEntity createPistonTile(IBlockData iblockdata, EnumDirection enumdirection, boolean flag, boolean flag1, TileEntity tileEntity) { + return new TileEntityPiston(iblockdata, enumdirection, flag, flag1, tileEntity); + // Purpur end - add tileEntity parameter } @Override diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java index d7beb47d9e..c7bedee89e 100644 --- a/src/main/java/net/minecraft/server/Chunk.java +++ b/src/main/java/net/minecraft/server/Chunk.java @@ -511,6 +511,12 @@ public class Chunk implements IChunkAccess { @Nullable public IBlockData setType(BlockPosition blockposition, IBlockData iblockdata, boolean flag, boolean doPlace) { + // Purpur start - add tileEntity parameter + return this.setType(blockposition, iblockdata, flag, doPlace, null); + } + @Nullable + public IBlockData setType(BlockPosition blockposition, IBlockData iblockdata, boolean flag, boolean doPlace, TileEntity newTileEntity) { + // Purpur end - add tileEntity parameter // CraftBukkit end int i = blockposition.getX() & 15; int j = blockposition.getY(); @@ -569,6 +575,10 @@ public class Chunk implements IChunkAccess { } if (block instanceof ITileEntity) { + // Purpur start - add tileEntity parameter + if (newTileEntity != null) { + this.world.setTileEntity(blockposition, newTileEntity); + } else { // Purpur end - add tileEntity parameter tileentity = this.a(blockposition, Chunk.EnumTileEntityState.CHECK); if (tileentity == null) { tileentity = ((ITileEntity) block).createTile(this.world); @@ -576,6 +586,7 @@ public class Chunk implements IChunkAccess { } else { tileentity.invalidateBlockCache(); } + } // Purpur - add tileEntity parameter } this.s = true; diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java index 87d5800211..6b97484031 100644 --- a/src/main/java/net/minecraft/server/MCUtil.java +++ b/src/main/java/net/minecraft/server/MCUtil.java @@ -48,6 +48,20 @@ public final class MCUtil { new ThreadFactoryBuilder().setNameFormat("Paper Object Cleaner").build() ); + // Purpur start + private static org.bukkit.entity.HumanEntity[] EMPTY_HUMAN_ARRAY = new org.bukkit.entity.HumanEntity[0]; + public static void closeInventory(IInventory inventory, org.bukkit.event.inventory.InventoryCloseEvent.Reason reason) { + List viewers = inventory.getViewers(); + if (viewers.isEmpty()) { + return; + } + + for (org.bukkit.entity.HumanEntity viewer : viewers.toArray(EMPTY_HUMAN_ARRAY)) { + viewer.closeInventory(reason); + } + } + // Purpur end + public static final long INVALID_CHUNK_KEY = getCoordinateKey(Integer.MAX_VALUE, Integer.MAX_VALUE); diff --git a/src/main/java/net/minecraft/server/TileEntity.java b/src/main/java/net/minecraft/server/TileEntity.java index a8e64dfdab..3cd3ba1175 100644 --- a/src/main/java/net/minecraft/server/TileEntity.java +++ b/src/main/java/net/minecraft/server/TileEntity.java @@ -12,7 +12,7 @@ import org.bukkit.inventory.InventoryHolder; import co.aikar.timings.MinecraftTimings; // Paper import co.aikar.timings.Timing; // Paper -public abstract class TileEntity implements KeyedObject { // Paper +public abstract class TileEntity implements KeyedObject, Cloneable { // Purpur // Paper public Timing tickTimer = MinecraftTimings.getTileEntityTimings(this); // Paper // CraftBukkit start - data containers @@ -27,7 +27,7 @@ public abstract class TileEntity implements KeyedObject { // Paper protected BlockPosition position; protected boolean f; @Nullable - private IBlockData c; + private IBlockData c;protected final IBlockData getBlockDataCache() { return this.c; } public final void setBlockDataCache(final IBlockData value) { this.c = value; } // Purpur - OBFHELPER private boolean g; public TileEntity(TileEntityTypes tileentitytypes) { @@ -35,6 +35,51 @@ public abstract class TileEntity implements KeyedObject { // Paper this.tileType = tileentitytypes; } + // Purpur start - pushable TE's + public boolean isPushable() { + if (!net.pl3x.purpur.PurpurConfig.pistonsCanPushTileEntities) { + return false; + } + IBlockData block = this.getBlock(); + if (this.isRemoved() || !this.tileType.isValidBlock(block.getBlock())) { + return false; + } + EnumPistonReaction reaction = block.getPushReaction(); + return reaction == EnumPistonReaction.NORMAL || reaction == EnumPistonReaction.PUSH_ONLY; + } + + @Override + protected final TileEntity clone() { + try { + return (TileEntity)super.clone(); + } catch (final Throwable thr) { + if (thr instanceof ThreadDeath) { + throw (ThreadDeath)thr; + } + throw new InternalError(thr); + } + } + + // this method presumes the old TE has been completely dropped from worldstate and has no ties to it anymore (this + // includes players interacting with them) + public TileEntity createCopyForPush(WorldServer world, BlockPosition oldPos, BlockPosition newPos, IBlockData blockData) { + final TileEntity copy = this.clone(); + + copy.world = world; + copy.position = newPos; + + // removed is manually set to false after placing the entity into the world. + copy.setBlockDataCache(blockData); + + return copy; + } + + // updates TE state to its new position + public void onPostPush() { + this.update(); + } + // Purpur end - pushable TE's + // Paper start private String tileEntityKeyString = null; private MinecraftKey tileEntityKey = null; diff --git a/src/main/java/net/minecraft/server/TileEntityBeacon.java b/src/main/java/net/minecraft/server/TileEntityBeacon.java index df2d6c3b07..333a40d4fa 100644 --- a/src/main/java/net/minecraft/server/TileEntityBeacon.java +++ b/src/main/java/net/minecraft/server/TileEntityBeacon.java @@ -35,7 +35,7 @@ public class TileEntityBeacon extends TileEntity implements ITileInventory, ITic @Nullable public IChatBaseComponent customName; public ChestLock chestLock; - private final IContainerProperties containerProperties; + private IContainerProperties containerProperties; // Purpur - need non-final for `createCopyForPush` // CraftBukkit start - add fields and methods public PotionEffect getPrimaryEffect() { return (this.primaryEffect != null) ? CraftPotionUtil.toBukkit(new MobEffect(this.primaryEffect, getLevel(), getAmplification(), true, true)) : null; @@ -49,8 +49,12 @@ public class TileEntityBeacon extends TileEntity implements ITileInventory, ITic public TileEntityBeacon() { super(TileEntityTypes.BEACON); this.chestLock = ChestLock.a; - this.containerProperties = new IContainerProperties() { - @Override + // Purpur start - pushable TE's + this.containerProperties = this.getNewContainerProperties(); + } + protected final IContainerProperties getNewContainerProperties() { + return new IContainerProperties() { + // Purpur end - pushable TE's public int getProperty(int i) { switch (i) { case 0: @@ -90,6 +94,16 @@ public class TileEntityBeacon extends TileEntity implements ITileInventory, ITic }; } + // Purpur start - pushable TE's + @Override + public TileEntity createCopyForPush(WorldServer world, BlockPosition oldPos, BlockPosition newPos, IBlockData blockData) { + TileEntityBeacon copy = (TileEntityBeacon)super.createCopyForPush(world, oldPos, newPos, blockData); + copy.containerProperties = copy.getNewContainerProperties(); // old properties retains reference to old te + + return copy; + } + // Purpur end - pushable TE's + @Override public void tick() { int i = this.position.getX(); diff --git a/src/main/java/net/minecraft/server/TileEntityBeehive.java b/src/main/java/net/minecraft/server/TileEntityBeehive.java index 417152d16c..b8e7e82ab3 100644 --- a/src/main/java/net/minecraft/server/TileEntityBeehive.java +++ b/src/main/java/net/minecraft/server/TileEntityBeehive.java @@ -12,6 +12,13 @@ public class TileEntityBeehive extends TileEntity implements ITickable { public BlockPosition flowerPos = null; public int maxBees = 3; // CraftBukkit - allow setting max amount of bees a hive can hold + // Purpur start - pushable TE's + @Override + public boolean isPushable() { + return false; // TODO until there is a good solution to making the already existing bees in the world re-acquire this position, this cannot be done. + } + // Purpur end - pushable TE's + public TileEntityBeehive() { super(TileEntityTypes.BEEHIVE); } diff --git a/src/main/java/net/minecraft/server/TileEntityBrewingStand.java b/src/main/java/net/minecraft/server/TileEntityBrewingStand.java index 441157cf7f..3a5b7a7151 100644 --- a/src/main/java/net/minecraft/server/TileEntityBrewingStand.java +++ b/src/main/java/net/minecraft/server/TileEntityBrewingStand.java @@ -24,7 +24,7 @@ public class TileEntityBrewingStand extends TileEntityContainer implements IWorl private boolean[] j; private Item k; public int fuelLevel; - protected final IContainerProperties a; + protected IContainerProperties a; protected final void setContainerProperties(IContainerProperties value) { this.a = value; } // Purpur - OBFHELPER // Purpur - need non-final for `createCopyForPush` // CraftBukkit start - add fields and methods private int lastTick = MinecraftServer.currentTick; public List transaction = new java.util.ArrayList(); @@ -59,7 +59,13 @@ public class TileEntityBrewingStand extends TileEntityContainer implements IWorl public TileEntityBrewingStand() { super(TileEntityTypes.BREWING_STAND); this.items = NonNullList.a(5, ItemStack.a); - this.a = new IContainerProperties() { + // Purpur start - pushable TE's + this.a = this.getNewContainerProperties(); + } + protected final IContainerProperties getNewContainerProperties() { + // moved from constructor - this should be re-copied if it changes + return new IContainerProperties() { + // Purpur end - pushable TE's @Override public int getProperty(int i) { switch (i) { @@ -91,6 +97,17 @@ public class TileEntityBrewingStand extends TileEntityContainer implements IWorl }; } + // Purpur start - pushable TE's + @Override + public TileEntity createCopyForPush(WorldServer world, BlockPosition oldPos, BlockPosition newPos, IBlockData blockData) { + TileEntityBrewingStand copy = (TileEntityBrewingStand)super.createCopyForPush(world, oldPos, newPos, blockData); + + copy.setContainerProperties(copy.getNewContainerProperties()); // old properties retains reference to old te + + return copy; + } + // Purpur end - pushable TE's + @Override protected IChatBaseComponent getContainerName() { return new ChatMessage("container.brewing", new Object[0]); diff --git a/src/main/java/net/minecraft/server/TileEntityChest.java b/src/main/java/net/minecraft/server/TileEntityChest.java index 9a5f2da8c0..f33f33bad9 100644 --- a/src/main/java/net/minecraft/server/TileEntityChest.java +++ b/src/main/java/net/minecraft/server/TileEntityChest.java @@ -45,6 +45,22 @@ public class TileEntityChest extends TileEntityLootable { // Paper - Remove ITic } // CraftBukkit end + // Purpur start + @Override + public boolean isPushable() { + if (!super.isPushable()) { + return false; + } + // what should happen when a double chest is moved is generally just a mess to deal with in the current + // codebase. TODO try and figure it out + IBlockData type = this.getBlock(); + if (type.getBlock() == Blocks.CHEST || type.getBlock() == Blocks.TRAPPED_CHEST) { + return type.get(BlockChest.getChestTypeEnum()) == BlockPropertyChestType.SINGLE; + } + return false; + } + // Purpur end + protected TileEntityChest(TileEntityTypes tileentitytypes) { super(tileentitytypes); this.items = NonNullList.a(27, ItemStack.a); diff --git a/src/main/java/net/minecraft/server/TileEntityConduit.java b/src/main/java/net/minecraft/server/TileEntityConduit.java index 07f265b299..44ea73a7c6 100644 --- a/src/main/java/net/minecraft/server/TileEntityConduit.java +++ b/src/main/java/net/minecraft/server/TileEntityConduit.java @@ -16,15 +16,32 @@ public class TileEntityConduit extends TileEntity implements ITickable { private static final Block[] b = new Block[]{Blocks.PRISMARINE, Blocks.PRISMARINE_BRICKS, Blocks.SEA_LANTERN, Blocks.DARK_PRISMARINE}; public int a; private float c; - private boolean g; + private boolean g; private final void setActive(boolean value) { this.g = value; } // Purpur - OBFHELPER private boolean h; - private final List i; + private final List i; private final List getPositionsActivating() { return this.i; } // Purpur - OBFHELPER @Nullable private EntityLiving target; @Nullable private UUID k; private long l; + // Purpur start - make TE's pushable + @Override + public TileEntity createCopyForPush(WorldServer world, BlockPosition oldPos, BlockPosition newPos, IBlockData blockData) { + final TileEntityConduit copy = (TileEntityConduit)super.createCopyForPush(world, oldPos, newPos, blockData); + + // the following states need to be re-calculated + copy.getPositionsActivating().clear(); + copy.setActive(false); + copy.target = null; + // also set our state because the copy and this share the same activating block list + this.setActive(false); + this.target = null; + + return copy; + } + // Purpur end - make TE's pushable + public TileEntityConduit() { this(TileEntityTypes.CONDUIT); } diff --git a/src/main/java/net/minecraft/server/TileEntityFurnace.java b/src/main/java/net/minecraft/server/TileEntityFurnace.java index 91ef7ac60f..3ad5041cab 100644 --- a/src/main/java/net/minecraft/server/TileEntityFurnace.java +++ b/src/main/java/net/minecraft/server/TileEntityFurnace.java @@ -30,14 +30,23 @@ public abstract class TileEntityFurnace extends TileEntityContainer implements I public double cookSpeedMultiplier = 1.0; // Paper - cook speed multiplier API public int cookTime; public int cookTimeTotal; - protected final IContainerProperties b; + protected IContainerProperties b; protected final void setContainerProperties(IContainerProperties value) { this.b = value; } // Purpur - OBFHELPER // Purpur - need non-final for `createCopyForPush` private final Map n; protected final Recipes c; protected TileEntityFurnace(TileEntityTypes tileentitytypes, Recipes recipes) { super(tileentitytypes); this.items = NonNullList.a(3, ItemStack.a); - this.b = new IContainerProperties() { + this.b = this.getNewContainerProperties(); // Purpur - pushable TE's + this.n = Maps.newHashMap(); + this.c = recipes; + } + + // Purpur start - pushable TE's + protected final IContainerProperties getNewContainerProperties() { + // moved from constructor - this should be re-copied if it changes + return new IContainerProperties() { + // Purpur end - pushable TE's @Override public int getProperty(int i) { switch (i) { @@ -77,10 +86,19 @@ public abstract class TileEntityFurnace extends TileEntityContainer implements I return 4; } }; - this.n = Maps.newHashMap(); - this.c = recipes; } + // Purpur start - pushable TE's + @Override + public TileEntity createCopyForPush(WorldServer world, BlockPosition oldPos, BlockPosition newPos, IBlockData blockData) { + TileEntityFurnace copy = (TileEntityFurnace)super.createCopyForPush(world, oldPos, newPos, blockData); + + copy.setContainerProperties(copy.getNewContainerProperties()); // old properties retains reference to old te + + return copy; + } + // Purpur end - pushable TE's + public static Map f() { Map map = Maps.newLinkedHashMap(); diff --git a/src/main/java/net/minecraft/server/TileEntityJukeBox.java b/src/main/java/net/minecraft/server/TileEntityJukeBox.java index d66d9ff188..284eb8d000 100644 --- a/src/main/java/net/minecraft/server/TileEntityJukeBox.java +++ b/src/main/java/net/minecraft/server/TileEntityJukeBox.java @@ -4,6 +4,13 @@ public class TileEntityJukeBox extends TileEntity implements Clearable { private ItemStack a; + // Purpur start - pushable TE's + @Override + public boolean isPushable() { + return false; // disabled due to buggy sound + } + // Purpur end - pushable TE's + public TileEntityJukeBox() { super(TileEntityTypes.JUKEBOX); this.a = ItemStack.a; diff --git a/src/main/java/net/minecraft/server/TileEntityLectern.java b/src/main/java/net/minecraft/server/TileEntityLectern.java index 6c2b48bdbe..dbe5faf0ed 100644 --- a/src/main/java/net/minecraft/server/TileEntityLectern.java +++ b/src/main/java/net/minecraft/server/TileEntityLectern.java @@ -16,7 +16,7 @@ import org.bukkit.inventory.InventoryHolder; public class TileEntityLectern extends TileEntity implements Clearable, ITileInventory, ICommandListener { // CraftBukkit - ICommandListener // CraftBukkit start - add fields and methods - public final IInventory inventory = new LecternInventory(); + public IInventory inventory = new LecternInventory(); // Purpur - need non-final for `createCopyForPush` public class LecternInventory implements IInventory { public List transaction = new ArrayList<>(); @@ -136,7 +136,12 @@ public class TileEntityLectern extends TileEntity implements Clearable, ITileInv @Override public void clear() {} }; - private final IContainerProperties containerProperties = new IContainerProperties() { + // Purpur start - pushable TE's + private IContainerProperties containerProperties = this.getNewContainerProperties(); // Purpur - need non-final for `createCopyForPush` + + protected final IContainerProperties getNewContainerProperties() { + return new IContainerProperties() { + // Purpur end - pushable TE's @Override public int getProperty(int i) { return i == 0 ? TileEntityLectern.this.page : 0; @@ -155,6 +160,20 @@ public class TileEntityLectern extends TileEntity implements Clearable, ITileInv return 1; } }; + // Purpur start - pushable TE's + } + + @Override + public TileEntity createCopyForPush(WorldServer world, BlockPosition oldPos, BlockPosition newPos, IBlockData blockData) { + TileEntityLectern copy = (TileEntityLectern)super.createCopyForPush(world, oldPos, newPos, blockData); + + copy.inventory = copy.new LecternInventory(); + copy.containerProperties = copy.getNewContainerProperties(); // old properties retains reference to old te + + return copy; + } + // Purpur end - pushable TE's + private ItemStack book; private int page; private int maxPage; diff --git a/src/main/java/net/minecraft/server/TileEntityPiston.java b/src/main/java/net/minecraft/server/TileEntityPiston.java index 946522f636..93c54eb08f 100644 --- a/src/main/java/net/minecraft/server/TileEntityPiston.java +++ b/src/main/java/net/minecraft/server/TileEntityPiston.java @@ -5,10 +5,10 @@ import java.util.List; public class TileEntityPiston extends TileEntity implements ITickable { - private IBlockData a; + private IBlockData a; protected final IBlockData getBlockData() { return this.a; } // Purpur - OBFHELPER private EnumDirection b; private boolean c; - private boolean g; + private boolean g; protected final boolean isSource() { return this.g; } // Purpur - OBFHELPER private static final ThreadLocal h = ThreadLocal.withInitial(() -> { return null; }); @@ -16,12 +16,27 @@ public class TileEntityPiston extends TileEntity implements ITickable { private float j; private long k; + // Purpur start - pushable TE's + private TileEntity tileEntity; + + @Override + public boolean isPushable() { + return false; // fuck no. + } + // Purpur end - pushable TE's + public TileEntityPiston() { super(TileEntityTypes.PISTON); } public TileEntityPiston(IBlockData iblockdata, EnumDirection enumdirection, boolean flag, boolean flag1) { + // Purpur start - add tileEntity parameter + this(iblockdata, enumdirection, flag, flag1, null); + } + public TileEntityPiston(IBlockData iblockdata, EnumDirection enumdirection, boolean flag, boolean flag1, TileEntity tileEntity) { this(); + this.tileEntity = tileEntity; + // Purpur end - add tileEntity parameter this.a = iblockdata; this.b = enumdirection; this.c = flag; @@ -30,7 +45,7 @@ public class TileEntityPiston extends TileEntity implements ITickable { @Override public NBTTagCompound b() { - return this.save(new NBTTagCompound()); + return this.save(new NBTTagCompound(), false); // Purpur - clients don't need the copied tile entity. } public boolean d() { @@ -257,7 +272,22 @@ public class TileEntityPiston extends TileEntity implements ITickable { iblockdata = Block.b(this.a, (GeneratorAccess) this.world, this.position); } - this.world.setTypeAndData(this.position, iblockdata, 3); + // Purpur start - pushable TE's + if ((iblockdata.isAir() && !this.isSource()) && !this.getBlockData().isAir()) { + // if the block can't exist at the location anymore, we need to fire drops for it, as + // setTypeAndData wont. + + // careful - the previous pos is moving_piston, which wont fire drops. So we're safe from dupes. + // but the setAir should be before the drop. + this.world.setAir(this.position, false); + Block.dropItems(this.getBlockData(), this.world, this.position, null, null, ItemStack.NULL_ITEM); + } else { + this.world.setTypeAndData(this.position, iblockdata, 3, iblockdata.getBlock() == this.getBlockData().getBlock() ? this.tileEntity : null); + } + if (this.tileEntity != null && this.world.getType(this.position).getBlock() == this.getBlockData().getBlock()) { + this.tileEntity.onPostPush(); + } + // Purpur end - pushable TE's this.world.a(this.position, iblockdata.getBlock(), this.position); } } @@ -282,7 +312,12 @@ public class TileEntityPiston extends TileEntity implements ITickable { iblockdata = (IBlockData) iblockdata.set(BlockProperties.C, false); } - this.world.setTypeAndData(this.position, iblockdata, 67); + // Purpur start - pushable TE's + this.world.setTypeAndData(this.position, iblockdata, 67, this.tileEntity); + if (this.tileEntity != null && this.world.getType(this.position).getBlock() == this.getBlockData().getBlock()) { + this.tileEntity.onPostPush(); + } + // Purpur end - pushable TE's this.world.a(this.position, iblockdata.getBlock(), this.position); } } @@ -309,16 +344,34 @@ public class TileEntityPiston extends TileEntity implements ITickable { this.j = this.i; this.c = nbttagcompound.getBoolean("extending"); this.g = nbttagcompound.getBoolean("source"); + // Purpur start - pushable TE's + if (nbttagcompound.hasKey("Purpur.tileEntity")) { + NBTTagCompound compound = nbttagcompound.getCompound("Purpur.tileEntity"); + if (!compound.isEmpty()) { + this.tileEntity = TileEntity.create(compound); + } + } + // Purpur end - pushable TE's } @Override public NBTTagCompound save(NBTTagCompound nbttagcompound) { + // Purpur start - add saveTile param + return this.save(nbttagcompound, true); + } + public NBTTagCompound save(NBTTagCompound nbttagcompound, boolean saveTile) { + // Purpur end - add saveTile param super.save(nbttagcompound); nbttagcompound.set("blockState", GameProfileSerializer.a(this.a)); nbttagcompound.setInt("facing", this.b.b()); nbttagcompound.setFloat("progress", this.j); nbttagcompound.setBoolean("extending", this.c); nbttagcompound.setBoolean("source", this.g); + // Purpur start - pushable TE's + if (saveTile && this.tileEntity != null) { + nbttagcompound.set("Purpur.tileEntity", this.tileEntity.save(new NBTTagCompound())); + } + // Purpur end - pushable TE's return nbttagcompound; } diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java index 41e977d1e0..1c444a6474 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java @@ -342,6 +342,15 @@ public abstract class World implements GeneratorAccess, AutoCloseable { @Override public boolean setTypeAndData(BlockPosition blockposition, IBlockData iblockdata, int i) { + // Purpur start - add tileEntity parameter + return this.setTypeAndData(blockposition, iblockdata, i, null); + } + public boolean setTypeAndData(BlockPosition blockposition, IBlockData iblockdata, int i, TileEntity tileEntity) { + if (tileEntity != null) { + // even though we set it later in the call, some calls actually remove it / check it. + this.setTileEntity(blockposition, tileEntity); + } + // Purpur end - add tileEntity parameter // CraftBukkit start - tree generation if (this.captureTreeGeneration) { // Paper start @@ -374,7 +383,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable { } // CraftBukkit end - IBlockData iblockdata1 = chunk.setType(blockposition, iblockdata, (i & 64) != 0, (i & 1024) == 0); // CraftBukkit custom NO_PLACE flag + IBlockData iblockdata1 = chunk.setType(blockposition, iblockdata, (i & 64) != 0, (i & 1024) == 0, tileEntity); // CraftBukkit custom NO_PLACE flag // Purpur - add tileEntity parameter this.chunkPacketBlockController.onBlockChange(this, blockposition, iblockdata, iblockdata1, i); // Paper - Anti-Xray if (iblockdata1 == null) { @@ -1111,7 +1120,7 @@ public abstract class World implements GeneratorAccess, AutoCloseable { while (iterator.hasNext()) { TileEntity tileentity1 = (TileEntity) iterator.next(); - if (tileentity1.getPosition().equals(blockposition)) { + if (tileentity != tileentity1 && tileentity1.getPosition().equals(blockposition)) { // Purpur - don't remove us if we double set... tileentity1.ab_(); iterator.remove(); } diff --git a/src/main/java/net/pl3x/purpur/PurpurConfig.java b/src/main/java/net/pl3x/purpur/PurpurConfig.java index ab88636cad..defe880a6e 100644 --- a/src/main/java/net/pl3x/purpur/PurpurConfig.java +++ b/src/main/java/net/pl3x/purpur/PurpurConfig.java @@ -206,4 +206,9 @@ public class PurpurConfig { enchantment.setMaxLevel(maxLevel); } } + + public static boolean pistonsCanPushTileEntities = false; + private static void pistonsCanPushTileEntities() { + pistonsCanPushTileEntities = getBoolean("settings.blocks.pistons.can-push-tile-entities", pistonsCanPushTileEntities); + } } -- 2.26.2