diff --git a/sources/src/main/java/net/minecraft/server/BlockChest.java b/sources/src/main/java/net/minecraft/server/BlockChest.java new file mode 100644 index 000000000..7e0e90fd3 --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/BlockChest.java @@ -0,0 +1,479 @@ +package net.minecraft.server; + +import java.util.Iterator; +import javax.annotation.Nullable; + +public class BlockChest extends BlockTileEntity { + + public static final BlockStateDirection FACING = BlockFacingHorizontal.FACING; + protected static final AxisAlignedBB b = new AxisAlignedBB(0.0625D, 0.0D, 0.0D, 0.9375D, 0.875D, 0.9375D); + protected static final AxisAlignedBB c = new AxisAlignedBB(0.0625D, 0.0D, 0.0625D, 0.9375D, 0.875D, 1.0D); + protected static final AxisAlignedBB d = new AxisAlignedBB(0.0D, 0.0D, 0.0625D, 0.9375D, 0.875D, 0.9375D); + protected static final AxisAlignedBB e = new AxisAlignedBB(0.0625D, 0.0D, 0.0625D, 1.0D, 0.875D, 0.9375D); + protected static final AxisAlignedBB f = new AxisAlignedBB(0.0625D, 0.0D, 0.0625D, 0.9375D, 0.875D, 0.9375D); + public final BlockChest.Type g; + + protected BlockChest(BlockChest.Type blockchest_type) { + super(Material.WOOD); + this.w(this.blockStateList.getBlockData().set(BlockChest.FACING, EnumDirection.NORTH)); + this.g = blockchest_type; + this.a(blockchest_type == BlockChest.Type.TRAP ? CreativeModeTab.d : CreativeModeTab.c); + } + + public boolean b(IBlockData iblockdata) { + return false; + } + + public boolean c(IBlockData iblockdata) { + return false; + } + + public EnumRenderType a(IBlockData iblockdata) { + return EnumRenderType.ENTITYBLOCK_ANIMATED; + } + + public AxisAlignedBB b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition) { + return iblockaccess.getType(blockposition.north()).getBlock() == this ? BlockChest.b : (iblockaccess.getType(blockposition.south()).getBlock() == this ? BlockChest.c : (iblockaccess.getType(blockposition.west()).getBlock() == this ? BlockChest.d : (iblockaccess.getType(blockposition.east()).getBlock() == this ? BlockChest.e : BlockChest.f))); + } + + public void onPlace(World world, BlockPosition blockposition, IBlockData iblockdata) { + this.e(world, blockposition, iblockdata); + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection = (EnumDirection) iterator.next(); + BlockPosition blockposition1 = blockposition.shift(enumdirection); + // NeonPaper start - Dont load chunks for chests + final IBlockData iblockdata1 = world.isLoaded(blockposition1) ? world.getType(blockposition1) : null; + if (iblockdata1 == null) { + continue; + } + // NeonPaper end + + if (iblockdata1.getBlock() == this) { + this.e(world, blockposition1, iblockdata1); + } + } + + } + + public IBlockData getPlacedState(World world, BlockPosition blockposition, EnumDirection enumdirection, float f, float f1, float f2, int i, EntityLiving entityliving) { + return this.getBlockData().set(BlockChest.FACING, entityliving.getDirection()); + } + + public void postPlace(World world, BlockPosition blockposition, IBlockData iblockdata, EntityLiving entityliving, ItemStack itemstack) { + EnumDirection enumdirection = EnumDirection.fromType2(MathHelper.floor((double) (entityliving.yaw * 4.0F / 360.0F) + 0.5D) & 3).opposite(); + + iblockdata = iblockdata.set(BlockChest.FACING, enumdirection); + BlockPosition blockposition1 = blockposition.north(); + BlockPosition blockposition2 = blockposition.south(); + BlockPosition blockposition3 = blockposition.west(); + BlockPosition blockposition4 = blockposition.east(); + boolean flag = this == world.getType(blockposition1).getBlock(); + boolean flag1 = this == world.getType(blockposition2).getBlock(); + boolean flag2 = this == world.getType(blockposition3).getBlock(); + boolean flag3 = this == world.getType(blockposition4).getBlock(); + + if (!flag && !flag1 && !flag2 && !flag3) { + world.setTypeAndData(blockposition, iblockdata, 3); + } else if (enumdirection.k() == EnumDirection.EnumAxis.X && (flag || flag1)) { + if (flag) { + world.setTypeAndData(blockposition1, iblockdata, 3); + } else { + world.setTypeAndData(blockposition2, iblockdata, 3); + } + + world.setTypeAndData(blockposition, iblockdata, 3); + } else if (enumdirection.k() == EnumDirection.EnumAxis.Z && (flag2 || flag3)) { + if (flag2) { + world.setTypeAndData(blockposition3, iblockdata, 3); + } else { + world.setTypeAndData(blockposition4, iblockdata, 3); + } + + world.setTypeAndData(blockposition, iblockdata, 3); + } + + if (itemstack.hasName()) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityChest) { + ((TileEntityChest) tileentity).setCustomName(itemstack.getName()); + } + } + + } + + public IBlockData e(World world, BlockPosition blockposition, IBlockData iblockdata) { + if (world.isClientSide) { + return iblockdata; + } else { + IBlockData iblockdata1 = world.getType(blockposition.north()); + IBlockData iblockdata2 = world.getType(blockposition.south()); + IBlockData iblockdata3 = world.getType(blockposition.west()); + IBlockData iblockdata4 = world.getType(blockposition.east()); + EnumDirection enumdirection = (EnumDirection) iblockdata.get(BlockChest.FACING); + + if (iblockdata1.getBlock() != this && iblockdata2.getBlock() != this) { + boolean flag = iblockdata1.b(); + boolean flag1 = iblockdata2.b(); + + if (iblockdata3.getBlock() == this || iblockdata4.getBlock() == this) { + BlockPosition blockposition1 = iblockdata3.getBlock() == this ? blockposition.west() : blockposition.east(); + IBlockData iblockdata5 = world.getType(blockposition1.north()); + IBlockData iblockdata6 = world.getType(blockposition1.south()); + + enumdirection = EnumDirection.SOUTH; + EnumDirection enumdirection1; + + if (iblockdata3.getBlock() == this) { + enumdirection1 = (EnumDirection) iblockdata3.get(BlockChest.FACING); + } else { + enumdirection1 = (EnumDirection) iblockdata4.get(BlockChest.FACING); + } + + if (enumdirection1 == EnumDirection.NORTH) { + enumdirection = EnumDirection.NORTH; + } + + if ((flag || iblockdata5.b()) && !flag1 && !iblockdata6.b()) { + enumdirection = EnumDirection.SOUTH; + } + + if ((flag1 || iblockdata6.b()) && !flag && !iblockdata5.b()) { + enumdirection = EnumDirection.NORTH; + } + } + } else { + BlockPosition blockposition2 = iblockdata1.getBlock() == this ? blockposition.north() : blockposition.south(); + IBlockData iblockdata7 = world.getType(blockposition2.west()); + IBlockData iblockdata8 = world.getType(blockposition2.east()); + + enumdirection = EnumDirection.EAST; + EnumDirection enumdirection2; + + if (iblockdata1.getBlock() == this) { + enumdirection2 = (EnumDirection) iblockdata1.get(BlockChest.FACING); + } else { + enumdirection2 = (EnumDirection) iblockdata2.get(BlockChest.FACING); + } + + if (enumdirection2 == EnumDirection.WEST) { + enumdirection = EnumDirection.WEST; + } + + if ((iblockdata3.b() || iblockdata7.b()) && !iblockdata4.b() && !iblockdata8.b()) { + enumdirection = EnumDirection.EAST; + } + + if ((iblockdata4.b() || iblockdata8.b()) && !iblockdata3.b() && !iblockdata7.b()) { + enumdirection = EnumDirection.WEST; + } + } + + iblockdata = iblockdata.set(BlockChest.FACING, enumdirection); + world.setTypeAndData(blockposition, iblockdata, 3); + return iblockdata; + } + } + + public IBlockData f(World world, BlockPosition blockposition, IBlockData iblockdata) { + EnumDirection enumdirection = null; + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection1 = (EnumDirection) iterator.next(); + IBlockData iblockdata1 = world.getType(blockposition.shift(enumdirection1)); + + if (iblockdata1.getBlock() == this) { + return iblockdata; + } + + if (iblockdata1.b()) { + if (enumdirection != null) { + enumdirection = null; + break; + } + + enumdirection = enumdirection1; + } + } + + if (enumdirection != null) { + return iblockdata.set(BlockChest.FACING, enumdirection.opposite()); + } else { + EnumDirection enumdirection2 = (EnumDirection) iblockdata.get(BlockChest.FACING); + + if (world.getType(blockposition.shift(enumdirection2)).b()) { + enumdirection2 = enumdirection2.opposite(); + } + + if (world.getType(blockposition.shift(enumdirection2)).b()) { + enumdirection2 = enumdirection2.e(); + } + + if (world.getType(blockposition.shift(enumdirection2)).b()) { + enumdirection2 = enumdirection2.opposite(); + } + + return iblockdata.set(BlockChest.FACING, enumdirection2); + } + } + + public boolean canPlace(World world, BlockPosition blockposition) { + int i = 0; + BlockPosition blockposition1 = blockposition.west(); + BlockPosition blockposition2 = blockposition.east(); + BlockPosition blockposition3 = blockposition.north(); + BlockPosition blockposition4 = blockposition.south(); + + if (world.getType(blockposition1).getBlock() == this) { + if (this.d(world, blockposition1)) { + return false; + } + + ++i; + } + + if (world.getType(blockposition2).getBlock() == this) { + if (this.d(world, blockposition2)) { + return false; + } + + ++i; + } + + if (world.getType(blockposition3).getBlock() == this) { + if (this.d(world, blockposition3)) { + return false; + } + + ++i; + } + + if (world.getType(blockposition4).getBlock() == this) { + if (this.d(world, blockposition4)) { + return false; + } + + ++i; + } + + return i <= 1; + } + + private boolean d(World world, BlockPosition blockposition) { + if (world.getType(blockposition).getBlock() != this) { + return false; + } else { + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + EnumDirection enumdirection; + + do { + if (!iterator.hasNext()) { + return false; + } + + enumdirection = (EnumDirection) iterator.next(); + } while (world.getType(blockposition.shift(enumdirection)).getBlock() != this); + + return true; + } + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) { + super.a(iblockdata, world, blockposition, block, blockposition1); + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityChest) { + tileentity.invalidateBlockCache(); + } + + } + + public void remove(World world, BlockPosition blockposition, IBlockData iblockdata) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof IInventory) { + InventoryUtils.dropInventory(world, blockposition, (IInventory) tileentity); + world.updateAdjacentComparators(blockposition, this); + } + + super.remove(world, blockposition, iblockdata); + } + + public boolean interact(World world, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman, EnumHand enumhand, EnumDirection enumdirection, float f, float f1, float f2) { + if (world.isClientSide) { + return true; + } else { + ITileInventory itileinventory = this.getInventory(world, blockposition); + + if (itileinventory != null) { + entityhuman.openContainer(itileinventory); + if (this.g == BlockChest.Type.BASIC) { + entityhuman.b(StatisticList.aa); + } else if (this.g == BlockChest.Type.TRAP) { + entityhuman.b(StatisticList.U); + } + } + + return true; + } + } + + @Nullable + public ITileInventory getInventory(World world, BlockPosition blockposition) { + return this.a(world, blockposition, false); + } + + @Nullable + public ITileInventory a(World world, BlockPosition blockposition, boolean flag) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (!(tileentity instanceof TileEntityChest)) { + return null; + } else { + Object object = (TileEntityChest) tileentity; + + if (!flag && this.e(world, blockposition)) { + return null; + } else { + Iterator iterator = EnumDirection.EnumDirectionLimit.HORIZONTAL.iterator(); + + while (iterator.hasNext()) { + EnumDirection enumdirection = (EnumDirection) iterator.next(); + BlockPosition blockposition1 = blockposition.shift(enumdirection); + // Paper start - don't load chunks if the other side of the chest is in unloaded chunk + final IBlockData type = world.getTypeIfLoaded(blockposition1); // Paper + if (type == null) { + continue; + } + Block block = type.getBlock(); + // Paper end + + if (block == this) { + if (!flag && this.e(world, blockposition1)) { // Paper - check for allowBlocked flag - MC-99321 + return null; + } + + TileEntity tileentity1 = world.getTileEntity(blockposition1); + + if (tileentity1 instanceof TileEntityChest) { + if (enumdirection != EnumDirection.WEST && enumdirection != EnumDirection.NORTH) { + object = new InventoryLargeChest("container.chestDouble", (ITileInventory) object, (TileEntityChest) tileentity1); + } else { + object = new InventoryLargeChest("container.chestDouble", (TileEntityChest) tileentity1, (ITileInventory) object); + } + } + } + } + + return (ITileInventory) object; + } + } + } + + public TileEntity a(World world, int i) { + return new TileEntityChest(); + } + + public boolean isPowerSource(IBlockData iblockdata) { + return this.g == BlockChest.Type.TRAP; + } + + public int b(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + if (!iblockdata.m()) { + return 0; + } else { + int i = 0; + TileEntity tileentity = iblockaccess.getTileEntity(blockposition); + + if (tileentity instanceof TileEntityChest) { + i = ((TileEntityChest) tileentity).l; + } + + return MathHelper.clamp(i, 0, 15); + } + } + + public int c(IBlockData iblockdata, IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { + return enumdirection == EnumDirection.UP ? iblockdata.a(iblockaccess, blockposition, enumdirection) : 0; + } + + private boolean e(World world, BlockPosition blockposition) { + return this.i(world, blockposition) || this.j(world, blockposition); + } + + private boolean i(World world, BlockPosition blockposition) { + return world.getType(blockposition.up()).l(); + } + + private boolean j(World world, BlockPosition blockposition) { + // Paper start - Option ti dsiable chest cat detection + if (world.paperConfig.disableChestCatDetection) { + return false; + } + // Paper end + Iterator iterator = world.a(EntityOcelot.class, new AxisAlignedBB((double) blockposition.getX(), (double) (blockposition.getY() + 1), (double) blockposition.getZ(), (double) (blockposition.getX() + 1), (double) (blockposition.getY() + 2), (double) (blockposition.getZ() + 1))).iterator(); + + EntityOcelot entityocelot; + + do { + if (!iterator.hasNext()) { + return false; + } + + Entity entity = (Entity) iterator.next(); + + entityocelot = (EntityOcelot) entity; + } while (!entityocelot.isSitting()); + + return true; + } + + public boolean isComplexRedstone(IBlockData iblockdata) { + return true; + } + + public int c(IBlockData iblockdata, World world, BlockPosition blockposition) { + return Container.b((IInventory) this.getInventory(world, blockposition)); + } + + public IBlockData fromLegacyData(int i) { + EnumDirection enumdirection = EnumDirection.fromType1(i); + + if (enumdirection.k() == EnumDirection.EnumAxis.Y) { + enumdirection = EnumDirection.NORTH; + } + + return this.getBlockData().set(BlockChest.FACING, enumdirection); + } + + public int toLegacyData(IBlockData iblockdata) { + return ((EnumDirection) iblockdata.get(BlockChest.FACING)).a(); + } + + public IBlockData a(IBlockData iblockdata, EnumBlockRotation enumblockrotation) { + return iblockdata.set(BlockChest.FACING, enumblockrotation.a((EnumDirection) iblockdata.get(BlockChest.FACING))); + } + + public IBlockData a(IBlockData iblockdata, EnumBlockMirror enumblockmirror) { + return iblockdata.a(enumblockmirror.a((EnumDirection) iblockdata.get(BlockChest.FACING))); + } + + protected BlockStateList getStateList() { + return new BlockStateList(this, new IBlockState[] { BlockChest.FACING}); + } + + public EnumBlockFaceShape a(IBlockAccess iblockaccess, IBlockData iblockdata, BlockPosition blockposition, EnumDirection enumdirection) { + return EnumBlockFaceShape.UNDEFINED; + } + + public static enum Type { + + BASIC, TRAP; + + private Type() {} + } +} diff --git a/sources/src/main/java/net/minecraft/server/BlockStationary.java b/sources/src/main/java/net/minecraft/server/BlockStationary.java new file mode 100644 index 000000000..31ffc3ac5 --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/BlockStationary.java @@ -0,0 +1,118 @@ +package net.minecraft.server; + +import java.util.Random; + +import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit + +public class BlockStationary extends BlockFluids { + + protected BlockStationary(Material material) { + super(material); + this.a(false); + if (material == Material.LAVA) { + this.a(true); + } + + } + + public void a(IBlockData iblockdata, World world, BlockPosition blockposition, Block block, BlockPosition blockposition1) { + if (!this.e(world, blockposition, iblockdata)) { + this.f(world, blockposition, iblockdata); + } + + } + + private void f(World world, BlockPosition blockposition, IBlockData iblockdata) { + BlockFlowing blockflowing = a(this.material); + + world.setTypeAndData(blockposition, blockflowing.getBlockData().set(BlockStationary.LEVEL, iblockdata.get(BlockStationary.LEVEL)), 2); + world.a(blockposition, (Block) blockflowing, this.a(world)); + } + + public void b(World world, BlockPosition blockposition, IBlockData iblockdata, Random random) { + if (this.material == Material.LAVA) { + if (world.getGameRules().getBoolean("doFireTick")) { + int i = random.nextInt(3); + + if (i > 0) { + BlockPosition blockposition1 = blockposition; + + for (int j = 0; j < i; ++j) { + blockposition1 = blockposition1.a(random.nextInt(3) - 1, 1, random.nextInt(3) - 1); + if (blockposition1.getY() >= 0 && blockposition1.getY() < 256 && !world.isLoaded(blockposition1)) { + return; + } + + Block block = world.getType(blockposition1).getBlock(); + + if (block.material == Material.AIR) { + if (this.c(world, blockposition1)) { + // CraftBukkit start - Prevent lava putting something on fire + if (world.getType(blockposition1) != Blocks.FIRE) { + if (CraftEventFactory.callBlockIgniteEvent(world, blockposition1.getX(), blockposition1.getY(), blockposition1.getZ(), blockposition.getX(), blockposition.getY(), blockposition.getZ()).isCancelled()) { + continue; + } + } + // CraftBukkit end + world.setTypeUpdate(blockposition1, Blocks.FIRE.getBlockData()); + return; + } + } else if (block.material.isSolid()) { + return; + } + } + } else { + for (int k = 0; k < 3; ++k) { + BlockPosition blockposition2 = blockposition.a(random.nextInt(3) - 1, 0, random.nextInt(3) - 1); + + if (blockposition2.getY() >= 0 && blockposition2.getY() < 256 && !world.isLoaded(blockposition2)) { + return; + } + + if (world.isEmpty(blockposition2.up()) && this.d(world, blockposition2)) { + // CraftBukkit start - Prevent lava putting something on fire + BlockPosition up = blockposition2.up(); + if (world.getType(up) != Blocks.FIRE) { + if (CraftEventFactory.callBlockIgniteEvent(world, up.getX(), up.getY(), up.getZ(), blockposition.getX(), blockposition.getY(), blockposition.getZ()).isCancelled()) { + continue; + } + } + // CraftBukkit end + world.setTypeUpdate(blockposition2.up(), Blocks.FIRE.getBlockData()); + } + } + } + + } + } + } + + protected boolean c(World world, BlockPosition blockposition) { + EnumDirection[] aenumdirection = EnumDirection.values(); + int i = aenumdirection.length; + + for (int j = 0; j < i; ++j) { + EnumDirection enumdirection = aenumdirection[j]; + + if (this.d(world, blockposition.shift(enumdirection))) { + return true; + } + } + + return false; + } + + private boolean d(World world, BlockPosition blockposition) { + // Dionysus start - improve fire spread checks + if (blockposition.getY() >= 0 && blockposition.getY() < 256) { + IBlockData blockData = world.getTypeIfLoaded(blockposition); + + if (blockData != null) { + return blockData.getMaterial().isBurnable(); + } + } + + return false; + // Dionysus end + } +} diff --git a/sources/src/main/java/net/minecraft/server/Chunk.java b/sources/src/main/java/net/minecraft/server/Chunk.java index c427250b9..778d898a6 100644 --- a/sources/src/main/java/net/minecraft/server/Chunk.java +++ b/sources/src/main/java/net/minecraft/server/Chunk.java @@ -1000,7 +1000,6 @@ public class Chunk { for (int k = i; k <= j; ++k) { if (!this.entitySlices[k].isEmpty()) { - Iterator iterator = this.entitySlices[k].iterator(); // Paper start - Don't search for inventories if we have none, and that is all we want /* @@ -1011,6 +1010,7 @@ public class Chunk { */ if (predicate == IEntitySelector.c && inventoryEntityCounts[k] <= 0) continue; // Paper end + Iterator iterator = this.entitySlices[k].iterator(); while (iterator.hasNext()) { Entity entity1 = (Entity) iterator.next(); @@ -1548,4 +1548,14 @@ public class Chunk { private EnumTileEntityState() {} } + // FlamePaper start - Hopper item lookup optimization + public int getItemCount(BlockPosition blockPosition) { + int k = MathHelper.floor(blockPosition.getY() / 16.0D); + + k = Math.max(0, k); + k = Math.min(this.entitySlices.length - 1, k); + + return itemCounts[k]; + } + // FlamePaper end - Hopper item lookup optimization } diff --git a/sources/src/main/java/net/minecraft/server/ContainerHorse.java b/sources/src/main/java/net/minecraft/server/ContainerHorse.java new file mode 100644 index 000000000..7187bbf4a --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/ContainerHorse.java @@ -0,0 +1,115 @@ +package net.minecraft.server; + +// CraftBukkit start +import org.bukkit.craftbukkit.inventory.CraftInventoryView; +import org.bukkit.inventory.InventoryView; +// CraftBukkit end + +public class ContainerHorse extends Container { + + private final IInventory a; + private final EntityHorseAbstract f; + + // CraftBukkit start + org.bukkit.craftbukkit.inventory.CraftInventoryView bukkitEntity; + PlayerInventory player; + + @Override + public InventoryView getBukkitView() { + if (bukkitEntity != null) { + return bukkitEntity; + } + + return bukkitEntity = new CraftInventoryView(player.player.getBukkitEntity(), a.getOwner().getInventory(), this); + } + + public ContainerHorse(IInventory iinventory, final IInventory iinventory1, final EntityHorseAbstract entityhorseabstract, EntityHuman entityhuman) { + player = (PlayerInventory) iinventory; + // CraftBukkit end + this.a = iinventory1; + this.f = entityhorseabstract; + boolean flag = true; + + iinventory1.startOpen(entityhuman); + boolean flag1 = true; + + this.a(new Slot(iinventory1, 0, 8, 18) { + public boolean isAllowed(ItemStack itemstack) { + return itemstack.getItem() == Items.SADDLE && !this.hasItem() && entityhorseabstract.dF(); + } + }); + this.a(new Slot(iinventory1, 1, 8, 36) { + public boolean isAllowed(ItemStack itemstack) { + return entityhorseabstract.f(itemstack); + } + + public int getMaxStackSize() { + return 1; + } + }); + int i; + int j; + + if (entityhorseabstract instanceof EntityHorseChestedAbstract && ((EntityHorseChestedAbstract) entityhorseabstract).isCarryingChest()) { + for (i = 0; i < 3; ++i) { + for (j = 0; j < ((EntityHorseChestedAbstract) entityhorseabstract).dt(); ++j) { + this.a(new Slot(iinventory1, 2 + j + i * ((EntityHorseChestedAbstract) entityhorseabstract).dt(), 80 + j * 18, 18 + i * 18)); + } + } + } + + for (i = 0; i < 3; ++i) { + for (j = 0; j < 9; ++j) { + this.a(new Slot(iinventory, j + i * 9 + 9, 8 + j * 18, 102 + i * 18 + -18)); + } + } + + for (i = 0; i < 9; ++i) { + this.a(new Slot(iinventory, i, 8 + i * 18, 142)); + } + + } + + public boolean canUse(EntityHuman entityhuman) { + return this.a.a(entityhuman) && this.f.isAlive() && this.f.valid && this.f.g((Entity) entityhuman) < 8.0F; // NeonPaper! - Fix MC-161754 + } + + public ItemStack shiftClick(EntityHuman entityhuman, int i) { + ItemStack itemstack = ItemStack.a; + Slot slot = (Slot) this.slots.get(i); + + if (slot != null && slot.hasItem()) { + ItemStack itemstack1 = slot.getItem(); + + itemstack = itemstack1.cloneItemStack(); + if (i < this.a.getSize()) { + if (!this.a(itemstack1, this.a.getSize(), this.slots.size(), true)) { + return ItemStack.a; + } + } else if (this.getSlot(1).isAllowed(itemstack1) && !this.getSlot(1).hasItem()) { + if (!this.a(itemstack1, 1, 2, false)) { + return ItemStack.a; + } + } else if (this.getSlot(0).isAllowed(itemstack1)) { + if (!this.a(itemstack1, 0, 1, false)) { + return ItemStack.a; + } + } else if (this.a.getSize() <= 2 || !this.a(itemstack1, 2, this.a.getSize(), false)) { + return ItemStack.a; + } + + if (itemstack1.isEmpty()) { + slot.set(ItemStack.a); + } else { + slot.f(); + } + } + + return itemstack; + } + + public void b(EntityHuman entityhuman) { + super.b(entityhuman); + this.a.closeContainer(entityhuman); + } +} diff --git a/sources/src/main/java/net/minecraft/server/EnchantmentManager.java b/sources/src/main/java/net/minecraft/server/EnchantmentManager.java index 10e89e7e9..237f8384b 100644 --- a/sources/src/main/java/net/minecraft/server/EnchantmentManager.java +++ b/sources/src/main/java/net/minecraft/server/EnchantmentManager.java @@ -118,6 +118,7 @@ public class EnchantmentManager { EnchantmentManager.a.a = 0; EnchantmentManager.a.b = damagesource; a(EnchantmentManager.a, iterable); + EnchantmentManager.a.b = null; // Reaper - Fix MC-128547 return EnchantmentManager.a.a; } @@ -144,6 +145,11 @@ public class EnchantmentManager { if (entity instanceof EntityHuman) { a(EnchantmentManager.c, entityliving.getItemInMainHand()); } + + // Reaper start - Fix MC-128547 + EnchantmentManager.c.b = null; + EnchantmentManager.c.a = null; + // Reaper end } @@ -157,7 +163,10 @@ public class EnchantmentManager { if (entityliving instanceof EntityHuman) { a(EnchantmentManager.d, entityliving.getItemInMainHand()); } - + // Reaper start - Fix MC-128547 + EnchantmentManager.d.b = null; + EnchantmentManager.d.a = null; + // Reaper end } public static int a(Enchantment enchantment, EntityLiving entityliving) { diff --git a/sources/src/main/java/net/minecraft/server/Entity.java b/sources/src/main/java/net/minecraft/server/Entity.java index a0802c4f7..2db63c442 100644 --- a/sources/src/main/java/net/minecraft/server/Entity.java +++ b/sources/src/main/java/net/minecraft/server/Entity.java @@ -45,6 +45,10 @@ import org.bukkit.event.entity.EntityPortalEvent; import org.bukkit.plugin.PluginManager; // CraftBukkit end +// Dionysus start +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +// Dionysus end + /** * Akarin Changes Note * 1) Random -> LightRandom (performance) @@ -91,7 +95,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper private static int entityCount = 1; // Paper - MC-111480 - ID 0 is treated as special for DataWatchers, start 1 private int id; public boolean i; public boolean blocksEntitySpawning() { return i; } // Paper - OBFHELPER - public final List passengers; + public final ObjectArrayList passengers; // Dionysus protected int j; private Entity au;public void setVehicle(Entity entity) { this.au = entity; } // Paper // OBFHELPER public boolean attachedToPlayer; @@ -205,7 +209,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper public Entity(World world) { this.id = Entity.entityCount++; - this.passengers = Lists.newArrayList(); + this.passengers = new ObjectArrayList<>(); // Dionysus this.boundingBox = Entity.c; this.width = 0.6F; this.length = 1.8F; @@ -1383,37 +1387,33 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper public void d(EntityHuman entityhuman) {} public void collide(Entity entity) { - if (!this.x(entity)) { - if (!entity.noclip && !this.noclip) { - double d0 = entity.locX - this.locX; - double d1 = entity.locZ - this.locZ; - double d2 = MathHelper.a(d0, d1); + if (entity.noclip || this.noclip || this.x(entity)) return; // NeonPaper - Test this earlier + double d0 = entity.locX - this.locX; + double d1 = entity.locZ - this.locZ; + double d2 = MathHelper.a(d0, d1); - if (d2 >= 0.009999999776482582D) { - d2 = (double) MathHelper.sqrt(d2); - d0 /= d2; - d1 /= d2; - double d3 = 1.0D / d2; + if (d2 >= 0.009999999776482582D) { + d2 = (double) MathHelper.sqrt(d2); + d0 /= d2; + d1 /= d2; + double d3 = 1.0D / d2; - if (d3 > 1.0D) { - d3 = 1.0D; - } + if (d3 > 1.0D) { + d3 = 1.0D; + } - d0 *= d3; - d1 *= d3; - d0 *= 0.05000000074505806D; - d1 *= 0.05000000074505806D; - d0 *= (double) (1.0F - this.R); - d1 *= (double) (1.0F - this.R); - if (!this.isVehicle()) { - this.f(-d0, 0.0D, -d1); - } - - if (!entity.isVehicle()) { - entity.f(d0, 0.0D, d1); - } - } + d0 *= d3; + d1 *= d3; + d0 *= 0.05000000074505806D; + d1 *= 0.05000000074505806D; + d0 *= (double) (1.0F - this.R); + d1 *= (double) (1.0F - this.R); + if (!this.isVehicle()) { + this.f(-d0, 0.0D, -d1); + } + if (!entity.isVehicle()) { + entity.f(d0, 0.0D, d1); } } } @@ -2881,7 +2881,7 @@ public abstract class Entity implements ICommandListener, KeyedObject { // Paper } public List bF() { - return (List) (this.passengers.isEmpty() ? Collections.emptyList() : Lists.newArrayList(this.passengers)); + return (List) (this.passengers.isEmpty() ? Collections.emptyList() : new ObjectArrayList<>(this.passengers)); // Dionysus } public boolean w(Entity entity) { diff --git a/sources/src/main/java/net/minecraft/server/EntityEnderman.java b/sources/src/main/java/net/minecraft/server/EntityEnderman.java new file mode 100644 index 000000000..f3a66481f --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/EntityEnderman.java @@ -0,0 +1,476 @@ +package net.minecraft.server; + +import com.destroystokyo.paper.event.entity.EndermanEscapeEvent; +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.base.Predicate; +import com.google.common.collect.Sets; +import org.bukkit.event.entity.EntityTargetEvent; + +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import javax.annotation.Nullable; + +public class EntityEnderman extends EntityMonster { + + private static final UUID a = UUID.fromString("020E0DFB-87AE-4653-9556-831010E291A0"); + private static final AttributeModifier b = (new AttributeModifier(EntityEnderman.a, "Attacking speed boost", 0.15000000596046448D, 0)).a(false); + private static final Set c = Sets.newIdentityHashSet(); + private static final DataWatcherObject> bx = DataWatcher.a(EntityEnderman.class, DataWatcherRegistry.g); + private static final DataWatcherObject by = DataWatcher.a(EntityEnderman.class, DataWatcherRegistry.h); + private int bz; + private int bA; + + public EntityEnderman(World world) { + super(world); + this.setSize(0.6F, 2.9F); + this.P = 1.0F; + this.a(PathType.WATER, -1.0F); + } + + protected void r() { + this.goalSelector.a(0, new PathfinderGoalFloat(this)); + this.goalSelector.a(2, new PathfinderGoalMeleeAttack(this, 1.0D, false)); + this.goalSelector.a(7, new PathfinderGoalRandomStrollLand(this, 1.0D, 0.0F)); + this.goalSelector.a(8, new PathfinderGoalLookAtPlayer(this, EntityHuman.class, 8.0F)); + this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this)); + this.goalSelector.a(10, new EntityEnderman.PathfinderGoalEndermanPlaceBlock(this)); + this.goalSelector.a(11, new EntityEnderman.PathfinderGoalEndermanPickupBlock(this)); + this.targetSelector.a(1, new EntityEnderman.PathfinderGoalPlayerWhoLookedAtTarget(this)); + this.targetSelector.a(2, new PathfinderGoalHurtByTarget(this, false, new Class[0])); + this.targetSelector.a(3, new PathfinderGoalNearestAttackableTarget(this, EntityEndermite.class, 10, true, false, new Predicate() { + public boolean a(@Nullable EntityEndermite entityendermite) { + return entityendermite.p(); + } + + public boolean apply(@Nullable Object object) { + return this.a((EntityEndermite) object); + } + })); + } + + protected void initAttributes() { + super.initAttributes(); + this.getAttributeInstance(GenericAttributes.maxHealth).setValue(40.0D); + this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED).setValue(0.30000001192092896D); + this.getAttributeInstance(GenericAttributes.ATTACK_DAMAGE).setValue(7.0D); + this.getAttributeInstance(GenericAttributes.FOLLOW_RANGE).setValue(64.0D); + } + + public void setGoalTarget(@Nullable EntityLiving entityliving) { + // CraftBukkit start - fire event + setGoalTarget(entityliving, EntityTargetEvent.TargetReason.UNKNOWN, true); + } + + // Paper start + private boolean tryEscape(EndermanEscapeEvent.Reason reason) { + return new EndermanEscapeEvent((org.bukkit.craftbukkit.entity.CraftEnderman) this.getBukkitEntity(), reason).callEvent(); + } + // Paper end + + @Override + public boolean setGoalTarget(EntityLiving entityliving, org.bukkit.event.entity.EntityTargetEvent.TargetReason reason, boolean fireEvent) { + if (!super.setGoalTarget(entityliving, reason, fireEvent)) { + return false; + } + entityliving = getGoalTarget(); + // CraftBukkit end + AttributeInstance attributeinstance = this.getAttributeInstance(GenericAttributes.MOVEMENT_SPEED); + + if (entityliving == null) { + this.bA = 0; + this.datawatcher.set(EntityEnderman.by, Boolean.valueOf(false)); + attributeinstance.c(EntityEnderman.b); + } else { + this.bA = this.ticksLived; + this.datawatcher.set(EntityEnderman.by, Boolean.valueOf(true)); + if (!attributeinstance.a(EntityEnderman.b)) { + attributeinstance.b(EntityEnderman.b); + } + } + return true; + + } + + protected void i() { + super.i(); + this.datawatcher.register(EntityEnderman.bx, Optional.absent()); + this.datawatcher.register(EntityEnderman.by, Boolean.valueOf(false)); + } + + public void p() { + if (this.ticksLived >= this.bz + 400) { + this.bz = this.ticksLived; + if (!this.isSilent()) { + this.world.a(this.locX, this.locY + (double) this.getHeadHeight(), this.locZ, SoundEffects.bh, this.bK(), 2.5F, 1.0F, false); + } + } + + } + + public void a(DataWatcherObject datawatcherobject) { + if (EntityEnderman.by.equals(datawatcherobject) && this.do_() && this.world.isClientSide) { + this.p(); + } + + super.a(datawatcherobject); + } + + public static void a(DataConverterManager dataconvertermanager) { + EntityInsentient.a(dataconvertermanager, EntityEnderman.class); + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + IBlockData iblockdata = this.getCarried(); + + if (iblockdata != null) { + nbttagcompound.setShort("carried", (short) Block.getId(iblockdata.getBlock())); + nbttagcompound.setShort("carriedData", (short) iblockdata.getBlock().toLegacyData(iblockdata)); + } + + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + IBlockData iblockdata; + + if (nbttagcompound.hasKeyOfType("carried", 8)) { + iblockdata = Block.getByName(nbttagcompound.getString("carried")).fromLegacyData(nbttagcompound.getShort("carriedData") & '\uffff'); + } else { + iblockdata = Block.getById(nbttagcompound.getShort("carried")).fromLegacyData(nbttagcompound.getShort("carriedData") & '\uffff'); + } + + if (iblockdata == null || iblockdata.getBlock() == null || iblockdata.getMaterial() == Material.AIR) { + iblockdata = null; + } + + this.setCarried(iblockdata); + } + + // Paper start - OBFHELPER - ok not really, but verify this on updates + private boolean f(EntityHuman entityhuman) { + boolean shouldAttack = f_real(entityhuman); + com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent event = new com.destroystokyo.paper.event.entity.EndermanAttackPlayerEvent((org.bukkit.entity.Enderman) getBukkitEntity(), (org.bukkit.entity.Player) entityhuman.getBukkitEntity()); + event.setCancelled(!shouldAttack); + return event.callEvent(); + } + private boolean f_real(EntityHuman entityhuman) { + // Paper end + ItemStack itemstack = (ItemStack) entityhuman.inventory.armor.get(3); + + if (itemstack.getItem() == Item.getItemOf(Blocks.PUMPKIN)) { + return false; + } else { + Vec3D vec3d = entityhuman.e(1.0F).a(); + Vec3D vec3d1 = new Vec3D(this.locX - entityhuman.locX, this.getBoundingBox().b + (double) this.getHeadHeight() - (entityhuman.locY + (double) entityhuman.getHeadHeight()), this.locZ - entityhuman.locZ); + double d0 = vec3d1.b(); + + vec3d1 = vec3d1.a(); + double d1 = vec3d.b(vec3d1); + + return d1 > 1.0D - 0.025D / d0 ? entityhuman.hasLineOfSight(this) : false; + } + } + + public float getHeadHeight() { + return 2.55F; + } + + public void n() { + if (this.world.isClientSide) { + for (int i = 0; i < 2; ++i) { + this.world.addParticle(EnumParticle.PORTAL, this.locX + (this.random.nextDouble() - 0.5D) * (double) this.width, this.locY + this.random.nextDouble() * (double) this.length - 0.25D, this.locZ + (this.random.nextDouble() - 0.5D) * (double) this.width, (this.random.nextDouble() - 0.5D) * 2.0D, -this.random.nextDouble(), (this.random.nextDouble() - 0.5D) * 2.0D, new int[0]); + } + } + + this.bd = false; + super.n(); + } + + protected void M() { + if (this.an()) { + this.damageEntity(DamageSource.DROWN, 1.0F); + } + + if (this.world.D() && this.ticksLived >= this.bA + 600) { + float f = this.aw(); + + if (f > 0.5F && this.world.h(new BlockPosition(this)) && this.random.nextFloat() * 30.0F < (f - 0.4F) * 2.0F && tryEscape(EndermanEscapeEvent.Reason.RUNAWAY)) { // Paper + this.setGoalTarget((EntityLiving) null); + this.dm(); + } + } + + super.M(); + } + + public boolean teleportRandomly() { return dm(); } // Paper - OBFHELPER + protected boolean dm() { + double d0 = this.locX + (this.random.nextDouble() - 0.5D) * 64.0D; + double d1 = this.locY + (double) (this.random.nextInt(64) - 32); + double d2 = this.locZ + (this.random.nextDouble() - 0.5D) * 64.0D; + + return this.k(d0, d1, d2); + } + + protected boolean a(Entity entity) { + Vec3D vec3d = new Vec3D(this.locX - entity.locX, this.getBoundingBox().b + (double) (this.length / 2.0F) - entity.locY + (double) entity.getHeadHeight(), this.locZ - entity.locZ); + + vec3d = vec3d.a(); + double d0 = 16.0D; + double d1 = this.locX + (this.random.nextDouble() - 0.5D) * 8.0D - vec3d.x * 16.0D; + double d2 = this.locY + (double) (this.random.nextInt(16) - 8) - vec3d.y * 16.0D; + double d3 = this.locZ + (this.random.nextDouble() - 0.5D) * 8.0D - vec3d.z * 16.0D; + + return this.k(d1, d2, d3); + } + + private boolean k(double d0, double d1, double d2) { + boolean flag = this.j(d0, d1, d2); + + if (flag) { + this.world.a((EntityHuman) null, this.lastX, this.lastY, this.lastZ, SoundEffects.bi, this.bK(), 1.0F, 1.0F); + this.a(SoundEffects.bi, 1.0F, 1.0F); + } + + return flag; + } + + protected SoundEffect F() { + return this.do_() ? SoundEffects.bg : SoundEffects.bd; + } + + protected SoundEffect d(DamageSource damagesource) { + return SoundEffects.bf; + } + + protected SoundEffect cf() { + return SoundEffects.be; + } + + protected void dropEquipment(boolean flag, int i) { + super.dropEquipment(flag, i); + IBlockData iblockdata = this.getCarried(); + + if (iblockdata != null) { + Item item = Item.getItemOf(iblockdata.getBlock()); + int j = item.k() ? iblockdata.getBlock().toLegacyData(iblockdata) : 0; + + this.a(new ItemStack(item, 1, j), 0.0F); + } + + } + + @Nullable + protected MinecraftKey J() { + return LootTables.w; + } + + public void setCarried(@Nullable IBlockData iblockdata) { + this.datawatcher.set(EntityEnderman.bx, Optional.fromNullable(iblockdata)); + } + + @Nullable + public IBlockData getCarried() { + return (IBlockData) ((Optional) this.datawatcher.get(EntityEnderman.bx)).orNull(); + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else if (damagesource instanceof EntityDamageSourceIndirect && tryEscape(EndermanEscapeEvent.Reason.INDIRECT)) { // Paper + for (int i = 0; i < 64; ++i) { + if (this.dm()) { + return true; + } + } + + return false; + } else { + boolean flag = super.damageEntity(damagesource, f); + + if (damagesource.ignoresArmor() && this.random.nextInt(10) != 0 && tryEscape(damagesource == DamageSource.DROWN ? EndermanEscapeEvent.Reason.DROWN : EndermanEscapeEvent.Reason.CRITICAL_HIT)) { // Paper + this.dm(); + } + + return flag; + } + } + + public boolean do_() { + return ((Boolean) this.datawatcher.get(EntityEnderman.by)).booleanValue(); + } + + static { + EntityEnderman.c.add(Blocks.GRASS); + EntityEnderman.c.add(Blocks.DIRT); + EntityEnderman.c.add(Blocks.SAND); + EntityEnderman.c.add(Blocks.GRAVEL); + EntityEnderman.c.add(Blocks.YELLOW_FLOWER); + EntityEnderman.c.add(Blocks.RED_FLOWER); + EntityEnderman.c.add(Blocks.BROWN_MUSHROOM); + EntityEnderman.c.add(Blocks.RED_MUSHROOM); + EntityEnderman.c.add(Blocks.TNT); + EntityEnderman.c.add(Blocks.CACTUS); + EntityEnderman.c.add(Blocks.CLAY); + EntityEnderman.c.add(Blocks.PUMPKIN); + EntityEnderman.c.add(Blocks.MELON_BLOCK); + EntityEnderman.c.add(Blocks.MYCELIUM); + EntityEnderman.c.add(Blocks.NETHERRACK); + } + + static class PathfinderGoalEndermanPickupBlock extends PathfinderGoal { + + private final EntityEnderman enderman; + + public PathfinderGoalEndermanPickupBlock(EntityEnderman entityenderman) { + this.enderman = entityenderman; + } + + public boolean a() { + return this.enderman.getCarried() != null ? false : (!this.enderman.world.getGameRules().getBoolean("mobGriefing") ? false : this.enderman.getRandom().nextInt(20) == 0); + } + + public void e() { + Random random = this.enderman.getRandom(); + World world = this.enderman.world; + int i = MathHelper.floor(this.enderman.locX - 2.0D + random.nextDouble() * 4.0D); + int j = MathHelper.floor(this.enderman.locY + random.nextDouble() * 3.0D); + int k = MathHelper.floor(this.enderman.locZ - 2.0D + random.nextDouble() * 4.0D); + BlockPosition blockposition = new BlockPosition(i, j, k); + IBlockData iblockdata = world.getTypeIfLoaded(blockposition); // NeonPaper + if (iblockdata == null) return; // NeonPaper + Block block = iblockdata.getBlock(); + MovingObjectPosition movingobjectposition = world.rayTrace(new Vec3D((double) ((float) MathHelper.floor(this.enderman.locX) + 0.5F), (double) ((float) j + 0.5F), (double) ((float) MathHelper.floor(this.enderman.locZ) + 0.5F)), new Vec3D((double) ((float) i + 0.5F), (double) ((float) j + 0.5F), (double) ((float) k + 0.5F)), false, true, false); + boolean flag = movingobjectposition != null && movingobjectposition.a().equals(blockposition); + + if (EntityEnderman.c.contains(block) && flag) { + // CraftBukkit start - Pickup event + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.enderman, this.enderman.world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), org.bukkit.Material.AIR).isCancelled()) { + this.enderman.setCarried(iblockdata); + world.setAir(blockposition); + } + // CraftBukkit end + } + + } + } + + static class PathfinderGoalEndermanPlaceBlock extends PathfinderGoal { + + private final EntityEnderman a; + + public PathfinderGoalEndermanPlaceBlock(EntityEnderman entityenderman) { + this.a = entityenderman; + } + + public boolean a() { + return this.a.getCarried() == null ? false : (!this.a.world.getGameRules().getBoolean("mobGriefing") ? false : this.a.getRandom().nextInt(2000) == 0); + } + + public void e() { + Random random = this.a.getRandom(); + World world = this.a.world; + int i = MathHelper.floor(this.a.locX - 1.0D + random.nextDouble() * 2.0D); + int j = MathHelper.floor(this.a.locY + random.nextDouble() * 2.0D); + int k = MathHelper.floor(this.a.locZ - 1.0D + random.nextDouble() * 2.0D); + BlockPosition blockposition = new BlockPosition(i, j, k); + IBlockData iblockdata = world.getTypeIfLoaded(blockposition); // NeonPaper + if (iblockdata == null) return; // NeonPaper + IBlockData iblockdata1 = world.getType(blockposition.down()); + IBlockData iblockdata2 = this.a.getCarried(); + + if (iblockdata2 != null && this.a(world, blockposition, iblockdata2.getBlock(), iblockdata, iblockdata1)) { + // CraftBukkit start - Place event + if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.a, blockposition, this.a.getCarried().getBlock(), this.a.getCarried().getBlock().toLegacyData(this.a.getCarried())).isCancelled()) { + world.setTypeAndData(blockposition, iblockdata2, 3); + this.a.setCarried((IBlockData) null); + } + // CraftBukkit end + } + + } + + private boolean a(World world, BlockPosition blockposition, Block block, IBlockData iblockdata, IBlockData iblockdata1) { + return !block.canPlace(world, blockposition) ? false : (iblockdata.getMaterial() != Material.AIR ? false : (iblockdata1.getMaterial() == Material.AIR ? false : iblockdata1.g())); + } + } + + static class PathfinderGoalPlayerWhoLookedAtTarget extends PathfinderGoalNearestAttackableTarget { + + private final EntityEnderman i; public EntityEnderman getEnderman() { return i; } // Paper - OBFHELPER + private EntityHuman j; + private int k; + private int l; + + public PathfinderGoalPlayerWhoLookedAtTarget(EntityEnderman entityenderman) { + super(entityenderman, EntityHuman.class, false); + this.i = entityenderman; + } + + public boolean a() { + double d0 = this.i(); + + this.j = this.i.world.a(this.i.locX, this.i.locY, this.i.locZ, d0, d0, (Function) null, new Predicate() { + public boolean a(@Nullable EntityHuman entityhuman) { + return entityhuman != null && PathfinderGoalPlayerWhoLookedAtTarget.this.i.f(entityhuman); + } + + public boolean apply(@Nullable Object object) { + return this.a((EntityHuman) object); + } + }); + return this.j != null; + } + + public void c() { + this.k = 5; + this.l = 0; + } + + public void d() { + this.j = null; + super.d(); + } + + public boolean b() { + if (this.j != null) { + if (!this.i.f(this.j)) { + return false; + } else { + this.i.a((Entity) this.j, 10.0F, 10.0F); + return true; + } + } else { + return this.d != null && ((EntityHuman) this.d).isAlive() ? true : super.b(); + } + } + + public void e() { + if (this.j != null) { + if (--this.k <= 0) { + this.d = this.j; + this.j = null; + super.c(); + } + } else { + if (this.d != null) { + if (this.i.f((EntityHuman) this.d)) { + if (((EntityHuman) this.d).h(this.i) < 16.0D && this.getEnderman().tryEscape(EndermanEscapeEvent.Reason.STARE)) { // Paper + this.i.dm(); + } + + this.l = 0; + } else if (((EntityHuman) this.d).h(this.i) > 256.0D && this.l++ >= 30 && this.i.a((Entity) this.d)) { + this.l = 0; + } + } + + super.e(); + } + + } + } +} diff --git a/sources/src/main/java/net/minecraft/server/EntityFishingHook.java b/sources/src/main/java/net/minecraft/server/EntityFishingHook.java new file mode 100644 index 000000000..7356fa6a5 --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/EntityFishingHook.java @@ -0,0 +1,538 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +// CraftBukkit start +import org.bukkit.entity.Player; +import org.bukkit.entity.Fish; +import org.bukkit.event.player.PlayerFishEvent; +// CraftBukkit end + +public class EntityFishingHook extends Entity { + + private static final DataWatcherObject b = DataWatcher.a(EntityFishingHook.class, DataWatcherRegistry.b); + private boolean isInGround; + private int d; + public EntityHuman owner; + private int f; + private int g; + private int h; + private int at; + private float au; + public Entity hooked; + private EntityFishingHook.HookState av; + private int aw; + private int ax; + + public EntityFishingHook(World world, EntityHuman entityhuman) { + super(world); + this.av = EntityFishingHook.HookState.FLYING; + this.a(entityhuman); + this.n(); + } + + private void a(EntityHuman entityhuman) { + this.setSize(0.25F, 0.25F); + this.ah = true; + this.owner = entityhuman; + this.owner.hookedFish = this; + } + + public void a(int i) { + this.ax = i; + } + + public void c(int i) { + this.aw = i; + } + + private void n() { + float f = this.owner.lastPitch + (this.owner.pitch - this.owner.lastPitch); + float f1 = this.owner.lastYaw + (this.owner.yaw - this.owner.lastYaw); + float f2 = MathHelper.cos(-f1 * 0.017453292F - 3.1415927F); + float f3 = MathHelper.sin(-f1 * 0.017453292F - 3.1415927F); + float f4 = -MathHelper.cos(-f * 0.017453292F); + float f5 = MathHelper.sin(-f * 0.017453292F); + double d0 = this.owner.lastX + (this.owner.locX - this.owner.lastX) - (double) f3 * 0.3D; + double d1 = this.owner.lastY + (this.owner.locY - this.owner.lastY) + (double) this.owner.getHeadHeight(); + double d2 = this.owner.lastZ + (this.owner.locZ - this.owner.lastZ) - (double) f2 * 0.3D; + + this.setPositionRotation(d0, d1, d2, f1, f); + this.motX = (double) (-f3); + this.motY = (double) MathHelper.a(-(f5 / f4), -5.0F, 5.0F); + this.motZ = (double) (-f2); + float f6 = MathHelper.sqrt(this.motX * this.motX + this.motY * this.motY + this.motZ * this.motZ); + + this.motX *= 0.6D / (double) f6 + 0.5D + this.random.nextGaussian() * 0.0045D; + this.motY *= 0.6D / (double) f6 + 0.5D + this.random.nextGaussian() * 0.0045D; + this.motZ *= 0.6D / (double) f6 + 0.5D + this.random.nextGaussian() * 0.0045D; + float f7 = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + this.yaw = (float) (MathHelper.c(this.motX, this.motZ) * 57.2957763671875D); + this.pitch = (float) (MathHelper.c(this.motY, (double) f7) * 57.2957763671875D); + this.lastYaw = this.yaw; + this.lastPitch = this.pitch; + } + + protected void i() { + this.getDataWatcher().register(EntityFishingHook.b, Integer.valueOf(0)); + } + + public void a(DataWatcherObject datawatcherobject) { + if (EntityFishingHook.b.equals(datawatcherobject)) { + int i = ((Integer) this.getDataWatcher().get(EntityFishingHook.b)).intValue(); + + this.hooked = i > 0 ? this.world.getEntity(i - 1) : null; + } + + super.a(datawatcherobject); + } + + public void B_() { + super.B_(); + if (this.owner == null) { + this.die(); + } else if (this.world.isClientSide || !this.p()) { + if (this.isInGround) { + ++this.d; + if (this.d >= 1200) { + this.die(); + return; + } + } + + float f = 0.0F; + BlockPosition blockposition = new BlockPosition(this); + IBlockData iblockdata = this.world.getType(blockposition); + + if (iblockdata.getMaterial() == Material.WATER) { + f = BlockFluids.g(iblockdata, this.world, blockposition); + } + + double d0; + + if (this.av == EntityFishingHook.HookState.FLYING) { + if (this.hooked != null) { + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + this.av = EntityFishingHook.HookState.HOOKED_IN_ENTITY; + return; + } + + if (f > 0.0F) { + this.motX *= 0.3D; + this.motY *= 0.2D; + this.motZ *= 0.3D; + this.av = EntityFishingHook.HookState.BOBBING; + return; + } + + if (!this.world.isClientSide) { + this.r(); + } + + if (!this.isInGround && !this.onGround && !this.positionChanged) { + ++this.f; + } else { + this.f = 0; + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + } + } else { + if (this.av == EntityFishingHook.HookState.HOOKED_IN_ENTITY) { + if (this.hooked != null) { + if (this.hooked.dead) { + this.hooked = null; + this.av = EntityFishingHook.HookState.FLYING; + } else { + this.locX = this.hooked.locX; + double d1 = (double) this.hooked.length; + + this.locY = this.hooked.getBoundingBox().b + d1 * 0.8D; + this.locZ = this.hooked.locZ; + this.setPosition(this.locX, this.locY, this.locZ); + if (this.ak) this.die(); // NeonPaper - Prevent going through portals + } + } + + return; + } + + if (this.av == EntityFishingHook.HookState.BOBBING) { + this.motX *= 0.9D; + this.motZ *= 0.9D; + d0 = this.locY + this.motY - (double) blockposition.getY() - (double) f; + if (Math.abs(d0) < 0.01D) { + d0 += Math.signum(d0) * 0.1D; + } + + this.motY -= d0 * (double) this.random.nextFloat() * 0.2D; + if (!this.world.isClientSide && f > 0.0F) { + this.a(blockposition); + } + } + } + + if (iblockdata.getMaterial() != Material.WATER) { + this.motY -= 0.03D; + } + + this.move(EnumMoveType.SELF, this.motX, this.motY, this.motZ); + this.q(); + d0 = 0.92D; + this.motX *= 0.92D; + this.motY *= 0.92D; + this.motZ *= 0.92D; + this.setPosition(this.locX, this.locY, this.locZ); + + // Paper start - These shouldn't be going through portals + if (this.inPortal()) { + this.die(); + } + // Paper end + } + } + + private boolean p() { + ItemStack itemstack = this.owner.getItemInMainHand(); + ItemStack itemstack1 = this.owner.getItemInOffHand(); + boolean flag = itemstack.getItem() == Items.FISHING_ROD; + boolean flag1 = itemstack1.getItem() == Items.FISHING_ROD; + + if (!this.owner.dead && this.owner.isAlive() && (flag || flag1) && this.h(this.owner) <= 1024.0D) { + return false; + } else { + this.die(); + return true; + } + } + + private void q() { + float f = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); + + this.yaw = (float) (MathHelper.c(this.motX, this.motZ) * 57.2957763671875D); + + for (this.pitch = (float) (MathHelper.c(this.motY, (double) f) * 57.2957763671875D); this.pitch - this.lastPitch < -180.0F; this.lastPitch -= 360.0F) { + ; + } + + while (this.pitch - this.lastPitch >= 180.0F) { + this.lastPitch += 360.0F; + } + + while (this.yaw - this.lastYaw < -180.0F) { + this.lastYaw -= 360.0F; + } + + while (this.yaw - this.lastYaw >= 180.0F) { + this.lastYaw += 360.0F; + } + + this.pitch = this.lastPitch + (this.pitch - this.lastPitch) * 0.2F; + this.yaw = this.lastYaw + (this.yaw - this.lastYaw) * 0.2F; + } + + private void r() { + Vec3D vec3d = new Vec3D(this.locX, this.locY, this.locZ); + Vec3D vec3d1 = new Vec3D(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + MovingObjectPosition movingobjectposition = this.world.rayTrace(vec3d, vec3d1, false, true, false); + + vec3d = new Vec3D(this.locX, this.locY, this.locZ); + vec3d1 = new Vec3D(this.locX + this.motX, this.locY + this.motY, this.locZ + this.motZ); + + // Paper start - Call ProjectileCollideEvent + if (movingobjectposition != null && movingobjectposition.entity != null) { + com.destroystokyo.paper.event.entity.ProjectileCollideEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileCollideEvent(this, movingobjectposition); + if (event.isCancelled()) { + movingobjectposition = null; + } + } + // Paper end + + if (movingobjectposition != null) { + vec3d1 = new Vec3D(movingobjectposition.pos.x, movingobjectposition.pos.y, movingobjectposition.pos.z); + } + + Entity entity = null; + List list = this.world.getEntities(this, this.getBoundingBox().b(this.motX, this.motY, this.motZ).g(1.0D)); + double d0 = 0.0D; + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { + Entity entity1 = (Entity) iterator.next(); + + if (this.a(entity1) && (entity1 != this.owner || this.f >= 5)) { + AxisAlignedBB axisalignedbb = entity1.getBoundingBox().g(0.30000001192092896D); + MovingObjectPosition movingobjectposition1 = axisalignedbb.b(vec3d, vec3d1); + + if (movingobjectposition1 != null) { + double d1 = vec3d.distanceSquared(movingobjectposition1.pos); + + if (d1 < d0 || d0 == 0.0D) { + entity = entity1; + d0 = d1; + } + } + } + } + + if (entity != null) { + movingobjectposition = new MovingObjectPosition(entity); + } + + if (movingobjectposition != null && movingobjectposition.type != MovingObjectPosition.EnumMovingObjectType.MISS) { + org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this, movingobjectposition); // Craftbukkit - Call event + if (movingobjectposition.type == MovingObjectPosition.EnumMovingObjectType.ENTITY) { + this.hooked = movingobjectposition.entity; + this.s(); + } else { + this.isInGround = true; + } + } + + } + + private void s() { + this.getDataWatcher().set(EntityFishingHook.b, Integer.valueOf(this.hooked.getId() + 1)); + } + + private void a(BlockPosition blockposition) { + WorldServer worldserver = (WorldServer) this.world; + int i = 1; + BlockPosition blockposition1 = blockposition.up(); + + if (this.random.nextFloat() < 0.25F && this.world.isRainingAt(blockposition1)) { + ++i; + } + + if (this.random.nextFloat() < 0.5F && !this.world.h(blockposition1)) { + --i; + } + + if (this.g > 0) { + --this.g; + if (this.g <= 0) { + this.h = 0; + this.at = 0; + // CraftBukkit start + PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.FAILED_ATTEMPT); + this.world.getServer().getPluginManager().callEvent(playerFishEvent); + // CraftBukkit end + } else { + this.motY -= 0.2D * (double) this.random.nextFloat() * (double) this.random.nextFloat(); + } + } else { + float f; + float f1; + float f2; + double d0; + double d1; + double d2; + Block block; + + if (this.at > 0) { + this.at -= i; + if (this.at > 0) { + this.au = (float) ((double) this.au + this.random.nextGaussian() * 4.0D); + f = this.au * 0.017453292F; + f1 = MathHelper.sin(f); + f2 = MathHelper.cos(f); + d0 = this.locX + (double) (f1 * (float) this.at * 0.1F); + d1 = (double) ((float) MathHelper.floor(this.getBoundingBox().b) + 1.0F); + d2 = this.locZ + (double) (f2 * (float) this.at * 0.1F); + block = worldserver.getType(new BlockPosition(d0, d1 - 1.0D, d2)).getBlock(); + if (block == Blocks.WATER || block == Blocks.FLOWING_WATER) { + if (this.random.nextFloat() < 0.15F) { + worldserver.a(EnumParticle.WATER_BUBBLE, d0, d1 - 0.10000000149011612D, d2, 1, (double) f1, 0.1D, (double) f2, 0.0D, new int[0]); + } + + float f3 = f1 * 0.04F; + float f4 = f2 * 0.04F; + + worldserver.a(EnumParticle.WATER_WAKE, d0, d1, d2, 0, (double) f4, 0.01D, (double) (-f3), 1.0D, new int[0]); + worldserver.a(EnumParticle.WATER_WAKE, d0, d1, d2, 0, (double) (-f4), 0.01D, (double) f3, 1.0D, new int[0]); + } + } else { + // CraftBukkit start + PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.BITE); + this.world.getServer().getPluginManager().callEvent(playerFishEvent); + if (playerFishEvent.isCancelled()) { + return; + } + // CraftBukkit end + this.motY = (double) (-0.4F * MathHelper.a(this.random, 0.6F, 1.0F)); + this.a(SoundEffects.K, 0.25F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F); + double d3 = this.getBoundingBox().b + 0.5D; + + worldserver.a(EnumParticle.WATER_BUBBLE, this.locX, d3, this.locZ, (int) (1.0F + this.width * 20.0F), (double) this.width, 0.0D, (double) this.width, 0.20000000298023224D, new int[0]); + worldserver.a(EnumParticle.WATER_WAKE, this.locX, d3, this.locZ, (int) (1.0F + this.width * 20.0F), (double) this.width, 0.0D, (double) this.width, 0.20000000298023224D, new int[0]); + this.g = MathHelper.nextInt(this.random, 20, 40); + } + } else if (this.h > 0) { + this.h -= i; + f = 0.15F; + if (this.h < 20) { + f = (float) ((double) f + (double) (20 - this.h) * 0.05D); + } else if (this.h < 40) { + f = (float) ((double) f + (double) (40 - this.h) * 0.02D); + } else if (this.h < 60) { + f = (float) ((double) f + (double) (60 - this.h) * 0.01D); + } + + if (this.random.nextFloat() < f) { + f1 = MathHelper.a(this.random, 0.0F, 360.0F) * 0.017453292F; + f2 = MathHelper.a(this.random, 25.0F, 60.0F); + d0 = this.locX + (double) (MathHelper.sin(f1) * f2 * 0.1F); + d1 = (double) ((float) MathHelper.floor(this.getBoundingBox().b) + 1.0F); + d2 = this.locZ + (double) (MathHelper.cos(f1) * f2 * 0.1F); + block = worldserver.getType(new BlockPosition((int) d0, (int) d1 - 1, (int) d2)).getBlock(); + if (block == Blocks.WATER || block == Blocks.FLOWING_WATER) { + worldserver.a(EnumParticle.WATER_SPLASH, d0, d1, d2, 2 + this.random.nextInt(2), 0.10000000149011612D, 0.0D, 0.10000000149011612D, 0.0D, new int[0]); + } + } + + if (this.h <= 0) { + this.au = MathHelper.a(this.random, 0.0F, 360.0F); + this.at = MathHelper.nextInt(this.random, 20, 80); + } + } else { + this.h = MathHelper.nextInt(this.random, world.paperConfig.fishingMinTicks, world.paperConfig.fishingMaxTicks); // Paper + this.h -= this.ax * 20 * 5; + } + } + + } + + protected boolean a(Entity entity) { + return entity.isInteractable() || entity instanceof EntityItem; + } + + public void b(NBTTagCompound nbttagcompound) {} + + public void a(NBTTagCompound nbttagcompound) {} + + public int j() { + if (!this.world.isClientSide && this.owner != null) { + int i = 0; + + if (this.hooked != null) { + // CraftBukkit start + PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), this.hooked.getBukkitEntity(), (Fish) this.getBukkitEntity(), PlayerFishEvent.State.CAUGHT_ENTITY); + this.world.getServer().getPluginManager().callEvent(playerFishEvent); + + if (playerFishEvent.isCancelled()) { + return 0; + } + // CraftBukkit end + this.k(); + this.world.broadcastEntityEffect(this, (byte) 31); + i = this.hooked instanceof EntityItem ? 3 : 5; + } else if (this.g > 0) { + LootTableInfo.a loottableinfo_a = new LootTableInfo.a((WorldServer) this.world); + + loottableinfo_a.a((float) this.aw + this.owner.du()); + Iterator iterator = this.world.getLootTableRegistry().a(LootTables.aA).a(this.random, loottableinfo_a.a()).iterator(); + + while (iterator.hasNext()) { + ItemStack itemstack = (ItemStack) iterator.next(); + EntityItem entityitem = new EntityItem(this.world, this.locX, this.locY, this.locZ, itemstack); + // CraftBukkit start + PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), entityitem.getBukkitEntity(), (Fish) this.getBukkitEntity(), PlayerFishEvent.State.CAUGHT_FISH); + playerFishEvent.setExpToDrop(this.random.nextInt(6) + 1); + this.world.getServer().getPluginManager().callEvent(playerFishEvent); + + if (playerFishEvent.isCancelled()) { + return 0; + } + // CraftBukkit end + double d0 = this.owner.locX - this.locX; + double d1 = this.owner.locY - this.locY; + double d2 = this.owner.locZ - this.locZ; + double d3 = (double) MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2); + double d4 = 0.1D; + + entityitem.motX = d0 * 0.1D; + entityitem.motY = d1 * 0.1D + (double) MathHelper.sqrt(d3) * 0.08D; + entityitem.motZ = d2 * 0.1D; + this.world.addEntity(entityitem); + // CraftBukkit start - this.random.nextInt(6) + 1 -> playerFishEvent.getExpToDrop() + if (playerFishEvent.getExpToDrop() > 0) { + this.owner.world.addEntity(new EntityExperienceOrb(this.owner.world, this.owner.locX, this.owner.locY + 0.5D, this.owner.locZ + 0.5D, playerFishEvent.getExpToDrop(), org.bukkit.entity.ExperienceOrb.SpawnReason.FISHING, this.owner, this)); // Paper + } + // CraftBukkit end + Item item = itemstack.getItem(); + + if (item == Items.FISH || item == Items.COOKED_FISH) { + this.owner.a(StatisticList.E, 1); + } + } + + i = 1; + } + + if (this.isInGround) { + // CraftBukkit start + PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.IN_GROUND); + this.world.getServer().getPluginManager().callEvent(playerFishEvent); + + if (playerFishEvent.isCancelled()) { + return 0; + } + // CraftBukkit end + i = 2; + } + // CraftBukkit start + if (i == 0) { + PlayerFishEvent playerFishEvent = new PlayerFishEvent((Player) this.owner.getBukkitEntity(), null, (Fish) this.getBukkitEntity(), PlayerFishEvent.State.FAILED_ATTEMPT); + this.world.getServer().getPluginManager().callEvent(playerFishEvent); + if (playerFishEvent.isCancelled()) { + return 0; + } + } + // CraftBukkit end + + this.die(); + return i; + } else { + return 0; + } + } + + protected void k() { + if (this.owner != null) { + double d0 = this.owner.locX - this.locX; + double d1 = this.owner.locY - this.locY; + double d2 = this.owner.locZ - this.locZ; + double d3 = 0.1D; + + this.hooked.motX += d0 * 0.1D; + this.hooked.motY += d1 * 0.1D; + this.hooked.motZ += d2 * 0.1D; + } + } + + protected boolean playStepSound() { + return false; + } + + public void die() { + super.die(); + if (this.owner != null) { + this.owner.hookedFish = null; + } + + } + + public EntityHuman l() { + return this.owner; + } + + static enum HookState { + + FLYING, HOOKED_IN_ENTITY, BOBBING; + + private HookState() {} + } +} diff --git a/sources/src/main/java/net/minecraft/server/EntityMushroomCow.java b/sources/src/main/java/net/minecraft/server/EntityMushroomCow.java new file mode 100644 index 000000000..b2c458c47 --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/EntityMushroomCow.java @@ -0,0 +1,84 @@ +package net.minecraft.server; + +import javax.annotation.Nullable; +import org.bukkit.event.player.PlayerShearEntityEvent; // CraftBukkit + +public class EntityMushroomCow extends EntityCow { + + public EntityMushroomCow(World world) { + super(world); + this.setSize(0.9F, 1.4F); + this.bA = Blocks.MYCELIUM; + } + + public static void c(DataConverterManager dataconvertermanager) { + EntityInsentient.a(dataconvertermanager, EntityMushroomCow.class); + } + + public boolean a(EntityHuman entityhuman, EnumHand enumhand) { + ItemStack itemstack = entityhuman.b(enumhand); + + if (itemstack.getItem() == Items.BOWL && this.getAge() >= 0 && !entityhuman.abilities.canInstantlyBuild) { + itemstack.subtract(1); + if (itemstack.isEmpty()) { + entityhuman.a(enumhand, new ItemStack(Items.MUSHROOM_STEW)); + } else if (!entityhuman.inventory.pickup(new ItemStack(Items.MUSHROOM_STEW))) { + entityhuman.drop(new ItemStack(Items.MUSHROOM_STEW), false); + } + + return true; + } else if (itemstack.getItem() == Items.SHEARS && this.getAge() >= 0) { + if (this.dead) return false; // Reaper - Fix cow dupe + // CraftBukkit start + PlayerShearEntityEvent event = new PlayerShearEntityEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), this.getBukkitEntity()); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return false; + } + // CraftBukkit end + this.die(); + this.world.addParticle(EnumParticle.EXPLOSION_LARGE, this.locX, this.locY + (double) (this.length / 2.0F), this.locZ, 0.0D, 0.0D, 0.0D, new int[0]); + if (!this.world.isClientSide) { + EntityCow entitycow = new EntityCow(this.world); + + entitycow.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, this.pitch); + entitycow.setHealth(this.getHealth()); + entitycow.aN = this.aN; + if (this.hasCustomName()) { + entitycow.setCustomName(this.getCustomName()); + } + + this.world.addEntity(entitycow); + + for (int i = 0; i < 5; ++i) { + this.world.addEntity(new EntityItem(this.world, this.locX, this.locY + (double) this.length, this.locZ, new ItemStack(Blocks.RED_MUSHROOM))); + } + + itemstack.damage(1, entityhuman); + this.a(SoundEffects.ei, 1.0F, 1.0F); + } + + return true; + } else { + return super.a(entityhuman, enumhand); + } + } + + public EntityMushroomCow c(EntityAgeable entityageable) { + return new EntityMushroomCow(this.world); + } + + @Nullable + protected MinecraftKey J() { + return LootTables.M; + } + + public EntityCow b(EntityAgeable entityageable) { + return this.c(entityageable); + } + + public EntityAgeable createChild(EntityAgeable entityageable) { + return this.c(entityageable); + } +} diff --git a/sources/src/main/java/net/minecraft/server/EntityPlayer.java b/sources/src/main/java/net/minecraft/server/EntityPlayer.java index 867b9e403..489421683 100644 --- a/sources/src/main/java/net/minecraft/server/EntityPlayer.java +++ b/sources/src/main/java/net/minecraft/server/EntityPlayer.java @@ -784,7 +784,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { protected void a(double d0, boolean flag, IBlockData iblockdata, BlockPosition blockposition) {} protected void b(BlockPosition blockposition) { - if (!this.isSpectator()) { + if (valid && (!this.isSpectator() || this.world.isLoaded(new BlockPosition(this)))) { // Dionysus - don't tick dead players that are not in the world currently (pending respawn) super.b(blockposition); } @@ -1156,6 +1156,7 @@ public class EntityPlayer extends EntityHuman implements ICrafting { this.getDataWatcher().set(EntityPlayer.br, entityplayer.getDataWatcher().get(EntityPlayer.br)); this.lastSentExp = -1; this.lastHealthSent = -1.0F; + setSneaking(false); // NeonPaper - fix MC-10657 this.ch = -1; // this.cr.a((RecipeBook) entityplayer.cr); // CraftBukkit // Paper start - Optimize remove queue diff --git a/sources/src/main/java/net/minecraft/server/EntityTrackerEntry.java b/sources/src/main/java/net/minecraft/server/EntityTrackerEntry.java new file mode 100644 index 000000000..2eff574d7 --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/EntityTrackerEntry.java @@ -0,0 +1,654 @@ +package net.minecraft.server; + +import com.google.common.collect.Sets; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// CraftBukkit start +import org.bukkit.entity.Player; +import org.bukkit.event.player.PlayerVelocityEvent; +// CraftBukkit end + +public class EntityTrackerEntry { + + private static final Logger c = LogManager.getLogger(); + private final Entity tracker; + private final int e; + private int f; + private final int g; + private long xLoc; + private long yLoc; + private long zLoc; + private int yRot; + private int xRot; + private int headYaw; + private double n; + private double o; + private double p; + public int a; + private double q; + private double r; + private double s; + private boolean isMoving; + private final boolean u; + private int v; + private List w = Collections.emptyList(); + private boolean x; + private boolean y; + public boolean b; + // Paper start + // Replace trackedPlayers Set with a Map. The value is true until the player receives + // their first update (which is forced to have absolute coordinates), false afterward. + public java.util.Map trackedPlayerMap = new java.util.HashMap(); + public Set trackedPlayers = trackedPlayerMap.keySet(); + // Paper end + + public EntityTrackerEntry(Entity entity, int i, int j, int k, boolean flag) { + entity.tracker = this; // Paper + this.tracker = entity; + this.e = i; + this.f = j; + this.g = k; + this.u = flag; + this.xLoc = EntityTracker.a(entity.locX); + this.yLoc = EntityTracker.a(entity.locY); + this.zLoc = EntityTracker.a(entity.locZ); + this.yRot = MathHelper.d(entity.yaw * 256.0F / 360.0F); + this.xRot = MathHelper.d(entity.pitch * 256.0F / 360.0F); + this.headYaw = MathHelper.d(entity.getHeadRotation() * 256.0F / 360.0F); + this.y = entity.onGround; + } + + public boolean equals(Object object) { + return object instanceof EntityTrackerEntry ? ((EntityTrackerEntry) object).tracker.getId() == this.tracker.getId() : false; + } + + public int hashCode() { + return this.tracker.getId(); + } + + public void track(List list) { + this.b = false; + if (!this.isMoving || this.tracker.d(this.q, this.r, this.s) > 16.0D) { + this.q = this.tracker.locX; + this.r = this.tracker.locY; + this.s = this.tracker.locZ; + this.isMoving = true; + this.b = true; + this.scanPlayers(list); + } + + List list1 = this.tracker.bF(); + + if (!list1.equals(this.w)) { + this.w = list1; + this.broadcastIncludingSelf(new PacketPlayOutMount(this.tracker)); // CraftBukkit + } + + // PAIL : rename + if (this.tracker instanceof EntityItemFrame && this.a % 20 == 0) { // Paper + EntityItemFrame entityitemframe = (EntityItemFrame) this.tracker; + ItemStack itemstack = entityitemframe.getItem(); + + if (itemstack != null && itemstack.getItem() instanceof ItemWorldMap) { // Paper - moved back up + WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, this.tracker.world); + Iterator iterator = this.trackedPlayers.iterator(); // CraftBukkit + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + EntityPlayer entityplayer = (EntityPlayer) entityhuman; + + worldmap.a(entityplayer, itemstack); + Packet packet = Items.FILLED_MAP.a(itemstack, this.tracker.world, (EntityHuman) entityplayer); + + if (packet != null) { + entityplayer.playerConnection.sendPacket(packet); + } + } + } + + this.d(); + } + + if (this.a % this.g == 0 || this.tracker.impulse || this.tracker.getDataWatcher().a()) { + int i; + + if (this.tracker.isPassenger()) { + i = MathHelper.d(this.tracker.yaw * 256.0F / 360.0F); + int j = MathHelper.d(this.tracker.pitch * 256.0F / 360.0F); + boolean flag = Math.abs(i - this.yRot) >= 1 || Math.abs(j - this.xRot) >= 1; + + if (flag) { + this.broadcast(new PacketPlayOutEntity.PacketPlayOutEntityLook(this.tracker.getId(), (byte) i, (byte) j, this.tracker.onGround)); + this.yRot = i; + this.xRot = j; + } + + this.xLoc = EntityTracker.a(this.tracker.locX); + this.yLoc = EntityTracker.a(this.tracker.locY); + this.zLoc = EntityTracker.a(this.tracker.locZ); + this.d(); + this.x = true; + } else { + ++this.v; + long k = EntityTracker.a(this.tracker.locX); + long l = EntityTracker.a(this.tracker.locY); + long i1 = EntityTracker.a(this.tracker.locZ); + int j1 = MathHelper.d(this.tracker.yaw * 256.0F / 360.0F); + int k1 = MathHelper.d(this.tracker.pitch * 256.0F / 360.0F); + long l1 = k - this.xLoc; + long i2 = l - this.yLoc; + long j2 = i1 - this.zLoc; + Object object = null; + boolean flag1 = l1 * l1 + i2 * i2 + j2 * j2 >= 128L || this.a % 60 == 0; + boolean flag2 = Math.abs(j1 - this.yRot) >= 1 || Math.abs(k1 - this.xRot) >= 1; + + if (this.a > 0 || this.tracker instanceof EntityArrow) { // Paper - Moved up + // CraftBukkit start - Code moved from below + if (flag1) { + this.xLoc = k; + this.yLoc = l; + this.zLoc = i1; + } + + if (flag2) { + this.yRot = j1; + this.xRot = k1; + } + // CraftBukkit end + + if (l1 >= -32768L && l1 < 32768L && i2 >= -32768L && i2 < 32768L && j2 >= -32768L && j2 < 32768L && this.v <= 400 && !this.x && this.y == this.tracker.onGround) { + if ((!flag1 || !flag2) && !(this.tracker instanceof EntityArrow)) { + if (flag1) { + object = new PacketPlayOutEntity.PacketPlayOutRelEntityMove(this.tracker.getId(), l1, i2, j2, this.tracker.onGround); + } else if (flag2) { + object = new PacketPlayOutEntity.PacketPlayOutEntityLook(this.tracker.getId(), (byte) j1, (byte) k1, this.tracker.onGround); + } + } else { + object = new PacketPlayOutEntity.PacketPlayOutRelEntityMoveLook(this.tracker.getId(), l1, i2, j2, (byte) j1, (byte) k1, this.tracker.onGround); + } + } else { + this.y = this.tracker.onGround; + this.v = 0; + // CraftBukkit start - Refresh list of who can see a player before sending teleport packet + if (this.tracker instanceof EntityPlayer) { + this.scanPlayers(new java.util.ArrayList(this.trackedPlayers)); + } + // CraftBukkit end + this.c(); + object = new PacketPlayOutEntityTeleport(this.tracker); + } + } + + boolean flag3 = this.u; + + if (this.tracker instanceof EntityLiving && ((EntityLiving) this.tracker).cP()) { + flag3 = true; + } + + if (flag3 && this.a > 0) { + double d0 = this.tracker.motX - this.n; + double d1 = this.tracker.motY - this.o; + double d2 = this.tracker.motZ - this.p; + double d3 = 0.02D; + double d4 = d0 * d0 + d1 * d1 + d2 * d2; + + if (d4 > 4.0E-4D || d4 > 0.0D && this.tracker.motX == 0.0D && this.tracker.motY == 0.0D && this.tracker.motZ == 0.0D) { + this.n = this.tracker.motX; + this.o = this.tracker.motY; + this.p = this.tracker.motZ; + this.broadcast(new PacketPlayOutEntityVelocity(this.tracker.getId(), this.n, this.o, this.p)); + } + } + + if (object != null) { + // Paper start - ensure fresh viewers get an absolute position on their first update, + // since we can't be certain what position they received in the spawn packet. + if (object instanceof PacketPlayOutEntityTeleport) { + this.broadcast((Packet) object); + } else { + PacketPlayOutEntityTeleport teleportPacket = null; + + for (java.util.Map.Entry viewer : trackedPlayerMap.entrySet()) { + if (viewer.getValue()) { + viewer.setValue(false); + if (teleportPacket == null) { + teleportPacket = new PacketPlayOutEntityTeleport(this.tracker); + } + viewer.getKey().playerConnection.sendPacket(teleportPacket); + } else { + viewer.getKey().playerConnection.sendPacket((Packet) object); + } + } + } + // Paper end + } + + this.d(); + /* CraftBukkit start - Code moved up + if (flag1) { + this.xLoc = k; + this.yLoc = l; + this.zLoc = i1; + } + + if (flag2) { + this.yRot = j1; + this.xRot = k1; + } + // CraftBukkit end */ + + this.x = false; + } + + i = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F); + if (Math.abs(i - this.headYaw) >= 1) { + this.broadcast(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) i)); + this.headYaw = i; + } + + this.tracker.impulse = false; + } + + ++this.a; + if (this.tracker.velocityChanged) { + // CraftBukkit start - Create PlayerVelocity event + boolean cancelled = false; + + if (this.tracker instanceof EntityPlayer) { + Player player = (Player) this.tracker.getBukkitEntity(); + org.bukkit.util.Vector velocity = player.getVelocity(); + + PlayerVelocityEvent event = new PlayerVelocityEvent(player, velocity.clone()); + this.tracker.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + cancelled = true; + } else if (!velocity.equals(event.getVelocity())) { + player.setVelocity(event.getVelocity()); + } + } + + if (!cancelled) { + this.broadcastIncludingSelf(new PacketPlayOutEntityVelocity(this.tracker)); + } + // CraftBukkit end + this.tracker.velocityChanged = false; + } + + } + + private void d() { + DataWatcher datawatcher = this.tracker.getDataWatcher(); + + if (datawatcher.a()) { + this.broadcastIncludingSelf(new PacketPlayOutEntityMetadata(this.tracker.getId(), datawatcher, false)); + } + + if (this.tracker instanceof EntityLiving) { + AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.tracker).getAttributeMap(); + Set set = attributemapserver.getAttributes(); + + if (!set.isEmpty()) { + // CraftBukkit start - Send scaled max health + if (this.tracker instanceof EntityPlayer) { + ((EntityPlayer) this.tracker).getBukkitEntity().injectScaledMaxHealth(set, false); + } + // CraftBukkit end + this.broadcastIncludingSelf(new PacketPlayOutUpdateAttributes(this.tracker.getId(), set)); + } + + set.clear(); + } + + } + + public void broadcast(Packet packet) { + Iterator iterator = this.trackedPlayers.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + entityplayer.playerConnection.sendPacket(packet); + } + + } + + public void broadcastIncludingSelf(Packet packet) { + this.broadcast(packet); + if (this.tracker instanceof EntityPlayer) { + ((EntityPlayer) this.tracker).playerConnection.sendPacket(packet); + } + + } + + public void a() { + Iterator iterator = this.trackedPlayers.iterator(); + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); + + this.tracker.c(entityplayer); + entityplayer.c(this.tracker); + } + + } + + public void a(EntityPlayer entityplayer) { + if (this.trackedPlayers.contains(entityplayer)) { + this.tracker.c(entityplayer); + entityplayer.c(this.tracker); + this.trackedPlayers.remove(entityplayer); + } + + } + + public void updatePlayer(EntityPlayer entityplayer) { + org.spigotmc.AsyncCatcher.catchOp( "player tracker update"); // Spigot + if (entityplayer != this.tracker) { + if (this.c(entityplayer)) { + if (!this.trackedPlayers.contains(entityplayer) && (this.e(entityplayer) || this.tracker.attachedToPlayer)) { + // CraftBukkit start - respect vanish API + if (this.tracker instanceof EntityPlayer) { + Player player = ((EntityPlayer) this.tracker).getBukkitEntity(); + if (!entityplayer.getBukkitEntity().canSee(player)) { + return; + } + } + + entityplayer.removeQueue.remove(Integer.valueOf(this.tracker.getId())); + // CraftBukkit end + this.trackedPlayerMap.put(entityplayer, true); // Paper + Packet packet = this.e(); + + entityplayer.playerConnection.sendPacket(packet); + if (!this.tracker.getDataWatcher().d()) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityMetadata(this.tracker.getId(), this.tracker.getDataWatcher(), true)); + } + + boolean flag = this.u; + + if (this.tracker instanceof EntityLiving) { + AttributeMapServer attributemapserver = (AttributeMapServer) ((EntityLiving) this.tracker).getAttributeMap(); + Collection collection = attributemapserver.c(); + + // CraftBukkit start - If sending own attributes send scaled health instead of current maximum health + if (this.tracker.getId() == entityplayer.getId()) { + ((EntityPlayer) this.tracker).getBukkitEntity().injectScaledMaxHealth(collection, false); + } + // CraftBukkit end + + if (!collection.isEmpty()) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateAttributes(this.tracker.getId(), collection)); + } + + if (((EntityLiving) this.tracker).cP()) { + flag = true; + } + } + + this.n = this.tracker.motX; + this.o = this.tracker.motY; + this.p = this.tracker.motZ; + if (flag && !(packet instanceof PacketPlayOutSpawnEntityLiving)) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityVelocity(this.tracker.getId(), this.tracker.motX, this.tracker.motY, this.tracker.motZ)); + } + + if (this.tracker instanceof EntityLiving) { + EnumItemSlot[] aenumitemslot = EnumItemSlot.values(); + int i = aenumitemslot.length; + + for (int j = 0; j < i; ++j) { + EnumItemSlot enumitemslot = aenumitemslot[j]; + ItemStack itemstack = ((EntityLiving) this.tracker).getEquipment(enumitemslot); + + if (!itemstack.isEmpty()) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEquipment(this.tracker.getId(), enumitemslot, itemstack)); + } + } + } + + if (this.tracker instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) this.tracker; + + if (entityhuman.isSleeping()) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutBed(entityhuman, new BlockPosition(this.tracker))); + } + } + + if (this.tracker instanceof EntityLiving) { + // CraftBukkit start - Fix for nonsensical head yaw + this.headYaw = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F); + this.broadcast(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) headYaw)); + // CraftBukkit end + EntityLiving entityliving = (EntityLiving) this.tracker; + Iterator iterator = entityliving.getEffects().iterator(); + + while (iterator.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator.next(); + + entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEffect(this.tracker.getId(), mobeffect)); + } + } + + if (!this.tracker.bF().isEmpty()) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutMount(this.tracker)); + } + + if (this.tracker.isPassenger()) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutMount(this.tracker.bJ())); + } + + this.tracker.b(entityplayer); + entityplayer.d(this.tracker); + updatePassengers(entityplayer); // Paper + } + } else if (this.trackedPlayers.contains(entityplayer)) { + this.trackedPlayers.remove(entityplayer); + this.tracker.c(entityplayer); + entityplayer.c(this.tracker); + updatePassengers(entityplayer); // Paper + } + + } + } + + public boolean c(EntityPlayer entityplayer) { + // Paper start + if (tracker.isPassenger()) { + return isTrackedBy(tracker.getVehicle(), entityplayer); + } else if (hasPassengerInRange(tracker, entityplayer)) { + return true; + } + + return isInRangeOfPlayer(entityplayer); + } + private static boolean hasPassengerInRange(Entity entity, EntityPlayer entityplayer) { + if (!entity.isVehicle()) { + return false; + } + for (Entity passenger : entity.passengers) { + if (passenger.tracker != null && passenger.tracker.isInRangeOfPlayer(entityplayer)) { + return true; + } + if (passenger.isVehicle()) { + if (hasPassengerInRange(passenger, entityplayer)) { + return true; + } + } + } + return false; + } + private static boolean isTrackedBy(Entity entity, EntityPlayer entityplayer) { + return entity == entityplayer || entity.tracker != null && entity.tracker.trackedPlayers.contains(entityplayer); + } + private void updatePassengers(EntityPlayer player) { + if (tracker.isVehicle()) { + tracker.passengers.forEach((e) -> { + if (e.tracker != null) { + e.tracker.updatePlayer(player); + } + }); + player.playerConnection.sendPacket(new PacketPlayOutMount(this.tracker)); + } + } + private boolean isInRangeOfPlayer(EntityPlayer entityplayer) { + // Paper end + double d0 = entityplayer.locX - (double) this.xLoc / 4096.0D; + double d1 = entityplayer.locZ - (double) this.zLoc / 4096.0D; + int i = Math.min(this.e, this.f); + + return d0 >= (double) (-i) && d0 <= (double) i && d1 >= (double) (-i) && d1 <= (double) i && this.tracker.a(entityplayer); + } + + private boolean e(EntityPlayer entityplayer) { + return entityplayer.x().getPlayerChunkMap().a(entityplayer, this.tracker.ab, this.tracker.ad); + } + + public void scanPlayers(List list) { + for (int i = 0; i < list.size(); ++i) { + this.updatePlayer((EntityPlayer) list.get(i)); + } + + } + + private Packet e() { + if (this.tracker.dead) { + // CraftBukkit start - Remove useless error spam, just return + // EntityTrackerEntry.d.warn("Fetching addPacket for removed entity"); + return null; + // CraftBukkit end + } + + if (this.tracker instanceof EntityPlayer) { + return new PacketPlayOutNamedEntitySpawn((EntityHuman) this.tracker); + } else if (this.tracker instanceof IAnimal) { + this.headYaw = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F); + return new PacketPlayOutSpawnEntityLiving((EntityLiving) this.tracker); + } else if (this.tracker instanceof EntityPainting) { + return new PacketPlayOutSpawnEntityPainting((EntityPainting) this.tracker); + } else if (this.tracker instanceof EntityItem) { + return new PacketPlayOutSpawnEntity(this.tracker, 2, 1); + } else if (this.tracker instanceof EntityMinecartAbstract) { + EntityMinecartAbstract entityminecartabstract = (EntityMinecartAbstract) this.tracker; + + return new PacketPlayOutSpawnEntity(this.tracker, 10, entityminecartabstract.v().a()); + } else if (this.tracker instanceof EntityBoat) { + return new PacketPlayOutSpawnEntity(this.tracker, 1); + } else if (this.tracker instanceof EntityExperienceOrb) { + return new PacketPlayOutSpawnEntityExperienceOrb((EntityExperienceOrb) this.tracker); + } else if (this.tracker instanceof EntityFishingHook) { + EntityHuman entityhuman = ((EntityFishingHook) this.tracker).l(); + + return new PacketPlayOutSpawnEntity(this.tracker, 90, entityhuman == null ? this.tracker.getId() : entityhuman.getId()); + } else { + Entity entity; + + if (this.tracker instanceof EntitySpectralArrow) { + entity = ((EntitySpectralArrow) this.tracker).shooter; + return new PacketPlayOutSpawnEntity(this.tracker, 91, 1 + (entity == null ? this.tracker.getId() : entity.getId())); + } else if (this.tracker instanceof EntityTippedArrow) { + entity = ((EntityArrow) this.tracker).shooter; + return new PacketPlayOutSpawnEntity(this.tracker, 60, 1 + (entity == null ? this.tracker.getId() : entity.getId())); + } else if (this.tracker instanceof EntitySnowball) { + return new PacketPlayOutSpawnEntity(this.tracker, 61); + } else if (this.tracker instanceof EntityLlamaSpit) { + return new PacketPlayOutSpawnEntity(this.tracker, 68); + } else if (this.tracker instanceof EntityPotion) { + return new PacketPlayOutSpawnEntity(this.tracker, 73); + } else if (this.tracker instanceof EntityThrownExpBottle) { + return new PacketPlayOutSpawnEntity(this.tracker, 75); + } else if (this.tracker instanceof EntityEnderPearl) { + return new PacketPlayOutSpawnEntity(this.tracker, 65); + } else if (this.tracker instanceof EntityEnderSignal) { + return new PacketPlayOutSpawnEntity(this.tracker, 72); + } else if (this.tracker instanceof EntityFireworks) { + return new PacketPlayOutSpawnEntity(this.tracker, 76); + } else if (this.tracker instanceof EntityFireball) { + EntityFireball entityfireball = (EntityFireball) this.tracker; + PacketPlayOutSpawnEntity packetplayoutspawnentity = null; + byte b0 = 63; + + if (this.tracker instanceof EntitySmallFireball) { + b0 = 64; + } else if (this.tracker instanceof EntityDragonFireball) { + b0 = 93; + } else if (this.tracker instanceof EntityWitherSkull) { + b0 = 66; + } + + if (entityfireball.shooter != null) { + packetplayoutspawnentity = new PacketPlayOutSpawnEntity(this.tracker, b0, ((EntityFireball) this.tracker).shooter.getId()); + } else { + packetplayoutspawnentity = new PacketPlayOutSpawnEntity(this.tracker, b0, 0); + } + + packetplayoutspawnentity.a((int) (entityfireball.dirX * 8000.0D)); + packetplayoutspawnentity.b((int) (entityfireball.dirY * 8000.0D)); + packetplayoutspawnentity.c((int) (entityfireball.dirZ * 8000.0D)); + return packetplayoutspawnentity; + } else if (this.tracker instanceof EntityShulkerBullet) { + PacketPlayOutSpawnEntity packetplayoutspawnentity1 = new PacketPlayOutSpawnEntity(this.tracker, 67, 0); + + packetplayoutspawnentity1.a((int) (this.tracker.motX * 8000.0D)); + packetplayoutspawnentity1.b((int) (this.tracker.motY * 8000.0D)); + packetplayoutspawnentity1.c((int) (this.tracker.motZ * 8000.0D)); + return packetplayoutspawnentity1; + } else if (this.tracker instanceof EntityEgg) { + return new PacketPlayOutSpawnEntity(this.tracker, 62); + } else if (this.tracker instanceof EntityEvokerFangs) { + return new PacketPlayOutSpawnEntity(this.tracker, 79); + } else if (this.tracker instanceof EntityTNTPrimed) { + return new PacketPlayOutSpawnEntity(this.tracker, 50); + } else if (this.tracker instanceof EntityEnderCrystal) { + return new PacketPlayOutSpawnEntity(this.tracker, 51); + } else if (this.tracker instanceof EntityFallingBlock) { + EntityFallingBlock entityfallingblock = (EntityFallingBlock) this.tracker; + + return new PacketPlayOutSpawnEntity(this.tracker, 70, Block.getCombinedId(entityfallingblock.getBlock())); + } else if (this.tracker instanceof EntityArmorStand) { + return new PacketPlayOutSpawnEntity(this.tracker, 78); + } else if (this.tracker instanceof EntityItemFrame) { + EntityItemFrame entityitemframe = (EntityItemFrame) this.tracker; + + return new PacketPlayOutSpawnEntity(this.tracker, 71, entityitemframe.direction.get2DRotationValue(), entityitemframe.getBlockPosition()); + } else if (this.tracker instanceof EntityLeash) { + EntityLeash entityleash = (EntityLeash) this.tracker; + + return new PacketPlayOutSpawnEntity(this.tracker, 77, 0, entityleash.getBlockPosition()); + } else if (this.tracker instanceof EntityAreaEffectCloud) { + return new PacketPlayOutSpawnEntity(this.tracker, 3); + } else { + throw new IllegalArgumentException("Don\'t know how to add " + this.tracker.getClass() + "!"); + } + } + } + + public void clear(EntityPlayer entityplayer) { + org.spigotmc.AsyncCatcher.catchOp( "player tracker clear"); // Spigot + if (this.trackedPlayers.contains(entityplayer)) { + this.trackedPlayers.remove(entityplayer); + this.tracker.c(entityplayer); + entityplayer.c(this.tracker); + updatePassengers(entityplayer); // Paper + } + + } + + public Entity b() { + return this.tracker; + } + + public void a(int i) { + this.f = i; + } + + public void c() { + this.isMoving = false; + } +} diff --git a/sources/src/main/java/net/minecraft/server/Explosion.java b/sources/src/main/java/net/minecraft/server/Explosion.java new file mode 100644 index 000000000..d19b61d8a --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/Explosion.java @@ -0,0 +1,504 @@ +package net.minecraft.server; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import it.unimi.dsi.fastutil.longs.LongOpenHashSet; + +import javax.annotation.Nullable; +import java.util.*; +import java.util.function.Predicate; + +// CraftBukkit start +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.Location; +import org.bukkit.event.block.BlockExplodeEvent; +// CraftBukkit end + +public class Explosion { + + private final boolean a; + private final boolean b; + private final Random c = new Random(); + private final World world; + private final double posX; + private final double posY; + private final double posZ; + public final Entity source; + private final float size; + private final ArrayList blocks = Lists.newArrayList(); + private final Map k = Maps.newHashMap(); + public boolean wasCanceled = false; // CraftBukkit - add field + + // Dionysus start + private final BlockPosition.MutableBlockPosition cachedPos = new BlockPosition.MutableBlockPosition(); + // The chunk coordinate of the most recently stepped through block. + private int prevChunkX = Integer.MIN_VALUE; + private int prevChunkZ = Integer.MIN_VALUE; + + // The chunk belonging to prevChunkPos. + private Chunk prevChunk; + + private static final com.google.common.base.Predicate hitPredicate = entity -> IEntitySelector.d.apply(entity) && !entity.dead; // Dionysus - Paper - don't hit dead entities + // Dionysus end + + public Explosion(World world, Entity entity, double d0, double d1, double d2, float f, boolean flag, boolean flag1) { + this.world = world; + this.source = entity; + this.size = (float) Math.max(f, 0.0); // CraftBukkit - clamp bad values + this.posX = d0; + this.posY = d1; + this.posZ = d2; + this.a = flag; + this.b = flag1; + } + + public void a() { + // CraftBukkit start + if (this.size < 0.1F) { + return; + } + // CraftBukkit end + // Dionysus start - optimize memory usage from explosions + // CaffeineMC optimized raytracing loop + // @author JellySquid + // @author nopjmp + // https://github.com/CaffeineMC/lithium-fabric + // Using integer encoding for the block positions provides a massive speedup and prevents us from needing to + // allocate a block position for every step we make along each ray, eliminating essentially all the memory + // allocations of this function. The overhead of packing block positions into integer format is negligible + // compared to a memory allocation and associated overhead of hashing real objects in a set. + final LongOpenHashSet touched = new LongOpenHashSet(0); + final Random random = this.world.random; + for (int rayX = 0; rayX < 16; ++rayX) { + boolean xPlane = rayX == 0 || rayX == 15; + double vecX = (((float) rayX / 15.0F) * 2.0F) - 1.0F; + + for (int rayY = 0; rayY < 16; ++rayY) { + boolean yPlane = rayY == 0 || rayY == 15; + double vecY = (((float) rayY / 15.0F) * 2.0F) - 1.0F; + + for (int rayZ = 0; rayZ < 16; ++rayZ) { + boolean zPlane = rayZ == 0 || rayZ == 15; + + // We only fire rays from the surface of our origin volume + if (xPlane || yPlane || zPlane) { + double vecZ = (((float) rayZ / 15.0F) * 2.0F) - 1.0F; + + this.performRayCast(random, vecX, vecY, vecZ, touched); + } + } + } + } + + // We can now iterate back over the set of positions we modified and re-build BlockPos objects from them + // This will only allocate as many objects as there are in the set, where otherwise we would allocate them + // each step of a every ray. + blocks.ensureCapacity(touched.size()); + for (Long longPos : touched) { + blocks.add(BlockPosition.fromLong(longPos)); + } + float f3 = this.size * 2.0F; + + int i = MathHelper.floor(this.posX - (double) f3 - 1.0D); + int j = MathHelper.floor(this.posX + (double) f3 + 1.0D); + int l = MathHelper.floor(this.posY - (double) f3 - 1.0D); + int i1 = MathHelper.floor(this.posY + (double) f3 + 1.0D); + int j1 = MathHelper.floor(this.posZ - (double) f3 - 1.0D); + int k1 = MathHelper.floor(this.posZ + (double) f3 + 1.0D); + // Paper start - Fix lag from explosions processing dead entities + List list = this.world.getEntities(this.source, new AxisAlignedBB(i, l, j1, j, i1, k1), hitPredicate); + // Paper end + Vec3D vec3d = new Vec3D(this.posX, this.posY, this.posZ); + + for (Entity entity : list) { + + if (!entity.bB()) { + double d7 = entity.e(this.posX, this.posY, this.posZ) / (double) f3; + + if (d7 <= 1.0D) { + double d8 = entity.locX - this.posX; + double d9 = entity.locY + (double) entity.getHeadHeight() - this.posY; + double d10 = entity.locZ - this.posZ; + double d11 = MathHelper.sqrt(d8 * d8 + d9 * d9 + d10 * d10); + + if (d11 != 0.0D) { + d8 /= d11; + d9 /= d11; + d10 /= d11; + double d12 = this.getBlockDensity(vec3d, entity.getBoundingBox()); // Paper - Optimize explosions + double d13 = (1.0D - d7) * d12; + + // CraftBukkit start + // entity.damageEntity(DamageSource.explosion(this), (float) ((int) ((d13 * d13 + d13) / 2.0D * 7.0D * (double) f3 + 1.0D))); + CraftEventFactory.entityDamage = source; + entity.forceExplosionKnockback = false; + boolean wasDamaged = entity.damageEntity(DamageSource.explosion(this), (float) ((int) ((d13 * d13 + d13) / 2.0D * 7.0D * (double) f3 + 1.0D))); + CraftEventFactory.entityDamage = null; + if (!wasDamaged && !(entity instanceof EntityTNTPrimed || entity instanceof EntityFallingBlock) && !entity.forceExplosionKnockback) { + continue; + } + // CraftBukkit end + double d14 = d13; + + if (entity instanceof EntityLiving) { + d14 = entity instanceof EntityHuman && world.paperConfig.disableExplosionKnockback ? 0 : EnchantmentProtection.a((EntityLiving) entity, d13); // Paper - Disable explosion knockback + } + + entity.motX += d8 * d14; + entity.motY += d9 * d14; + entity.motZ += d10 * d14; + if (entity instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) entity; + + if (!entityhuman.isSpectator() && (!entityhuman.z() && !world.paperConfig.disableExplosionKnockback || !entityhuman.abilities.isFlying)) { // Paper - Disable explosion knockback + this.k.put(entityhuman, new Vec3D(d8 * d13, d9 * d13, d10 * d13)); + } + } + } + } + } + } + + } + + private void performRayCast(Random random, double vecX, double vecY, double vecZ, LongOpenHashSet touched) { + double dist = Math.sqrt((vecX * vecX) + (vecY * vecY) + (vecZ * vecZ)); + + double normX = (vecX / dist) * 0.3D; + double normY = (vecY / dist) * 0.3D; + double normZ = (vecZ / dist) * 0.3D; + + float strength = this.size * (0.7F + (random.nextFloat() * 0.6F)); + + double stepX = this.posX; + double stepY = this.posY; + double stepZ = this.posZ; + + int prevX = Integer.MIN_VALUE; + int prevY = Integer.MIN_VALUE; + int prevZ = Integer.MIN_VALUE; + + float prevResistance = 0.0F; + + // Step through the ray until it is finally stopped + while (strength > 0.0F) { + int blockX = MathHelper.floor(stepX); + int blockY = MathHelper.floor(stepY); + int blockZ = MathHelper.floor(stepZ); + + float resistance; + + // Check whether or not we have actually moved into a new block this step. Due to how rays are stepped through, + // over-sampling of the same block positions will occur. Changing this behaviour would introduce differences in + // aliasing and sampling, which is unacceptable for our purposes. As a band-aid, we can simply re-use the + // previous result and get a decent boost. + if (prevX != blockX || prevY != blockY || prevZ != blockZ) { + resistance = this.traverseBlock(strength, blockX, blockY, blockZ, touched); + + prevX = blockX; + prevY = blockY; + prevZ = blockZ; + + prevResistance = resistance; + } else { + resistance = prevResistance; + } + + strength -= resistance; + // Apply a constant fall-off + strength -= 0.22500001F; + + stepX += normX; + stepY += normY; + stepZ += normZ; + } + } + + /** + * Called for every step made by a ray being cast by an explosion. + * + * @param strength The strength of the ray during this step + * @param blockX The x-coordinate of the block the ray is inside of + * @param blockY The y-coordinate of the block the ray is inside of + * @param blockZ The z-coordinate of the block the ray is inside of + * @return The resistance of the current block space to the ray + */ + private float traverseBlock(float strength, int blockX, int blockY, int blockZ, LongOpenHashSet touched) { + cachedPos.c(blockX, blockY, blockZ); + IBlockData iblockdata = this.world.getType(cachedPos); + + // Early-exit if the y-coordinate is out of bounds. + if (cachedPos.isInvalidYLocation()) { + if (iblockdata.getMaterial() != Material.AIR) { + float blastResistance = this.source != null ? this.source.a(this, this.world, cachedPos, iblockdata) : iblockdata.getBlock().a((Entity) null); + return (blastResistance + 0.3F) * 0.3F; + } + return 0.0F; + } + + + int chunkX = blockX >> 4; + int chunkZ = blockZ >> 4; + + // Avoid calling into the chunk manager as much as possible through managing chunks locally + if (this.prevChunkX != chunkX || this.prevChunkZ != chunkZ) { + this.prevChunk = this.world.getChunkAt(chunkX, chunkZ); + + this.prevChunkX = chunkX; + this.prevChunkZ = chunkZ; + } + + final Chunk chunk = this.prevChunk; + + float totalResistance = 0.0F; + Optional blastResistance = Optional.empty(); + + // If the chunk is missing or out of bounds, assume that it is air + if (chunk != null) { + // We operate directly on chunk sections to avoid interacting with BlockPos and to squeeze out as much + // performance as possible here + ChunkSection section = chunk.getSections()[blockY >> 4]; + + // If the section doesn't exist or it's empty, assume that the block is air + if (section != null && !section.a()) { + // Retrieve the block state from the chunk section directly to avoid associated overhead + IBlockData blockState = section.getType(blockX & 15, blockY & 15, blockZ & 15); + + // If the block state is air, it cannot have fluid or any kind of resistance, so just leave + if (blockState.getBlock() != Blocks.AIR) { + // Get the explosion resistance like vanilla + blastResistance = Optional.of(this.source != null ? this.source.a(this, this.world, cachedPos, iblockdata) : iblockdata.getBlock().a((Entity) null)); + } + } + } + + // Calculate how much this block will resist an explosion's ray + if (blastResistance.isPresent()) { + totalResistance = (blastResistance.get() + 0.3F) * 0.3F; + } + + // Check if this ray is still strong enough to break blocks, and if so, add this position to the set + // of positions to destroy + float reducedStrength = strength - totalResistance; + if (reducedStrength > 0.0F && (this.a || iblockdata.getMaterial() != Material.AIR)) { + if ((this.source == null || this.source.a(this, this.world, cachedPos, iblockdata, reducedStrength)) && cachedPos.getY() < 256 && cachedPos.getY() >= 0) { + touched.add(cachedPos.asLong()); + } + } + + return totalResistance; + } + // Dionysus end + + + public void a(boolean flag) { + this.world.a((EntityHuman) null, this.posX, this.posY, this.posZ, SoundEffects.bV, SoundCategory.BLOCKS, 4.0F, (1.0F + (this.world.random.nextFloat() - this.world.random.nextFloat()) * 0.2F) * 0.7F); + if (this.size >= 2.0F && this.b) { + this.world.addParticle(EnumParticle.EXPLOSION_HUGE, this.posX, this.posY, this.posZ, 1.0D, 0.0D, 0.0D); + } else { + this.world.addParticle(EnumParticle.EXPLOSION_LARGE, this.posX, this.posY, this.posZ, 1.0D, 0.0D, 0.0D); + } + + Iterator iterator; + BlockPosition blockposition; + + if (this.b) { + // CraftBukkit start + org.bukkit.World bworld = this.world.getWorld(); + org.bukkit.entity.Entity explode = this.source == null ? null : this.source.getBukkitEntity(); + Location location = new Location(bworld, this.posX, this.posY, this.posZ); + + List blockList = Lists.newArrayList(); + for (int i1 = this.blocks.size() - 1; i1 >= 0; i1--) { + BlockPosition cpos = (BlockPosition) this.blocks.get(i1); + org.bukkit.block.Block bblock = bworld.getBlockAt(cpos.getX(), cpos.getY(), cpos.getZ()); + if (bblock.getType() != org.bukkit.Material.AIR) { + blockList.add(bblock); + } + } + + boolean cancelled; + List bukkitBlocks; + float yield; + + if (explode != null) { + EntityExplodeEvent event = new EntityExplodeEvent(explode, location, blockList, 1.0F / this.size); + this.world.getServer().getPluginManager().callEvent(event); + cancelled = event.isCancelled(); + bukkitBlocks = event.blockList(); + yield = event.getYield(); + } else { + BlockExplodeEvent event = new BlockExplodeEvent(location.getBlock(), blockList, 1.0F / this.size); + this.world.getServer().getPluginManager().callEvent(event); + cancelled = event.isCancelled(); + bukkitBlocks = event.blockList(); + yield = event.getYield(); + } + + this.blocks.clear(); + + this.blocks.ensureCapacity(bukkitBlocks.size()); + for (org.bukkit.block.Block bblock : bukkitBlocks) { + BlockPosition coords = new BlockPosition(bblock.getX(), bblock.getY(), bblock.getZ()); + blocks.add(coords); + } + + if (cancelled) { + this.wasCanceled = true; + return; + } + // CraftBukkit end + iterator = this.blocks.iterator(); + + while (iterator.hasNext()) { + blockposition = (BlockPosition) iterator.next(); + IBlockData iblockdata = this.world.getType(blockposition); + Block block = iblockdata.getBlock(); + this.world.chunkPacketBlockController.updateNearbyBlocks(this.world, blockposition); // Paper - Anti-Xray + + if (flag) { + double d0 = (double) ((float) blockposition.getX() + this.world.random.nextFloat()); + double d1 = (double) ((float) blockposition.getY() + this.world.random.nextFloat()); + double d2 = (double) ((float) blockposition.getZ() + this.world.random.nextFloat()); + double d3 = d0 - this.posX; + double d4 = d1 - this.posY; + double d5 = d2 - this.posZ; + double d6 = (double) MathHelper.sqrt(d3 * d3 + d4 * d4 + d5 * d5); + + d3 /= d6; + d4 /= d6; + d5 /= d6; + double d7 = 0.5D / (d6 / (double) this.size + 0.1D); + + d7 *= (double) (this.world.random.nextFloat() * this.world.random.nextFloat() + 0.3F); + d3 *= d7; + d4 *= d7; + d5 *= d7; + this.world.addParticle(EnumParticle.EXPLOSION_NORMAL, (d0 + this.posX) / 2.0D, (d1 + this.posY) / 2.0D, (d2 + this.posZ) / 2.0D, d3, d4, d5); + this.world.addParticle(EnumParticle.SMOKE_NORMAL, d0, d1, d2, d3, d4, d5); + } + + if (iblockdata.getMaterial() != Material.AIR) { + if (block.a(this)) { + // CraftBukkit - add yield + block.dropNaturally(this.world, blockposition, this.world.getType(blockposition), yield, 0); + } + + this.world.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 3); + block.wasExploded(this.world, blockposition, this); + } + } + } + + if (this.a) { + iterator = this.blocks.iterator(); + + while (iterator.hasNext()) { + blockposition = (BlockPosition) iterator.next(); + if (this.world.getType(blockposition).getMaterial() == Material.AIR && this.world.getType(blockposition.down()).b() && this.c.nextInt(3) == 0) { + // CraftBukkit start - Ignition by explosion + if (!CraftEventFactory.callBlockIgniteEvent(this.world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this).isCancelled()) { + this.world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData()); + } + // CraftBukkit end + } + } + } + + } + + public Map b() { + return this.k; + } + + @Nullable + public EntityLiving getSource() { + // CraftBukkit start - obtain Fireball shooter for explosion tracking + return this.source == null ? null : (this.source instanceof EntityTNTPrimed ? ((EntityTNTPrimed) this.source).getSource() : (this.source instanceof EntityLiving ? (EntityLiving) this.source : (this.source instanceof EntityFireball ? ((EntityFireball) this.source).shooter : null))); + // CraftBukkit end + } + + public void clearBlocks() { + this.blocks.clear(); + } + + public List getBlocks() { + return this.blocks; + } + + // Paper start - Optimize explosions + private float getBlockDensity(Vec3D vec3d, AxisAlignedBB aabb) { + if (!this.world.paperConfig.optimizeExplosions) { + return this.world.a(vec3d, aabb); + } + CacheKey key = new CacheKey(this, aabb); + return this.world.explosionDensityCache.computeIfAbsent(key, k1 -> this.world.a(vec3d, aabb)); + } + + static class CacheKey { + private final World world; + private final double posX, posY, posZ; + private final double minX, minY, minZ; + private final double maxX, maxY, maxZ; + + public CacheKey(Explosion explosion, AxisAlignedBB aabb) { + this.world = explosion.world; + this.posX = explosion.posX; + this.posY = explosion.posY; + this.posZ = explosion.posZ; + this.minX = aabb.a; + this.minY = aabb.b; + this.minZ = aabb.c; + this.maxX = aabb.d; + this.maxY = aabb.e; + this.maxZ = aabb.f; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CacheKey cacheKey = (CacheKey) o; + + if (Double.compare(cacheKey.posX, posX) != 0) return false; + if (Double.compare(cacheKey.posY, posY) != 0) return false; + if (Double.compare(cacheKey.posZ, posZ) != 0) return false; + if (Double.compare(cacheKey.minX, minX) != 0) return false; + if (Double.compare(cacheKey.minY, minY) != 0) return false; + if (Double.compare(cacheKey.minZ, minZ) != 0) return false; + if (Double.compare(cacheKey.maxX, maxX) != 0) return false; + if (Double.compare(cacheKey.maxY, maxY) != 0) return false; + if (Double.compare(cacheKey.maxZ, maxZ) != 0) return false; + return world.equals(cacheKey.world); + } + + @Override + public int hashCode() { + int result; + long temp; + result = world.hashCode(); + temp = Double.doubleToLongBits(posX); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(posY); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(posZ); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(minX); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(minY); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(minZ); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(maxX); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(maxY); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(maxZ); + result = 31 * result + (int) (temp ^ (temp >>> 32)); + return result; + } + } + // Paper end +} diff --git a/sources/src/main/java/net/minecraft/server/IntCache.java b/sources/src/main/java/net/minecraft/server/IntCache.java new file mode 100644 index 000000000..2679c1106 --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/IntCache.java @@ -0,0 +1,113 @@ +package net.minecraft.server; + +// NeonPaper start +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import java.lang.ref.WeakReference; +import java.util.Iterator; +import java.util.List; +// NeonPaper end + +public class IntCache { + // NeonPaper start - Refactored IntCache to be thread local instead of static + private static final ThreadLocal caches = new ThreadLocal() { + @Override + protected IntCache initialValue() { + IntCache cache = new IntCache(); + synchronized (ALL_CACHES) { + ALL_CACHES.add(new WeakReference<>(cache)); + } + return new IntCache(); + } + }; + + private static final List> ALL_CACHES = new ObjectArrayList<>(); + + private int a = 256; + private final List b = new ObjectArrayList<>(); + private final List c = new ObjectArrayList<>(); + private final List d = new ObjectArrayList<>(); + private final List e = new ObjectArrayList<>(); + + private final int cacheLimit = org.spigotmc.SpigotConfig.intCacheLimit; + + public static int[] a(int i) { + return caches.get().aNonStatic(i); + } + + public int[] aNonStatic(int i) { + int[] aint; + + if (i <= 256) { + if (this.b.isEmpty()) { + aint = new int[256]; + if (c.size() < cacheLimit) this.c.add(aint); + return aint; + } else { + aint = this.b.remove(this.b.size() - 1); + if (c.size() < cacheLimit) this.c.add(aint); + return aint; + } + } else if (i > this.a) { + this.a = i; + this.d.clear(); + this.e.clear(); + aint = new int[this.a]; + if (e.size() < cacheLimit) this.e.add(aint); + return aint; + } else if (this.d.isEmpty()) { + aint = new int[this.a]; + if (e.size() < cacheLimit) this.e.add(aint); + return aint; + } else { + aint = this.d.remove(this.d.size() - 1); + if (e.size() < cacheLimit) this.e.add(aint); + return aint; + } + } + + public static void a() { + caches.get().aNonStatic(); + } + + public void aNonStatic() { + if (!this.d.isEmpty()) { + this.d.remove(this.d.size() - 1); + } + + if (!this.b.isEmpty()) { + this.b.remove(this.b.size() - 1); + } + + this.d.addAll(this.e); + this.b.addAll(this.c); + this.e.clear(); + this.c.clear(); + } + + public static String b() { + int cache = 0; + int tcache = 0; + int allocated = 0; + int tallocated = 0; + int numberOfCaches; + + synchronized (ALL_CACHES) { + numberOfCaches = ALL_CACHES.size(); + Iterator> iter = ALL_CACHES.iterator(); + while (iter.hasNext()) { + WeakReference reference = iter.next(); + IntCache intcache = reference.get(); + if (intcache != null) { + cache += intcache.d.size(); + tcache += intcache.b.size(); + allocated += intcache.e.size(); + tallocated += intcache.c.size(); + } else { + iter.remove(); + } + } + } + return numberOfCaches + " IntCaches. In Total => cache: " + cache + ", tcache: " + tcache + ", allocated: " + allocated + ", tallocated: " + tallocated; + } +// NeonPaper end +} \ No newline at end of file diff --git a/sources/src/main/java/net/minecraft/server/ItemWorldMap.java b/sources/src/main/java/net/minecraft/server/ItemWorldMap.java new file mode 100644 index 000000000..c438fc5f7 --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/ItemWorldMap.java @@ -0,0 +1,367 @@ +package net.minecraft.server; + +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Iterables; +import com.google.common.collect.Multisets; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.Bukkit; +import org.bukkit.event.server.MapInitializeEvent; +// CraftBukkit end + +public class ItemWorldMap extends ItemWorldMapBase { + + protected ItemWorldMap() { + this.a(true); + } + + public static ItemStack a(World world, double d0, double d1, byte b0, boolean flag, boolean flag1) { + World worldMain = world.getServer().getServer().worlds.get(0); // CraftBukkit - store reference to primary world + ItemStack itemstack = new ItemStack(Items.FILLED_MAP, 1, worldMain.b("map")); // CraftBukkit - use primary world for maps + String s = "map_" + itemstack.getData(); + WorldMap worldmap = new WorldMap(s); + + worldMain.a(s, (PersistentBase) worldmap); // CraftBukkit + worldmap.scale = b0; + worldmap.a(d0, d1, worldmap.scale); + worldmap.map = (byte) ((WorldServer) world).dimension; // CraftBukkit - use bukkit dimension + worldmap.track = flag; + worldmap.unlimitedTracking = flag1; + worldmap.c(); + org.bukkit.craftbukkit.event.CraftEventFactory.callEvent(new org.bukkit.event.server.MapInitializeEvent(worldmap.mapView)); // CraftBukkit + return itemstack; + } + + @Nullable + public WorldMap getSavedMap(ItemStack itemstack, World world) { + World worldMain = world.getServer().getServer().worlds.get(0); // CraftBukkit - store reference to primary world + String s = "map_" + itemstack.getData(); + WorldMap worldmap = (WorldMap) worldMain.a(WorldMap.class, s); // CraftBukkit - use primary world for maps + + if (worldmap == null && !world.isClientSide) { + itemstack.setData(worldMain.b("map")); // CraftBukkit - use primary world for maps + s = "map_" + itemstack.getData(); + worldmap = new WorldMap(s); + worldmap.scale = 3; + worldmap.a((double) world.getWorldData().b(), (double) world.getWorldData().d(), worldmap.scale); + worldmap.map = (byte) ((WorldServer) world).dimension; // CraftBukkit - fixes Bukkit multiworld maps + worldmap.c(); + worldMain.a(s, (PersistentBase) worldmap); // CraftBukkit - use primary world for maps + + // CraftBukkit start + MapInitializeEvent event = new MapInitializeEvent(worldmap.mapView); + Bukkit.getServer().getPluginManager().callEvent(event); + // CraftBukkit end + } + + return worldmap; + } + + public void a(World world, Entity entity, WorldMap worldmap) { + // CraftBukkit - world.worldProvider -> ((WorldServer) world) + if (((WorldServer) world).dimension == worldmap.map && entity instanceof EntityHuman) { + int i = 1 << worldmap.scale; + int j = worldmap.centerX; + int k = worldmap.centerZ; + int l = MathHelper.floor(entity.locX - (double) j) / i + 64; + int i1 = MathHelper.floor(entity.locZ - (double) k) / i + 64; + int j1 = 128 / i; + + if (world.worldProvider.n()) { + j1 /= 2; + } + + WorldMap.WorldMapHumanTracker worldmap_worldmaphumantracker = worldmap.a((EntityHuman) entity); + + ++worldmap_worldmaphumantracker.b; + boolean flag = false; + + for (int k1 = l - j1 + 1; k1 < l + j1; ++k1) { + if ((k1 & 15) == (worldmap_worldmaphumantracker.b & 15) || flag) { + flag = false; + double d0 = 0.0D; + + for (int l1 = i1 - j1 - 1; l1 < i1 + j1; ++l1) { + if (k1 >= 0 && l1 >= -1 && k1 < 128 && l1 < 128) { + int i2 = k1 - l; + int j2 = l1 - i1; + boolean flag1 = i2 * i2 + j2 * j2 > (j1 - 2) * (j1 - 2); + int k2 = (j / i + k1 - 64) * i; + int l2 = (k / i + l1 - 64) * i; + HashMultiset hashmultiset = HashMultiset.create(); + Chunk chunk = world.getChunkIfLoaded(new BlockPosition(k2, 0, l2)); // NeonPaper - Maps shouldn't load chunks + + if (chunk != null && !chunk.isEmpty()) { // NeonPaper - Maps shouldn't load chunks + int i3 = k2 & 15; + int j3 = l2 & 15; + int k3 = 0; + double d1 = 0.0D; + + if (world.worldProvider.n()) { + int l3 = k2 + l2 * 231871; + + l3 = l3 * l3 * 31287121 + l3 * 11; + if ((l3 >> 20 & 1) == 0) { + hashmultiset.add(Blocks.DIRT.getBlockData().set(BlockDirt.VARIANT, BlockDirt.EnumDirtVariant.DIRT).a((IBlockAccess) world, BlockPosition.ZERO), 10); + } else { + hashmultiset.add(Blocks.STONE.getBlockData().set(BlockStone.VARIANT, BlockStone.EnumStoneVariant.STONE).a((IBlockAccess) world, BlockPosition.ZERO), 100); + } + + d1 = 100.0D; + } else { + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + + for (int i4 = 0; i4 < i; ++i4) { + for (int j4 = 0; j4 < i; ++j4) { + int k4 = chunk.b(i4 + i3, j4 + j3) + 1; + IBlockData iblockdata = Blocks.AIR.getBlockData(); + + if (k4 > 1) { + do { + --k4; + iblockdata = chunk.a(i4 + i3, k4, j4 + j3); + blockposition_mutableblockposition.c((chunk.locX << 4) + i4 + i3, k4, (chunk.locZ << 4) + j4 + j3); + } while (iblockdata.a((IBlockAccess) world, blockposition_mutableblockposition) == MaterialMapColor.c && k4 > 0); + + if (k4 > 0 && iblockdata.getMaterial().isLiquid()) { + int l4 = k4 - 1; + + IBlockData iblockdata1; + + do { + iblockdata1 = chunk.a(i4 + i3, l4--, j4 + j3); + ++k3; + } while (l4 > 0 && iblockdata1.getMaterial().isLiquid()); + } + } else { + iblockdata = Blocks.BEDROCK.getBlockData(); + } + + d1 += (double) k4 / (double) (i * i); + hashmultiset.add(iblockdata.a((IBlockAccess) world, blockposition_mutableblockposition)); + } + } + } + + k3 /= i * i; + double d2 = (d1 - d0) * 4.0D / (double) (i + 4) + ((double) (k1 + l1 & 1) - 0.5D) * 0.4D; + byte b0 = 1; + + if (d2 > 0.6D) { + b0 = 2; + } + + if (d2 < -0.6D) { + b0 = 0; + } + + MaterialMapColor materialmapcolor = (MaterialMapColor) Iterables.getFirst(Multisets.copyHighestCountFirst(hashmultiset), MaterialMapColor.c); + + if (materialmapcolor == MaterialMapColor.o) { + d2 = (double) k3 * 0.1D + (double) (k1 + l1 & 1) * 0.2D; + b0 = 1; + if (d2 < 0.5D) { + b0 = 2; + } + + if (d2 > 0.9D) { + b0 = 0; + } + } + + d0 = d1; + if (l1 >= 0 && i2 * i2 + j2 * j2 < j1 * j1 && (!flag1 || (k1 + l1 & 1) != 0)) { + byte b1 = worldmap.colors[k1 + l1 * 128]; + byte b2 = (byte) (materialmapcolor.ad * 4 + b0); + + if (b1 != b2) { + worldmap.colors[k1 + l1 * 128] = b2; + worldmap.flagDirty(k1, l1); + flag = true; + } + } + } + } + } + } + } + + } + } + + public static void a(World world, ItemStack itemstack) { + if (itemstack.getItem() == Items.FILLED_MAP) { + WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, world); + + if (worldmap != null) { + if (world.worldProvider.getDimensionManager().getDimensionID() == worldmap.map) { + int i = 1 << worldmap.scale; + int j = worldmap.centerX; + int k = worldmap.centerZ; + BiomeBase[] abiomebase = world.getWorldChunkManager().a((BiomeBase[]) null, (j / i - 64) * i, (k / i - 64) * i, 128 * i, 128 * i, false); + + for (int l = 0; l < 128; ++l) { + for (int i1 = 0; i1 < 128; ++i1) { + int j1 = l * i; + int k1 = i1 * i; + BiomeBase biomebase = abiomebase[j1 + k1 * 128 * i]; + MaterialMapColor materialmapcolor = MaterialMapColor.c; + int l1 = 3; + int i2 = 8; + + if (l > 0 && i1 > 0 && l < 127 && i1 < 127) { + if (abiomebase[(l - 1) * i + (i1 - 1) * i * 128 * i].j() >= 0.0F) { + --i2; + } + + if (abiomebase[(l - 1) * i + (i1 + 1) * i * 128 * i].j() >= 0.0F) { + --i2; + } + + if (abiomebase[(l - 1) * i + i1 * i * 128 * i].j() >= 0.0F) { + --i2; + } + + if (abiomebase[(l + 1) * i + (i1 - 1) * i * 128 * i].j() >= 0.0F) { + --i2; + } + + if (abiomebase[(l + 1) * i + (i1 + 1) * i * 128 * i].j() >= 0.0F) { + --i2; + } + + if (abiomebase[(l + 1) * i + i1 * i * 128 * i].j() >= 0.0F) { + --i2; + } + + if (abiomebase[l * i + (i1 - 1) * i * 128 * i].j() >= 0.0F) { + --i2; + } + + if (abiomebase[l * i + (i1 + 1) * i * 128 * i].j() >= 0.0F) { + --i2; + } + + if (biomebase.j() < 0.0F) { + materialmapcolor = MaterialMapColor.r; + if (i2 > 7 && i1 % 2 == 0) { + l1 = (l + (int) (MathHelper.sin((float) i1 + 0.0F) * 7.0F)) / 8 % 5; + if (l1 == 3) { + l1 = 1; + } else if (l1 == 4) { + l1 = 0; + } + } else if (i2 > 7) { + materialmapcolor = MaterialMapColor.c; + } else if (i2 > 5) { + l1 = 1; + } else if (i2 > 3) { + l1 = 0; + } else if (i2 > 1) { + l1 = 0; + } + } else if (i2 > 0) { + materialmapcolor = MaterialMapColor.C; + if (i2 > 3) { + l1 = 1; + } else { + l1 = 3; + } + } + } + + if (materialmapcolor != MaterialMapColor.c) { + worldmap.colors[l + i1 * 128] = (byte) (materialmapcolor.ad * 4 + l1); + worldmap.flagDirty(l, i1); + } + } + } + + } + } + } + } + + public void a(ItemStack itemstack, World world, Entity entity, int i, boolean flag) { + if (!world.isClientSide) { + WorldMap worldmap = this.getSavedMap(itemstack, world); + + if (entity instanceof EntityHuman) { + EntityHuman entityhuman = (EntityHuman) entity; + + worldmap.a(entityhuman, itemstack); + } + + if (flag || entity instanceof EntityHuman && ((EntityHuman) entity).getItemInOffHand() == itemstack) { + this.a(world, entity, worldmap); + } + + } + } + + @Nullable + public Packet a(ItemStack itemstack, World world, EntityHuman entityhuman) { + return this.getSavedMap(itemstack, world).a(itemstack, world, entityhuman); + } + + public void b(ItemStack itemstack, World world, EntityHuman entityhuman) { + NBTTagCompound nbttagcompound = itemstack.getTag(); + + if (nbttagcompound != null) { + if (nbttagcompound.hasKeyOfType("map_scale_direction", 99)) { + a(itemstack, world, nbttagcompound.getInt("map_scale_direction")); + nbttagcompound.remove("map_scale_direction"); + } else if (nbttagcompound.getBoolean("map_tracking_position")) { + b(itemstack, world); + nbttagcompound.remove("map_tracking_position"); + } + } + + } + + protected static void a(ItemStack itemstack, World world, int i) { + WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, world); + + world = world.getServer().getServer().worlds.get(0); // CraftBukkit - use primary world for maps + itemstack.setData(world.b("map")); + WorldMap worldmap1 = new WorldMap("map_" + itemstack.getData()); + + if (worldmap != null) { + worldmap1.scale = (byte) MathHelper.clamp(worldmap.scale + i, 0, 4); + worldmap1.track = worldmap.track; + worldmap1.a((double) worldmap.centerX, (double) worldmap.centerZ, worldmap1.scale); + worldmap1.map = worldmap.map; + worldmap1.c(); + world.a("map_" + itemstack.getData(), (PersistentBase) worldmap1); + // CraftBukkit start + MapInitializeEvent event = new MapInitializeEvent(worldmap1.mapView); + Bukkit.getServer().getPluginManager().callEvent(event); + // CraftBukkit end + } + + } + + protected static void b(ItemStack itemstack, World world) { + WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, world); + + world = world.getServer().getServer().worlds.get(0); // CraftBukkit - use primary world for maps + itemstack.setData(world.b("map")); + WorldMap worldmap1 = new WorldMap("map_" + itemstack.getData()); + + worldmap1.track = true; + if (worldmap != null) { + worldmap1.centerX = worldmap.centerX; + worldmap1.centerZ = worldmap.centerZ; + worldmap1.scale = worldmap.scale; + worldmap1.map = worldmap.map; + worldmap1.c(); + world.a("map_" + itemstack.getData(), (PersistentBase) worldmap1); + // CraftBukkit start + MapInitializeEvent event = new MapInitializeEvent(worldmap1.mapView); + Bukkit.getServer().getPluginManager().callEvent(event); + // CraftBukkit end + } + + } +} diff --git a/sources/src/main/java/net/minecraft/server/NetworkManager.java b/sources/src/main/java/net/minecraft/server/NetworkManager.java index 1cc6da9ed..aa0a9b3e2 100644 --- a/sources/src/main/java/net/minecraft/server/NetworkManager.java +++ b/sources/src/main/java/net/minecraft/server/NetworkManager.java @@ -365,7 +365,10 @@ public class NetworkManager extends SimpleChannelInboundHandler> { @Override protected void channelRead0(ChannelHandlerContext channelhandlercontext, Packet object) throws Exception { // CraftBukkit - fix decompile error - this.a(channelhandlercontext, object); + // FlamePaper - Check if channel is opened before reading packet + if (isConnected()) { + this.a(channelhandlercontext, object); + } } public static class QueuedPacket { // Akarin - default -> public diff --git a/sources/src/main/java/net/minecraft/server/PlayerConnection.java b/sources/src/main/java/net/minecraft/server/PlayerConnection.java index 9ec316dae..23d0e77f6 100644 --- a/sources/src/main/java/net/minecraft/server/PlayerConnection.java +++ b/sources/src/main/java/net/minecraft/server/PlayerConnection.java @@ -916,6 +916,11 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable { case START_DESTROY_BLOCK: case ABORT_DESTROY_BLOCK: case STOP_DESTROY_BLOCK: + // NeonPaper start - Don't allow digging in unloaded chunks + if (!worldserver.isChunkLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4, true)) { + return; + } + // NeonPaper end - Don't allow digging in unloaded chunks double d0 = this.player.locX - ((double) blockposition.getX() + 0.5D); double d1 = this.player.locY - ((double) blockposition.getY() + 0.5D) + 1.5D; double d2 = this.player.locZ - ((double) blockposition.getZ() + 0.5D); diff --git a/sources/src/main/java/net/minecraft/server/PlayerInteractManager.java b/sources/src/main/java/net/minecraft/server/PlayerInteractManager.java new file mode 100644 index 000000000..1ca7148a9 --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/PlayerInteractManager.java @@ -0,0 +1,568 @@ +package net.minecraft.server; + +// CraftBukkit start +import java.util.ArrayList; +import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.craftbukkit.event.CraftEventFactory; +import org.bukkit.event.Event; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +// CraftBukkit end + +public class PlayerInteractManager { + + public World world; + public EntityPlayer player; + private EnumGamemode gamemode; + private boolean d; + private int lastDigTick; + private BlockPosition f; + private int currentTick; + private boolean h; + private BlockPosition i; + private int j; + private int k; + + public PlayerInteractManager(World world) { + this.gamemode = EnumGamemode.NOT_SET; + this.f = BlockPosition.ZERO; + this.i = BlockPosition.ZERO; + this.k = -1; + this.world = world; + } + + public void setGameMode(EnumGamemode enumgamemode) { + this.gamemode = enumgamemode; + enumgamemode.a(this.player.abilities); + this.player.updateAbilities(); + this.player.server.getPlayerList().sendAll(new PacketPlayOutPlayerInfo(PacketPlayOutPlayerInfo.EnumPlayerInfoAction.UPDATE_GAME_MODE, new EntityPlayer[] { this.player}), this.player); // CraftBukkit + this.world.everyoneSleeping(); + } + + public EnumGamemode getGameMode() { + return this.gamemode; + } + + public boolean c() { + return this.gamemode.e(); + } + + public boolean isCreative() { + return this.gamemode.isCreative(); + } + + public void b(EnumGamemode enumgamemode) { + if (this.gamemode == EnumGamemode.NOT_SET) { + this.gamemode = enumgamemode; + } + + this.setGameMode(this.gamemode); + } + + public void a() { + this.currentTick = MinecraftServer.currentTick; // CraftBukkit; + float f; + int i; + + if (this.h) { + int j = this.currentTick - this.j; + IBlockData iblockdata = this.world.getType(this.i); + + if (iblockdata.getMaterial() == Material.AIR) { + this.h = false; + } else { + f = iblockdata.a((EntityHuman) this.player, this.player.world, this.i) * (float) (j + 1); + i = (int) (f * 10.0F); + if (i != this.k) { + this.world.c(this.player.getId(), this.i, i); + this.k = i; + } + + if (f >= 1.0F) { + this.h = false; + this.breakBlock(this.i); + } + } + } else if (this.d) { + IBlockData iblockdata1 = this.world.getType(this.f); + + if (iblockdata1.getMaterial() == Material.AIR) { + this.world.c(this.player.getId(), this.f, -1); + this.k = -1; + this.d = false; + } else { + int k = this.currentTick - this.lastDigTick; + + f = iblockdata1.a((EntityHuman) this.player, this.player.world, this.i) * (float) (k + 1); + i = (int) (f * 10.0F); + if (i != this.k) { + this.world.c(this.player.getId(), this.f, i); + this.k = i; + } + } + } + + } + + public void a(BlockPosition blockposition, EnumDirection enumdirection) { + // CraftBukkit start + PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, blockposition, enumdirection, this.player.inventory.getItemInHand(), EnumHand.MAIN_HAND); + if (event.isCancelled()) { + // Let the client know the block still exists + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); + cancelBreakBlock(blockposition, this.world.getType(blockposition)); // Paper - Avoid visual issues on the client + // Update any tile entity data for this block + TileEntity tileentity = this.world.getTileEntity(blockposition); + if (tileentity != null) { + this.player.playerConnection.sendPacket(tileentity.getUpdatePacket()); + } + return; + } + // CraftBukkit end + if (this.isCreative()) { + if (!this.world.douseFire((EntityHuman) null, blockposition, enumdirection)) { + this.breakBlock(blockposition); + } + + } else { + IBlockData iblockdata = this.world.getType(blockposition); + Block block = iblockdata.getBlock(); + + if (this.gamemode.c()) { + if (this.gamemode == EnumGamemode.SPECTATOR) { + return; + } + + if (!this.player.dk()) { + ItemStack itemstack = this.player.getItemInMainHand(); + + if (itemstack.isEmpty()) { + return; + } + + if (!itemstack.a(block)) { + return; + } + } + } + + // this.world.douseFire((EntityHuman) null, blockposition, enumdirection); // CraftBukkit - Moved down + this.lastDigTick = this.currentTick; + float f = 1.0F; + + // CraftBukkit start - Swings at air do *NOT* exist. + if (event.useInteractedBlock() == Event.Result.DENY) { + // If we denied a door from opening, we need to send a correcting update to the client, as it already opened the door. + IBlockData data = this.world.getType(blockposition); + if (block == Blocks.WOODEN_DOOR) { + // For some reason *BOTH* the bottom/top part have to be marked updated. + boolean bottom = data.get(BlockDoor.HALF) == BlockDoor.EnumDoorHalf.LOWER; + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, bottom ? blockposition.up() : blockposition.down())); + } else if (block == Blocks.TRAPDOOR) { + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); + } + } else if (iblockdata.getMaterial() != Material.AIR) { + block.attack(this.world, blockposition, this.player); + f = iblockdata.a((EntityHuman) this.player, this.player.world, blockposition); + // Allow fire punching to be blocked + this.world.douseFire((EntityHuman) null, blockposition, enumdirection); + } + + if (event.useItemInHand() == Event.Result.DENY) { + // If we 'insta destroyed' then the client needs to be informed. + if (f > 1.0f) { + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); + } + return; + } + org.bukkit.event.block.BlockDamageEvent blockEvent = CraftEventFactory.callBlockDamageEvent(this.player, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this.player.inventory.getItemInHand(), f >= 1.0f); + + if (blockEvent.isCancelled()) { + // Let the client know the block still exists + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); + return; + } + + if (blockEvent.getInstaBreak()) { + f = 2.0f; + } + // CraftBukkit end + + if (iblockdata.getMaterial() != Material.AIR && f >= 1.0F) { + this.breakBlock(blockposition); + } else { + this.d = true; + this.f = blockposition; + int i = (int) (f * 10.0F); + + this.world.c(this.player.getId(), blockposition, i); + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(world, blockposition)); // Paper - MC-54026 - backport from 1.13 + this.k = i; + } + + } + + this.world.chunkPacketBlockController.updateNearbyBlocks(this.world, blockposition); // Paper - Anti-Xray + } + + public void a(BlockPosition blockposition) { + if (blockposition.equals(this.f)) { + this.currentTick = MinecraftServer.currentTick; // CraftBukkit + int i = this.currentTick - this.lastDigTick; + IBlockData iblockdata = this.world.getType(blockposition); + + if (iblockdata.getMaterial() != Material.AIR) { + float f = iblockdata.a((EntityHuman) this.player, this.player.world, blockposition) * (float) (i + 1); + + if (f >= 0.7F) { + this.d = false; + this.world.c(this.player.getId(), blockposition, -1); + this.breakBlock(blockposition); + } else if (!this.h) { + this.d = false; + this.h = true; + this.i = blockposition; + this.j = this.lastDigTick; + } + } + // CraftBukkit start - Force block reset to client + } else { + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); + // CraftBukkit end + } + + } + + public void e() { + this.d = false; + this.world.c(this.player.getId(), this.f, -1); + } + + private boolean c(BlockPosition blockposition) { + IBlockData iblockdata = this.world.getType(blockposition); + + iblockdata.getBlock().a(this.world, blockposition, iblockdata, (EntityHuman) this.player); + boolean flag = this.world.setAir(blockposition); + + if (flag) { + iblockdata.getBlock().postBreak(this.world, blockposition, iblockdata); + } + + return flag; + } + + // Paper start - Extra method to avoid visual issues on the client when cancelling block breaks + private void cancelBreakBlock(BlockPosition position, IBlockData data) { + Block block = data.getBlock(); + // Send other half of the door + if (block instanceof BlockDoor) { + boolean bottom = data.get(BlockDoor.HALF) == BlockDoor.EnumDoorHalf.LOWER; + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(world, bottom ? position.up() : position.down())); + } else if (block instanceof BlockTallPlant) { + boolean bottom = data.get(BlockTallPlant.HALF) == BlockTallPlant.EnumTallPlantHalf.LOWER; + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(world, bottom ? position.up() : position.down())); + } else if (block instanceof BlockPistonExtension) { + BlockPosition piston = position.shift(data.get(BlockPistonExtension.FACING).opposite()); + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(world, piston)); + } else if (block instanceof BlockBed) { + if (data.get(BlockBed.PART) == BlockBed.EnumBedPart.FOOT) { + // Restore head of bed + BlockPosition head = position.shift(data.get(BlockBed.FACING)); + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(world, head)); + + TileEntity tileentity = this.world.getTileEntity(head); + if (tileentity != null) { + this.player.playerConnection.sendPacket(tileentity.getUpdatePacket()); + } + } + } + } + // Paper end + + public boolean breakBlock(BlockPosition blockposition) { + // CraftBukkit start - fire BlockBreakEvent + BlockBreakEvent event = null; + + if (this.player instanceof EntityPlayer) { + org.bukkit.block.Block block = this.world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); + + // Sword + Creative mode pre-cancel + boolean isSwordNoBreak = this.gamemode.isCreative() && !this.player.getItemInMainHand().isEmpty() && this.player.getItemInMainHand().getItem() instanceof ItemSword; + + // Tell client the block is gone immediately then process events + // Don't tell the client if its a creative sword break because its not broken! + if (world.getTileEntity(blockposition) == null && !isSwordNoBreak) { + PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(this.world, blockposition); + packet.block = Blocks.AIR.getBlockData(); + ((EntityPlayer) this.player).playerConnection.sendPacket(packet); + } + + event = new BlockBreakEvent(block, this.player.getBukkitEntity()); + + // Sword + Creative mode pre-cancel + event.setCancelled(isSwordNoBreak); + + // Calculate default block experience + IBlockData nmsData = this.world.getType(blockposition); + Block nmsBlock = nmsData.getBlock(); + + ItemStack itemstack = this.player.getEquipment(EnumItemSlot.MAINHAND); + + if (nmsBlock != null && !event.isCancelled() && !this.isCreative() && this.player.hasBlock(nmsBlock.getBlockData())) { + // Copied from block.a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, @Nullable TileEntity tileentity, ItemStack itemstack) + // PAIL: checkme each update + if (!(nmsBlock.o() && EnchantmentManager.getEnchantmentLevel(Enchantments.SILK_TOUCH, itemstack) > 0)) { + int bonusLevel = EnchantmentManager.getEnchantmentLevel(Enchantments.LOOT_BONUS_BLOCKS, itemstack); + + event.setExpToDrop(nmsBlock.getExpDrop(this.world, nmsData, bonusLevel)); + } + } + + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + if (isSwordNoBreak) { + return false; + } + // Let the client know the block still exists + ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); + cancelBreakBlock(blockposition, nmsData); // Paper - Move cancellation code to extra "cancelBreakBlock" method + // Update any tile entity data for this block + TileEntity tileentity = this.world.getTileEntity(blockposition); + if (tileentity != null) { + this.player.playerConnection.sendPacket(tileentity.getUpdatePacket()); + } + return false; + } + } + if (false && this.gamemode.isCreative() && !this.player.getItemInMainHand().isEmpty() && this.player.getItemInMainHand().getItem() instanceof ItemSword) { // CraftBukkit - false + return false; + } else { + IBlockData iblockdata = this.world.getType(blockposition); + if (iblockdata.getBlock() == Blocks.AIR) return false; // CraftBukkit - A plugin set block to air without cancelling + TileEntity tileentity = this.world.getTileEntity(blockposition); + Block block = iblockdata.getBlock(); + + // CraftBukkit start - Special case skulls, their item data comes from a tile entity (Also check if block should drop items) + if (iblockdata.getBlock() == Blocks.SKULL && !this.isCreative() && event.isDropItems()) { + iblockdata.getBlock().dropNaturally(world, blockposition, iblockdata, 1.0F, 0); + return this.c(blockposition); + } + + // And shulker boxes too for duplication on cancel reasons (Also check if block should drop items) + if (iblockdata.getBlock() instanceof BlockShulkerBox && event.isDropItems()) { + iblockdata.getBlock().dropNaturally(world, blockposition, iblockdata, 1.0F, 0); + return this.c(blockposition); + } + // CraftBukkit end + + if ((block instanceof BlockCommand || block instanceof BlockStructure) && !this.player.isCreativeAndOp()) { + this.world.notify(blockposition, iblockdata, iblockdata, 3); + return false; + } else { + if (this.gamemode.c()) { + if (this.gamemode == EnumGamemode.SPECTATOR) { + return false; + } + + if (!this.player.dk()) { + ItemStack itemstack = this.player.getItemInMainHand(); + + if (itemstack.isEmpty()) { + return false; + } + + if (!itemstack.a(block)) { + return false; + } + } + } + + this.world.a(this.player, 2001, blockposition, Block.getCombinedId(iblockdata)); + // CraftBukkit start + world.captureDrops = new ArrayList<>(); + boolean flag = this.c(blockposition); + if (event.isDropItems()) { + for (EntityItem item : world.captureDrops) { + world.addEntity(item); + } + } + world.captureDrops = null; + // CraftBukkit end + + if (this.isCreative()) { + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); + } else { + ItemStack itemstack1 = this.player.getItemInMainHand(); + //ItemStack itemstack2 = itemstack1.isEmpty() ? ItemStack.a : itemstack1.cloneItemStack(); //NeonPaper move up + boolean flag1 = this.player.hasBlock(iblockdata); + ItemStack itemstack2 = flag && flag1 && event.isDropItems() && !itemstack1.isEmpty() ? itemstack1.cloneItemStack() : ItemStack.a; // NeonPaper - clone before use + + if (!itemstack1.isEmpty()) { + itemstack1.a(this.world, iblockdata, blockposition, this.player); + } + + // CraftBukkit start - Check if block should drop items + if (flag && flag1 && event.isDropItems()) { + iblockdata.getBlock().a(this.world, this.player, blockposition, iblockdata, tileentity, itemstack2); + } + // CraftBukkit end + } + + // CraftBukkit start - Drop event experience + if (flag && event != null) { + iblockdata.getBlock().dropExperience(this.world, blockposition, event.getExpToDrop(), this.player); // Paper + } + // CraftBukkit end + + return flag; + } + } + } + + public EnumInteractionResult a(EntityHuman entityhuman, World world, ItemStack itemstack, EnumHand enumhand) { + if (this.gamemode == EnumGamemode.SPECTATOR) { + return EnumInteractionResult.PASS; + } else if (entityhuman.getCooldownTracker().a(itemstack.getItem())) { + return EnumInteractionResult.PASS; + } else { + int i = itemstack.getCount(); + int j = itemstack.getData(); + InteractionResultWrapper interactionresultwrapper = itemstack.a(world, entityhuman, enumhand); + ItemStack itemstack1 = (ItemStack) interactionresultwrapper.b(); + + if (itemstack1 == itemstack && itemstack1.getCount() == i && itemstack1.m() <= 0 && itemstack1.getData() == j) { + return interactionresultwrapper.a(); + } else if (interactionresultwrapper.a() == EnumInteractionResult.FAIL && itemstack1.m() > 0 && !entityhuman.isHandRaised()) { + return interactionresultwrapper.a(); + } else { + entityhuman.a(enumhand, itemstack1); + if (this.isCreative()) { + itemstack1.setCount(i); + if (itemstack1.f()) { + itemstack1.setData(j); + } + } + + if (itemstack1.isEmpty()) { + entityhuman.a(enumhand, ItemStack.a); + } + + if (!entityhuman.isHandRaised()) { + ((EntityPlayer) entityhuman).updateInventory(entityhuman.defaultContainer); + } + + return interactionresultwrapper.a(); + } + } + } + + // CraftBukkit start - whole method + public boolean interactResult = false; + public boolean firedInteract = false; + public EnumInteractionResult a(EntityHuman entityhuman, World world, ItemStack itemstack, EnumHand enumhand, BlockPosition blockposition, EnumDirection enumdirection, float f, float f1, float f2) { + IBlockData blockdata = world.getType(blockposition); + EnumInteractionResult enuminteractionresult = EnumInteractionResult.FAIL; + if (blockdata.getBlock() != Blocks.AIR) { + boolean cancelledBlock = false; + + if (this.gamemode == EnumGamemode.SPECTATOR) { + TileEntity tileentity = world.getTileEntity(blockposition); + cancelledBlock = !(tileentity instanceof ITileInventory || tileentity instanceof IInventory); + } + + if (entityhuman.getCooldownTracker().a(itemstack.getItem())) { + cancelledBlock = true; + } + + if (itemstack.getItem() instanceof ItemBlock && !entityhuman.isCreativeAndOp()) { + Block block1 = ((ItemBlock) itemstack.getItem()).getBlock(); + + if (block1 instanceof BlockCommand || block1 instanceof BlockStructure) { + cancelledBlock = true; + } + } + + PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(entityhuman, Action.RIGHT_CLICK_BLOCK, blockposition, enumdirection, itemstack, cancelledBlock, enumhand); + firedInteract = true; + interactResult = event.useItemInHand() == Event.Result.DENY; + + if (event.useInteractedBlock() == Event.Result.DENY) { + // If we denied a door from opening, we need to send a correcting update to the client, as it already opened the door. + if (blockdata.getBlock() instanceof BlockDoor) { + boolean bottom = blockdata.get(BlockDoor.HALF) == BlockDoor.EnumDoorHalf.LOWER; + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutBlockChange(world, bottom ? blockposition.up() : blockposition.down())); + } else if (blockdata.getBlock() instanceof BlockCake) { + ((EntityPlayer) entityhuman).getBukkitEntity().sendHealthUpdate(); // SPIGOT-1341 - reset health for cake + // Paper start - extend Player Interact cancellation + } else if (blockdata.getBlock() instanceof BlockStructure) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutCloseWindow()); + } else if (blockdata.getBlock() instanceof BlockCommand) { + ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutCloseWindow()); + } else if (blockdata.getBlock() instanceof BlockFlowerPot) { + // Send a block change to air and then send back the correct block, just to make the client happy + PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(this.world, blockposition); + packet.block = Blocks.AIR.getBlockData(); + this.player.playerConnection.sendPacket(packet); + + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); + + TileEntity tileentity = this.world.getTileEntity(blockposition); + if (tileentity != null) { + player.playerConnection.sendPacket(tileentity.getUpdatePacket()); + } + // Paper end - extend Player Interact cancellation + } + ((EntityPlayer) entityhuman).getBukkitEntity().updateInventory(); // SPIGOT-2867 + enuminteractionresult = (event.useItemInHand() != Event.Result.ALLOW) ? EnumInteractionResult.SUCCESS : EnumInteractionResult.PASS; + } else if (this.gamemode == EnumGamemode.SPECTATOR) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof ITileInventory) { + Block block = world.getType(blockposition).getBlock(); + ITileInventory itileinventory = (ITileInventory) tileentity; + + if (itileinventory instanceof TileEntityChest && block instanceof BlockChest) { + itileinventory = ((BlockChest) block).getInventory(world, blockposition); + } + + if (itileinventory != null) { + entityhuman.openContainer(itileinventory); + return EnumInteractionResult.SUCCESS; + } + } else if (tileentity instanceof IInventory) { + entityhuman.openContainer((IInventory) tileentity); + return EnumInteractionResult.SUCCESS; + } + + return EnumInteractionResult.PASS; + } else { + if (!entityhuman.isSneaking() || entityhuman.getItemInMainHand().isEmpty() && entityhuman.getItemInOffHand().isEmpty()) { + IBlockData iblockdata = world.getType(blockposition); + + enuminteractionresult = iblockdata.getBlock().interact(world, blockposition, iblockdata, entityhuman, enumhand, enumdirection, f, f1, f2) ? EnumInteractionResult.SUCCESS : EnumInteractionResult.PASS; + } + } + + if (!itemstack.isEmpty() && enuminteractionresult != EnumInteractionResult.SUCCESS && !interactResult) { // add !interactResult SPIGOT-764 + int i = itemstack.getData(); + int j = itemstack.getCount(); + + enuminteractionresult = itemstack.placeItem(entityhuman, world, blockposition, enumhand, enumdirection, f, f1, f2); + + // The item count should not decrement in Creative mode. + if (this.isCreative()) { + itemstack.setData(i); + itemstack.setCount(j); + } + } + } + return enuminteractionresult; + // CraftBukkit end + } + + public void a(WorldServer worldserver) { + this.world = worldserver; + } +} diff --git a/sources/src/main/java/net/minecraft/server/PlayerList.java b/sources/src/main/java/net/minecraft/server/PlayerList.java index a67804877..1fe4a330f 100644 --- a/sources/src/main/java/net/minecraft/server/PlayerList.java +++ b/sources/src/main/java/net/minecraft/server/PlayerList.java @@ -558,9 +558,10 @@ public abstract class PlayerList { Player player = entity.getBukkitEntity(); PlayerLoginEvent event = new PlayerLoginEvent(player, hostname, ((java.net.InetSocketAddress) socketaddress).getAddress(), ((java.net.InetSocketAddress) loginlistener.networkManager.getRawAddress()).getAddress()); String s; - - if (getProfileBans().isBanned(gameprofile) && !getProfileBans().get(gameprofile).hasExpired()) { - GameProfileBanEntry gameprofilebanentry = this.k.get(gameprofile); + // NeonPaper start - Fix MC-158900 + GameProfileBanEntry gameprofilebanentry; + if (getProfileBans().isBanned(gameprofile) && (gameprofilebanentry = getProfileBans().get(gameprofile)) != null) { + // NeonPaper end s = LocaleI18n.a(AkarinGlobalConfig.messageBan, gameprofilebanentry.getReason().equals(Akari.EMPTY_STRING) ? Akari.EMPTY_STRING : AkarinGlobalConfig.messageBanReason + gameprofilebanentry.getReason(), diff --git a/sources/src/main/java/net/minecraft/server/SpawnerCreature.java b/sources/src/main/java/net/minecraft/server/SpawnerCreature.java new file mode 100644 index 000000000..0a2f97784 --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/SpawnerCreature.java @@ -0,0 +1,356 @@ +package net.minecraft.server; + +import com.google.common.collect.Sets; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.Set; + +// CraftBukkit start +import com.destroystokyo.paper.exception.ServerInternalException; +import org.bukkit.craftbukkit.util.LongHash; +import org.bukkit.craftbukkit.util.LongHashSet; +import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; +// CraftBukkit end + +public final class SpawnerCreature { + + private static final int a = (int) Math.pow(17.0D, 2.0D); + private final LongHashSet b = new LongHashSet(); // CraftBukkit + + public SpawnerCreature() {} + + // Spigot start - get entity count only from chunks being processed in b + private int getEntityCount(WorldServer server, Class oClass) + { + // Paper start - use entire world, not just active chunks. Spigot broke vanilla expectations. + if (true) { + int sum = 0; + for (Chunk c : server.getChunkProviderServer().chunks.values()) { + sum += c.entityCount.get(oClass); + } + return sum; + } + // Paper end + int i = 0; + Iterator it = this.b.iterator(); + while ( it.hasNext() ) + { + Long coord = it.next(); + int x = LongHash.msw( coord ); + int z = LongHash.lsw( coord ); + if ( !((ChunkProviderServer)server.chunkProvider).unloadQueue.contains( coord ) && server.isChunkLoaded( x, z, true ) ) + { + i += server.getChunkAt( x, z ).entityCount.get( oClass ); + } + } + return i; + } + // Spigot end + + public int a(WorldServer worldserver, boolean flag, boolean flag1, boolean flag2) { + org.spigotmc.AsyncCatcher.catchOp("check for eligible spawn chunks"); // Paper - At least until we figure out what is calling this async + if (!flag && !flag1) { + return 0; + } else { + this.b.clear(); + int i = 0; + Iterator iterator = worldserver.players.iterator(); + + int j; + int k; + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); + + if (!entityhuman.isSpectator() && entityhuman.affectsSpawning) { + int l = MathHelper.floor(entityhuman.locX / 16.0D); + + j = MathHelper.floor(entityhuman.locZ / 16.0D); + boolean flag3 = true; + // Spigot Start + byte b0 = worldserver.spigotConfig.mobSpawnRange; + b0 = ( b0 > worldserver.spigotConfig.viewDistance ) ? (byte) worldserver.spigotConfig.viewDistance : b0; + b0 = ( b0 > 8 ) ? 8 : b0; + // Paper start + com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent event; + event = new com.destroystokyo.paper.event.entity.PlayerNaturallySpawnCreaturesEvent( + (org.bukkit.entity.Player) entityhuman.getBukkitEntity(), b0); + if (!event.callEvent()) { + continue; + } + b0 = event.getSpawnRadius(); + // Paperr end + + for (int i1 = -b0; i1 <= b0; ++i1) { + for (k = -b0; k <= b0; ++k) { + boolean flag4 = i1 == -b0 || i1 == b0 || k == -b0 || k == b0; + // Spigot End + ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i1 + l, k + j); + + // CraftBukkit start - use LongHash and LongHashSet + long chunkCoords = LongHash.toLong(chunkcoordintpair.x, chunkcoordintpair.z); + if (!this.b.contains(chunkCoords)) { + ++i; + if (!flag4 && worldserver.isChunkLoaded(chunkcoordintpair.x, chunkcoordintpair.z, true) && worldserver.getWorldBorder().isInBounds(chunkcoordintpair)) { + PlayerChunk playerchunk = worldserver.getPlayerChunkMap().getChunk(chunkcoordintpair.x, chunkcoordintpair.z); + + if (playerchunk != null && playerchunk.e()) { + this.b.add(chunkCoords); + // CraftBukkit end + } + } + } + } + } + } + } + + int j1 = 0; + BlockPosition blockposition = worldserver.getSpawn(); + EnumCreatureType[] aenumcreaturetype = EnumCreatureType.values(); + + j = aenumcreaturetype.length; + + for (int k1 = 0; k1 < j; ++k1) { + EnumCreatureType enumcreaturetype = aenumcreaturetype[k1]; + + // CraftBukkit start - Use per-world spawn limits + int limit = enumcreaturetype.b(); + switch (enumcreaturetype) { + case MONSTER: + limit = worldserver.getWorld().getMonsterSpawnLimit(); + break; + case CREATURE: + limit = worldserver.getWorld().getAnimalSpawnLimit(); + break; + case WATER_CREATURE: + limit = worldserver.getWorld().getWaterAnimalSpawnLimit(); + break; + case AMBIENT: + limit = worldserver.getWorld().getAmbientSpawnLimit(); + break; + } + + if (limit == 0) { + continue; + } + int mobcnt = 0; // Spigot + // CraftBukkit end + + if ((!enumcreaturetype.d() || flag1) && (enumcreaturetype.d() || flag) && (!enumcreaturetype.e() || flag2)) { + /* Paper start - As far as I can tell neither of these are even used + k = worldserver.a(enumcreaturetype.a()); + int l1 = limit * i / a; // CraftBukkit - use per-world limits + */ // Paper end + + if ((mobcnt = getEntityCount(worldserver, enumcreaturetype.a())) <= limit * i / 289) { // Paper - use 17x17 like vanilla (a at top of file) + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(); + Iterator iterator1 = this.b.iterator(); + + int moblimit = (limit * i / 256) - mobcnt + 1; // Spigot - up to 1 more than limit + label120: + while (iterator1.hasNext() && (moblimit > 0)) { // Spigot - while more allowed + // CraftBukkit start = use LongHash and LongObjectHashMap + long key = ((Long) iterator1.next()).longValue(); + BlockPosition blockposition1 = getRandomPosition(worldserver, LongHash.msw(key), LongHash.lsw(key)); + // CraftBukkit + int i2 = blockposition1.getX(); + int j2 = blockposition1.getY(); + int k2 = blockposition1.getZ(); + IBlockData iblockdata = worldserver.getWorldBorder().isInBounds(blockposition1) ? worldserver.getTypeIfLoaded(blockposition1) : null; // Paper + + if (iblockdata != null && !iblockdata.l()) { // Paper + int l2 = 0; + int i3 = 0; + + while (i3 < 3) { + int j3 = i2; + int k3 = j2; + int l3 = k2; + boolean flag5 = true; + BiomeBase.BiomeMeta biomebase_biomemeta = null; + GroupDataEntity groupdataentity = null; + int i4 = MathHelper.f(Math.random() * 4.0D); + int j4 = 0; + + while (true) { + if (j4 < i4) { + label113: { + j3 += worldserver.random.nextInt(6) - worldserver.random.nextInt(6); + k3 += worldserver.random.nextInt(1) - worldserver.random.nextInt(1); + l3 += worldserver.random.nextInt(6) - worldserver.random.nextInt(6); + blockposition_mutableblockposition.c(j3, k3, l3); + float f = (float) j3 + 0.5F; + float f1 = (float) l3 + 0.5F; + + if (worldserver.getWorldBorder().isInBounds(blockposition_mutableblockposition) && worldserver.getChunkIfLoaded(blockposition_mutableblockposition) != null && !worldserver.isPlayerNearby((double) f, (double) k3, (double) f1, 24.0D) && blockposition.distanceSquared((double) f, (double) k3, (double) f1) >= 576.0D) { // Paper - Prevent mob spawning from loading/generating chunks + if (biomebase_biomemeta == null) { + biomebase_biomemeta = worldserver.a(enumcreaturetype, (BlockPosition) blockposition_mutableblockposition); + if (biomebase_biomemeta == null) { + break label113; + } + } + + if (worldserver.a(enumcreaturetype, biomebase_biomemeta, (BlockPosition) blockposition_mutableblockposition) && a(EntityPositionTypes.a(biomebase_biomemeta.b), worldserver, blockposition_mutableblockposition)) { + // Paper start + com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent event; + Class cls = biomebase_biomemeta.b; + org.bukkit.entity.EntityType type = EntityTypes.clsToTypeMap.get(cls); + if (type != null) { + event = new com.destroystokyo.paper.event.entity.PreCreatureSpawnEvent( + MCUtil.toLocation(worldserver, blockposition_mutableblockposition), + type, SpawnReason.NATURAL + ); + if (!event.callEvent()) { + if (event.shouldAbortSpawn()) { + continue label120; + } + j1 += l2; + ++j4; + continue; + } + } + // Paper end + EntityInsentient entityinsentient; + + try { + entityinsentient = (EntityInsentient) biomebase_biomemeta.b.getConstructor(new Class[] { World.class}).newInstance(new Object[] { worldserver}); + } catch (Exception exception) { + exception.printStackTrace(); + ServerInternalException.reportInternalException(exception); // Paper + return j1; + } + + entityinsentient.setPositionRotation((double) f, (double) k3, (double) f1, worldserver.random.nextFloat() * 360.0F, 0.0F); + if (entityinsentient.P() && entityinsentient.canSpawn()) { + groupdataentity = entityinsentient.prepare(worldserver.D(new BlockPosition(entityinsentient)), groupdataentity); + if (entityinsentient.canSpawn()) { + // CraftBukkit start + if (worldserver.addEntity(entityinsentient, SpawnReason.NATURAL)) { + ++l2; + moblimit--; // Spigot + } + // CraftBukkit end + } else { + entityinsentient.die(); + } + + // Spigot start + if ( moblimit <= 0 ) { + // If we're past limit, stop spawn + // Spigot end + continue label120; + } + } + + j1 += l2; + } + } + + ++j4; + continue; + } + } + + ++i3; + break; + } + } + } + } + } + } + } + + return j1; + } + } + + private static BlockPosition getRandomPosition(World world, int i, int j) { + Chunk chunk = world.getChunkAt(i, j); + int k = i * 16 + world.random.nextInt(16); + int l = j * 16 + world.random.nextInt(16); + int i1 = MathHelper.c(chunk.e(new BlockPosition(k, 0, l)) + 1, 16); + int j1 = world.random.nextInt(i1 > 0 ? i1 : chunk.g() + 16 - 1); + + return new BlockPosition(k, j1, l); + } + + public static boolean a(IBlockData iblockdata) { + return iblockdata.k() ? false : (iblockdata.m() ? false : (iblockdata.getMaterial().isLiquid() ? false : !BlockMinecartTrackAbstract.i(iblockdata))); + } + + public static boolean a(EntityInsentient.EnumEntityPositionType entityinsentient_enumentitypositiontype, World world, BlockPosition blockposition) { + if (!world.getWorldBorder().a(blockposition)) { + return false; + } else { + IBlockData iblockdata = world.getType(blockposition); + + if (entityinsentient_enumentitypositiontype == EntityInsentient.EnumEntityPositionType.IN_WATER) { + return iblockdata.getMaterial() == Material.WATER && world.getType(blockposition.down()).getMaterial() == Material.WATER && !world.getType(blockposition.up()).l(); + } else { + BlockPosition blockposition1 = blockposition.down(); + + if (!world.getType(blockposition1).q()) { + return false; + } else { + Block block = world.getType(blockposition1).getBlock(); + boolean flag = block != Blocks.BEDROCK && block != Blocks.BARRIER; + + return flag && a(iblockdata) && a(world.getType(blockposition.up())); + } + } + } + } + + public static void a(World world, BiomeBase biomebase, int i, int j, int k, int l, Random random) { + List list = biomebase.getMobs(EnumCreatureType.CREATURE); + + if (!list.isEmpty()) { + while (random.nextFloat() < biomebase.f()) { + BiomeBase.BiomeMeta biomebase_biomemeta = (BiomeBase.BiomeMeta) WeightedRandom.a(world.random, list); + int i1 = biomebase_biomemeta.c + random.nextInt(1 + biomebase_biomemeta.d - biomebase_biomemeta.c); + GroupDataEntity groupdataentity = null; + int j1 = i + random.nextInt(k); + int k1 = j + random.nextInt(l); + int l1 = j1; + int i2 = k1; + + for (int j2 = 0; j2 < i1; ++j2) { + boolean flag = false; + + for (int k2 = 0; !flag && k2 < 4; ++k2) { + BlockPosition blockposition = world.q(new BlockPosition(j1, 0, k1)); + + if (a(EntityInsentient.EnumEntityPositionType.ON_GROUND, world, blockposition)) { + EntityInsentient entityinsentient; + + try { + entityinsentient = (EntityInsentient) biomebase_biomemeta.b.getConstructor(new Class[] { World.class}).newInstance(new Object[] { world}); + } catch (Exception exception) { + exception.printStackTrace(); + ServerInternalException.reportInternalException(exception); // Paper + continue; + } + + entityinsentient.setPositionRotation((double) ((float) j1 + 0.5F), (double) blockposition.getY(), (double) ((float) k1 + 0.5F), random.nextFloat() * 360.0F, 0.0F); + // CraftBukkit start - Added a reason for spawning this creature, moved entityinsentient.prepare(groupdataentity) up + groupdataentity = entityinsentient.prepare(world.D(new BlockPosition(entityinsentient)), groupdataentity); + world.addEntity(entityinsentient, SpawnReason.CHUNK_GEN); + // CraftBukkit end + flag = true; + } + + j1 += random.nextInt(5) - random.nextInt(5); + + for (k1 += random.nextInt(5) - random.nextInt(5); j1 < i || j1 >= i + k || k1 < j || k1 >= j + k; k1 = i2 + random.nextInt(5) - random.nextInt(5)) { + j1 = l1 + random.nextInt(5) - random.nextInt(5); + } + } + } + } + + } + } +} diff --git a/sources/src/main/java/net/minecraft/server/TileEntityFurnace.java b/sources/src/main/java/net/minecraft/server/TileEntityFurnace.java new file mode 100644 index 000000000..3d919a36f --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/TileEntityFurnace.java @@ -0,0 +1,423 @@ +package net.minecraft.server; + +import java.util.Iterator; +// CraftBukkit start +import java.util.List; + +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.FurnaceBurnEvent; +import org.bukkit.event.inventory.FurnaceSmeltEvent; +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +// CraftBukkit end + +public class TileEntityFurnace extends TileEntityContainer implements ITickable, IWorldInventory { + + private static final int[] a = new int[] { 0}; + private static final int[] f = new int[] { 2, 1}; + private static final int[] g = new int[] { 1}; + private NonNullList items; + private int burnTime; + private int ticksForCurrentFuel; + private int cookTime; + private int cookTimeTotal; + private String m; + + // CraftBukkit start - add fields and methods + private int lastTick = MinecraftServer.currentTick; + private int maxStack = MAX_STACK; + public List transaction = new java.util.ArrayList(); + + public List getContents() { + return this.items; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + public TileEntityFurnace() { + this.items = NonNullList.a(3, ItemStack.a); + } + + public int getSize() { + return this.items.size(); + } + + public boolean x_() { + Iterator iterator = this.items.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (itemstack.isEmpty()); + + return false; + } + + public ItemStack getItem(int i) { + return (ItemStack) this.items.get(i); + } + + public ItemStack splitStack(int i, int j) { + return ContainerUtil.a(this.items, i, j); + } + + public ItemStack splitWithoutUpdate(int i) { + return ContainerUtil.a(this.items, i); + } + + public void setItem(int i, ItemStack itemstack) { + ItemStack itemstack1 = (ItemStack) this.items.get(i); + boolean flag = !itemstack.isEmpty() && itemstack.doMaterialsMatch(itemstack1) && ItemStack.equals(itemstack, itemstack1); + + this.items.set(i, itemstack); + if (itemstack.getCount() > this.getMaxStackSize()) { + itemstack.setCount(this.getMaxStackSize()); + } + + if (i == 0 && !flag) { + this.cookTimeTotal = this.a(itemstack); + this.cookTime = 0; + this.update(); + } + + } + + public String getName() { + return this.hasCustomName() ? this.m : "container.furnace"; + } + + public boolean hasCustomName() { + return this.m != null && !this.m.isEmpty(); + } + + public void setCustomName(String s) { + this.m = s; + } + + public static void a(DataConverterManager dataconvertermanager) { + dataconvertermanager.a(DataConverterTypes.BLOCK_ENTITY, (DataInspector) (new DataInspectorItemList(TileEntityFurnace.class, new String[] { "Items"}))); + } + + public void load(NBTTagCompound nbttagcompound) { + super.load(nbttagcompound); + this.items = NonNullList.a(this.getSize(), ItemStack.a); + ContainerUtil.b(nbttagcompound, this.items); + this.burnTime = nbttagcompound.getShort("BurnTime"); + this.cookTime = nbttagcompound.getShort("CookTime"); + this.cookTimeTotal = nbttagcompound.getShort("CookTimeTotal"); + this.ticksForCurrentFuel = fuelTime((ItemStack) this.items.get(1)); + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.m = nbttagcompound.getString("CustomName"); + } + + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + super.save(nbttagcompound); + nbttagcompound.setShort("BurnTime", (short) this.burnTime); + nbttagcompound.setShort("CookTime", (short) this.cookTime); + nbttagcompound.setShort("CookTimeTotal", (short) this.cookTimeTotal); + ContainerUtil.a(nbttagcompound, this.items); + if (this.hasCustomName()) { + nbttagcompound.setString("CustomName", this.m); + } + + return nbttagcompound; + } + + public int getMaxStackSize() { + return 64; + } + + public boolean isBurning() { + return this.burnTime > 0; + } + + public void e() { + boolean flag = (this.getBlock() == Blocks.LIT_FURNACE); // CraftBukkit - SPIGOT-844 - Check if furnace block is lit using the block instead of burn time + boolean flag1 = false; + + // CraftBukkit start - Use wall time instead of ticks for cooking + int elapsedTicks = MinecraftServer.currentTick - this.lastTick; + this.lastTick = MinecraftServer.currentTick; + + // CraftBukkit - moved from below - edited for wall time + if (this.isBurning() && this.canBurn()) { + this.cookTime += elapsedTicks; + if (this.cookTime >= this.cookTimeTotal) { + this.cookTime -= this.cookTimeTotal; // Paper + this.cookTimeTotal = this.a((ItemStack) this.items.get(0)); + this.burn(); + flag1 = true; + } + } else { + this.cookTime = 0; + } + // CraftBukkit end + + if (this.isBurning()) { + this.burnTime -= elapsedTicks; // CraftBukkit - use elapsedTicks in place of constant + } + + if (!this.world.isClientSide) { + ItemStack itemstack = (ItemStack) this.items.get(1); + + if (!this.isBurning() && (itemstack.isEmpty() || ((ItemStack) this.items.get(0)).isEmpty())) { + if (!this.isBurning() && this.cookTime > 0) { + this.cookTime = MathHelper.clamp(this.cookTime - 2, 0, this.cookTimeTotal); + } + } else if(this.items.get(1) != null && this.items.get(1).getItem() != Items.BUCKET) { + // CraftBukkit start - Handle multiple elapsed ticks + if (this.burnTime <= 0 && this.canBurn()) { // CraftBukkit - == to <= + CraftItemStack fuel = CraftItemStack.asCraftMirror(itemstack); + + FurnaceBurnEvent furnaceBurnEvent = new FurnaceBurnEvent(this.world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()), fuel, fuelTime(itemstack)); + this.world.getServer().getPluginManager().callEvent(furnaceBurnEvent); + + if (furnaceBurnEvent.isCancelled()) { + return; + } + + this.ticksForCurrentFuel = furnaceBurnEvent.getBurnTime(); + this.burnTime += this.ticksForCurrentFuel; + if (this.burnTime > 0 && furnaceBurnEvent.isBurning()) { + // CraftBukkit end + flag1 = true; + if (!itemstack.isEmpty()) { + Item item = itemstack.getItem(); + + itemstack.subtract(1); + if (itemstack.isEmpty()) { + Item item1 = item.q(); + + this.items.set(1, item1 == null ? ItemStack.a : new ItemStack(item1)); + } + } + } + } + + /* CraftBukkit start - Moved up + if (this.isBurning() && this.canBurn()) { + ++this.cookTime; + if (this.cookTime == this.cookTimeTotal) { + this.cookTime = 0; + this.cookTimeTotal = this.a((ItemStack) this.items.get(0)); + this.burn(); + flag1 = true; + } + } else { + this.cookTime = 0; + } + */ + } + + if (flag != this.isBurning()) { + flag1 = true; + BlockFurnace.a(this.isBurning(), this.world, this.position); + this.invalidateBlockCache(); // CraftBukkit - Invalidate tile entity's cached block type + } + } + + if (flag1) { + this.update(); + } + + } + + public int a(ItemStack itemstack) { + return 200; + } + + private boolean canBurn() { + if (((ItemStack) this.items.get(0)).isEmpty()) { + return false; + } else { + ItemStack itemstack = RecipesFurnace.getInstance().getResult((ItemStack) this.items.get(0)); + + if (itemstack.isEmpty()) { + return false; + } else { + ItemStack itemstack1 = (ItemStack) this.items.get(2); + + // CraftBukkit - consider resultant count instead of current count + return itemstack1.isEmpty() ? true : (!itemstack1.doMaterialsMatch(itemstack) ? false : (itemstack1.getCount() + itemstack.getCount() <= this.getMaxStackSize() && itemstack1.getCount() + itemstack.getCount() < itemstack1.getMaxStackSize() ? true : itemstack1.getCount() + itemstack.getCount() <= itemstack.getMaxStackSize())); + } + } + } + + public void burn() { + if (this.canBurn()) { + ItemStack itemstack = (ItemStack) this.items.get(0); + ItemStack itemstack1 = RecipesFurnace.getInstance().getResult(itemstack); + ItemStack itemstack2 = (ItemStack) this.items.get(2); + + // CraftBukkit start - fire FurnaceSmeltEvent + CraftItemStack source = CraftItemStack.asCraftMirror(itemstack); + org.bukkit.inventory.ItemStack result = CraftItemStack.asBukkitCopy(itemstack1); + + FurnaceSmeltEvent furnaceSmeltEvent = new FurnaceSmeltEvent(this.world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()), source, result); + this.world.getServer().getPluginManager().callEvent(furnaceSmeltEvent); + + if (furnaceSmeltEvent.isCancelled()) { + return; + } + + result = furnaceSmeltEvent.getResult(); + itemstack1 = CraftItemStack.asNMSCopy(result); + + if (!itemstack1.isEmpty()) { + if (itemstack2.isEmpty()) { + this.items.set(2, itemstack1.cloneItemStack()); + } else if (CraftItemStack.asCraftMirror(itemstack2).isSimilar(result)) { + itemstack2.add(itemstack1.getCount()); + } else { + return; + } + } + + /* + if (itemstack2.isEmpty()) { + this.items.set(2, itemstack1.cloneItemStack()); + } else if (itemstack2.getItem() == itemstack1.getItem()) { + itemstack2.add(1); + } + */ + // CraftBukkit end + + if (itemstack.getItem() == Item.getItemOf(Blocks.SPONGE) && itemstack.getData() == 1 && !((ItemStack) this.items.get(1)).isEmpty() && ((ItemStack) this.items.get(1)).getItem() == Items.BUCKET) { + this.items.set(1, new ItemStack(Items.WATER_BUCKET)); + } + + itemstack.subtract(1); + } + } + + public static int fuelTime(ItemStack itemstack) { + if (itemstack.isEmpty()) { + return 0; + } else { + Item item = itemstack.getItem(); + + return item == Item.getItemOf(Blocks.WOODEN_SLAB) ? 150 : (item == Item.getItemOf(Blocks.WOOL) ? 100 : (item == Item.getItemOf(Blocks.CARPET) ? 67 : (item == Item.getItemOf(Blocks.LADDER) ? 300 : (item == Item.getItemOf(Blocks.WOODEN_BUTTON) ? 100 : (Block.asBlock(item).getBlockData().getMaterial() == Material.WOOD ? 300 : (item == Item.getItemOf(Blocks.COAL_BLOCK) ? 16000 : (item instanceof ItemTool && "WOOD".equals(((ItemTool) item).h()) ? 200 : (item instanceof ItemSword && "WOOD".equals(((ItemSword) item).h()) ? 200 : (item instanceof ItemHoe && "WOOD".equals(((ItemHoe) item).g()) ? 200 : (item == Items.STICK ? 100 : (item != Items.BOW && item != Items.FISHING_ROD ? (item == Items.SIGN ? 200 : (item == Items.COAL ? 1600 : (item == Items.LAVA_BUCKET ? 20000 : (item != Item.getItemOf(Blocks.SAPLING) && item != Items.BOWL ? (item == Items.BLAZE_ROD ? 2400 : (item instanceof ItemDoor && item != Items.IRON_DOOR ? 200 : (item instanceof ItemBoat ? 400 : 0))) : 100)))) : 300))))))))))); + } + } + + public static boolean isFuel(ItemStack itemstack) { + return fuelTime(itemstack) > 0; + } + + public boolean a(EntityHuman entityhuman) { + return this.world.getTileEntity(this.position) != this ? false : entityhuman.d((double) this.position.getX() + 0.5D, (double) this.position.getY() + 0.5D, (double) this.position.getZ() + 0.5D) <= 64.0D; + } + + public void startOpen(EntityHuman entityhuman) {} + + public void closeContainer(EntityHuman entityhuman) {} + + public boolean b(int i, ItemStack itemstack) { + if (i == 2) { + return false; + } else if (i != 1) { + return true; + } else { + ItemStack itemstack1 = (ItemStack) this.items.get(1); + + return isFuel(itemstack) || SlotFurnaceFuel.d_(itemstack) && itemstack1.getItem() != Items.BUCKET; + } + } + + public int[] getSlotsForFace(EnumDirection enumdirection) { + return enumdirection == EnumDirection.DOWN ? TileEntityFurnace.f : (enumdirection == EnumDirection.UP ? TileEntityFurnace.a : TileEntityFurnace.g); + } + + public boolean canPlaceItemThroughFace(int i, ItemStack itemstack, EnumDirection enumdirection) { + return this.b(i, itemstack); + } + + public boolean canTakeItemThroughFace(int i, ItemStack itemstack, EnumDirection enumdirection) { + if (enumdirection == EnumDirection.DOWN && i == 1) { + Item item = itemstack.getItem(); + + if (item != Items.WATER_BUCKET && item != Items.BUCKET) { + return false; + } + } + + return true; + } + + public String getContainerName() { + return "minecraft:furnace"; + } + + public Container createContainer(PlayerInventory playerinventory, EntityHuman entityhuman) { + return new ContainerFurnace(playerinventory, this); + } + + public int getProperty(int i) { + switch (i) { + case 0: + return this.burnTime; + + case 1: + return this.ticksForCurrentFuel; + + case 2: + return this.cookTime; + + case 3: + return this.cookTimeTotal; + + default: + return 0; + } + } + + public void setProperty(int i, int j) { + switch (i) { + case 0: + this.burnTime = j; + break; + + case 1: + this.ticksForCurrentFuel = j; + break; + + case 2: + this.cookTime = j; + break; + + case 3: + this.cookTimeTotal = j; + } + + } + + public int h() { + return 4; + } + + public void clear() { + this.items.clear(); + } +} diff --git a/sources/src/main/java/net/minecraft/server/TileEntityHopper.java b/sources/src/main/java/net/minecraft/server/TileEntityHopper.java new file mode 100644 index 000000000..d9cbba32b --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/TileEntityHopper.java @@ -0,0 +1,784 @@ +package net.minecraft.server; + +import java.util.Iterator; +import java.util.List; +import javax.annotation.Nullable; + +// CraftBukkit start +import org.bukkit.craftbukkit.entity.CraftHumanEntity; +import org.bukkit.craftbukkit.inventory.CraftItemStack; +import org.bukkit.entity.HumanEntity; +import org.bukkit.event.inventory.InventoryMoveItemEvent; +import org.bukkit.event.inventory.InventoryPickupItemEvent; +import org.bukkit.inventory.Inventory; +// CraftBukkit end + +public class TileEntityHopper extends TileEntityLootable implements IHopper, ITickable { + + private NonNullList items; + private int f; + private long g; + + // CraftBukkit start - add fields and methods + public List transaction = new java.util.ArrayList(); + private int maxStack = MAX_STACK; + + public List getContents() { + return this.items; + } + + public void onOpen(CraftHumanEntity who) { + transaction.add(who); + } + + public void onClose(CraftHumanEntity who) { + transaction.remove(who); + } + + public List getViewers() { + return transaction; + } + + public void setMaxStackSize(int size) { + maxStack = size; + } + // CraftBukkit end + + public TileEntityHopper() { + this.items = NonNullList.a(5, ItemStack.a); + this.f = -1; + } + + public static void a(DataConverterManager dataconvertermanager) { + dataconvertermanager.a(DataConverterTypes.BLOCK_ENTITY, (DataInspector) (new DataInspectorItemList(TileEntityHopper.class, new String[] { "Items"}))); + } + + public void load(NBTTagCompound nbttagcompound) { + super.load(nbttagcompound); + this.items = NonNullList.a(this.getSize(), ItemStack.a); + if (!this.c(nbttagcompound)) { + ContainerUtil.b(nbttagcompound, this.items); + } + + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.o = nbttagcompound.getString("CustomName"); + } + + this.f = nbttagcompound.getInt("TransferCooldown"); + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + super.save(nbttagcompound); + if (!this.d(nbttagcompound)) { + ContainerUtil.a(nbttagcompound, this.items); + } + + nbttagcompound.setInt("TransferCooldown", this.f); + if (this.hasCustomName()) { + nbttagcompound.setString("CustomName", this.o); + } + + return nbttagcompound; + } + + public int getSize() { + return this.items.size(); + } + + public ItemStack splitStack(int i, int j) { + this.d((EntityHuman) null); + ItemStack itemstack = ContainerUtil.a(this.q(), i, j); + + return itemstack; + } + + public void setItem(int i, ItemStack itemstack) { + this.d((EntityHuman) null); + this.q().set(i, itemstack); + if (itemstack.getCount() > this.getMaxStackSize()) { + itemstack.setCount(this.getMaxStackSize()); + } + + } + + public String getName() { + return this.hasCustomName() ? this.o : "container.hopper"; + } + + public int getMaxStackSize() { + return maxStack; // CraftBukkit + } + + public void e() { + if (this.world != null && !this.world.isClientSide) { + --this.f; + this.g = this.world.getTime(); + if (!this.J()) { + this.setCooldown(0); + // Spigot start + if (!this.o() && this.world.spigotConfig.hopperCheck > 1) { + this.setCooldown(this.world.spigotConfig.hopperCheck); + } + // Spigot end + } + + } + } + + private boolean o() { + mayAcceptItems = false; // Paper - at the beginning of a tick, assume we can't accept items + if (this.world != null && !this.world.isClientSide) { + if (!this.J() && BlockHopper.f(this.v())) { + boolean flag = false; + + if (!this.p()) { + flag = this.s(); + } + + if (!this.r()) { + mayAcceptItems = true; // Paper - flag this hopper to be able to accept items + flag = a((IHopper) this) || flag; + } + + if (flag) { + this.setCooldown(world.spigotConfig.hopperTransfer); // Spigot + this.update(); + return true; + } + } + return false; + } else { + return false; + } + } + + // Paper start + private boolean mayAcceptItems = false; + + public boolean canAcceptItems() { + return mayAcceptItems; + } + // Paper end + + private boolean p() { + Iterator iterator = this.items.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (itemstack.isEmpty()); + + return false; + } + + public boolean x_() { + return this.p(); + } + + private boolean r() { + Iterator iterator = this.items.iterator(); + + ItemStack itemstack; + + do { + if (!iterator.hasNext()) { + return true; + } + + itemstack = (ItemStack) iterator.next(); + } while (!itemstack.isEmpty() && itemstack.getCount() == itemstack.getMaxStackSize()); + + return false; + } + + // Paper start - Optimize Hoppers + private static boolean skipPullModeEventFire = false; + private static boolean skipPushModeEventFire = false; + static boolean skipHopperEvents = false; + + private boolean hopperPush(IInventory iinventory, EnumDirection enumdirection) { + skipPushModeEventFire = skipHopperEvents; + boolean foundItem = false; + for (int i = 0; i < this.getSize(); ++i) { + if (!this.getItem(i).isEmpty()) { + foundItem = true; + ItemStack origItemStack = this.getItem(i); + ItemStack itemstack = origItemStack; + + final int origCount = origItemStack.getCount(); + final int moved = Math.min(world.spigotConfig.hopperAmount, origCount); + origItemStack.setCount(moved); + + // We only need to fire the event once to give protection plugins a chance to cancel this event + // Because nothing uses getItem, every event call should end up the same result. + if (!skipPushModeEventFire) { + itemstack = callPushMoveEvent(iinventory, itemstack); + if (itemstack == null) { // cancelled + origItemStack.setCount(origCount); + return false; + } + } + final ItemStack itemstack2 = addItem(this, iinventory, itemstack, enumdirection); + final int remaining = itemstack2.getCount(); + if (remaining != moved) { + origItemStack = origItemStack.cloneItemStack(); + origItemStack.setCount(origCount - moved + remaining); + this.setItem(i, origItemStack); + iinventory.update(); + return true; + } + origItemStack.setCount(origCount); + } + } + if (foundItem && world.paperConfig.cooldownHopperWhenFull) { // Inventory was full - cooldown + this.setCooldown(world.spigotConfig.hopperTransfer); + } + return false; + } + + private static boolean hopperPull(IHopper ihopper, IInventory iinventory, int i) { + ItemStack origItemStack = iinventory.getItem(i); + ItemStack itemstack = origItemStack; + final int origCount = origItemStack.getCount(); + final World world = ihopper.getWorld(); + final int moved = Math.min(world.spigotConfig.hopperAmount, origCount); + itemstack.setCount(moved); + + if (!skipPullModeEventFire) { + itemstack = callPullMoveEvent(ihopper, iinventory, itemstack); + if (itemstack == null) { // cancelled + origItemStack.setCount(origCount); + // Drastically improve performance by returning true. + // No plugin could of relied on the behavior of false as the other call + // site for IMIE did not exhibit the same behavior + return true; + } + } + + final ItemStack itemstack2 = addItem(iinventory, ihopper, itemstack, null); + final int remaining = itemstack2.getCount(); + if (remaining != moved) { + origItemStack = origItemStack.cloneItemStack(); + origItemStack.setCount(origCount - moved + remaining); + IGNORE_TILE_UPDATES = true; + iinventory.setItem(i, origItemStack); + IGNORE_TILE_UPDATES = false; + iinventory.update(); + return true; + } + origItemStack.setCount(origCount); + + if (world.paperConfig.cooldownHopperWhenFull) { + cooldownHopper(ihopper); + } + + return false; + } + + private ItemStack callPushMoveEvent(IInventory iinventory, ItemStack itemstack) { + Inventory destinationInventory = getInventory(iinventory); + InventoryMoveItemEvent event = new InventoryMoveItemEvent(this.getOwner(false).getInventory(), + CraftItemStack.asCraftMirror(itemstack), destinationInventory, true); + boolean result = event.callEvent(); + if (!event.calledGetItem && !event.calledSetItem) { + skipPushModeEventFire = true; + } + if (!result) { + cooldownHopper(this); + return null; + } + + if (event.calledSetItem) { + return CraftItemStack.asNMSCopy(event.getItem()); + } else { + return itemstack; + } + } + + private static ItemStack callPullMoveEvent(IHopper hopper, IInventory iinventory, ItemStack itemstack) { + Inventory sourceInventory = getInventory(iinventory); + Inventory destination = getInventory(hopper); + + InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, + // Mirror is safe as we no plugins ever use this item + CraftItemStack.asCraftMirror(itemstack), destination, false); + boolean result = event.callEvent(); + if (!event.calledGetItem && !event.calledSetItem) { + skipPullModeEventFire = true; + } + if (!result) { + cooldownHopper(hopper); + return null; + } + + if (event.calledSetItem) { + return CraftItemStack.asNMSCopy(event.getItem()); + } else { + return itemstack; + } + } + + private static Inventory getInventory(IInventory iinventory) { + Inventory sourceInventory;// Have to special case large chests as they work oddly + if (iinventory instanceof InventoryLargeChest) { + sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory); + } else if (iinventory instanceof TileEntity) { + sourceInventory = ((TileEntity) iinventory).getOwner(false).getInventory(); + } else { + sourceInventory = iinventory.getOwner().getInventory(); + } + return sourceInventory; + } + + private static void cooldownHopper(IHopper hopper) { + if (hopper instanceof TileEntityHopper) { + ((TileEntityHopper) hopper).setCooldown(hopper.getWorld().spigotConfig.hopperTransfer); + } else if (hopper instanceof EntityMinecartHopper) { + ((EntityMinecartHopper) hopper).setCooldown(hopper.getWorld().spigotConfig.hopperTransfer / 2); + } + } + + // Paper end + private boolean s() { + IInventory iinventory = this.I(); + + if (iinventory == null) { + return false; + } else { + EnumDirection enumdirection = BlockHopper.b(this.v()).opposite(); + + if (this.a(iinventory, enumdirection)) { + return false; + } else { + return hopperPush(iinventory, enumdirection); /* // Paper - disable rest + for (int i = 0; i < this.getSize(); ++i) { + if (!this.getItem(i).isEmpty()) { + ItemStack itemstack = this.getItem(i).cloneItemStack(); + // ItemStack itemstack1 = addItem(this, iinventory, this.splitStack(i, 1), enumdirection); + + // CraftBukkit start - Call event when pushing items into other inventories + CraftItemStack oitemstack = CraftItemStack.asCraftMirror(this.splitStack(i, world.spigotConfig.hopperAmount)); // Spigot + + Inventory destinationInventory; + // Have to special case large chests as they work oddly + if (iinventory instanceof InventoryLargeChest) { + destinationInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory); + } else { + destinationInventory = iinventory.getOwner().getInventory(); + } + + InventoryMoveItemEvent event = new InventoryMoveItemEvent(this.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true); + this.getWorld().getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + this.setItem(i, itemstack); + this.setCooldown(world.spigotConfig.hopperTransfer); // Spigot + return false; + } + int origCount = event.getItem().getAmount(); // Spigot + ItemStack itemstack1 = addItem(this, iinventory, CraftItemStack.asNMSCopy(event.getItem()), enumdirection); + + if (itemstack1.isEmpty()) { + if (event.getItem().equals(oitemstack)) { + iinventory.update(); + } else { + this.setItem(i, itemstack); + } + // CraftBukkit end + return true; + } + + itemstack.subtract(origCount - itemstack1.getCount()); // Spigot + this.setItem(i, itemstack); + } + } + + return false;*/ // Paper - end commenting out replaced block for Hopper Optimizations + } + } + } + + private boolean a(IInventory iinventory, EnumDirection enumdirection) { + if (iinventory instanceof IWorldInventory) { + IWorldInventory iworldinventory = (IWorldInventory) iinventory; + int[] aint = iworldinventory.getSlotsForFace(enumdirection); + int[] aint1 = aint; + int i = aint.length; + + for (int j = 0; j < i; ++j) { + int k = aint1[j]; + ItemStack itemstack = iworldinventory.getItem(k); + + if (itemstack.isEmpty() || itemstack.getCount() != itemstack.getMaxStackSize()) { + return false; + } + } + } else { + int l = iinventory.getSize(); + + for (int i1 = 0; i1 < l; ++i1) { + ItemStack itemstack1 = iinventory.getItem(i1); + + if (itemstack1.isEmpty() || itemstack1.getCount() != itemstack1.getMaxStackSize()) { + return false; + } + } + } + + return true; + } + + private static boolean b(IInventory iinventory, EnumDirection enumdirection) { + if (iinventory instanceof IWorldInventory) { + IWorldInventory iworldinventory = (IWorldInventory) iinventory; + int[] aint = iworldinventory.getSlotsForFace(enumdirection); + int[] aint1 = aint; + int i = aint.length; + + for (int j = 0; j < i; ++j) { + int k = aint1[j]; + + if (!iworldinventory.getItem(k).isEmpty()) { + return false; + } + } + } else { + int l = iinventory.getSize(); + + for (int i1 = 0; i1 < l; ++i1) { + if (!iinventory.getItem(i1).isEmpty()) { + return false; + } + } + } + + return true; + } + + // Paper start - split methods, and only do entity lookup if in pull mode + public static boolean a(IHopper ihopper) { + IInventory iinventory = getInventory(ihopper, !(ihopper instanceof TileEntityHopper) || !ihopper.getWorld().paperConfig.isHopperPushBased); + + return acceptItem(ihopper, iinventory); + } + + public static boolean acceptItem(IHopper ihopper, IInventory iinventory) { + // Paper end + + if (iinventory != null) { + EnumDirection enumdirection = EnumDirection.DOWN; + + if (b(iinventory, enumdirection)) { + return false; + } + skipPullModeEventFire = skipHopperEvents; // Paper + + if (iinventory instanceof IWorldInventory) { + IWorldInventory iworldinventory = (IWorldInventory) iinventory; + int[] aint = iworldinventory.getSlotsForFace(enumdirection); + int[] aint1 = aint; + int i = aint.length; + + for (int j = 0; j < i; ++j) { + int k = aint1[j]; + + if (a(ihopper, iinventory, k, enumdirection)) { + return true; + } + } + } else { + int l = iinventory.getSize(); + + for (int i1 = 0; i1 < l; ++i1) { + if (a(ihopper, iinventory, i1, enumdirection)) { + return true; + } + } + } + } else if (!ihopper.getWorld().paperConfig.isHopperPushBased || !(ihopper instanceof TileEntityHopper)) { // Paper - only search for entities in 'pull mode' + Iterator iterator = a(ihopper.getWorld(), ihopper.E(), ihopper.F(), ihopper.G()).iterator(); // Change getHopperLookupBoundingBox() if this ever changes + + while (iterator.hasNext()) { + EntityItem entityitem = (EntityItem) iterator.next(); + + if (a((IInventory) null, ihopper, entityitem)) { + return true; + } + } + } + + return false; + } + + private static boolean a(IHopper ihopper, IInventory iinventory, int i, EnumDirection enumdirection) { + ItemStack itemstack = iinventory.getItem(i); + + if (!itemstack.isEmpty() && b(iinventory, itemstack, i, enumdirection)) { + return hopperPull(ihopper, iinventory, i); /* // Paper - disable rest + ItemStack itemstack1 = itemstack.cloneItemStack(); + // ItemStack itemstack2 = addItem(iinventory, ihopper, iinventory.splitStack(i, 1), (EnumDirection) null); + // CraftBukkit start - Call event on collection of items from inventories into the hopper + CraftItemStack oitemstack = CraftItemStack.asCraftMirror(iinventory.splitStack(i, ihopper.getWorld().spigotConfig.hopperAmount)); // Spigot + + Inventory sourceInventory; + // Have to special case large chests as they work oddly + if (iinventory instanceof InventoryLargeChest) { + sourceInventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) iinventory); + } else { + sourceInventory = iinventory.getOwner().getInventory(); + } + + InventoryMoveItemEvent event = new InventoryMoveItemEvent(sourceInventory, oitemstack.clone(), ihopper.getOwner().getInventory(), false); + + ihopper.getWorld().getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + iinventory.setItem(i, itemstack1); + + if (ihopper instanceof TileEntityHopper) { + ((TileEntityHopper) ihopper).setCooldown(ihopper.getWorld().spigotConfig.hopperTransfer); // Spigot + } else if (ihopper instanceof EntityMinecartHopper) { + ((EntityMinecartHopper) ihopper).setCooldown(ihopper.getWorld().spigotConfig.hopperTransfer / 2); // Spigot + } + return false; + } + int origCount = event.getItem().getAmount(); // Spigot + ItemStack itemstack2 = addItem(iinventory, ihopper, CraftItemStack.asNMSCopy(event.getItem()), null); + + if (itemstack2.isEmpty()) { + if (event.getItem().equals(oitemstack)) { + iinventory.update(); + } else { + iinventory.setItem(i, itemstack1); + } + // CraftBukkit end + return true; + } + + itemstack1.subtract(origCount - itemstack2.getCount()); // Spigot + iinventory.setItem(i, itemstack1);*/ // Paper - end commenting out replaced block for Hopper Optimizations + } + + return false; + } + + public static boolean putDropInInventory(IInventory iinventory, IInventory iinventory1, EntityItem entityitem) { return a(iinventory, iinventory1, entityitem); } // Paper - OBFHELPER + public static boolean a(IInventory iinventory, IInventory iinventory1, EntityItem entityitem) { + boolean flag = false; + + if (entityitem == null) { + return false; + } else { + // CraftBukkit start + InventoryPickupItemEvent event = new InventoryPickupItemEvent(getInventory(iinventory1), (org.bukkit.entity.Item) entityitem.getBukkitEntity()); // Paper - avoid snapshot creation + entityitem.world.getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return false; + } + // CraftBukkit end + ItemStack itemstack = entityitem.getItemStack().cloneItemStack(); + ItemStack itemstack1 = addItem(iinventory, iinventory1, itemstack, (EnumDirection) null); + + if (itemstack1.isEmpty()) { + flag = true; + entityitem.die(); + } else { + entityitem.setItemStack(itemstack1); + } + + return flag; + } + } + + public static ItemStack addItem(IInventory iinventory, IInventory iinventory1, ItemStack itemstack, @Nullable EnumDirection enumdirection) { + if (iinventory1 instanceof IWorldInventory && enumdirection != null) { + IWorldInventory iworldinventory = (IWorldInventory) iinventory1; + int[] aint = iworldinventory.getSlotsForFace(enumdirection); + + for (int i = 0; i < aint.length && !itemstack.isEmpty(); ++i) { + itemstack = a(iinventory, iinventory1, itemstack, aint[i], enumdirection); + } + } else { + int j = iinventory1.getSize(); + + for (int k = 0; k < j && !itemstack.isEmpty(); ++k) { + itemstack = a(iinventory, iinventory1, itemstack, k, enumdirection); + } + } + + return itemstack; + } + + private static boolean a(IInventory iinventory, ItemStack itemstack, int i, EnumDirection enumdirection) { + return !iinventory.b(i, itemstack) ? false : !(iinventory instanceof IWorldInventory) || ((IWorldInventory) iinventory).canPlaceItemThroughFace(i, itemstack, enumdirection); + } + + private static boolean b(IInventory iinventory, ItemStack itemstack, int i, EnumDirection enumdirection) { + return !(iinventory instanceof IWorldInventory) || ((IWorldInventory) iinventory).canTakeItemThroughFace(i, itemstack, enumdirection); + } + + private static ItemStack a(IInventory iinventory, IInventory iinventory1, ItemStack itemstack, int i, EnumDirection enumdirection) { + ItemStack itemstack1 = iinventory1.getItem(i); + + if (a(iinventory1, itemstack, i, enumdirection)) { + boolean flag = false; + boolean flag1 = iinventory1.x_(); + + if (itemstack1.isEmpty()) { + IGNORE_TILE_UPDATES = true; // Paper + iinventory1.setItem(i, itemstack); + IGNORE_TILE_UPDATES = false; // Paper + itemstack = ItemStack.a; + flag = true; + } else if (a(itemstack1, itemstack)) { + int j = itemstack.getMaxStackSize() - itemstack1.getCount(); + int k = Math.min(itemstack.getCount(), j); + + itemstack.subtract(k); + itemstack1.add(k); + flag = k > 0; + } + + if (flag) { + if (flag1 && iinventory1 instanceof TileEntityHopper) { + TileEntityHopper tileentityhopper = (TileEntityHopper) iinventory1; + + if (!tileentityhopper.K()) { + byte b0 = 0; + + if (iinventory != null && iinventory instanceof TileEntityHopper) { + TileEntityHopper tileentityhopper1 = (TileEntityHopper) iinventory; + + if (tileentityhopper.g >= tileentityhopper1.g) { + b0 = 1; + } + } + + tileentityhopper.setCooldown(tileentityhopper.world.spigotConfig.hopperTransfer - b0); // Spigot + } + } + + iinventory1.update(); + } + } + + return itemstack; + } + + private IInventory I() { + EnumDirection enumdirection = BlockHopper.b(this.v()); + + // Paper start - don't search for entities in push mode + World world = getWorld(); + return getInventory(world, this.E() + (double) enumdirection.getAdjacentX(), this.F() + (double) enumdirection.getAdjacentY(), this.G() + (double) enumdirection.getAdjacentZ(), !world.paperConfig.isHopperPushBased); + // Paper end + } + + // Paper start - add option to search for entities + public static IInventory b(IHopper hopper) { + return getInventory(hopper, true); + } + + public static IInventory getInventory(IHopper ihopper, boolean searchForEntities) { + return getInventory(ihopper.getWorld(), ihopper.E(), ihopper.F() + 1.0D, ihopper.G(), searchForEntities); + // Paper end + } + + public static List a(World world, double d0, double d1, double d2) { + return world.a(EntityItem.class, new AxisAlignedBB(d0 - 0.5D, d1, d2 - 0.5D, d0 + 0.5D, d1 + 1.5D, d2 + 0.5D), IEntitySelector.a); // Change getHopperLookupBoundingBox(double, double, double) if the bounding box calculation is ever changed + } + + // Paper start + public AxisAlignedBB getHopperLookupBoundingBox() { + return getHopperLookupBoundingBox(this.getX(), this.getY(), this.getZ()); + } + + private static AxisAlignedBB getHopperLookupBoundingBox(double d0, double d1, double d2) { + // Change this if a(World, double, double, double) above ever changes + return new AxisAlignedBB(d0 - 0.5D, d1, d2 - 0.5D, d0 + 0.5D, d1 + 1.5D, d2 + 0.5D); + } + // Paper end + + // Paper start - add option to searchForEntities + public static IInventory b(World world, double d0, double d1, double d2) { + return getInventory(world, d0, d1, d2, true); + } + + public static IInventory getInventory(World world, double d0, double d1, double d2, boolean searchForEntities) { + // Paper end + Object object = null; + int i = MathHelper.floor(d0); + int j = MathHelper.floor(d1); + int k = MathHelper.floor(d2); + BlockPosition blockposition = new BlockPosition(i, j, k); + if ( !world.isLoaded( blockposition ) ) return null; // Spigot + Block block = world.getType(blockposition).getBlock(); + + if (block.isTileEntity()) { + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof IInventory) { + object = (IInventory) tileentity; + if (object instanceof TileEntityChest && block instanceof BlockChest) { + object = ((BlockChest) block).a(world, blockposition, true); + } + } + } + + net.minecraft.server.Chunk chunk = world.getChunkAtWorldCoords(blockposition); + + if (object == null && searchForEntities && !org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(block).isOccluding() && chunk.getItemCount(blockposition) > 0) { // Paper - only if searchForEntities + List list = world.getEntities((Entity) null, new AxisAlignedBB(d0 - 0.5D, d1 - 0.5D, d2 - 0.5D, d0 + 0.5D, d1 + 0.5D, d2 + 0.5D), IEntitySelector.c); + + if (!list.isEmpty()) { + object = (IInventory) list.get(world.random.nextInt(list.size())); + } + } + + return (IInventory) object; + } + + private static boolean a(ItemStack itemstack, ItemStack itemstack1) { + return itemstack.getItem() != itemstack1.getItem() ? false : (itemstack.getData() != itemstack1.getData() ? false : (itemstack.getCount() > itemstack.getMaxStackSize() ? false : ItemStack.equals(itemstack, itemstack1))); + } + + public double E() { + return (double) this.position.getX() + 0.5D; + } + + public double F() { + return (double) this.position.getY() + 0.5D; + } + + public double G() { + return (double) this.position.getZ() + 0.5D; + } + + private void setCooldown(int i) { + this.f = i; + } + + private boolean J() { + return this.f > 0; + } + + private boolean K() { + return this.f > 8; + } + + public String getContainerName() { + return "minecraft:hopper"; + } + + public Container createContainer(PlayerInventory playerinventory, EntityHuman entityhuman) { + this.d(entityhuman); + return new ContainerHopper(playerinventory, this, entityhuman); + } + + protected NonNullList q() { + return this.items; + } +} diff --git a/sources/src/main/java/net/minecraft/server/World.java b/sources/src/main/java/net/minecraft/server/World.java index 1f675d363..8265c9041 100644 --- a/sources/src/main/java/net/minecraft/server/World.java +++ b/sources/src/main/java/net/minecraft/server/World.java @@ -587,7 +587,9 @@ public abstract class World implements IBlockAccess { public void a(BlockPosition blockposition, final Block block, BlockPosition blockposition1) { if (!this.isClientSide) { - IBlockData iblockdata = this.getType(blockposition); + //IBlockData iblockdata = this.getType(blockposition); + IBlockData iblockdata = this.getTypeIfLoaded(blockposition); // NeonPaper Don't load chunks for physics + if (iblockdata == null) return; // NeonPaper Don't load chunks for physics try { // CraftBukkit start @@ -697,7 +699,7 @@ public abstract class World implements IBlockAccess { if (blockposition.getY() >= 256) { blockposition = new BlockPosition(blockposition.getX(), 255, blockposition.getZ()); } - + if (!this.isLoaded(blockposition)) return 0; // Paper return this.getChunkAtWorldCoords(blockposition).a(blockposition, 0); } } @@ -1891,7 +1893,7 @@ public abstract class World implements IBlockAccess { for (int i = 0; i < list.size(); ++i) { Entity entity1 = (Entity) list.get(i); - if (!entity1.dead && entity1.i && entity1 != entity && (entity == null || entity1.x(entity))) { + if (!entity1.dead && entity1.i && entity1 != entity && (entity == null || !entity1.x(entity))) { // Reaper - Fix MC-103516 return false; } } diff --git a/sources/src/main/java/net/minecraft/server/WorldManager.java b/sources/src/main/java/net/minecraft/server/WorldManager.java new file mode 100644 index 000000000..7d40e9c6e --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/WorldManager.java @@ -0,0 +1,101 @@ +package net.minecraft.server; + +import java.util.Iterator; +import javax.annotation.Nullable; + +public class WorldManager implements IWorldAccess { + + private final MinecraftServer a; + private final WorldServer world; + + public WorldManager(MinecraftServer minecraftserver, WorldServer worldserver) { + this.a = minecraftserver; + this.world = worldserver; + } + + public void a(int i, boolean flag, double d0, double d1, double d2, double d3, double d4, double d5, int... aint) {} + + public void a(int i, boolean flag, boolean flag1, double d0, double d1, double d2, double d3, double d4, double d5, int... aint) {} + + public void a(Entity entity) { + this.world.getTracker().track(entity); + if (entity instanceof EntityPlayer) { + this.world.worldProvider.a((EntityPlayer) entity); + } + + } + + public void b(Entity entity) { + this.world.getTracker().untrackEntity(entity); + this.world.getScoreboard().a(entity); + if (entity instanceof EntityPlayer) { + this.world.worldProvider.b((EntityPlayer) entity); + } + + } + + public void a(@Nullable EntityHuman entityhuman, SoundEffect soundeffect, SoundCategory soundcategory, double d0, double d1, double d2, float f, float f1) { + // CraftBukkit - this.world.dimension, // Paper - this.world.dimension -> this.world + this.a.getPlayerList().sendPacketNearby(entityhuman, d0, d1, d2, f > 1.0F ? (double) (16.0F * f) : 16.0D, this.world, new PacketPlayOutNamedSoundEffect(soundeffect, soundcategory, d0, d1, d2, f, f1)); + } + + public void a(int i, int j, int k, int l, int i1, int j1) {} + + public void a(World world, BlockPosition blockposition, IBlockData iblockdata, IBlockData iblockdata1, int i) { + this.world.getPlayerChunkMap().flagDirty(blockposition); + } + + public void a(BlockPosition blockposition) {} + + public void a(SoundEffect soundeffect, BlockPosition blockposition) {} + + public void a(EntityHuman entityhuman, int i, BlockPosition blockposition, int j) { + // CraftBukkit - this.world.dimension + this.a.getPlayerList().sendPacketNearby(entityhuman, (double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ(), 64.0D, this.world, new PacketPlayOutWorldEvent(i, blockposition, j, false)); + } + + public void a(int i, BlockPosition blockposition, int j) { + this.a.getPlayerList().sendAll(new PacketPlayOutWorldEvent(i, blockposition, j, true)); + } + + public void b(int i, BlockPosition blockposition, int j) { + // Iterator iterator = this.a.getPlayerList().v().iterator(); // Paper + + // CraftBukkit start + EntityHuman entityhuman = null; + Entity entity = world.getEntity(i); + if (entity instanceof EntityHuman) entityhuman = (EntityHuman) entity; + // CraftBukkit end + + // Paper start + java.util.List list = entity != null ? entity.world.players : this.a.getPlayerList().v(); + Iterator iterator = list.iterator(); + PacketPlayOutBlockBreakAnimation packet = null; // NeonPaper - cache packet + while (iterator.hasNext()) { + EntityHuman human = iterator.next(); + if (!(human instanceof EntityPlayer)) continue; + EntityPlayer entityplayer = (EntityPlayer) human; + // Paper end + + if (entityplayer != null && entityplayer.world == this.world && entityplayer.getId() != i) { + double d0 = (double) blockposition.getX() - entityplayer.locX; + double d1 = (double) blockposition.getY() - entityplayer.locY; + double d2 = (double) blockposition.getZ() - entityplayer.locZ; + + // CraftBukkit start + if (entityhuman != null && entityhuman instanceof EntityPlayer && !entityplayer.getBukkitEntity().canSee(((EntityPlayer) entityhuman).getBukkitEntity())) { + continue; + } + // CraftBukkit end + + if (d0 * d0 + d1 * d1 + d2 * d2 < 1024.0D) { + // NeonPaper start + if (packet == null) packet = new PacketPlayOutBlockBreakAnimation(i, blockposition, j); + entityplayer.playerConnection.sendPacket(packet); + // NeonPaper end + } + } + } + + } +} diff --git a/sources/src/main/java/org/bukkit/craftbukkit/CraftChunk.java b/sources/src/main/java/org/bukkit/craftbukkit/CraftChunk.java new file mode 100644 index 000000000..13e429ded --- /dev/null +++ b/sources/src/main/java/org/bukkit/craftbukkit/CraftChunk.java @@ -0,0 +1,317 @@ +package org.bukkit.craftbukkit; + +import java.lang.ref.WeakReference; +import java.util.Arrays; + +import java.util.Random; +import net.minecraft.server.*; + +import org.bukkit.Chunk; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.block.BlockState; +import org.bukkit.craftbukkit.block.CraftBlock; +import org.bukkit.entity.Entity; +import org.bukkit.ChunkSnapshot; + +public class CraftChunk implements Chunk { + private WeakReference weakChunk; + private final WorldServer worldServer; + private final int x; + private final int z; + private static final byte[] emptyData = new byte[2048]; + private static final short[] emptyBlockIDs = new short[4096]; + private static final byte[] emptySkyLight = new byte[2048]; + + public CraftChunk(net.minecraft.server.Chunk chunk) { + this.weakChunk = new WeakReference(chunk); + + worldServer = (WorldServer) getHandle().world; + x = getHandle().locX; + z = getHandle().locZ; + } + + public World getWorld() { + return worldServer.getWorld(); + } + + public CraftWorld getCraftWorld() { + return (CraftWorld) getWorld(); + } + + public net.minecraft.server.Chunk getHandle() { + net.minecraft.server.Chunk c = weakChunk.get(); + + if (c == null) { + c = worldServer.getChunkAt(x, z); + + weakChunk = new WeakReference(c); + } + + return c; + } + + void breakLink() { + weakChunk.clear(); + } + + public int getX() { + return x; + } + + public int getZ() { + return z; + } + + @Override + public String toString() { + return "CraftChunk{" + "x=" + getX() + "z=" + getZ() + '}'; + } + + public Block getBlock(int x, int y, int z) { + return new CraftBlock(this, (getX() << 4) | (x & 0xF), y, (getZ() << 4) | (z & 0xF)); + } + + public Entity[] getEntities() { + int count = 0, index = 0; + net.minecraft.server.Chunk chunk = getHandle(); + + for (int i = 0; i < 16; i++) { + count += chunk.entitySlices[i].size(); + } + + Entity[] entities = new Entity[count]; + + for (int i = 0; i < 16; i++) { + + //for (Object obj : chunk.entitySlices[i].toArray()) { + // if (!(obj instanceof net.minecraft.server.Entity)) { + // NeonPaper start - speed up (was with chunk.entitySlices[i].toArray() and cast checks which costs a lot of performance if called often) + for (net.minecraft.server.Entity entity : chunk.entitySlices[i]) { + if (entity == null) { + continue; + } + + //entities[index++] = ((net.minecraft.server.Entity) obj).getBukkitEntity(); + entities[index++] = entity.getBukkitEntity(); + } + //NeonPaper end + } + + return entities; + } + + public BlockState[] getTileEntities() { + int index = 0; + net.minecraft.server.Chunk chunk = getHandle(); + + BlockState[] entities = new BlockState[chunk.tileEntities.size()]; + + for (Object obj : chunk.tileEntities.keySet().toArray()) { + if (!(obj instanceof BlockPosition)) { + continue; + } + + BlockPosition position = (BlockPosition) obj; + entities[index++] = worldServer.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()).getState(); + } + + return entities; + } + + public boolean isLoaded() { + return getWorld().isChunkLoaded(this); + } + + public boolean load() { + return getWorld().loadChunk(getX(), getZ(), true); + } + + public boolean load(boolean generate) { + return getWorld().loadChunk(getX(), getZ(), generate); + } + + public boolean unload() { + return getWorld().unloadChunk(getX(), getZ()); + } + + @Override + public boolean isSlimeChunk() { + // 987234911L is deterimined in EntitySlime when seeing if a slime can spawn in a chunk + return getHandle().a(worldServer.spigotConfig.slimeSeed).nextInt(10) == 0; + } + + public boolean unload(boolean save) { + return getWorld().unloadChunk(getX(), getZ(), save); + } + + public boolean unload(boolean save, boolean safe) { + return getWorld().unloadChunk(getX(), getZ(), save, safe); + } + + public ChunkSnapshot getChunkSnapshot() { + return getChunkSnapshot(true, false, false); + } + + public ChunkSnapshot getChunkSnapshot(boolean includeMaxBlockY, boolean includeBiome, boolean includeBiomeTempRain) { + net.minecraft.server.Chunk chunk = getHandle(); + + ChunkSection[] cs = chunk.getSections(); + short[][] sectionBlockIDs = new short[cs.length][]; + byte[][] sectionBlockData = new byte[cs.length][]; + byte[][] sectionSkyLights = new byte[cs.length][]; + byte[][] sectionEmitLights = new byte[cs.length][]; + boolean[] sectionEmpty = new boolean[cs.length]; + + for (int i = 0; i < cs.length; i++) { + if (cs[i] == null) { // Section is empty? + sectionBlockIDs[i] = emptyBlockIDs; + sectionBlockData[i] = emptyData; + sectionSkyLights[i] = emptySkyLight; + sectionEmitLights[i] = emptyData; + sectionEmpty[i] = true; + } else { // Not empty + short[] blockids = new short[4096]; + + byte[] rawIds = new byte[4096]; + NibbleArray data = new NibbleArray(); + cs[i].getBlocks().exportData(rawIds, data); + + byte[] dataValues = sectionBlockData[i] = data.asBytes(); + + // Copy base IDs + for (int j = 0; j < 4096; j++) { + blockids[j] = (short) (rawIds[j] & 0xFF); + } + + sectionBlockIDs[i] = blockids; + + if (cs[i].getSkyLightArray() == null) { + sectionSkyLights[i] = emptyData; + } else { + sectionSkyLights[i] = new byte[2048]; + System.arraycopy(cs[i].getSkyLightArray().asBytes(), 0, sectionSkyLights[i], 0, 2048); + } + sectionEmitLights[i] = new byte[2048]; + System.arraycopy(cs[i].getEmittedLightArray().asBytes(), 0, sectionEmitLights[i], 0, 2048); + } + } + + int[] hmap = null; + + if (includeMaxBlockY) { + hmap = new int[256]; // Get copy of height map + System.arraycopy(chunk.heightMap, 0, hmap, 0, 256); + } + + BiomeBase[] biome = null; + double[] biomeTemp = null; + double[] biomeRain = null; + + if (includeBiome || includeBiomeTempRain) { + WorldChunkManager wcm = chunk.world.getWorldChunkManager(); + + if (includeBiome) { + biome = new BiomeBase[256]; + for (int i = 0; i < 256; i++) { + biome[i] = chunk.getBiome(new BlockPosition(i & 0xF, 0, i >> 4), wcm); + } + } + + if (includeBiomeTempRain) { + biomeTemp = new double[256]; + biomeRain = new double[256]; + float[] dat = getTemperatures(wcm, getX() << 4, getZ() << 4); + + for (int i = 0; i < 256; i++) { + biomeTemp[i] = dat[i]; + } + + /* Removed 15w46a + dat = wcm.getWetness(null, getX() << 4, getZ() << 4, 16, 16); + + for (int i = 0; i < 256; i++) { + biomeRain[i] = dat[i]; + } + */ + } + } + + World world = getWorld(); + return new CraftChunkSnapshot(getX(), getZ(), world.getName(), world.getFullTime(), sectionBlockIDs, sectionBlockData, sectionSkyLights, sectionEmitLights, sectionEmpty, hmap, biome, biomeTemp, biomeRain); + } + + public static ChunkSnapshot getEmptyChunkSnapshot(int x, int z, CraftWorld world, boolean includeBiome, boolean includeBiomeTempRain) { + BiomeBase[] biome = null; + double[] biomeTemp = null; + double[] biomeRain = null; + + if (includeBiome || includeBiomeTempRain) { + WorldChunkManager wcm = world.getHandle().getWorldChunkManager(); + + if (includeBiome) { + biome = new BiomeBase[256]; + for (int i = 0; i < 256; i++) { + biome[i] = world.getHandle().getBiome(new BlockPosition((x << 4) + (i & 0xF), 0, (z << 4) + (i >> 4))); + } + } + + if (includeBiomeTempRain) { + biomeTemp = new double[256]; + biomeRain = new double[256]; + float[] dat = getTemperatures(wcm, x << 4, z << 4); + + for (int i = 0; i < 256; i++) { + biomeTemp[i] = dat[i]; + } + + /* Removed 15w46a + dat = wcm.getWetness(null, x << 4, z << 4, 16, 16); + + for (int i = 0; i < 256; i++) { + biomeRain[i] = dat[i]; + } + */ + } + } + + /* Fill with empty data */ + int hSection = world.getMaxHeight() >> 4; + short[][] blockIDs = new short[hSection][]; + byte[][] skyLight = new byte[hSection][]; + byte[][] emitLight = new byte[hSection][]; + byte[][] blockData = new byte[hSection][]; + boolean[] empty = new boolean[hSection]; + + for (int i = 0; i < hSection; i++) { + blockIDs[i] = emptyBlockIDs; + skyLight[i] = emptySkyLight; + emitLight[i] = emptyData; + blockData[i] = emptyData; + empty[i] = true; + } + + return new CraftChunkSnapshot(x, z, world.getName(), world.getFullTime(), blockIDs, blockData, skyLight, emitLight, empty, new int[256], biome, biomeTemp, biomeRain); + } + + private static float[] getTemperatures(WorldChunkManager chunkmanager, int chunkX, int chunkZ) { + BiomeBase[] biomes = chunkmanager.getBiomes(null, chunkX, chunkZ, 16, 16); + float[] temps = new float[biomes.length]; + + for (int i = 0; i < biomes.length; i++) { + float temp = biomes[i].getTemperature(); // Vanilla of olde: ((int) biomes[i].temperature * 65536.0F) / 65536.0F + + if (temp > 1F) { + temp = 1F; + } + + temps[i] = temp; + } + + return temps; + } + + static { + Arrays.fill(emptySkyLight, (byte) 0xFF); + } +} diff --git a/sources/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/sources/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java index 9400de675..df420057c 100644 --- a/sources/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +++ b/sources/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java @@ -1488,11 +1488,16 @@ public class CraftPlayer extends CraftHumanEntity implements Player { @Override public int getNoDamageTicks() { - if (getHandle().invulnerableTicks > 0) { - return Math.max(getHandle().invulnerableTicks, getHandle().noDamageTicks); - } else { - return getHandle().noDamageTicks; - } + // TacoSpigot start - fix incorrect calculation of getNoDamageTicks + /* + if (getHandle().invulnerableTicks > 0) { + return Math.max(getHandle().invulnerableTicks, getHandle().noDamageTicks); + } else { + return getHandle().noDamageTicks; + } + */ + return Math.max(getHandle().invulnerableTicks, Math.max(0, getHandle().noDamageTicks - (getHandle().maxNoDamageTicks >> 1))); + // TacoSpigot end } @Override