diff options
Diffstat (limited to 'nms-patches')
264 files changed, 20633 insertions, 0 deletions
diff --git a/nms-patches/BiomeTheEndDecorator.patch b/nms-patches/BiomeTheEndDecorator.patch new file mode 100644 index 00000000..6c957c2a --- /dev/null +++ b/nms-patches/BiomeTheEndDecorator.patch @@ -0,0 +1,11 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BiomeTheEndDecorator.java 2014-11-27 08:59:46.501422728 +1100 ++++ src/main/java/net/minecraft/server/BiomeTheEndDecorator.java 2014-11-27 08:42:10.136850942 +1100 +@@ -21,7 +21,7 @@ + EntityEnderDragon entityenderdragon = new EntityEnderDragon(this.a); + + entityenderdragon.setPositionRotation(0.0D, 128.0D, 0.0D, this.b.nextFloat() * 360.0F, 0.0F); +- this.a.addEntity(entityenderdragon); ++ this.a.addEntity(entityenderdragon, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN); // CraftBukkit - add SpawnReason + } + + } diff --git a/nms-patches/Block.patch b/nms-patches/Block.patch new file mode 100644 index 00000000..fc44dc75 --- /dev/null +++ b/nms-patches/Block.patch @@ -0,0 +1,23 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/Block.java 2014-11-27 08:59:46.537422569 +1100 ++++ src/main/java/net/minecraft/server/Block.java 2014-11-27 08:42:10.172850872 +1100 +@@ -295,7 +295,8 @@ + int j = this.getDropCount(i, world.random); + + for (int k = 0; k < j; ++k) { +- if (world.random.nextFloat() <= f) { ++ // CraftBukkit - <= to < to allow for plugins to completely disable block drops from explosions ++ if (world.random.nextFloat() < f) { + Item item = this.getDropType(iblockdata, world.random, i); + + if (item != null) { +@@ -920,4 +921,10 @@ + private static void a(int i, String s, Block block) { + a(i, new MinecraftKey(s), block); + } ++ ++ // CraftBukkit start ++ public int getExpDrop(World world, IBlockData data, int enchantmentLevel) { ++ return 0; ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/BlockBloodStone.patch b/nms-patches/BlockBloodStone.patch new file mode 100644 index 00000000..82233f90 --- /dev/null +++ b/nms-patches/BlockBloodStone.patch @@ -0,0 +1,28 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockBloodStone.java 2014-11-27 08:59:46.505422709 +1100 ++++ src/main/java/net/minecraft/server/BlockBloodStone.java 2014-11-27 08:42:10.124850965 +1100 +@@ -1,5 +1,7 @@ + package net.minecraft.server; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public class BlockBloodStone extends Block { + + public BlockBloodStone() { +@@ -10,4 +12,17 @@ + public MaterialMapColor g(IBlockData iblockdata) { + return MaterialMapColor.K; + } ++ ++ // CraftBukkit start ++ @Override ++ public void doPhysics(World world, BlockPosition position, IBlockData iblockdata, Block block) { ++ if (block != null && block.isPowerSource()) { ++ org.bukkit.block.Block bl = world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()); ++ int power = bl.getBlockPower(); ++ ++ BlockRedstoneEvent event = new BlockRedstoneEvent(bl, power, power); ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/BlockButtonAbstract.patch b/nms-patches/BlockButtonAbstract.patch new file mode 100644 index 00000000..b41c8016 --- /dev/null +++ b/nms-patches/BlockButtonAbstract.patch @@ -0,0 +1,111 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockButtonAbstract.java 2014-11-27 08:59:46.505422709 +1100 ++++ src/main/java/net/minecraft/server/BlockButtonAbstract.java 2014-11-27 08:42:10.160850895 +1100 +@@ -3,6 +3,11 @@ + import java.util.List; + import java.util.Random; + ++// CraftBukkit start ++import org.bukkit.event.block.BlockRedstoneEvent; ++import org.bukkit.event.entity.EntityInteractEvent; ++// CraftBukkit end ++ + public abstract class BlockButtonAbstract extends Block { + + public static final BlockStateDirection FACING = BlockStateDirection.of("facing"); +@@ -122,6 +127,19 @@ + if (((Boolean) iblockdata.get(BlockButtonAbstract.POWERED)).booleanValue()) { + return true; + } else { ++ // CraftBukkit start ++ boolean powered = ((Boolean) iblockdata.get(POWERED)); ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ int old = (powered) ? 15 : 0; ++ int current = (!powered) ? 15 : 0; ++ ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, old, current); ++ world.getServer().getPluginManager().callEvent(eventRedstone); ++ ++ if ((eventRedstone.getNewCurrent() > 0) != (!powered)) { ++ return true; ++ } ++ // CraftBukkit end + world.setTypeAndData(blockposition, iblockdata.set(BlockButtonAbstract.POWERED, Boolean.valueOf(true)), 3); + world.b(blockposition, blockposition); + world.makeSound((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D, "random.click", 0.3F, 0.6F); +@@ -159,6 +177,16 @@ + if (this.M) { + this.f(world, blockposition, iblockdata); + } else { ++ // CraftBukkit start ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, 15, 0); ++ world.getServer().getPluginManager().callEvent(eventRedstone); ++ ++ if (eventRedstone.getNewCurrent() > 0) { ++ return; ++ } ++ // CraftBukkit end + world.setTypeUpdate(blockposition, iblockdata.set(BlockButtonAbstract.POWERED, Boolean.valueOf(false))); + this.b(world, blockposition, (EnumDirection) iblockdata.get(BlockButtonAbstract.FACING)); + world.makeSound((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D, "random.click", 0.3F, 0.5F); +@@ -192,8 +220,42 @@ + List list = world.a(EntityArrow.class, new AxisAlignedBB((double) blockposition.getX() + this.minX, (double) blockposition.getY() + this.minY, (double) blockposition.getZ() + this.minZ, (double) blockposition.getX() + this.maxX, (double) blockposition.getY() + this.maxY, (double) blockposition.getZ() + this.maxZ)); + boolean flag = !list.isEmpty(); + boolean flag1 = ((Boolean) iblockdata.get(BlockButtonAbstract.POWERED)).booleanValue(); ++ ++ // CraftBukkit start - Call interact event when arrows turn on wooden buttons ++ if (flag1 != flag && flag) { ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ boolean allowed = false; ++ ++ // If all of the events are cancelled block the button press, else allow ++ for (Object object : list) { ++ if (object != null) { ++ EntityInteractEvent event = new EntityInteractEvent(((Entity) object).getBukkitEntity(), block); ++ world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ allowed = true; ++ break; ++ } ++ } ++ } ++ ++ if (!allowed) { ++ return; ++ } ++ } ++ // CraftBukkit end + + if (flag && !flag1) { ++ // CraftBukkit start ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, 0, 15); ++ world.getServer().getPluginManager().callEvent(eventRedstone); ++ ++ if (eventRedstone.getNewCurrent() <= 0) { ++ return; ++ } ++ // CraftBukkit end + world.setTypeUpdate(blockposition, iblockdata.set(BlockButtonAbstract.POWERED, Boolean.valueOf(true))); + this.b(world, blockposition, (EnumDirection) iblockdata.get(BlockButtonAbstract.FACING)); + world.b(blockposition, blockposition); +@@ -201,6 +263,16 @@ + } + + if (!flag && flag1) { ++ // CraftBukkit start ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, 15, 0); ++ world.getServer().getPluginManager().callEvent(eventRedstone); ++ ++ if (eventRedstone.getNewCurrent() > 0) { ++ return; ++ } ++ // CraftBukkit end + world.setTypeUpdate(blockposition, iblockdata.set(BlockButtonAbstract.POWERED, Boolean.valueOf(false))); + this.b(world, blockposition, (EnumDirection) iblockdata.get(BlockButtonAbstract.FACING)); + world.b(blockposition, blockposition); diff --git a/nms-patches/BlockCactus.patch b/nms-patches/BlockCactus.patch new file mode 100644 index 00000000..7159a89e --- /dev/null +++ b/nms-patches/BlockCactus.patch @@ -0,0 +1,31 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockCactus.java 2014-11-27 08:59:46.509422692 +1100 ++++ src/main/java/net/minecraft/server/BlockCactus.java 2014-11-27 08:42:10.152850911 +1100 +@@ -3,6 +3,8 @@ + import java.util.Iterator; + import java.util.Random; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class BlockCactus extends Block { + + public static final BlockStateInteger AGE = BlockStateInteger.of("age", 0, 15); +@@ -31,7 +33,8 @@ + world.setTypeUpdate(blockposition1, this.getBlockData()); + IBlockData iblockdata1 = iblockdata.set(BlockCactus.AGE, Integer.valueOf(0)); + +- world.setTypeAndData(blockposition, iblockdata1, 4); ++ CraftEventFactory.handleBlockGrowEvent(world, blockposition1.getX(), blockposition1.getY(), blockposition1.getZ(), this, 0); // CraftBukkit ++ // world.setTypeAndData(blockposition, iblockdata1, 4); // CraftBukkit + this.doPhysics(world, blockposition1, iblockdata1, this); + } else { + world.setTypeAndData(blockposition, iblockdata.set(BlockCactus.AGE, Integer.valueOf(j + 1)), 4); +@@ -83,7 +86,9 @@ + } + + public void a(World world, BlockPosition blockposition, IBlockData iblockdata, Entity entity) { ++ CraftEventFactory.blockDamage = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); // CraftBukkit + entity.damageEntity(DamageSource.CACTUS, 1.0F); ++ CraftEventFactory.blockDamage = null; // CraftBukkit + } + + public IBlockData fromLegacyData(int i) { diff --git a/nms-patches/BlockCake.patch b/nms-patches/BlockCake.patch new file mode 100644 index 00000000..facc1d4f --- /dev/null +++ b/nms-patches/BlockCake.patch @@ -0,0 +1,22 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockCake.java 2014-11-27 08:59:46.509422692 +1100 ++++ src/main/java/net/minecraft/server/BlockCake.java 2014-11-27 08:42:10.168850880 +1100 +@@ -54,7 +54,18 @@ + + private void b(World world, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman) { + if (entityhuman.j(false)) { +- entityhuman.getFoodData().eat(2, 0.1F); ++ // CraftBukkit start ++ // entityhuman.getFoodData().eat(2, 0.1F); ++ int oldFoodLevel = entityhuman.getFoodData().foodLevel; ++ ++ org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(entityhuman, 2 + oldFoodLevel); ++ ++ if (!event.isCancelled()) { ++ entityhuman.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, 0.1F); ++ } ++ ++ ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutUpdateHealth(((EntityPlayer) entityhuman).getBukkitEntity().getScaledHealth(), entityhuman.getFoodData().foodLevel, entityhuman.getFoodData().saturationLevel)); ++ // CraftBukkit end + int i = ((Integer) iblockdata.get(BlockCake.BITES)).intValue(); + + if (i < 6) { diff --git a/nms-patches/BlockCocoa.patch b/nms-patches/BlockCocoa.patch new file mode 100644 index 00000000..40006fff --- /dev/null +++ b/nms-patches/BlockCocoa.patch @@ -0,0 +1,35 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockCocoa.java 2014-11-27 08:59:46.513422674 +1100 ++++ src/main/java/net/minecraft/server/BlockCocoa.java 2014-11-27 08:42:10.152850911 +1100 +@@ -2,6 +2,8 @@ + + import java.util.Random; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class BlockCocoa extends BlockDirectional implements IBlockFragilePlantElement { + + public static final BlockStateInteger AGE = BlockStateInteger.of("age", 0, 2); +@@ -19,7 +21,10 @@ + int i = ((Integer) iblockdata.get(BlockCocoa.AGE)).intValue(); + + if (i < 2) { +- world.setTypeAndData(blockposition, iblockdata.set(BlockCocoa.AGE, Integer.valueOf(i + 1)), 2); ++ // CraftBukkit start ++ IBlockData data = iblockdata.set(AGE, Integer.valueOf(i + 1)); ++ CraftEventFactory.handleBlockGrowEvent(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this, toLegacyData(data)); ++ // CraftBukkit end + } + } + +@@ -125,7 +130,10 @@ + } + + public void b(World world, Random random, BlockPosition blockposition, IBlockData iblockdata) { +- world.setTypeAndData(blockposition, iblockdata.set(BlockCocoa.AGE, Integer.valueOf(((Integer) iblockdata.get(BlockCocoa.AGE)).intValue() + 1)), 2); ++ // CraftBukkit start ++ IBlockData data = iblockdata.set(AGE, Integer.valueOf(((Integer) iblockdata.get(AGE)).intValue() + 1)); ++ CraftEventFactory.handleBlockGrowEvent(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this, toLegacyData(data)); ++ // CraftBukkit end + } + + public IBlockData fromLegacyData(int i) { diff --git a/nms-patches/BlockCommand.patch b/nms-patches/BlockCommand.patch new file mode 100644 index 00000000..e7fe808d --- /dev/null +++ b/nms-patches/BlockCommand.patch @@ -0,0 +1,34 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockCommand.java 2014-11-27 08:59:46.513422674 +1100 ++++ src/main/java/net/minecraft/server/BlockCommand.java 2014-11-27 08:42:10.152850911 +1100 +@@ -2,6 +2,8 @@ + + import java.util.Random; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public class BlockCommand extends BlockContainer { + + public static final BlockStateBoolean TRIGGERED = BlockStateBoolean.of("triggered"); +@@ -19,11 +21,20 @@ + if (!world.isStatic) { + boolean flag = world.isBlockIndirectlyPowered(blockposition); + boolean flag1 = ((Boolean) iblockdata.get(BlockCommand.TRIGGERED)).booleanValue(); ++ ++ // CraftBukkit start ++ org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ int old = flag1 ? 15 : 0; ++ int current = flag ? 15 : 0; ++ ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, old, current); ++ world.getServer().getPluginManager().callEvent(eventRedstone); ++ // CraftBukkit end + +- if (flag && !flag1) { ++ if (eventRedstone.getNewCurrent() > 0 && !(eventRedstone.getOldCurrent() > 0)) { // CraftBukkit + world.setTypeAndData(blockposition, iblockdata.set(BlockCommand.TRIGGERED, Boolean.valueOf(true)), 4); + world.a(blockposition, (Block) this, this.a(world)); +- } else if (!flag && flag1) { ++ } else if (!(eventRedstone.getNewCurrent() > 0) && eventRedstone.getOldCurrent() > 0) { // CraftBukkit + world.setTypeAndData(blockposition, iblockdata.set(BlockCommand.TRIGGERED, Boolean.valueOf(false)), 4); + } + } diff --git a/nms-patches/BlockCrops.patch b/nms-patches/BlockCrops.patch new file mode 100644 index 00000000..fde60f42 --- /dev/null +++ b/nms-patches/BlockCrops.patch @@ -0,0 +1,35 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockCrops.java 2014-11-27 08:59:46.517422657 +1100 ++++ src/main/java/net/minecraft/server/BlockCrops.java 2014-11-27 08:42:10.160850895 +1100 +@@ -2,6 +2,8 @@ + + import java.util.Random; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class BlockCrops extends BlockPlant implements IBlockFragilePlantElement { + + public static final BlockStateInteger AGE = BlockStateInteger.of("age", 0, 7); +@@ -31,7 +33,10 @@ + float f = a((Block) this, world, blockposition); + + if (random.nextInt((int) (25.0F / f) + 1) == 0) { +- world.setTypeAndData(blockposition, iblockdata.set(BlockCrops.AGE, Integer.valueOf(i + 1)), 2); ++ // CraftBukkit start ++ IBlockData data = iblockdata.set(AGE, Integer.valueOf(i + 1)); ++ CraftEventFactory.handleBlockGrowEvent(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this, toLegacyData(data)); ++ // CraftBukkit end + } + } + } +@@ -45,7 +50,10 @@ + i = 7; + } + +- world.setTypeAndData(blockposition, iblockdata.set(BlockCrops.AGE, Integer.valueOf(i)), 2); ++ // CraftBukkit start ++ IBlockData data = iblockdata.set(AGE, Integer.valueOf(i)); ++ CraftEventFactory.handleBlockGrowEvent(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this, toLegacyData(data)); ++ // CraftBukkit end + } + + protected static float a(Block block, World world, BlockPosition blockposition) { diff --git a/nms-patches/BlockDaylightDetector.patch b/nms-patches/BlockDaylightDetector.patch new file mode 100644 index 00000000..7f37a071 --- /dev/null +++ b/nms-patches/BlockDaylightDetector.patch @@ -0,0 +1,10 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockDaylightDetector.java 2014-11-27 08:59:46.517422657 +1100 ++++ src/main/java/net/minecraft/server/BlockDaylightDetector.java 2014-11-27 08:42:10.164850887 +1100 +@@ -41,6 +41,7 @@ + } + + if (((Integer) iblockdata.get(BlockDaylightDetector.POWER)).intValue() != i) { ++ i = org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), ((Integer) iblockdata.get(POWER)), i).getNewCurrent(); // CraftBukkit - Call BlockRedstoneEvent + world.setTypeAndData(blockposition, iblockdata.set(BlockDaylightDetector.POWER, Integer.valueOf(i)), 3); + } + diff --git a/nms-patches/BlockDiodeAbstract.patch b/nms-patches/BlockDiodeAbstract.patch new file mode 100644 index 00000000..f0c210d8 --- /dev/null +++ b/nms-patches/BlockDiodeAbstract.patch @@ -0,0 +1,30 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockDiodeAbstract.java 2014-11-27 08:59:46.517422657 +1100 ++++ src/main/java/net/minecraft/server/BlockDiodeAbstract.java 2014-11-27 08:42:10.172850872 +1100 +@@ -2,6 +2,8 @@ + + import java.util.Random; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public abstract class BlockDiodeAbstract extends BlockDirectional { + + protected final boolean M; +@@ -31,8 +33,18 @@ + boolean flag = this.e(world, blockposition, iblockdata); + + if (this.M && !flag) { ++ // CraftBukkit start ++ if (CraftEventFactory.callRedstoneChange(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), 15, 0).getNewCurrent() != 0) { ++ return; ++ } ++ // CraftBukkit end + world.setTypeAndData(blockposition, this.k(iblockdata), 2); + } else if (!this.M) { ++ // CraftBukkit start ++ if (CraftEventFactory.callRedstoneChange(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), 0, 15).getNewCurrent() != 15) { ++ return; ++ } ++ // CraftBukkit end + world.setTypeAndData(blockposition, this.e(iblockdata), 2); + if (!flag) { + world.a(blockposition, this.e(iblockdata).getBlock(), this.m(iblockdata), -1); diff --git a/nms-patches/BlockDispenser.patch b/nms-patches/BlockDispenser.patch new file mode 100644 index 00000000..35ad0d58 --- /dev/null +++ b/nms-patches/BlockDispenser.patch @@ -0,0 +1,18 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockDispenser.java 2014-11-27 08:59:46.521422639 +1100 ++++ src/main/java/net/minecraft/server/BlockDispenser.java 2014-11-27 08:42:10.084851043 +1100 +@@ -8,6 +8,7 @@ + public static final BlockStateBoolean TRIGGERED = BlockStateBoolean.of("triggered"); + public static final RegistryDefault M = new RegistryDefault(new DispenseBehaviorItem()); + protected Random N = new Random(); ++ public static boolean eventFired = false; // CraftBukkit + + protected BlockDispenser() { + super(Material.STONE); +@@ -78,6 +79,7 @@ + + if (idispensebehavior != IDispenseBehavior.a) { + ItemStack itemstack1 = idispensebehavior.a(sourceblock, itemstack); ++ eventFired = false; // CraftBukkit - reset event status + + tileentitydispenser.setItem(i, itemstack1.count == 0 ? null : itemstack1); + } diff --git a/nms-patches/BlockDoor.patch b/nms-patches/BlockDoor.patch new file mode 100644 index 00000000..a14fff0f --- /dev/null +++ b/nms-patches/BlockDoor.patch @@ -0,0 +1,43 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockDoor.java 2014-11-27 08:59:46.521422639 +1100 ++++ src/main/java/net/minecraft/server/BlockDoor.java 2014-11-27 08:42:10.156850903 +1100 +@@ -3,6 +3,8 @@ + import com.google.common.base.Predicate; + import java.util.Random; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public class BlockDoor extends Block { + + public static final BlockStateDirection FACING = BlockStateDirection.of("facing", (Predicate) EnumDirectionLimit.HORIZONTAL); +@@ -151,9 +153,21 @@ + this.b(world, blockposition, iblockdata, 0); + } + } else { +- boolean flag1 = world.isBlockIndirectlyPowered(blockposition) || world.isBlockIndirectlyPowered(blockposition2); ++ // CraftBukkit start ++ org.bukkit.World bworld = world.getWorld(); ++ org.bukkit.block.Block bukkitBlock = bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ org.bukkit.block.Block blockTop = bworld.getBlockAt(blockposition2.getX(), blockposition2.getY(), blockposition2.getZ()); ++ ++ int power = bukkitBlock.getBlockPower(); ++ int powerTop = blockTop.getBlockPower(); ++ if (powerTop > power) power = powerTop; ++ int oldPower = (Boolean)iblockdata2.get(POWERED) ? 15 : 0; ++ ++ if (oldPower == 0 ^ power == 0) { ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, oldPower, power); ++ world.getServer().getPluginManager().callEvent(eventRedstone); + +- if ((flag1 || block.isPowerSource()) && block != this && flag1 != ((Boolean) iblockdata2.get(BlockDoor.POWERED)).booleanValue()) { ++ boolean flag1 = eventRedstone.getNewCurrent() > 0; + world.setTypeAndData(blockposition2, iblockdata2.set(BlockDoor.POWERED, Boolean.valueOf(flag1)), 2); + if (flag1 != ((Boolean) iblockdata.get(BlockDoor.OPEN)).booleanValue()) { + world.setTypeAndData(blockposition, iblockdata.set(BlockDoor.OPEN, Boolean.valueOf(flag1)), 2); +@@ -161,6 +175,7 @@ + world.a((EntityHuman) null, flag1 ? 1003 : 1006, blockposition, 0); + } + } ++ // CraftBukkit end + } + } + diff --git a/nms-patches/BlockDragonEgg.patch b/nms-patches/BlockDragonEgg.patch new file mode 100644 index 00000000..d2415b9b --- /dev/null +++ b/nms-patches/BlockDragonEgg.patch @@ -0,0 +1,30 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockDragonEgg.java 2014-11-27 08:59:46.525422622 +1100 ++++ src/main/java/net/minecraft/server/BlockDragonEgg.java 2014-11-27 08:42:10.152850911 +1100 +@@ -2,6 +2,8 @@ + + import java.util.Random; + ++import org.bukkit.event.block.BlockFromToEvent; // CraftBukkit ++ + public class BlockDragonEgg extends Block { + + public BlockDragonEgg() { +@@ -61,6 +63,18 @@ + BlockPosition blockposition1 = blockposition.a(world.random.nextInt(16) - world.random.nextInt(16), world.random.nextInt(8) - world.random.nextInt(8), world.random.nextInt(16) - world.random.nextInt(16)); + + if (world.getType(blockposition1).getBlock().material == Material.AIR) { ++ // CraftBukkit start ++ org.bukkit.block.Block from = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ org.bukkit.block.Block to = world.getWorld().getBlockAt(blockposition1.getX(), blockposition1.getY(), blockposition1.getZ()); ++ BlockFromToEvent event = new BlockFromToEvent(from, to); ++ org.bukkit.Bukkit.getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ return; ++ } ++ ++ blockposition1 = new BlockPosition(event.getToBlock().getX(), event.getToBlock().getY(), event.getToBlock().getZ()); ++ // CraftBukkit end + if (world.isStatic) { + for (int j = 0; j < 128; ++j) { + double d0 = world.random.nextDouble(); diff --git a/nms-patches/BlockDropper.patch b/nms-patches/BlockDropper.patch new file mode 100644 index 00000000..43df579c --- /dev/null +++ b/nms-patches/BlockDropper.patch @@ -0,0 +1,41 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockDropper.java 2014-11-27 08:59:46.525422622 +1100 ++++ src/main/java/net/minecraft/server/BlockDropper.java 2014-11-27 08:42:10.124850965 +1100 +@@ -1,5 +1,10 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.inventory.InventoryMoveItemEvent; ++// CraftBukkit end ++ + public class BlockDropper extends BlockDispenser { + + private final IDispenseBehavior O = new DispenseBehaviorItem(); +@@ -38,8 +43,25 @@ + itemstack1 = null; + } + } else { +- itemstack1 = TileEntityHopper.addItem(iinventory, itemstack.cloneItemStack().a(1), enumdirection.opposite()); +- if (itemstack1 == null) { ++ // CraftBukkit start - Fire event when pushing items into other inventories ++ CraftItemStack oitemstack = CraftItemStack.asCraftMirror(itemstack.cloneItemStack().a(1)); ++ ++ org.bukkit.inventory.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(tileentitydispenser.getOwner().getInventory(), oitemstack.clone(), destinationInventory, true); ++ world.getServer().getPluginManager().callEvent(event); ++ if (event.isCancelled()) { ++ return; ++ } ++ itemstack1 = TileEntityHopper.addItem(iinventory, CraftItemStack.asNMSCopy(event.getItem()), enumdirection.opposite()); ++ if (event.getItem().equals(oitemstack) && itemstack1 == null) { ++ // CraftBukkit end + itemstack1 = itemstack.cloneItemStack(); + if (--itemstack1.count == 0) { + itemstack1 = null; diff --git a/nms-patches/BlockEnderPortal.patch b/nms-patches/BlockEnderPortal.patch new file mode 100644 index 00000000..ae5743c2 --- /dev/null +++ b/nms-patches/BlockEnderPortal.patch @@ -0,0 +1,22 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockEnderPortal.java 2014-11-27 08:59:46.529422604 +1100 ++++ src/main/java/net/minecraft/server/BlockEnderPortal.java 2014-11-27 08:42:10.104851005 +1100 +@@ -3,6 +3,8 @@ + import java.util.List; + import java.util.Random; + ++import org.bukkit.event.entity.EntityPortalEnterEvent; // CraftBukkit ++ + public class BlockEnderPortal extends BlockContainer { + + protected BlockEnderPortal(Material material) { +@@ -36,6 +38,10 @@ + + public void a(World world, BlockPosition blockposition, IBlockData iblockdata, Entity entity) { + if (entity.vehicle == null && entity.passenger == null && !world.isStatic) { ++ // CraftBukkit start - Entity in portal ++ EntityPortalEnterEvent event = new EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ())); ++ world.getServer().getPluginManager().callEvent(event); ++ // CraftBukkit end + entity.c(1); + } + diff --git a/nms-patches/BlockFire.patch b/nms-patches/BlockFire.patch new file mode 100644 index 00000000..0abc00c0 --- /dev/null +++ b/nms-patches/BlockFire.patch @@ -0,0 +1,110 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockFire.java 2014-11-27 08:59:46.529422604 +1100 ++++ src/main/java/net/minecraft/server/BlockFire.java 2014-11-27 08:42:10.168850880 +1100 +@@ -4,6 +4,12 @@ + import java.util.Map; + import java.util.Random; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.block.BlockBurnEvent; ++import org.bukkit.event.block.BlockSpreadEvent; ++// CraftBukkit end ++ + public class BlockFire extends Block { + + public static final BlockStateInteger AGE = BlockStateInteger.of("age", 0, 15); +@@ -109,7 +115,7 @@ + public void b(World world, BlockPosition blockposition, IBlockData iblockdata, Random random) { + if (world.getGameRules().getBoolean("doFireTick")) { + if (!this.canPlace(world, blockposition)) { +- world.setAir(blockposition); ++ fireExtinguished(world, blockposition); // CraftBukkit - invalid place location + } + + Block block = world.getType(blockposition.down()).getBlock(); +@@ -120,7 +126,7 @@ + } + + if (!flag && world.S() && this.d(world, blockposition)) { +- world.setAir(blockposition); ++ fireExtinguished(world, blockposition); // CraftBukkit - extinguished by rain + } else { + int i = ((Integer) iblockdata.get(BlockFire.AGE)).intValue(); + +@@ -186,7 +192,26 @@ + l1 = 15; + } + +- world.setTypeAndData(blockposition1, iblockdata.set(BlockFire.AGE, Integer.valueOf(l1)), 3); ++ // CraftBukkit start - Call to stop spread of fire ++ if (world.getType(blockposition1) != Blocks.FIRE) { ++ if (CraftEventFactory.callBlockIgniteEvent(world, i1, k1, j1, i, j, k).isCancelled()) { ++ continue; ++ } ++ ++ org.bukkit.Server server = world.getServer(); ++ org.bukkit.World bworld = world.getWorld(); ++ org.bukkit.block.BlockState blockState = bworld.getBlockAt(i1, k1, j1).getState(); ++ blockState.setTypeId(Block.getId(this)); ++ blockState.setData(new org.bukkit.material.MaterialData(Block.getId(this), (byte) l1)); ++ ++ BlockSpreadEvent spreadEvent = new BlockSpreadEvent(blockState.getBlock(), bworld.getBlockAt(i, j, k), blockState); ++ server.getPluginManager().callEvent(spreadEvent); ++ ++ if (!spreadEvent.isCancelled()) { ++ blockState.update(true); ++ } ++ } ++ // CraftBukkit end + } + } + } +@@ -223,6 +248,17 @@ + + if (random.nextInt(i) < k) { + IBlockData iblockdata = world.getType(blockposition); ++ ++ // CraftBukkit start ++ org.bukkit.block.Block theBlock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ ++ BlockBurnEvent event = new BlockBurnEvent(theBlock); ++ world.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ return; ++ } ++ // CraftBukkit end + + if (random.nextInt(j + 10) < 5 && !world.isRainingAt(blockposition)) { + int l = j + random.nextInt(5) / 4; +@@ -290,7 +326,7 @@ + + public void doPhysics(World world, BlockPosition blockposition, IBlockData iblockdata, Block block) { + if (!World.a((IBlockAccess) world, blockposition.down()) && !this.e(world, blockposition)) { +- world.setAir(blockposition); ++ fireExtinguished(world, blockposition); // CraftBukkit - fuel block gone + } + + } +@@ -298,7 +334,7 @@ + public void onPlace(World world, BlockPosition blockposition, IBlockData iblockdata) { + if (world.worldProvider.getDimension() > 0 || !Blocks.PORTAL.d(world, blockposition)) { + if (!World.a((IBlockAccess) world, blockposition.down()) && !this.e(world, blockposition)) { +- world.setAir(blockposition); ++ fireExtinguished(world, blockposition); // CraftBukkit - fuel block broke + } else { + world.a(blockposition, (Block) this, this.a(world) + world.random.nextInt(10)); + } +@@ -320,4 +356,12 @@ + protected BlockStateList getStateList() { + return new BlockStateList(this, new IBlockState[] { BlockFire.AGE, BlockFire.NORTH, BlockFire.EAST, BlockFire.SOUTH, BlockFire.WEST, BlockFire.UPPER, BlockFire.FLIP, BlockFire.ALT}); + } ++ ++ // CraftBukkit start ++ private void fireExtinguished(World world, BlockPosition position) { ++ if (!CraftEventFactory.callBlockFadeEvent(world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()), Blocks.AIR).isCancelled()) { ++ world.setAir(position); ++ } ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/BlockFlowing.patch b/nms-patches/BlockFlowing.patch new file mode 100644 index 00000000..84b3101d --- /dev/null +++ b/nms-patches/BlockFlowing.patch @@ -0,0 +1,83 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockFlowing.java 2014-11-27 08:59:46.529422604 +1100 ++++ src/main/java/net/minecraft/server/BlockFlowing.java 2014-11-27 08:42:10.112850989 +1100 +@@ -5,6 +5,11 @@ + import java.util.Random; + import java.util.Set; + ++// CraftBukkit start ++import org.bukkit.block.BlockFace; ++import org.bukkit.event.block.BlockFromToEvent; ++// CraftBukkit end ++ + public class BlockFlowing extends BlockFluids { + + int a; +@@ -18,7 +23,12 @@ + } + + public void b(World world, BlockPosition blockposition, IBlockData iblockdata, Random random) { +- int i = ((Integer) iblockdata.get(BlockFlowing.LEVEL)).intValue(); ++ // CraftBukkit start ++ org.bukkit.World bworld = world.getWorld(); ++ org.bukkit.Server server = world.getServer(); ++ org.bukkit.block.Block source = bworld == null ? null : bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ // CraftBukkit end ++ int i = ((Integer) iblockdata.get(LEVEL)).intValue(); + byte b0 = 1; + + if (this.material == Material.LAVA && !world.worldProvider.n()) { +@@ -88,17 +98,25 @@ + IBlockData iblockdata2 = world.getType(blockposition.down()); + + if (this.h(world, blockposition.down(), iblockdata2)) { +- if (this.material == Material.LAVA && world.getType(blockposition.down()).getBlock().getMaterial() == Material.WATER) { +- world.setTypeUpdate(blockposition.down(), Blocks.STONE.getBlockData()); +- this.fizz(world, blockposition.down()); +- return; +- } ++ // CraftBukkit start - Send "down" to the server ++ BlockFromToEvent event = new BlockFromToEvent(source, BlockFace.DOWN); ++ if (server != null) { ++ server.getPluginManager().callEvent(event); ++ } ++ if (!event.isCancelled()) { ++ if (this.material == Material.LAVA && world.getType(blockposition.down()).getBlock().getMaterial() == Material.WATER) { ++ world.setTypeUpdate(blockposition.down(), Blocks.STONE.getBlockData()); ++ this.fizz(world, blockposition.down()); ++ return; ++ } + +- if (i >= 8) { +- this.flow(world, blockposition.down(), iblockdata2, i); +- } else { +- this.flow(world, blockposition.down(), iblockdata2, i + 8); ++ if (i >= 8) { ++ this.flow(world, blockposition.down(), iblockdata2, i); ++ } else { ++ this.flow(world, blockposition.down(), iblockdata2, i + 8); ++ } + } ++ // CraftBukkit end + } else if (i >= 0 && (i == 0 || this.g(world, blockposition.down(), iblockdata2))) { + Set set = this.e(world, blockposition); + +@@ -115,8 +133,17 @@ + + while (iterator1.hasNext()) { + EnumDirection enumdirection1 = (EnumDirection) iterator1.next(); +- +- this.flow(world, blockposition.shift(enumdirection1), world.getType(blockposition.shift(enumdirection1)), k); ++ ++ // CraftBukkit start ++ BlockFromToEvent event = new BlockFromToEvent(source, org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(enumdirection1)); ++ if (server != null) { ++ server.getPluginManager().callEvent(event); ++ } ++ ++ if (!event.isCancelled()) { ++ this.flow(world, blockposition.shift(enumdirection1), world.getType(blockposition.shift(enumdirection1)), k); ++ } ++ // CraftBukkit end + } + } + diff --git a/nms-patches/BlockGrass.patch b/nms-patches/BlockGrass.patch new file mode 100644 index 00000000..b52f01fd --- /dev/null +++ b/nms-patches/BlockGrass.patch @@ -0,0 +1,77 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockGrass.java 2014-11-27 08:59:46.533422586 +1100 ++++ src/main/java/net/minecraft/server/BlockGrass.java 2014-11-27 08:42:10.172850872 +1100 +@@ -2,6 +2,14 @@ + + import java.util.Random; + ++// CraftBukkit start ++import org.bukkit.block.BlockState; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.util.CraftMagicNumbers; ++import org.bukkit.event.block.BlockSpreadEvent; ++import org.bukkit.event.block.BlockFadeEvent; ++// CraftBukkit end ++ + public class BlockGrass extends Block implements IBlockFragilePlantElement { + + public static final BlockStateBoolean SNOWY = BlockStateBoolean.of("snowy"); +@@ -22,7 +30,19 @@ + public void b(World world, BlockPosition blockposition, IBlockData iblockdata, Random random) { + if (!world.isStatic) { + if (world.getLightLevel(blockposition.up()) < 4 && world.getType(blockposition.up()).getBlock().n() > 2) { +- world.setTypeUpdate(blockposition, Blocks.DIRT.getBlockData()); ++ // CraftBukkit start ++ // world.setTypeUpdate(blockposition, Blocks.DIRT.getBlockData()); ++ org.bukkit.World bworld = world.getWorld(); ++ BlockState blockState = bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()).getState(); ++ blockState.setType(CraftMagicNumbers.getMaterial(Blocks.DIRT)); ++ ++ BlockFadeEvent event = new BlockFadeEvent(blockState.getBlock(), blockState); ++ world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ blockState.update(true); ++ } ++ // CraftBukkit end + } else { + if (world.getLightLevel(blockposition.up()) >= 9) { + for (int i = 0; i < 4; ++i) { +@@ -31,7 +51,19 @@ + IBlockData iblockdata1 = world.getType(blockposition1); + + if (iblockdata1.getBlock() == Blocks.DIRT && iblockdata1.get(BlockDirt.VARIANT) == EnumDirtVariant.DIRT && world.getLightLevel(blockposition1.up()) >= 4 && block.n() <= 2) { +- world.setTypeUpdate(blockposition1, Blocks.GRASS.getBlockData()); ++ // CraftBukkit start ++ // world.setTypeUpdate(blockposition1, Blocks.GRASS.getBlockData()); ++ org.bukkit.World bworld = world.getWorld(); ++ BlockState blockState = bworld.getBlockAt(blockposition1.getX(), blockposition1.getY(), blockposition1.getZ()).getState(); ++ blockState.setType(CraftMagicNumbers.getMaterial(Blocks.GRASS)); ++ ++ BlockSpreadEvent event = new BlockSpreadEvent(blockState.getBlock(), bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), blockState); ++ world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ blockState.update(true); ++ } ++ // CraftBukkit end + } + } + } +@@ -74,13 +106,15 @@ + IBlockData iblockdata1 = blockflowers.getBlockData().set(blockflowers.l(), enumflowervarient); + + if (blockflowers.f(world, blockposition2, iblockdata1)) { +- world.setTypeAndData(blockposition2, iblockdata1, 3); ++ // world.setTypeAndData(blockposition2, iblockdata1, 3); // CraftBukkit ++ CraftEventFactory.handleBlockGrowEvent(world, blockposition2.getX(), blockposition2.getY(), blockposition2.getZ(), iblockdata1.getBlock(), iblockdata1.getBlock().toLegacyData(iblockdata1)); // CraftBukkit + } + } else { + IBlockData iblockdata2 = Blocks.TALLGRASS.getBlockData().set(BlockLongGrass.TYPE, EnumTallGrassType.GRASS); + + if (Blocks.TALLGRASS.f(world, blockposition2, iblockdata2)) { +- world.setTypeAndData(blockposition2, iblockdata2, 3); ++ // world.setTypeAndData(blockposition2, iblockdata2, 3); // CRaftBukkit ++ CraftEventFactory.handleBlockGrowEvent(world, blockposition2.getX(), blockposition2.getY(), blockposition2.getZ(), iblockdata2.getBlock(), iblockdata2.getBlock().toLegacyData(iblockdata2)); // CraftBukkit + } + } + } diff --git a/nms-patches/BlockIce.patch b/nms-patches/BlockIce.patch new file mode 100644 index 00000000..91033f00 --- /dev/null +++ b/nms-patches/BlockIce.patch @@ -0,0 +1,15 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockIce.java 2014-11-27 08:59:46.533422586 +1100 ++++ src/main/java/net/minecraft/server/BlockIce.java 2014-11-27 08:42:10.168850880 +1100 +@@ -44,6 +44,12 @@ + + public void b(World world, BlockPosition blockposition, IBlockData iblockdata, Random random) { + if (world.b(EnumSkyBlock.BLOCK, blockposition) > 11 - this.n()) { ++ // CraftBukkit start ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), world.worldProvider.n() ? Blocks.AIR : Blocks.WATER).isCancelled()) { ++ return; ++ } ++ // CraftBukkit end ++ + if (world.worldProvider.n()) { + world.setAir(blockposition); + } else { diff --git a/nms-patches/BlockLeaves.patch b/nms-patches/BlockLeaves.patch new file mode 100644 index 00000000..067c5808 --- /dev/null +++ b/nms-patches/BlockLeaves.patch @@ -0,0 +1,26 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockLeaves.java 2014-11-27 08:59:46.537422569 +1100 ++++ src/main/java/net/minecraft/server/BlockLeaves.java 2014-11-27 08:42:10.132850949 +1100 +@@ -2,6 +2,8 @@ + + import java.util.Random; + ++import org.bukkit.event.block.LeavesDecayEvent; // CraftBukkit ++ + public abstract class BlockLeaves extends BlockTransparent { + + public static final BlockStateBoolean DECAYABLE = BlockStateBoolean.of("decayable"); +@@ -128,6 +130,14 @@ + } + + private void d(World world, BlockPosition blockposition) { ++ // CraftBukkit start ++ LeavesDecayEvent event = new LeavesDecayEvent(world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ())); ++ world.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ return; ++ } ++ // CraftBukkit end + this.b(world, blockposition, world.getType(blockposition), 0); + world.setAir(blockposition); + } diff --git a/nms-patches/BlockLever.patch b/nms-patches/BlockLever.patch new file mode 100644 index 00000000..fa9482b1 --- /dev/null +++ b/nms-patches/BlockLever.patch @@ -0,0 +1,32 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockLever.java 2014-11-27 08:59:46.541422551 +1100 ++++ src/main/java/net/minecraft/server/BlockLever.java 2014-11-27 08:42:10.168850880 +1100 +@@ -2,6 +2,8 @@ + + import java.util.Iterator; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public class BlockLever extends Block { + + public static final BlockStateEnum FACING = BlockStateEnum.of("facing", EnumLeverPosition.class); +@@ -144,6 +146,20 @@ + if (world.isStatic) { + return true; + } else { ++ // CraftBukkit start - Interact Lever ++ boolean powered = (Boolean)iblockdata.get(POWERED); ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ int old = (powered) ? 15 : 0; ++ int current = (!powered) ? 15 : 0; ++ ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, old, current); ++ world.getServer().getPluginManager().callEvent(eventRedstone); ++ ++ if ((eventRedstone.getNewCurrent() > 0) != (!powered)) { ++ return true; ++ } ++ // CraftBukkit end ++ + iblockdata = iblockdata.a(BlockLever.POWERED); + world.setTypeAndData(blockposition, iblockdata, 3); + world.makeSound((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D, "random.click", 0.3F, ((Boolean) iblockdata.get(BlockLever.POWERED)).booleanValue() ? 0.6F : 0.5F); diff --git a/nms-patches/BlockMinecartDetector.patch b/nms-patches/BlockMinecartDetector.patch new file mode 100644 index 00000000..4e33f431 --- /dev/null +++ b/nms-patches/BlockMinecartDetector.patch @@ -0,0 +1,29 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockMinecartDetector.java 2014-11-27 08:59:46.541422551 +1100 ++++ src/main/java/net/minecraft/server/BlockMinecartDetector.java 2014-11-27 08:42:10.124850965 +1100 +@@ -4,6 +4,8 @@ + import java.util.List; + import java.util.Random; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public class BlockMinecartDetector extends BlockMinecartTrackAbstract { + + public static final BlockStateEnum SHAPE = BlockStateEnum.a("shape", EnumTrackPosition.class, (Predicate) (new BlockMinecartDetectorInnerClass1())); +@@ -55,6 +57,17 @@ + if (!list.isEmpty()) { + flag1 = true; + } ++ ++ // CraftBukkit start ++ if (flag != flag1) { ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, flag ? 15 : 0, flag1 ? 15 : 0); ++ world.getServer().getPluginManager().callEvent(eventRedstone); ++ ++ flag1 = eventRedstone.getNewCurrent() > 0; ++ } ++ // CraftBukkit end + + if (flag1 && !flag) { + world.setTypeAndData(blockposition, iblockdata.set(BlockMinecartDetector.POWERED, Boolean.valueOf(true)), 3); diff --git a/nms-patches/BlockMobSpawner.patch b/nms-patches/BlockMobSpawner.patch new file mode 100644 index 00000000..5be7f790 --- /dev/null +++ b/nms-patches/BlockMobSpawner.patch @@ -0,0 +1,22 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockMobSpawner.java 2014-11-27 08:59:46.541422551 +1100 ++++ src/main/java/net/minecraft/server/BlockMobSpawner.java 2014-11-27 08:42:10.172850872 +1100 +@@ -22,9 +22,19 @@ + + public void dropNaturally(World world, BlockPosition blockposition, IBlockData iblockdata, float f, int i) { + super.dropNaturally(world, blockposition, iblockdata, f, i); ++ /* CraftBukkit start - Delegate to getExpDrop + int j = 15 + world.random.nextInt(15) + world.random.nextInt(15); + + this.dropExperience(world, blockposition, j); ++ */ ++ } ++ ++ @Override ++ public int getExpDrop(World world, IBlockData iblockdata, int enchantmentLevel) { ++ int j = 15 + world.random.nextInt(15) + world.random.nextInt(15); ++ ++ return j; ++ // CraftBukkit end + } + + public boolean c() { diff --git a/nms-patches/BlockMonsterEggs.patch b/nms-patches/BlockMonsterEggs.patch new file mode 100644 index 00000000..9260842f --- /dev/null +++ b/nms-patches/BlockMonsterEggs.patch @@ -0,0 +1,20 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockMonsterEggs.java 2014-11-27 08:59:46.545422534 +1100 ++++ src/main/java/net/minecraft/server/BlockMonsterEggs.java 2014-11-27 08:42:10.136850942 +1100 +@@ -2,6 +2,8 @@ + + import java.util.Random; + ++import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; // CraftBukkit ++ + public class BlockMonsterEggs extends Block { + + public static final BlockStateEnum VARIANT = BlockStateEnum.of("variant", EnumMonsterEggVarient.class); +@@ -50,7 +52,7 @@ + EntitySilverfish entitysilverfish = new EntitySilverfish(world); + + entitysilverfish.setPositionRotation((double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, 0.0F, 0.0F); +- world.addEntity(entitysilverfish); ++ world.addEntity(entitysilverfish, SpawnReason.SILVERFISH_BLOCK); // CraftBukkit - add SpawnReason + entitysilverfish.y(); + } + diff --git a/nms-patches/BlockMushroom.patch b/nms-patches/BlockMushroom.patch new file mode 100644 index 00000000..5f2477b7 --- /dev/null +++ b/nms-patches/BlockMushroom.patch @@ -0,0 +1,57 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockMushroom.java 2014-11-27 08:59:46.545422534 +1100 ++++ src/main/java/net/minecraft/server/BlockMushroom.java 2014-11-27 08:42:10.100851012 +1100 +@@ -3,6 +3,12 @@ + import java.util.Iterator; + import java.util.Random; + ++// CraftBukkit start ++import org.bukkit.TreeType; ++import org.bukkit.block.BlockState; ++import org.bukkit.event.block.BlockSpreadEvent; ++// CraftBukkit end ++ + public class BlockMushroom extends BlockPlant implements IBlockFragilePlantElement { + + protected BlockMushroom() { +@@ -13,6 +19,7 @@ + } + + public void b(World world, BlockPosition blockposition, IBlockData iblockdata, Random random) { ++ final int sourceX = blockposition.getX(), sourceY = blockposition.getY(), sourceZ = blockposition.getZ(); // CraftBukkit + if (random.nextInt(25) == 0) { + int i = 5; + boolean flag = true; +@@ -39,8 +46,20 @@ + blockposition2 = blockposition.a(random.nextInt(3) - 1, random.nextInt(2) - random.nextInt(2), random.nextInt(3) - 1); + } + +- if (world.isEmpty(blockposition2) && this.f(world, blockposition2, this.getBlockData())) { +- world.setTypeAndData(blockposition2, this.getBlockData(), 2); ++ if (world.isEmpty(blockposition2) && this.f(world, blockposition2, this.getBlockData())) { ++ // CraftBukkit start ++ // world.setTypeAndData(blockposition2, this.getBlockData(), 2); ++ org.bukkit.World bworld = world.getWorld(); ++ BlockState blockState = bworld.getBlockAt(blockposition2.getX(), blockposition2.getY(), blockposition2.getZ()).getState(); ++ blockState.setType(org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(this)); // nms: this.id, 0, 2 ++ ++ BlockSpreadEvent event = new BlockSpreadEvent(blockState.getBlock(), bworld.getBlockAt(sourceX, sourceY, sourceZ), blockState); ++ world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ blockState.update(true); ++ } ++ // CraftBukkit end + } + } + +@@ -69,8 +88,10 @@ + WorldGenHugeMushroom worldgenhugemushroom = null; + + if (this == Blocks.BROWN_MUSHROOM) { ++ BlockSapling.treeType = TreeType.BROWN_MUSHROOM; // CraftBukkit + worldgenhugemushroom = new WorldGenHugeMushroom(0); + } else if (this == Blocks.RED_MUSHROOM) { ++ BlockSapling.treeType = TreeType.RED_MUSHROOM; // CraftBukkit + worldgenhugemushroom = new WorldGenHugeMushroom(1); + } + diff --git a/nms-patches/BlockMycel.patch b/nms-patches/BlockMycel.patch new file mode 100644 index 00000000..0a0bd4fb --- /dev/null +++ b/nms-patches/BlockMycel.patch @@ -0,0 +1,58 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockMycel.java 2014-11-27 08:59:46.549422516 +1100 ++++ src/main/java/net/minecraft/server/BlockMycel.java 2014-11-27 08:42:10.172850872 +1100 +@@ -2,6 +2,13 @@ + + import java.util.Random; + ++// CraftBukkit start ++import org.bukkit.block.BlockState; ++import org.bukkit.craftbukkit.util.CraftMagicNumbers; ++import org.bukkit.event.block.BlockFadeEvent; ++import org.bukkit.event.block.BlockSpreadEvent; ++// CraftBukkit end ++ + public class BlockMycel extends Block { + + public static final BlockStateBoolean SNOWY = BlockStateBoolean.of("snowy"); +@@ -22,7 +29,19 @@ + public void b(World world, BlockPosition blockposition, IBlockData iblockdata, Random random) { + if (!world.isStatic) { + if (world.getLightLevel(blockposition.up()) < 4 && world.getType(blockposition.up()).getBlock().n() > 2) { +- world.setTypeUpdate(blockposition, Blocks.DIRT.getBlockData().set(BlockDirt.VARIANT, EnumDirtVariant.DIRT)); ++ // CraftBukkit start ++ // world.setTypeUpdate(blockposition, Blocks.DIRT.getBlockData().set(BlockDirt.VARIANT, EnumDirtVariant.DIRT)); ++ org.bukkit.World bworld = world.getWorld(); ++ BlockState blockState = bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()).getState(); ++ blockState.setType(CraftMagicNumbers.getMaterial(Blocks.DIRT)); ++ ++ BlockFadeEvent event = new BlockFadeEvent(blockState.getBlock(), blockState); ++ world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ blockState.update(true); ++ } ++ // CraftBukkit end + } else { + if (world.getLightLevel(blockposition.up()) >= 9) { + for (int i = 0; i < 4; ++i) { +@@ -31,7 +50,19 @@ + Block block = world.getType(blockposition1.up()).getBlock(); + + if (iblockdata1.getBlock() == Blocks.DIRT && iblockdata1.get(BlockDirt.VARIANT) == EnumDirtVariant.DIRT && world.getLightLevel(blockposition1.up()) >= 4 && block.n() <= 2) { +- world.setTypeUpdate(blockposition1, this.getBlockData()); ++ // CraftBukkit start ++ // world.setTypeUpdate(blockposition1, this.getBlockData()); ++ org.bukkit.World bworld = world.getWorld(); ++ BlockState blockState = bworld.getBlockAt(blockposition1.getX(), blockposition1.getY(), blockposition1.getZ()).getState(); ++ blockState.setType(CraftMagicNumbers.getMaterial(this)); ++ ++ BlockSpreadEvent event = new BlockSpreadEvent(blockState.getBlock(), bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), blockState); ++ world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ blockState.update(true); ++ } ++ // CraftBukkit end + } + } + } diff --git a/nms-patches/BlockNetherWart.patch b/nms-patches/BlockNetherWart.patch new file mode 100644 index 00000000..c6f458eb --- /dev/null +++ b/nms-patches/BlockNetherWart.patch @@ -0,0 +1,12 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockNetherWart.java 2014-11-27 08:59:46.549422516 +1100 ++++ src/main/java/net/minecraft/server/BlockNetherWart.java 2014-11-27 08:42:10.140850934 +1100 +@@ -28,7 +28,8 @@ + + if (i < 3 && random.nextInt(10) == 0) { + iblockdata = iblockdata.set(BlockNetherWart.AGE, Integer.valueOf(i + 1)); +- world.setTypeAndData(blockposition, iblockdata, 2); ++ // world.setTypeAndData(blockposition, iblockdata, 2); // CraftBukkit ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this, toLegacyData(iblockdata)); // CraftBukkit + } + + super.b(world, blockposition, iblockdata, random); diff --git a/nms-patches/BlockOre.patch b/nms-patches/BlockOre.patch new file mode 100644 index 00000000..473fcbaf --- /dev/null +++ b/nms-patches/BlockOre.patch @@ -0,0 +1,42 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockOre.java 2014-11-27 08:59:46.549422516 +1100 ++++ src/main/java/net/minecraft/server/BlockOre.java 2014-11-27 08:42:10.144850927 +1100 +@@ -33,6 +33,7 @@ + + public void dropNaturally(World world, BlockPosition blockposition, IBlockData iblockdata, float f, int i) { + super.dropNaturally(world, blockposition, iblockdata, f, i); ++ /* CraftBukkit start - Delegated to getExpDrop + if (this.getDropType(iblockdata, world.random, i) != Item.getItemOf(this)) { + int j = 0; + +@@ -50,7 +51,31 @@ + + this.dropExperience(world, blockposition, j); + } ++ // */ ++ } ++ ++ @Override ++ public int getExpDrop(World world, IBlockData iblockdata, int i) { ++ if (this.getDropType(iblockdata, world.random, i) != Item.getItemOf(this)) { ++ int j = 0; ++ ++ if (this == Blocks.COAL_ORE) { ++ j = MathHelper.nextInt(world.random, 0, 2); ++ } else if (this == Blocks.DIAMOND_ORE) { ++ j = MathHelper.nextInt(world.random, 3, 7); ++ } else if (this == Blocks.EMERALD_ORE) { ++ j = MathHelper.nextInt(world.random, 3, 7); ++ } else if (this == Blocks.LAPIS_ORE) { ++ j = MathHelper.nextInt(world.random, 2, 5); ++ } else if (this == Blocks.QUARTZ_ORE) { ++ j = MathHelper.nextInt(world.random, 2, 5); ++ } ++ ++ return j; ++ } + ++ return 0; ++ // CraftBukkit end + } + + public int getDropData(World world, BlockPosition blockposition) { diff --git a/nms-patches/BlockPiston.patch b/nms-patches/BlockPiston.patch new file mode 100644 index 00000000..816eebdc --- /dev/null +++ b/nms-patches/BlockPiston.patch @@ -0,0 +1,76 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockPiston.java 2014-11-27 08:59:46.553422499 +1100 ++++ src/main/java/net/minecraft/server/BlockPiston.java 2014-11-27 08:42:10.168850880 +1100 +@@ -1,6 +1,16 @@ + package net.minecraft.server; + ++import java.util.AbstractList; ++import java.util.Collection; ++import java.util.Iterator; + import java.util.List; ++import java.util.ListIterator; ++ ++// CraftBukkit start ++import org.bukkit.craftbukkit.block.CraftBlock; ++import org.bukkit.event.block.BlockPistonRetractEvent; ++import org.bukkit.event.block.BlockPistonExtendEvent; ++// CraftBukkit end + + public class BlockPiston extends Block { + +@@ -52,10 +62,19 @@ + boolean flag = this.b(world, blockposition, enumdirection); + + if (flag && !((Boolean) iblockdata.get(BlockPiston.EXTENDED)).booleanValue()) { +- if ((new PistonExtendsChecker(world, blockposition, enumdirection, true)).a()) { ++ if ((new PistonExtendsChecker(world, blockposition, enumdirection, true)).a()) { + world.playBlockAction(blockposition, this, 0, enumdirection.a()); + } + } else if (!flag && ((Boolean) iblockdata.get(BlockPiston.EXTENDED)).booleanValue()) { ++ // CraftBukkit start ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ BlockPistonRetractEvent event = new BlockPistonRetractEvent(block, CraftBlock.notchToBlockFace(enumdirection)); ++ world.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ return; ++ } ++ // CraftBukkit end + world.setTypeAndData(blockposition, iblockdata.set(BlockPiston.EXTENDED, Boolean.valueOf(false)), 2); + world.playBlockAction(blockposition, this, 1, enumdirection.a()); + } +@@ -286,6 +305,35 @@ + if (!pistonextendschecker.a()) { + return false; + } else { ++ final org.bukkit.block.Block bblock = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ ++ final List moved = pistonextendschecker.getMovedBlocks(); ++ final List broken = pistonextendschecker.getBrokenBlocks(); ++ ++ List<org.bukkit.block.Block> blocks = new AbstractList<org.bukkit.block.Block>() { ++ ++ @Override ++ public int size() { ++ return moved.size() + broken.size(); ++ } ++ ++ @Override ++ public org.bukkit.block.Block get(int index) { ++ if (index >= size() || index < 0) { ++ throw new ArrayIndexOutOfBoundsException(index); ++ } ++ BlockPosition pos = (BlockPosition) (index < moved.size() ? moved.get(index) : broken.get(index - moved.size())); ++ return bblock.getWorld().getBlockAt(pos.getX(), pos.getY(), pos.getZ()); ++ } ++ }; ++ ++ BlockPistonExtendEvent event = new BlockPistonExtendEvent(bblock, blocks, CraftBlock.notchToBlockFace(enumdirection)); ++ world.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ return false; ++ } ++ // CraftBukkit end + int i = list.size() + list1.size(); + Block[] ablock = new Block[i]; + EnumDirection enumdirection1 = flag ? enumdirection : enumdirection.opposite(); diff --git a/nms-patches/BlockPortal.patch b/nms-patches/BlockPortal.patch new file mode 100644 index 00000000..d575fc78 --- /dev/null +++ b/nms-patches/BlockPortal.patch @@ -0,0 +1,53 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockPortal.java 2014-11-27 08:59:46.553422499 +1100 ++++ src/main/java/net/minecraft/server/BlockPortal.java 2014-11-27 08:42:10.172850872 +1100 +@@ -2,6 +2,8 @@ + + import java.util.Random; + ++import org.bukkit.event.entity.EntityPortalEnterEvent; // CraftBukkit ++ + public class BlockPortal extends BlockHalfTransparent { + + public static final BlockStateEnum AXIS = BlockStateEnum.of("axis", EnumAxis.class, new EnumAxis[] { EnumAxis.X, EnumAxis.Z}); +@@ -24,7 +26,8 @@ + } + + if (i > 0 && !world.getType(blockposition1.up()).getBlock().isOccluding()) { +- Entity entity = ItemMonsterEgg.a(world, 57, (double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 1.1D, (double) blockposition1.getZ() + 0.5D); ++ // CraftBukkit - set spawn reason to NETHER_PORTAL ++ Entity entity = ItemMonsterEgg.spawnCreature(world, 57, (double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 1.1D, (double) blockposition1.getZ() + 0.5D, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NETHER_PORTAL); + + if (entity != null) { + entity.portalCooldown = entity.ar(); +@@ -66,14 +69,16 @@ + PortalCreator portalcreator = new PortalCreator(world, blockposition, EnumAxis.X); + + if (portalcreator.b() && PortalCreator.a(portalcreator) == 0) { +- portalcreator.c(); +- return true; ++ // CraftBukkit start - return portalcreator ++ return portalcreator.c(); ++ // return true; + } else { + PortalCreator portalcreator1 = new PortalCreator(world, blockposition, EnumAxis.Z); + + if (portalcreator1.b() && PortalCreator.a(portalcreator1) == 0) { +- portalcreator1.c(); +- return true; ++ return portalcreator1.c(); ++ // return true; ++ // CraftBukkit end + } else { + return false; + } +@@ -104,6 +109,10 @@ + + public void a(World world, BlockPosition blockposition, IBlockData iblockdata, Entity entity) { + if (entity.vehicle == null && entity.passenger == null) { ++ // CraftBukkit start - Entity in portal ++ EntityPortalEnterEvent event = new EntityPortalEnterEvent(entity.getBukkitEntity(), new org.bukkit.Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ())); ++ world.getServer().getPluginManager().callEvent(event); ++ // CraftBukkit end + entity.aq(); + } + diff --git a/nms-patches/BlockPoweredRail.patch b/nms-patches/BlockPoweredRail.patch new file mode 100644 index 00000000..a43bdf22 --- /dev/null +++ b/nms-patches/BlockPoweredRail.patch @@ -0,0 +1,25 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockPoweredRail.java 2014-11-27 08:59:46.557422481 +1100 ++++ src/main/java/net/minecraft/server/BlockPoweredRail.java 2014-11-27 08:42:10.124850965 +1100 +@@ -2,6 +2,8 @@ + + import com.google.common.base.Predicate; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class BlockPoweredRail extends BlockMinecartTrackAbstract { + + public static final BlockStateEnum SHAPE = BlockStateEnum.a("shape", EnumTrackPosition.class, (Predicate) (new BlockPoweredRailInnerClass1())); +@@ -108,6 +110,13 @@ + boolean flag1 = world.isBlockIndirectlyPowered(blockposition) || this.a(world, blockposition, iblockdata, true, 0) || this.a(world, blockposition, iblockdata, false, 0); + + if (flag1 != flag) { ++ // CraftBukkit start ++ int power = (Boolean)iblockdata.get(POWERED) ? 15 : 0; ++ int newPower = CraftEventFactory.callRedstoneChange(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), power, 15 - power).getNewCurrent(); ++ if (newPower == power) { ++ return; ++ } ++ // CraftBukkit end + world.setTypeAndData(blockposition, iblockdata.set(BlockPoweredRail.POWERED, Boolean.valueOf(flag1)), 3); + world.applyPhysics(blockposition.down(), this); + if (((EnumTrackPosition) iblockdata.get(BlockPoweredRail.SHAPE)).c()) { diff --git a/nms-patches/BlockPressurePlateAbstract.patch b/nms-patches/BlockPressurePlateAbstract.patch new file mode 100644 index 00000000..a226e9c4 --- /dev/null +++ b/nms-patches/BlockPressurePlateAbstract.patch @@ -0,0 +1,31 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockPressurePlateAbstract.java 2014-11-27 08:59:46.557422481 +1100 ++++ src/main/java/net/minecraft/server/BlockPressurePlateAbstract.java 2014-11-27 08:42:10.144850927 +1100 +@@ -2,6 +2,8 @@ + + import java.util.Random; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public abstract class BlockPressurePlateAbstract extends Block { + + protected BlockPressurePlateAbstract(Material material) { +@@ -90,6 +92,19 @@ + int j = this.e(world, blockposition); + boolean flag = i > 0; + boolean flag1 = j > 0; ++ ++ // CraftBukkit start - Interact Pressure Plate ++ org.bukkit.World bworld = world.getWorld(); ++ org.bukkit.plugin.PluginManager manager = world.getServer().getPluginManager(); ++ ++ if (flag != flag1) { ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), i, j); ++ manager.callEvent(eventRedstone); ++ ++ flag1 = eventRedstone.getNewCurrent() > 0; ++ j = eventRedstone.getNewCurrent(); ++ } ++ // CraftBukkit end + + if (i != j) { + iblockdata = this.a(iblockdata, j); diff --git a/nms-patches/BlockPressurePlateBinary.patch b/nms-patches/BlockPressurePlateBinary.patch new file mode 100644 index 00000000..a1d38a16 --- /dev/null +++ b/nms-patches/BlockPressurePlateBinary.patch @@ -0,0 +1,38 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockPressurePlateBinary.java 2014-11-27 08:59:46.557422481 +1100 ++++ src/main/java/net/minecraft/server/BlockPressurePlateBinary.java 2014-11-27 08:42:10.152850911 +1100 +@@ -3,6 +3,8 @@ + import java.util.Iterator; + import java.util.List; + ++import org.bukkit.event.entity.EntityInteractEvent; // CraftBukkit ++ + public class BlockPressurePlateBinary extends BlockPressurePlateAbstract { + + public static final BlockStateBoolean POWERED = BlockStateBoolean.of("powered"); +@@ -44,6 +46,26 @@ + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); ++ ++ // CraftBukkit start - Call interact event when turning on a pressure plate ++ if (this.e(world.getType(blockposition)) == 0) { ++ org.bukkit.World bworld = world.getWorld(); ++ org.bukkit.plugin.PluginManager manager = world.getServer().getPluginManager(); ++ org.bukkit.event.Cancellable cancellable; ++ ++ if (entity instanceof EntityHuman) { ++ cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((EntityHuman) entity, org.bukkit.event.block.Action.PHYSICAL, blockposition, null, null); ++ } else { ++ cancellable = new EntityInteractEvent(entity.getBukkitEntity(), bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ())); ++ manager.callEvent((EntityInteractEvent) cancellable); ++ } ++ ++ // We only want to block turning the plate on if all events are cancelled ++ if (cancellable.isCancelled()) { ++ continue; ++ } ++ } ++ // CraftBukkit end + + if (!entity.aH()) { + return 15; diff --git a/nms-patches/BlockPressurePlateWeighted.patch b/nms-patches/BlockPressurePlateWeighted.patch new file mode 100644 index 00000000..bd07b6a6 --- /dev/null +++ b/nms-patches/BlockPressurePlateWeighted.patch @@ -0,0 +1,43 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockPressurePlateWeighted.java 2014-11-27 08:59:46.561422463 +1100 ++++ src/main/java/net/minecraft/server/BlockPressurePlateWeighted.java 2014-11-27 08:42:10.160850895 +1100 +@@ -1,5 +1,7 @@ + package net.minecraft.server; + ++import org.bukkit.event.entity.EntityInteractEvent; // CraftBukkit ++ + public class BlockPressurePlateWeighted extends BlockPressurePlateAbstract { + + public static final BlockStateInteger POWER = BlockStateInteger.of("power", 0, 15); +@@ -12,7 +14,31 @@ + } + + protected int e(World world, BlockPosition blockposition) { +- int i = Math.min(world.a(Entity.class, this.a(blockposition)).size(), this.b); ++ // CraftBukkit start ++ //int i = Math.min(world.a(Entity.class, this.a(blockposition)).size(), this.b); ++ int i = 0; ++ java.util.Iterator iterator = world.a(Entity.class, this.a(blockposition)).iterator(); ++ ++ while (iterator.hasNext()) { ++ Entity entity = (Entity) iterator.next(); ++ ++ org.bukkit.event.Cancellable cancellable; ++ ++ if (entity instanceof EntityHuman) { ++ cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((EntityHuman) entity, org.bukkit.event.block.Action.PHYSICAL, blockposition, null, null); ++ } else { ++ cancellable = new EntityInteractEvent(entity.getBukkitEntity(), world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ())); ++ world.getServer().getPluginManager().callEvent((EntityInteractEvent) cancellable); ++ } ++ ++ // We only want to block turning the plate on if all events are cancelled ++ if (!cancellable.isCancelled()) { ++ i++; ++ } ++ } ++ ++ i = Math.min(i, this.b); ++ // CraftBukkit end + + if (i > 0) { + float f = (float) Math.min(this.b, i) / (float) this.b; diff --git a/nms-patches/BlockPumpkin.patch b/nms-patches/BlockPumpkin.patch new file mode 100644 index 00000000..abf3e858 --- /dev/null +++ b/nms-patches/BlockPumpkin.patch @@ -0,0 +1,117 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockPumpkin.java 2014-11-27 08:59:46.561422463 +1100 ++++ src/main/java/net/minecraft/server/BlockPumpkin.java 2014-11-27 08:42:10.108850996 +1100 +@@ -1,5 +1,11 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.util.BlockStateListPopulator; ++import org.bukkit.event.block.BlockRedstoneEvent; ++import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; ++// CraftBukkit end ++ + public class BlockPumpkin extends BlockDirectional { + + private ShapeDetector snowGolemPart; +@@ -29,31 +35,45 @@ + int j; + + if ((shapedetectorcollection = this.getDetectorSnowGolem().a(world, blockposition)) != null) { ++ BlockStateListPopulator blockList = new BlockStateListPopulator(world.getWorld()); // CraftBukkit - Use BlockStateListPopulator + for (i = 0; i < this.getDetectorSnowGolem().b(); ++i) { + ShapeDetectorBlock shapedetectorblock = shapedetectorcollection.a(0, i, 0); + +- world.setTypeAndData(shapedetectorblock.d(), Blocks.AIR.getBlockData(), 2); ++ // CraftBukkit start ++ // world.setTypeAndData(shapedetectorblock.d(), Blocks.AIR.getBlockData(), 2); ++ BlockPosition pos = shapedetectorblock.d(); ++ blockList.setTypeId(pos.getX(), pos.getY(), pos.getZ(), 0); ++ // CraftBukkit end + } + + EntitySnowman entitysnowman = new EntitySnowman(world); + BlockPosition blockposition1 = shapedetectorcollection.a(0, 2, 0).d(); + + entitysnowman.setPositionRotation((double) blockposition1.getX() + 0.5D, (double) blockposition1.getY() + 0.05D, (double) blockposition1.getZ() + 0.5D, 0.0F, 0.0F); +- world.addEntity(entitysnowman); ++ // CraftBukkit start ++ if (world.addEntity(entitysnowman, SpawnReason.BUILD_SNOWMAN)) { ++ blockList.updateList(); + +- for (j = 0; j < 120; ++j) { +- world.addParticle(EnumParticle.SNOW_SHOVEL, (double) blockposition1.getX() + world.random.nextDouble(), (double) blockposition1.getY() + world.random.nextDouble() * 2.5D, (double) blockposition1.getZ() + world.random.nextDouble(), 0.0D, 0.0D, 0.0D, new int[0]); +- } ++ for (j = 0; j < 120; ++j) { ++ world.addParticle(EnumParticle.SNOW_SHOVEL, (double) blockposition1.getX() + world.random.nextDouble(), (double) blockposition1.getY() + world.random.nextDouble() * 2.5D, (double) blockposition1.getZ() + world.random.nextDouble(), 0.0D, 0.0D, 0.0D, new int[0]); ++ } + +- for (j = 0; j < this.getDetectorSnowGolem().b(); ++j) { +- ShapeDetectorBlock shapedetectorblock1 = shapedetectorcollection.a(0, j, 0); ++ for (j = 0; j < this.getDetectorSnowGolem().b(); ++j) { ++ ShapeDetectorBlock shapedetectorblock1 = shapedetectorcollection.a(0, j, 0); + +- world.update(shapedetectorblock1.d(), Blocks.AIR); ++ world.update(shapedetectorblock1.d(), Blocks.AIR); ++ } + } ++ // CraftBukkit end + } else if ((shapedetectorcollection = this.getDetectorIronGolem().a(world, blockposition)) != null) { ++ BlockStateListPopulator blockList = new BlockStateListPopulator(world.getWorld()); // CraftBukkit - Use BlockStateListPopulator + for (i = 0; i < this.getDetectorIronGolem().c(); ++i) { + for (int k = 0; k < this.getDetectorIronGolem().b(); ++k) { +- world.setTypeAndData(shapedetectorcollection.a(i, k, 0).d(), Blocks.AIR.getBlockData(), 2); ++ // CraftBukkit start ++ // world.setTypeAndData(shapedetectorcollection.a(i, k, 0).d(), Blocks.AIR.getBlockData(), 2); ++ BlockPosition pos = shapedetectorcollection.a(i, k, 0).d(); ++ blockList.setTypeId(pos.getX(), pos.getY(), pos.getZ(), 0); ++ // CraftBukkit end + } + } + +@@ -62,22 +82,38 @@ + + entityirongolem.setPlayerCreated(true); + entityirongolem.setPositionRotation((double) blockposition2.getX() + 0.5D, (double) blockposition2.getY() + 0.05D, (double) blockposition2.getZ() + 0.5D, 0.0F, 0.0F); +- world.addEntity(entityirongolem); +- +- for (j = 0; j < 120; ++j) { +- world.addParticle(EnumParticle.SNOWBALL, (double) blockposition2.getX() + world.random.nextDouble(), (double) blockposition2.getY() + world.random.nextDouble() * 3.9D, (double) blockposition2.getZ() + world.random.nextDouble(), 0.0D, 0.0D, 0.0D, new int[0]); +- } ++ // CraftBukkit start ++ if (world.addEntity(entityirongolem, SpawnReason.BUILD_IRONGOLEM)) { ++ blockList.updateList(); ++ ++ for (j = 0; j < 120; ++j) { ++ world.addParticle(EnumParticle.SNOWBALL, (double) blockposition2.getX() + world.random.nextDouble(), (double) blockposition2.getY() + world.random.nextDouble() * 3.9D, (double) blockposition2.getZ() + world.random.nextDouble(), 0.0D, 0.0D, 0.0D, new int[0]); ++ } + +- for (j = 0; j < this.getDetectorIronGolem().c(); ++j) { +- for (int l = 0; l < this.getDetectorIronGolem().b(); ++l) { +- ShapeDetectorBlock shapedetectorblock2 = shapedetectorcollection.a(j, l, 0); ++ for (j = 0; j < this.getDetectorIronGolem().c(); ++j) { ++ for (int l = 0; l < this.getDetectorIronGolem().b(); ++l) { ++ ShapeDetectorBlock shapedetectorblock2 = shapedetectorcollection.a(j, l, 0); + +- world.update(shapedetectorblock2.d(), Blocks.AIR); ++ world.update(shapedetectorblock2.d(), Blocks.AIR); ++ } + } + } ++ // CraftBukkit end + } ++ } + ++ // CraftBukkit start ++ @Override ++ public void doPhysics(World world, BlockPosition position, IBlockData data, Block block) { ++ if (block != null && block.isPowerSource()) { ++ org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()); ++ int power = bukkitBlock.getBlockPower(); ++ ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bukkitBlock, power, power); ++ world.getServer().getPluginManager().callEvent(eventRedstone); ++ } + } ++ // CraftBukkit end + + public boolean canPlace(World world, BlockPosition blockposition) { + return world.getType(blockposition).getBlock().material.isReplaceable() && World.a((IBlockAccess) world, blockposition.down()); diff --git a/nms-patches/BlockRedstoneLamp.patch b/nms-patches/BlockRedstoneLamp.patch new file mode 100644 index 00000000..66c81280 --- /dev/null +++ b/nms-patches/BlockRedstoneLamp.patch @@ -0,0 +1,47 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockRedstoneLamp.java 2014-11-27 08:59:46.565422446 +1100 ++++ src/main/java/net/minecraft/server/BlockRedstoneLamp.java 2014-11-27 08:42:10.140850934 +1100 +@@ -2,6 +2,8 @@ + + import java.util.Random; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class BlockRedstoneLamp extends Block { + + private final boolean a; +@@ -20,6 +22,11 @@ + if (this.a && !world.isBlockIndirectlyPowered(blockposition)) { + world.setTypeAndData(blockposition, Blocks.REDSTONE_LAMP.getBlockData(), 2); + } else if (!this.a && world.isBlockIndirectlyPowered(blockposition)) { ++ // CraftBukkit start ++ if (CraftEventFactory.callRedstoneChange(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), 0, 15).getNewCurrent() != 15) { ++ return; ++ } ++ // CraftBukkit end + world.setTypeAndData(blockposition, Blocks.LIT_REDSTONE_LAMP.getBlockData(), 2); + } + +@@ -31,6 +38,11 @@ + if (this.a && !world.isBlockIndirectlyPowered(blockposition)) { + world.a(blockposition, (Block) this, 4); + } else if (!this.a && world.isBlockIndirectlyPowered(blockposition)) { ++ // CraftBukkit start ++ if (CraftEventFactory.callRedstoneChange(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), 0, 15).getNewCurrent() != 15) { ++ return; ++ } ++ // CraftBukkit end + world.setTypeAndData(blockposition, Blocks.LIT_REDSTONE_LAMP.getBlockData(), 2); + } + +@@ -40,6 +52,11 @@ + public void b(World world, BlockPosition blockposition, IBlockData iblockdata, Random random) { + if (!world.isStatic) { + if (this.a && !world.isBlockIndirectlyPowered(blockposition)) { ++ // CraftBukkit start ++ if (CraftEventFactory.callRedstoneChange(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), 15, 0).getNewCurrent() != 0) { ++ return; ++ } ++ // CraftBukkit end + world.setTypeAndData(blockposition, Blocks.REDSTONE_LAMP.getBlockData(), 2); + } + diff --git a/nms-patches/BlockRedstoneOre.patch b/nms-patches/BlockRedstoneOre.patch new file mode 100644 index 00000000..fcc0a637 --- /dev/null +++ b/nms-patches/BlockRedstoneOre.patch @@ -0,0 +1,102 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockRedstoneOre.java 2014-11-27 08:59:46.565422446 +1100 ++++ src/main/java/net/minecraft/server/BlockRedstoneOre.java 2014-11-27 08:42:10.112850989 +1100 +@@ -2,6 +2,11 @@ + + import java.util.Random; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.EntityInteractEvent; ++// CraftBukkit end ++ + public class BlockRedstoneOre extends Block { + + private final boolean a; +@@ -20,23 +25,44 @@ + } + + public void attack(World world, BlockPosition blockposition, EntityHuman entityhuman) { +- this.d(world, blockposition); ++ this.d(world, blockposition, entityhuman); // CraftBukkit - add entityhuman + super.attack(world, blockposition, entityhuman); + } + +- public void a(World world, BlockPosition blockposition, Entity entity) { +- this.d(world, blockposition); +- super.a(world, blockposition, entity); ++ public void a(World world, BlockPosition blockposition, Entity entity) { ++ // CraftBukkit start ++ // this.d(world, blockposition); ++ // super.a(world, blockposition, entity); ++ if (entity instanceof EntityHuman) { ++ org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((EntityHuman) entity, org.bukkit.event.block.Action.PHYSICAL, blockposition, null, null); ++ if (!event.isCancelled()) { ++ this.d(world, blockposition, entity); // add entity ++ super.a(world, blockposition, entity); ++ } ++ } else { ++ EntityInteractEvent event = new EntityInteractEvent(entity.getBukkitEntity(), world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ())); ++ world.getServer().getPluginManager().callEvent(event); ++ if (!event.isCancelled()) { ++ this.d(world, blockposition, entity); // add entity ++ super.a(world, blockposition, entity); ++ } ++ } ++ // CraftBukkit end + } + + public boolean interact(World world, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman, EnumDirection enumdirection, float f, float f1, float f2) { +- this.d(world, blockposition); ++ this.d(world, blockposition, entityhuman); // CraftBukkit - add entityhuman + return super.interact(world, blockposition, iblockdata, entityhuman, enumdirection, f, f1, f2); + } + +- private void d(World world, BlockPosition blockposition) { ++ private void d(World world, BlockPosition blockposition, Entity entity) { // CraftBukkit - add Entity + this.e(world, blockposition); + if (this == Blocks.REDSTONE_ORE) { ++ // CraftBukkit start ++ if (CraftEventFactory.callEntityChangeBlockEvent(entity, blockposition.getX(), blockposition.getY(), blockposition.getZ(), Blocks.LIT_REDSTONE_ORE, 0).isCancelled()) { ++ return; ++ } ++ // CraftBukkit end + world.setTypeUpdate(blockposition, Blocks.LIT_REDSTONE_ORE.getBlockData()); + } + +@@ -44,6 +70,11 @@ + + public void b(World world, BlockPosition blockposition, IBlockData iblockdata, Random random) { + if (this == Blocks.LIT_REDSTONE_ORE) { ++ // CraftBukkit start ++ if (CraftEventFactory.callBlockFadeEvent(world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), Blocks.REDSTONE_ORE).isCancelled()) { ++ return; ++ } ++ // CraftBukkit end + world.setTypeUpdate(blockposition, Blocks.REDSTONE_ORE.getBlockData()); + } + +@@ -63,12 +94,24 @@ + + public void dropNaturally(World world, BlockPosition blockposition, IBlockData iblockdata, float f, int i) { + super.dropNaturally(world, blockposition, iblockdata, f, i); ++ /* CraftBukkit start - Delegated to getExpDrop + if (this.getDropType(iblockdata, world.random, i) != Item.getItemOf(this)) { + int j = 1 + world.random.nextInt(5); + + this.dropExperience(world, blockposition, j); + } ++ // */ ++ } + ++ @Override ++ public int getExpDrop(World world, IBlockData data, int i) { ++ if (this.getDropType(data, world.random, i) != Item.getItemOf(this)) { ++ int j = 1 + world.random.nextInt(5); ++ ++ return j; ++ } ++ return 0; ++ // CraftBukkit end + } + + private void e(World world, BlockPosition blockposition) { diff --git a/nms-patches/BlockRedstoneTorch.patch b/nms-patches/BlockRedstoneTorch.patch new file mode 100644 index 00000000..60516593 --- /dev/null +++ b/nms-patches/BlockRedstoneTorch.patch @@ -0,0 +1,55 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockRedstoneTorch.java 2014-11-27 08:59:46.565422446 +1100 ++++ src/main/java/net/minecraft/server/BlockRedstoneTorch.java 2014-11-27 08:42:10.156850903 +1100 +@@ -6,6 +6,8 @@ + import java.util.Map; + import java.util.Random; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public class BlockRedstoneTorch extends BlockTorch { + + private static Map b = Maps.newHashMap(); +@@ -95,9 +97,26 @@ + while (list != null && !list.isEmpty() && world.getTime() - ((RedstoneUpdateInfo) list.get(0)).b > 60L) { + list.remove(0); + } ++ ++ // CraftBukkit start ++ org.bukkit.plugin.PluginManager manager = world.getServer().getPluginManager(); ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ int oldCurrent = this.isOn ? 15 : 0; ++ ++ BlockRedstoneEvent event = new BlockRedstoneEvent(block, oldCurrent, oldCurrent); ++ // CraftBukkit end + + if (this.isOn) { + if (flag) { ++ // CraftBukkit start ++ if (oldCurrent != 0) { ++ event.setNewCurrent(0); ++ manager.callEvent(event); ++ if (event.getNewCurrent() != 0) { ++ return; ++ } ++ } ++ // CraftBukkit end + world.setTypeAndData(blockposition, Blocks.UNLIT_REDSTONE_TORCH.getBlockData().set(BlockRedstoneTorch.FACING, iblockdata.get(BlockRedstoneTorch.FACING)), 3); + if (this.a(world, blockposition, true)) { + world.makeSound((double) ((float) blockposition.getX() + 0.5F), (double) ((float) blockposition.getY() + 0.5F), (double) ((float) blockposition.getZ() + 0.5F), "random.fizz", 0.5F, 2.6F + (world.random.nextFloat() - world.random.nextFloat()) * 0.8F); +@@ -114,6 +133,16 @@ + } + } + } else if (!flag && !this.a(world, blockposition, false)) { ++ // CraftBukkit start ++ if (oldCurrent != 15) { ++ event.setNewCurrent(15); ++ manager.callEvent(event); ++ if (event.getNewCurrent() != 15) { ++ return; ++ } ++ } ++ // CraftBukkit end ++ + world.setTypeAndData(blockposition, Blocks.REDSTONE_TORCH.getBlockData().set(BlockRedstoneTorch.FACING, iblockdata.get(BlockRedstoneTorch.FACING)), 3); + } + diff --git a/nms-patches/BlockRedstoneWire.patch b/nms-patches/BlockRedstoneWire.patch new file mode 100644 index 00000000..79e7b08f --- /dev/null +++ b/nms-patches/BlockRedstoneWire.patch @@ -0,0 +1,27 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockRedstoneWire.java 2014-11-27 08:59:46.569422428 +1100 ++++ src/main/java/net/minecraft/server/BlockRedstoneWire.java 2014-11-27 08:42:10.136850942 +1100 +@@ -8,6 +8,8 @@ + import java.util.Random; + import java.util.Set; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public class BlockRedstoneWire extends Block { + + public static final BlockStateEnum NORTH = BlockStateEnum.of("north", EnumRedstoneWireConnection.class); +@@ -123,6 +125,15 @@ + if (k > j - 1) { + j = k; + } ++ ++ // CraftBukkit start ++ if (i != j) { ++ BlockRedstoneEvent event = new BlockRedstoneEvent(world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), i, j); ++ world.getServer().getPluginManager().callEvent(event); ++ ++ j = event.getNewCurrent(); ++ } ++ // CraftBukkit end + + if (i != j) { + iblockdata = iblockdata.set(BlockRedstoneWire.POWER, Integer.valueOf(j)); diff --git a/nms-patches/BlockReed.patch b/nms-patches/BlockReed.patch new file mode 100644 index 00000000..667af180 --- /dev/null +++ b/nms-patches/BlockReed.patch @@ -0,0 +1,18 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockReed.java 2014-11-27 08:59:46.569422428 +1100 ++++ src/main/java/net/minecraft/server/BlockReed.java 2014-11-27 08:42:10.120850973 +1100 +@@ -29,8 +29,13 @@ + int j = ((Integer) iblockdata.get(BlockReed.AGE)).intValue(); + + if (j == 15) { +- world.setTypeUpdate(blockposition.up(), this.getBlockData()); +- world.setTypeAndData(blockposition, iblockdata.set(BlockReed.AGE, Integer.valueOf(0)), 4); ++ // CraftBukkit start ++ // world.setTypeUpdate(blockposition.up(), this.getBlockData()); ++ // world.setTypeAndData(blockposition, iblockdata.set(BlockReed.AGE, Integer.valueOf(0)), 4); ++ BlockPosition upPos = blockposition.up(); ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleBlockGrowEvent(world, upPos.getX(), upPos.getY(), upPos.getZ(), this, 0); ++ // CraftBukkit end ++ + } else { + world.setTypeAndData(blockposition, iblockdata.set(BlockReed.AGE, Integer.valueOf(j + 1)), 4); + } diff --git a/nms-patches/BlockSapling.patch b/nms-patches/BlockSapling.patch new file mode 100644 index 00000000..5212c9d9 --- /dev/null +++ b/nms-patches/BlockSapling.patch @@ -0,0 +1,125 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockSapling.java 2014-11-27 08:59:46.573422410 +1100 ++++ src/main/java/net/minecraft/server/BlockSapling.java 2014-11-27 08:42:10.108850996 +1100 +@@ -2,10 +2,20 @@ + + import java.util.Random; + ++// CraftBukkit start ++import java.util.List; ++ ++import org.bukkit.Location; ++import org.bukkit.TreeType; ++import org.bukkit.block.BlockState; ++import org.bukkit.event.world.StructureGrowEvent; ++// CraftBukkit end ++ + public class BlockSapling extends BlockPlant implements IBlockFragilePlantElement { + + public static final BlockStateEnum TYPE = BlockStateEnum.of("type", EnumLogVariant.class); + public static final BlockStateInteger STAGE = BlockStateInteger.of("stage", 0, 1); ++ public static TreeType treeType; // CraftBukkit + + protected BlockSapling() { + this.j(this.blockStateList.getBlockData().set(BlockSapling.TYPE, EnumLogVariant.OAK).set(BlockSapling.STAGE, Integer.valueOf(0))); +@@ -19,7 +29,30 @@ + if (!world.isStatic) { + super.b(world, blockposition, iblockdata, random); + if (world.getLightLevel(blockposition.up()) >= 9 && random.nextInt(7) == 0) { ++ // CraftBukkit start ++ world.captureTreeGeneration = true; ++ // CraftBukkit end + this.grow(world, blockposition, iblockdata, random); ++ // CraftBukkit start ++ world.captureTreeGeneration = false; ++ if (world.capturedBlockStates.size() > 0) { ++ TreeType treeType = BlockSapling.treeType; ++ BlockSapling.treeType = null; ++ Location location = new Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ List<BlockState> blocks = (List<BlockState>) world.capturedBlockStates.clone(); ++ world.capturedBlockStates.clear(); ++ StructureGrowEvent event = null; ++ if (treeType != null) { ++ event = new StructureGrowEvent(location, treeType, false, null, blocks); ++ org.bukkit.Bukkit.getPluginManager().callEvent(event); ++ } ++ if (event == null || !event.isCancelled()) { ++ for (BlockState blockstate : blocks) { ++ blockstate.update(true); ++ } ++ } ++ } ++ // CraftBukkit end + } + + } +@@ -35,7 +68,17 @@ + } + + public void e(World world, BlockPosition blockposition, IBlockData iblockdata, Random random) { +- Object object = random.nextInt(10) == 0 ? new WorldGenBigTree(true) : new WorldGenTrees(true); ++ // CraftBukkit start - Turn ternary operator into if statement to set treeType ++ // Object object = random.nextInt(10) == 0 ? new WorldGenBigTree(true) : new WorldGenTrees(true); ++ Object object; ++ if (random.nextInt(10) == 0) { ++ treeType = TreeType.BIG_TREE; ++ object = new WorldGenBigTree(true); ++ } else { ++ treeType = TreeType.TREE; ++ object = new WorldGenTrees(true); ++ } ++ // CraftBukkit end + int i = 0; + int j = 0; + boolean flag = false; +@@ -46,6 +89,7 @@ + for (i = 0; i >= -1; --i) { + for (j = 0; j >= -1; --j) { + if (this.a(world, blockposition.a(i, 0, j), EnumLogVariant.SPRUCE) && this.a(world, blockposition.a(i + 1, 0, j), EnumLogVariant.SPRUCE) && this.a(world, blockposition.a(i, 0, j + 1), EnumLogVariant.SPRUCE) && this.a(world, blockposition.a(i + 1, 0, j + 1), EnumLogVariant.SPRUCE)) { ++ treeType = TreeType.MEGA_REDWOOD; // CraftBukkit + object = new WorldGenMegaTree(false, random.nextBoolean()); + flag = true; + break label78; +@@ -56,11 +100,13 @@ + if (!flag) { + j = 0; + i = 0; ++ treeType = TreeType.REDWOOD; // CraftBukkit + object = new WorldGenTaiga2(true); + } + break; + + case 2: ++ treeType = TreeType.BIRCH; // CraftBukkit + object = new WorldGenForest(true, false); + break; + +@@ -69,6 +115,7 @@ + for (i = 0; i >= -1; --i) { + for (j = 0; j >= -1; --j) { + if (this.a(world, blockposition.a(i, 0, j), EnumLogVariant.JUNGLE) && this.a(world, blockposition.a(i + 1, 0, j), EnumLogVariant.JUNGLE) && this.a(world, blockposition.a(i, 0, j + 1), EnumLogVariant.JUNGLE) && this.a(world, blockposition.a(i + 1, 0, j + 1), EnumLogVariant.JUNGLE)) { ++ treeType = TreeType.JUNGLE; // CraftBukkit + object = new WorldGenJungleTree(true, 10, 20, EnumLogVariant.JUNGLE.a(), EnumLogVariant.JUNGLE.a()); + flag = true; + break label93; +@@ -79,11 +126,13 @@ + if (!flag) { + j = 0; + i = 0; ++ treeType = TreeType.SMALL_JUNGLE; // CraftBukkit + object = new WorldGenTrees(true, 4 + random.nextInt(7), EnumLogVariant.JUNGLE.a(), EnumLogVariant.JUNGLE.a(), false); + } + break; + + case 4: ++ treeType = TreeType.ACACIA; // CraftBukki + object = new WorldGenAcaciaTree(true); + break; + +@@ -92,6 +141,7 @@ + for (i = 0; i >= -1; --i) { + for (j = 0; j >= -1; --j) { + if (this.a(world, blockposition.a(i, 0, j), EnumLogVariant.DARK_OAK) && this.a(world, blockposition.a(i + 1, 0, j), EnumLogVariant.DARK_OAK) && this.a(world, blockposition.a(i, 0, j + 1), EnumLogVariant.DARK_OAK) && this.a(world, blockposition.a(i + 1, 0, j + 1), EnumLogVariant.DARK_OAK)) { ++ treeType = TreeType.DARK_OAK; // CraftBukkit + object = new WorldGenForestTree(true); + flag = true; + break label108; diff --git a/nms-patches/BlockSkull.patch b/nms-patches/BlockSkull.patch new file mode 100644 index 00000000..0bebb84d --- /dev/null +++ b/nms-patches/BlockSkull.patch @@ -0,0 +1,133 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockSkull.java 2014-11-27 08:59:46.573422410 +1100 ++++ src/main/java/net/minecraft/server/BlockSkull.java 2014-11-27 08:42:10.156850903 +1100 +@@ -4,6 +4,11 @@ + import java.util.Iterator; + import java.util.Random; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.util.BlockStateListPopulator; ++import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; ++// CraftBukkit end ++ + public class BlockSkull extends BlockContainer { + + public static final BlockStateDirection FACING = BlockStateDirection.of("facing"); +@@ -69,8 +74,25 @@ + + return tileentity instanceof TileEntitySkull ? ((TileEntitySkull) tileentity).getSkullType() : super.getDropData(world, blockposition); + } ++ ++ // CraftBukkit start - Special case dropping so we can get info from the tile entity ++ public void dropNaturally(World world, BlockPosition blockposition, IBlockData iblockdata, float f, int i) { ++ if (world.random.nextFloat() < f) { ++ ItemStack itemstack = new ItemStack(Items.SKULL, 1, this.getDropData(world, blockposition)); ++ TileEntitySkull tileentityskull = (TileEntitySkull) world.getTileEntity(blockposition); ++ ++ if (tileentityskull.getSkullType() == 3 && tileentityskull.getGameProfile() != null) { ++ itemstack.setTag(new NBTTagCompound()); ++ NBTTagCompound nbttagcompound = new NBTTagCompound(); ++ ++ GameProfileSerializer.serialize(nbttagcompound, tileentityskull.getGameProfile()); ++ itemstack.getTag().set("SkullOwner", nbttagcompound); ++ } + +- public void dropNaturally(World world, BlockPosition blockposition, IBlockData iblockdata, float f, int i) {} ++ a(world, blockposition, itemstack); ++ } ++ } ++ // CraftBukkit end + + public void a(World world, BlockPosition blockposition, IBlockData iblockdata, EntityHuman entityhuman) { + if (entityhuman.abilities.canInstantlyBuild) { +@@ -83,7 +105,10 @@ + + public void remove(World world, BlockPosition blockposition, IBlockData iblockdata) { + if (!world.isStatic) { +- if (!((Boolean) iblockdata.get(BlockSkull.NODROP)).booleanValue()) { ++ // CraftBukkit start - Drop item in code above, not here ++ // if (!((Boolean) iblockdata.get(BlockSkull.NODROP)).booleanValue()) { ++ if (false) { ++ // CraftBukkit end + TileEntity tileentity = world.getTileEntity(blockposition); + + if (tileentity instanceof TileEntitySkull) { +@@ -120,19 +145,30 @@ + ShapeDetectorCollection shapedetectorcollection = shapedetector.a(world, blockposition); + + if (shapedetectorcollection != null) { ++ // CraftBukkit start - Use BlockStateListPopulator ++ BlockStateListPopulator blockList = new BlockStateListPopulator(world.getWorld()); + int i; + + for (i = 0; i < 3; ++i) { + ShapeDetectorBlock shapedetectorblock = shapedetectorcollection.a(i, 0, 0); + +- world.setTypeAndData(shapedetectorblock.d(), shapedetectorblock.a().set(BlockSkull.NODROP, Boolean.valueOf(true)), 2); ++ // CraftBukkit start ++ // world.setTypeAndData(shapedetectorblock.d(), shapedetectorblock.a().set(BlockSkull.NODROP, Boolean.valueOf(true)), 2); ++ BlockPosition pos = shapedetectorblock.d(); ++ IBlockData data = shapedetectorblock.a().set(BlockSkull.NODROP, Boolean.valueOf(true)); ++ blockList.setTypeAndData(pos.getX(), pos.getY(), pos.getZ(), data.getBlock(), data.getBlock().toLegacyData(data), 2); ++ // CraftBukkit end + } + + for (i = 0; i < shapedetector.c(); ++i) { + for (int j = 0; j < shapedetector.b(); ++j) { + ShapeDetectorBlock shapedetectorblock1 = shapedetectorcollection.a(i, j, 0); + +- world.setTypeAndData(shapedetectorblock1.d(), Blocks.AIR.getBlockData(), 2); ++ // CraftBukkit start ++ // world.setTypeAndData(shapedetectorblock1.d(), Blocks.AIR.getBlockData(), 2); ++ BlockPosition pos = shapedetectorblock1.d(); ++ blockList.setTypeAndData(pos.getX(), pos.getY(), pos.getZ(), Blocks.AIR, 0, 2); ++ // CraftBukkit end + } + } + +@@ -145,28 +181,31 @@ + entitywither.n(); + Iterator iterator = world.a(EntityHuman.class, entitywither.getBoundingBox().grow(50.0D, 50.0D, 50.0D)).iterator(); + +- while (iterator.hasNext()) { +- EntityHuman entityhuman = (EntityHuman) iterator.next(); ++ // CraftBukkit start ++ if (world.addEntity(entitywither, SpawnReason.BUILD_WITHER)) { ++ while (iterator.hasNext()) { ++ EntityHuman entityhuman = (EntityHuman) iterator.next(); + +- entityhuman.b((Statistic) AchievementList.I); +- } +- +- world.addEntity(entitywither); ++ entityhuman.b((Statistic) AchievementList.I); ++ } ++ ++ blockList.updateList(); + +- int k; ++ int k; + +- for (k = 0; k < 120; ++k) { +- world.addParticle(EnumParticle.SNOWBALL, (double) blockposition1.getX() + world.random.nextDouble(), (double) (blockposition1.getY() - 2) + world.random.nextDouble() * 3.9D, (double) blockposition1.getZ() + world.random.nextDouble(), 0.0D, 0.0D, 0.0D, new int[0]); +- } ++ for (k = 0; k < 120; ++k) { ++ world.addParticle(EnumParticle.SNOWBALL, (double) blockposition1.getX() + world.random.nextDouble(), (double) (blockposition1.getY() - 2) + world.random.nextDouble() * 3.9D, (double) blockposition1.getZ() + world.random.nextDouble(), 0.0D, 0.0D, 0.0D, new int[0]); ++ } + +- for (k = 0; k < shapedetector.c(); ++k) { +- for (int l = 0; l < shapedetector.b(); ++l) { +- ShapeDetectorBlock shapedetectorblock2 = shapedetectorcollection.a(k, l, 0); ++ for (k = 0; k < shapedetector.c(); ++k) { ++ for (int l = 0; l < shapedetector.b(); ++l) { ++ ShapeDetectorBlock shapedetectorblock2 = shapedetectorcollection.a(k, l, 0); + +- world.update(shapedetectorblock2.d(), Blocks.AIR); ++ world.update(shapedetectorblock2.d(), Blocks.AIR); ++ } + } + } +- ++ // CraftBukkit end + } + } + } diff --git a/nms-patches/BlockSnow.patch b/nms-patches/BlockSnow.patch new file mode 100644 index 00000000..5727d5a5 --- /dev/null +++ b/nms-patches/BlockSnow.patch @@ -0,0 +1,14 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockSnow.java 2014-11-27 08:59:46.577422392 +1100 ++++ src/main/java/net/minecraft/server/BlockSnow.java 2014-11-27 08:42:10.144850927 +1100 +@@ -85,6 +85,11 @@ + + public void b(World world, BlockPosition blockposition, IBlockData iblockdata, Random random) { + if (world.b(EnumSkyBlock.BLOCK, blockposition) > 11) { ++ // CraftBukkit start ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockFadeEvent(world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), Blocks.AIR).isCancelled()) { ++ return; ++ } ++ // CraftBukkit end + this.b(world, blockposition, world.getType(blockposition), 0); + world.setAir(blockposition); + } diff --git a/nms-patches/BlockSoil.patch b/nms-patches/BlockSoil.patch new file mode 100644 index 00000000..cd0c3d50 --- /dev/null +++ b/nms-patches/BlockSoil.patch @@ -0,0 +1,52 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockSoil.java 2014-11-27 08:59:46.577422392 +1100 ++++ src/main/java/net/minecraft/server/BlockSoil.java 2014-11-27 08:42:10.168850880 +1100 +@@ -3,6 +3,11 @@ + import java.util.Iterator; + import java.util.Random; + ++// CraftBukkit start ++import org.bukkit.event.entity.EntityInteractEvent; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++// CraftBukkit end ++ + public class BlockSoil extends Block { + + public static final BlockStateInteger MOISTURE = BlockStateInteger.of("moisture", 0, 7); +@@ -34,6 +39,12 @@ + if (i > 0) { + world.setTypeAndData(blockposition, iblockdata.set(BlockSoil.MOISTURE, Integer.valueOf(i - 1)), 2); + } else if (!this.d(world, blockposition)) { ++ // CraftBukkit start ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ if (CraftEventFactory.callBlockFadeEvent(block, Blocks.DIRT).isCancelled()) { ++ return; ++ } ++ // CraftBukkit end + world.setTypeUpdate(blockposition, Blocks.DIRT.getBlockData()); + } + } else if (i < 7) { +@@ -49,6 +60,24 @@ + return; + } + ++ // CraftBukkit start - Interact soil ++ org.bukkit.event.Cancellable cancellable; ++ if (entity instanceof EntityHuman) { ++ cancellable = CraftEventFactory.callPlayerInteractEvent((EntityHuman) entity, org.bukkit.event.block.Action.PHYSICAL, blockposition, null, null); ++ } else { ++ cancellable = new EntityInteractEvent(entity.getBukkitEntity(), world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ())); ++ world.getServer().getPluginManager().callEvent((EntityInteractEvent) cancellable); ++ } ++ ++ if (cancellable.isCancelled()) { ++ return; ++ } ++ ++ if (CraftEventFactory.callEntityChangeBlockEvent(entity, blockposition.getX(), blockposition.getY(), blockposition.getZ(), Blocks.DIRT, 0).isCancelled()) { ++ return; ++ } ++ // CraftBukkit end ++ + world.setTypeUpdate(blockposition, Blocks.DIRT.getBlockData()); + } + diff --git a/nms-patches/BlockStationary.patch b/nms-patches/BlockStationary.patch new file mode 100644 index 00000000..5ffd0a2e --- /dev/null +++ b/nms-patches/BlockStationary.patch @@ -0,0 +1,40 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockStationary.java 2014-11-27 08:59:46.577422392 +1100 ++++ src/main/java/net/minecraft/server/BlockStationary.java 2014-11-27 08:42:10.152850911 +1100 +@@ -2,6 +2,8 @@ + + import java.util.Random; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class BlockStationary extends BlockFluids { + + protected BlockStationary(Material material) { +@@ -41,6 +43,13 @@ + + if (block.material == Material.AIR) { + if (this.e(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; + } +@@ -53,6 +62,14 @@ + BlockPosition blockposition2 = blockposition.a(random.nextInt(3) - 1, 0, random.nextInt(3) - 1); + + if (world.isEmpty(blockposition2.up()) && this.m(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()); + } + } diff --git a/nms-patches/BlockStem.patch b/nms-patches/BlockStem.patch new file mode 100644 index 00000000..b20e09ca --- /dev/null +++ b/nms-patches/BlockStem.patch @@ -0,0 +1,41 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockStem.java 2014-11-27 08:59:46.581422375 +1100 ++++ src/main/java/net/minecraft/server/BlockStem.java 2014-11-27 08:42:10.152850911 +1100 +@@ -4,6 +4,8 @@ + import java.util.Iterator; + import java.util.Random; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class BlockStem extends BlockPlant implements IBlockFragilePlantElement { + + public static final BlockStateInteger AGE = BlockStateInteger.of("age", 0, 7); +@@ -50,7 +52,8 @@ + + if (i < 7) { + iblockdata = iblockdata.set(BlockStem.AGE, Integer.valueOf(i + 1)); +- world.setTypeAndData(blockposition, iblockdata, 2); ++ // world.setTypeAndData(blockposition, iblockdata, 2); // CraftBukkit ++ CraftEventFactory.handleBlockGrowEvent(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this, toLegacyData(iblockdata)); // CraftBukkit + } else { + Iterator iterator = EnumDirectionLimit.HORIZONTAL.iterator(); + +@@ -66,7 +69,8 @@ + Block block = world.getType(blockposition.down()).getBlock(); + + if (world.getType(blockposition).getBlock().material == Material.AIR && (block == Blocks.FARMLAND || block == Blocks.DIRT || block == Blocks.GRASS)) { +- world.setTypeUpdate(blockposition, this.blockFruit.getBlockData()); ++ // world.setTypeUpdate(blockposition, this.blockFruit.getBlockData()); // CraftBukkit ++ CraftEventFactory.handleBlockGrowEvent(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this.blockFruit, 0); // CraftBukkit + } + } + } +@@ -77,7 +81,8 @@ + public void g(World world, BlockPosition blockposition, IBlockData iblockdata) { + int i = ((Integer) iblockdata.get(BlockStem.AGE)).intValue() + MathHelper.nextInt(world.random, 2, 5); + +- world.setTypeAndData(blockposition, iblockdata.set(BlockStem.AGE, Integer.valueOf(Math.min(7, i))), 2); ++ // world.setTypeAndData(blockposition, iblockdata.set(BlockStem.AGE, Integer.valueOf(Math.min(7, i))), 2); ++ CraftEventFactory.handleBlockGrowEvent(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this, Math.min(7, i)); // CraftBukkit + } + + public void h() { diff --git a/nms-patches/BlockTrapdoor.patch b/nms-patches/BlockTrapdoor.patch new file mode 100644 index 00000000..8b2992a9 --- /dev/null +++ b/nms-patches/BlockTrapdoor.patch @@ -0,0 +1,31 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockTrapdoor.java 2014-11-27 08:59:46.581422375 +1100 ++++ src/main/java/net/minecraft/server/BlockTrapdoor.java 2014-11-27 08:42:10.124850965 +1100 +@@ -2,6 +2,8 @@ + + import com.google.common.base.Predicate; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public class BlockTrapdoor extends Block { + + public static final BlockStateDirection FACING = BlockStateDirection.of("facing", (Predicate) EnumDirectionLimit.HORIZONTAL); +@@ -101,6 +103,19 @@ + boolean flag = world.isBlockIndirectlyPowered(blockposition); + + if (flag || block.isPowerSource()) { ++ // CraftBukkit start ++ org.bukkit.World bworld = world.getWorld(); ++ org.bukkit.block.Block bblock = bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ ++ int power = bblock.getBlockPower(); ++ int oldPower = (Boolean) iblockdata.get(OPEN) ? 15 : 0; ++ ++ if (oldPower == 0 ^ power == 0 || block.isPowerSource()) { ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(bblock, oldPower, power); ++ world.getServer().getPluginManager().callEvent(eventRedstone); ++ flag = eventRedstone.getNewCurrent() > 0; ++ } ++ // CraftBukkit end + boolean flag1 = ((Boolean) iblockdata.get(BlockTrapdoor.OPEN)).booleanValue(); + + if (flag1 != flag) { diff --git a/nms-patches/BlockTripwire.patch b/nms-patches/BlockTripwire.patch new file mode 100644 index 00000000..f3954aa5 --- /dev/null +++ b/nms-patches/BlockTripwire.patch @@ -0,0 +1,52 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockTripwire.java 2014-11-27 08:59:46.585422357 +1100 ++++ src/main/java/net/minecraft/server/BlockTripwire.java 2014-11-27 08:42:10.140850934 +1100 +@@ -4,6 +4,8 @@ + import java.util.List; + import java.util.Random; + ++import org.bukkit.event.entity.EntityInteractEvent; // CraftBukkit ++ + public class BlockTripwire extends Block { + + public static final BlockStateBoolean POWERED = BlockStateBoolean.of("powered"); +@@ -154,6 +156,40 @@ + } + } + } ++ ++ // CraftBukkit start - Call interact even when triggering connected tripwire ++ if (flag != flag1 && flag1 && (Boolean)iblockdata.get(ATTACHED)) { ++ org.bukkit.World bworld = world.getWorld(); ++ org.bukkit.plugin.PluginManager manager = world.getServer().getPluginManager(); ++ org.bukkit.block.Block block = bworld.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ boolean allowed = false; ++ ++ // If all of the events are cancelled block the tripwire trigger, else allow ++ for (Object object : list) { ++ if (object != null) { ++ org.bukkit.event.Cancellable cancellable; ++ ++ if (object instanceof EntityHuman) { ++ cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent((EntityHuman) object, org.bukkit.event.block.Action.PHYSICAL, blockposition, null, null); ++ } else if (object instanceof Entity) { ++ cancellable = new EntityInteractEvent(((Entity) object).getBukkitEntity(), block); ++ manager.callEvent((EntityInteractEvent) cancellable); ++ } else { ++ continue; ++ } ++ ++ if (!cancellable.isCancelled()) { ++ allowed = true; ++ break; ++ } ++ } ++ } ++ ++ if (!allowed) { ++ return; ++ } ++ } ++ // CraftBukkit end + + if (flag1 != flag) { + iblockdata = iblockdata.set(BlockTripwire.POWERED, Boolean.valueOf(flag1)); diff --git a/nms-patches/BlockTripwireHook.patch b/nms-patches/BlockTripwireHook.patch new file mode 100644 index 00000000..d4da9326 --- /dev/null +++ b/nms-patches/BlockTripwireHook.patch @@ -0,0 +1,29 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockTripwireHook.java 2014-11-27 08:59:46.585422357 +1100 ++++ src/main/java/net/minecraft/server/BlockTripwireHook.java 2014-11-27 08:42:10.144850927 +1100 +@@ -5,6 +5,8 @@ + import java.util.Iterator; + import java.util.Random; + ++import org.bukkit.event.block.BlockRedstoneEvent; // CraftBukkit ++ + public class BlockTripwireHook extends Block { + + public static final BlockStateDirection FACING = BlockStateDirection.of("facing", (Predicate) EnumDirectionLimit.HORIZONTAL); +@@ -141,6 +143,17 @@ + this.a(world, blockposition1, flag5, flag6, flag2, flag3); + } + ++ // CraftBukkit start ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ ++ BlockRedstoneEvent eventRedstone = new BlockRedstoneEvent(block, 15, 0); ++ world.getServer().getPluginManager().callEvent(eventRedstone); ++ ++ if (eventRedstone.getNewCurrent() > 0) { ++ return; ++ } ++ // CraftBukkit end ++ + this.a(world, blockposition, flag5, flag6, flag2, flag3); + if (!flag) { + world.setTypeAndData(blockposition, iblockdata3.set(BlockTripwireHook.FACING, enumdirection), 3); diff --git a/nms-patches/BlockVine.patch b/nms-patches/BlockVine.patch new file mode 100644 index 00000000..5e920272 --- /dev/null +++ b/nms-patches/BlockVine.patch @@ -0,0 +1,76 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/BlockVine.java 2014-11-27 08:59:46.589422340 +1100 ++++ src/main/java/net/minecraft/server/BlockVine.java 2014-11-27 08:42:10.156850903 +1100 +@@ -3,6 +3,8 @@ + import java.util.Iterator; + import java.util.Random; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class BlockVine extends Block { + + public static final BlockStateBoolean UP = BlockStateBoolean.of("up"); +@@ -203,7 +205,13 @@ + } + + if (((Boolean) iblockdata1.get(BlockVine.NORTH)).booleanValue() || ((Boolean) iblockdata1.get(BlockVine.EAST)).booleanValue() || ((Boolean) iblockdata1.get(BlockVine.SOUTH)).booleanValue() || ((Boolean) iblockdata1.get(BlockVine.WEST)).booleanValue()) { +- world.setTypeAndData(blockposition.up(), iblockdata1, 2); ++ // CraftBukkit start - Call BlockSpreadEvent ++ // world.setTypeAndData(blockposition.up(), iblockdata1, 2); ++ BlockPosition target = blockposition.up(); ++ org.bukkit.block.Block source = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(target.getX(), target.getY(), target.getZ()); ++ CraftEventFactory.handleBlockSpreadEvent(block, source, this, toLegacyData(iblockdata1)); ++ // CraftBukkit end + } + + } +@@ -222,18 +230,30 @@ + boolean flag2 = ((Boolean) iblockdata.get(a(enumdirection2))).booleanValue(); + BlockPosition blockposition2 = blockposition1.shift(enumdirection1); + BlockPosition blockposition3 = blockposition1.shift(enumdirection2); ++ ++ // CraftBukkit start - Call BlockSpreadEvent ++ org.bukkit.block.Block source = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(blockposition1.getX(), blockposition1.getY(), blockposition1.getZ()); + + if (flag1 && this.c(world.getType(blockposition2).getBlock())) { +- world.setTypeAndData(blockposition1, this.getBlockData().set(a(enumdirection1), Boolean.valueOf(true)), 2); ++ // world.setTypeAndData(blockposition1, this.getBlockData().set(a(enumdirection1), Boolean.valueOf(true)), 2); ++ CraftEventFactory.handleBlockSpreadEvent(bukkitBlock, source, block, toLegacyData(this.getBlockData().set(a(enumdirection1), Boolean.valueOf(true)))); + } else if (flag2 && this.c(world.getType(blockposition3).getBlock())) { +- world.setTypeAndData(blockposition1, this.getBlockData().set(a(enumdirection2), Boolean.valueOf(true)), 2); ++ // world.setTypeAndData(blockposition1, this.getBlockData().set(a(enumdirection2), Boolean.valueOf(true)), 2); ++ CraftEventFactory.handleBlockSpreadEvent(bukkitBlock, source, block, toLegacyData(this.getBlockData().set(a(enumdirection2), Boolean.valueOf(true)))); + } else if (flag1 && world.isEmpty(blockposition2) && this.c(world.getType(blockposition.shift(enumdirection1)).getBlock())) { +- world.setTypeAndData(blockposition2, this.getBlockData().set(a(enumdirection.opposite()), Boolean.valueOf(true)), 2); ++ // world.setTypeAndData(blockposition2, this.getBlockData().set(a(enumdirection.opposite()), Boolean.valueOf(true)), 2); ++ bukkitBlock = world.getWorld().getBlockAt(blockposition2.getX(), blockposition2.getY(), blockposition2.getZ()); ++ CraftEventFactory.handleBlockSpreadEvent(bukkitBlock, source, block, toLegacyData(this.getBlockData().set(a(enumdirection.opposite()), Boolean.valueOf(true)))); + } else if (flag2 && world.isEmpty(blockposition3) && this.c(world.getType(blockposition.shift(enumdirection2)).getBlock())) { +- world.setTypeAndData(blockposition3, this.getBlockData().set(a(enumdirection.opposite()), Boolean.valueOf(true)), 2); ++ // world.setTypeAndData(blockposition3, this.getBlockData().set(a(enumdirection.opposite()), Boolean.valueOf(true)), 2); ++ bukkitBlock = world.getWorld().getBlockAt(blockposition3.getX(), blockposition3.getY(), blockposition3.getZ()); ++ CraftEventFactory.handleBlockSpreadEvent(bukkitBlock, source, block, toLegacyData(this.getBlockData().set(a(enumdirection.opposite()), Boolean.valueOf(true)))); + } else if (this.c(world.getType(blockposition1.up()).getBlock())) { +- world.setTypeAndData(blockposition1, this.getBlockData(), 2); ++ // world.setTypeAndData(blockposition1, this.getBlockData(), 2); ++ CraftEventFactory.handleBlockSpreadEvent(bukkitBlock, source, block, toLegacyData(this.getBlockData())); + } ++ // CraftBukkit end + } else if (block.material.k() && block.d()) { + world.setTypeAndData(blockposition, iblockdata.set(a(enumdirection), Boolean.valueOf(true)), 2); + } +@@ -260,7 +280,12 @@ + } + + if (((Boolean) iblockdata3.get(BlockVine.NORTH)).booleanValue() || ((Boolean) iblockdata3.get(BlockVine.EAST)).booleanValue() || ((Boolean) iblockdata3.get(BlockVine.SOUTH)).booleanValue() || ((Boolean) iblockdata3.get(BlockVine.WEST)).booleanValue()) { +- world.setTypeAndData(blockposition1, iblockdata3, 2); ++ // CraftBukkit start - Call BlockSpreadEvent ++ // world.setTypeAndData(blockposition1, iblockdata3, 2); ++ org.bukkit.block.Block source = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(blockposition1.getX(), blockposition1.getY(), blockposition1.getZ()); ++ CraftEventFactory.handleBlockSpreadEvent(bukkitBlock, source, this, toLegacyData(iblockdata3)); ++ // CraftBukkit end + } + } else if (block1 == this) { + iblockdata3 = iblockdata2; diff --git a/nms-patches/Chunk.patch b/nms-patches/Chunk.patch new file mode 100644 index 00000000..d2f4392f --- /dev/null +++ b/nms-patches/Chunk.patch @@ -0,0 +1,169 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/Chunk.java 2014-11-27 08:59:46.589422340 +1100 ++++ src/main/java/net/minecraft/server/Chunk.java 2014-11-27 08:42:10.164850887 +1100 +@@ -1,6 +1,7 @@ + package net.minecraft.server; + + import com.google.common.base.Predicate; ++import com.google.common.collect.Lists; // CraftBukkit + import com.google.common.collect.Maps; + import com.google.common.collect.Queues; + import java.util.Arrays; +@@ -14,6 +15,8 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++import org.bukkit.Bukkit; // CraftBukkit ++ + public class Chunk { + + private static final Logger c = LogManager.getLogger(); +@@ -23,7 +26,7 @@ + private final boolean[] g; + private boolean h; + public final World world; +- private final int[] heightMap; ++ public final int[] heightMap; // CraftBukkit - make public + public final int locX; + public final int locZ; + private boolean k; +@@ -40,6 +43,34 @@ + private int v; + private ConcurrentLinkedQueue w; + ++ // CraftBukkit start - Neighbor loaded cache for chunk lighting and entity ticking ++ private int neighbors = 0x1 << 12; ++ ++ public boolean areNeighborsLoaded(final int radius) { ++ switch (radius) { ++ case 2: ++ return this.neighbors == Integer.MAX_VALUE >> 6; ++ case 1: ++ final int mask = ++ // x z offset x z offset x z offset ++ (0x1 << (1 * 5 + 1 + 12)) | (0x1 << (0 * 5 + 1 + 12)) | (0x1 << (-1 * 5 + 1 + 12)) | ++ (0x1 << (1 * 5 + 0 + 12)) | (0x1 << (0 * 5 + 0 + 12)) | (0x1 << (-1 * 5 + 0 + 12)) | ++ (0x1 << (1 * 5 + -1 + 12)) | (0x1 << (0 * 5 + -1 + 12)) | (0x1 << (-1 * 5 + -1 + 12)); ++ return (this.neighbors & mask) == mask; ++ default: ++ throw new UnsupportedOperationException(String.valueOf(radius)); ++ } ++ } ++ ++ public void setNeighborLoaded(final int x, final int z) { ++ this.neighbors |= 0x1 << (x * 5 + 12 + z); ++ } ++ ++ public void setNeighborUnloaded(final int x, final int z) { ++ this.neighbors &= ~(0x1 << (x * 5 + 12 + z)); ++ } ++ // CraftBukkit end ++ + public Chunk(World world, int i, int j) { + this.sections = new ChunkSection[16]; + this.e = new byte[256]; +@@ -60,8 +91,17 @@ + + Arrays.fill(this.f, -999); + Arrays.fill(this.e, (byte) -1); ++ ++ // CraftBukkit start ++ if (!(this instanceof EmptyChunk)) { ++ this.bukkitChunk = new org.bukkit.craftbukkit.CraftChunk(this); ++ } + } + ++ public org.bukkit.Chunk bukkitChunk; ++ public boolean mustSave; ++ // CraftBukkit end ++ + public Chunk(World world, ChunkSnapshot chunksnapshot, int i, int j) { + this(world, i, j); + short short0 = 256; +@@ -465,7 +505,13 @@ + flag = j >= i1; + } + +- chunksection.setType(i, j & 15, k, iblockdata); ++ // CraftBukkit start - Delay removing containers until after they're cleaned up ++ if (!(block1 instanceof IContainer)) { ++ chunksection.setType(i, j & 15, k, iblockdata); ++ } ++ // CraftBukkit end ++ ++ + if (block1 != block) { + if (!this.world.isStatic) { + block1.remove(this.world, blockposition, iblockdata1); +@@ -474,6 +520,12 @@ + } + } + ++ // CraftBukkit start - Delay removing containers until after they're cleaned up ++ if (block1 instanceof IContainer) { ++ chunksection.setType(i, j & 15, k, iblockdata); ++ } ++ // CraftBukkit end ++ + if (chunksection.b(i, j & 15, k) != block) { + return null; + } else { +@@ -505,7 +557,8 @@ + } + } + +- if (!this.world.isStatic && block1 != block) { ++ // CraftBukkit - Don't place while processing the BlockPlaceEvent, unless it's a BlockContainer. Prevents blocks such as TNT from activating when cancelled. ++ if (!this.world.isStatic && block1 != block && (!this.world.captureBlockStates || block instanceof BlockContainer)) { + block.onPlace(this.world, blockposition, iblockdata); + } + +@@ -586,7 +639,11 @@ + int j = MathHelper.floor(entity.locZ / 16.0D); + + if (i != this.locX || j != this.locZ) { +- Chunk.c.warn("Wrong location! (" + i + ", " + j + ") should be (" + this.locX + ", " + this.locZ + "), " + entity, new Object[] { entity}); ++ // CraftBukkit start ++ Bukkit.getLogger().warning("Wrong location for " + entity + " in world '" + world.getWorld().getName() + "'!"); ++ // Chunk.c.warn("Wrong location! (" + i + ", " + j + ") should be (" + this.locX + ", " + this.locZ + "), " + entity, new Object[] { entity}); ++ Bukkit.getLogger().warning("Entity is at " + entity.locX + "," + entity.locZ + " (chunk " + i + "," + j + ") but was stored in chunk " + this.locX + "," + this.locZ); ++ // CraftBukkit end + entity.die(); + } + +@@ -673,6 +730,13 @@ + + tileentity.D(); + this.tileEntities.put(blockposition, tileentity); ++ // CraftBukkit start ++ } else { ++ System.out.println("Attempted to place a tile entity (" + tileentity + ") at " + tileentity.position.getX() + "," + tileentity.position.getY() + "," + tileentity.position.getZ() ++ + " (" + org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(getType(blockposition)) + ") where there was no entity tile!"); ++ System.out.println("Chunk coordinates: " + (this.locX * 16) + "," + (this.locZ * 16)); ++ new Exception().printStackTrace(); ++ // CraftBukkit end + } + } + +@@ -716,7 +780,21 @@ + } + + for (int i = 0; i < this.entitySlices.length; ++i) { +- this.world.c((Collection) this.entitySlices[i]); ++ // CraftBukkit start ++ List<Entity> newList = Lists.newArrayList(this.entitySlices[i]); ++ java.util.Iterator<Entity> iter = newList.iterator(); ++ while (iter.hasNext()) { ++ Entity entity = iter.next(); ++ ++ // Do not pass along players, as doing so can get them stuck outside of time. ++ // (which for example disables inventory icon updates and prevents block breaking) ++ if (entity instanceof EntityPlayer) { ++ iter.remove(); ++ } ++ } ++ ++ this.world.c((Collection) newList); ++ // CraftBukkit end + } + + } diff --git a/nms-patches/ChunkProviderServer.patch b/nms-patches/ChunkProviderServer.patch new file mode 100644 index 00000000..bbe662e2 --- /dev/null +++ b/nms-patches/ChunkProviderServer.patch @@ -0,0 +1,348 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ChunkProviderServer.java 2014-11-27 08:59:46.593422322 +1100 ++++ src/main/java/net/minecraft/server/ChunkProviderServer.java 2014-11-27 08:42:10.124850965 +1100 +@@ -10,17 +10,28 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// CraftBukkit start ++import java.util.Random; ++import java.util.logging.Level; ++ ++import org.bukkit.Server; ++import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor; ++import org.bukkit.craftbukkit.util.LongHash; ++import org.bukkit.craftbukkit.util.LongHashSet; ++import org.bukkit.craftbukkit.util.LongObjectHashMap; ++import org.bukkit.event.world.ChunkUnloadEvent; ++// CraftBukkit end ++ + public class ChunkProviderServer implements IChunkProvider { + + private static final Logger b = LogManager.getLogger(); +- public Set unloadQueue = Collections.newSetFromMap(new ConcurrentHashMap()); ++ public LongHashSet unloadQueue = new LongHashSet(); // CraftBukkit - LongHashSet + public Chunk emptyChunk; + public IChunkProvider chunkProvider; + private IChunkLoader chunkLoader; +- public boolean forceChunkLoad = true; +- public LongHashMap chunks = new LongHashMap(); +- private List chunkList = Lists.newArrayList(); +- private WorldServer world; ++ public boolean forceChunkLoad = false; // CraftBukkit - true -> false ++ public LongObjectHashMap<Chunk> chunks = new LongObjectHashMap<Chunk>(); ++ public WorldServer world; // CraftBukkit- public + + public ChunkProviderServer(WorldServer worldserver, IChunkLoader ichunkloader, IChunkProvider ichunkprovider) { + this.emptyChunk = new EmptyChunk(worldserver, 0, 0); +@@ -30,40 +41,93 @@ + } + + public boolean isChunkLoaded(int i, int j) { +- return this.chunks.contains(ChunkCoordIntPair.a(i, j)); ++ return this.chunks.containsKey(LongHash.toLong(i, j)); // CraftBukkit + } + +- public List a() { +- return this.chunkList; ++ // CraftBukkit start - Change return type to Collection and return the values of our chunk map ++ public java.util.Collection a() { ++ // return this.chunkList; ++ return this.chunks.values(); ++ // CraftBukkit end + } + + public void queueUnload(int i, int j) { + if (this.world.worldProvider.e()) { + if (!this.world.c(i, j)) { +- this.unloadQueue.add(Long.valueOf(ChunkCoordIntPair.a(i, j))); ++ // CraftBukkit start ++ this.unloadQueue.add(i, j); ++ ++ Chunk c = chunks.get(LongHash.toLong(i, j)); ++ if (c != null) { ++ c.mustSave = true; ++ } ++ // CraftBukkit end + } + } else { +- this.unloadQueue.add(Long.valueOf(ChunkCoordIntPair.a(i, j))); ++ // CraftBukkit start ++ this.unloadQueue.add(i, j); ++ ++ Chunk c = chunks.get(LongHash.toLong(i, j)); ++ if (c != null) { ++ c.mustSave = true; ++ } ++ // CraftBukkit end + } + + } + + public void b() { +- Iterator iterator = this.chunkList.iterator(); ++ Iterator iterator = this.chunks.values().iterator(); + + while (iterator.hasNext()) { + Chunk chunk = (Chunk) iterator.next(); + + this.queueUnload(chunk.locX, chunk.locZ); + } +- ++ } ++ ++ // CraftBukkit start - Add async variant, provide compatibility ++ public Chunk getChunkIfLoaded(int x, int z) { ++ return chunks.get(LongHash.toLong(x, z)); + } + + public Chunk getChunkAt(int i, int j) { +- long k = ChunkCoordIntPair.a(i, j); +- +- this.unloadQueue.remove(Long.valueOf(k)); +- Chunk chunk = (Chunk) this.chunks.getEntry(k); ++ return getChunkAt(i, j, null); ++ } ++ ++ public Chunk getChunkAt(int i, int j, Runnable runnable) { ++ unloadQueue.remove(i, j); ++ Chunk chunk = chunks.get(LongHash.toLong(i, j)); ++ ChunkRegionLoader loader = null; ++ ++ if (this.chunkLoader instanceof ChunkRegionLoader) { ++ loader = (ChunkRegionLoader) this.chunkLoader; ++ ++ } ++ // We can only use the queue for already generated chunks ++ if (chunk == null && loader != null && loader.chunkExists(world, i, j)) { ++ if (runnable != null) { ++ ChunkIOExecutor.queueChunkLoad(world, loader, this, i, j, runnable); ++ return null; ++ } else { ++ chunk = ChunkIOExecutor.syncChunkLoad(world, loader, this, i, j); ++ } ++ } else if (chunk == null) { ++ chunk = originalGetChunkAt(i, j); ++ } ++ ++ // If we didn't load the chunk async and have a callback run it now ++ if (runnable != null) { ++ runnable.run(); ++ } ++ ++ return chunk; ++ } ++ public Chunk originalGetChunkAt(int i, int j) { ++ this.unloadQueue.remove(i, j); ++ Chunk chunk = (Chunk) this.chunks.get(LongHash.toLong(i, j)); ++ boolean newChunk = false; ++ // CraftBukkit end + + if (chunk == null) { + chunk = this.loadChunk(i, j); +@@ -78,16 +142,44 @@ + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Chunk to be generated"); + + crashreportsystemdetails.a("Location", (Object) String.format("%d,%d", new Object[] { Integer.valueOf(i), Integer.valueOf(j)})); +- crashreportsystemdetails.a("Position hash", (Object) Long.valueOf(k)); ++ crashreportsystemdetails.a("Position hash", (Object) Long.valueOf(LongHash.toLong(i, j))); // CraftBukkit - Use LongHash + crashreportsystemdetails.a("Generator", (Object) this.chunkProvider.getName()); + throw new ReportedException(crashreport); + } + } ++ newChunk = true; // CraftBukkit + } + +- this.chunks.put(k, chunk); +- this.chunkList.add(chunk); ++ this.chunks.put(LongHash.toLong(i, j), chunk); + chunk.addEntities(); ++ ++ // CraftBukkit start ++ Server server = world.getServer(); ++ if (server != null) { ++ /* ++ * If it's a new world, the first few chunks are generated inside ++ * the World constructor. We can't reliably alter that, so we have ++ * no way of creating a CraftWorld/CraftServer at that point. ++ */ ++ server.getPluginManager().callEvent(new org.bukkit.event.world.ChunkLoadEvent(chunk.bukkitChunk, newChunk)); ++ } ++ ++ // Update neighbor counts ++ for (int x = -2; x < 3; x++) { ++ for (int z = -2; z < 3; z++) { ++ if (x == 0 && z == 0) { ++ continue; ++ } ++ ++ Chunk neighbor = this.getChunkIfLoaded(chunk.locX + x, chunk.locZ + z); ++ if (neighbor != null) { ++ neighbor.setNeighborLoaded(-x, -z); ++ chunk.setNeighborLoaded(x, z); ++ } ++ } ++ } ++ // CraftBukkit end ++ + chunk.loadNearby(this, this, i, j); + } + +@@ -95,9 +187,22 @@ + } + + public Chunk getOrCreateChunk(int i, int j) { +- Chunk chunk = (Chunk) this.chunks.getEntry(ChunkCoordIntPair.a(i, j)); ++ // CraftBukkit start ++ Chunk chunk = (Chunk) this.chunks.get(LongHash.toLong(i, j)); + +- return chunk == null ? (!this.world.ad() && !this.forceChunkLoad ? this.emptyChunk : this.getChunkAt(i, j)) : chunk; ++ chunk = chunk == null ? (!this.world.ad() && !this.forceChunkLoad ? this.emptyChunk : this.getChunkAt(i, j)) : chunk; ++ ++ if (chunk == emptyChunk) return chunk; ++ if (i != chunk.locX || j != chunk.locZ) { ++ b.error("Chunk (" + chunk.locX + ", " + chunk.locZ + ") stored at (" + i + ", " + j + ") in world '" + world.getWorld().getName() + "'"); ++ b.error(chunk.getClass().getName()); ++ Throwable ex = new Throwable(); ++ ex.fillInStackTrace(); ++ ex.printStackTrace(); ++ } ++ ++ return chunk; ++ // CraftBukkit end + } + + public Chunk loadChunk(int i, int j) { +@@ -138,10 +243,13 @@ + try { + chunk.setLastSaved(this.world.getTime()); + this.chunkLoader.a(this.world, chunk); +- } catch (IOException ioexception) { ++ // CraftBukkit start - IOException to Exception ++ } catch (Exception ioexception) { + ChunkProviderServer.b.error("Couldn\'t save chunk", ioexception); ++ /* Remove extra exception + } catch (ExceptionWorldConflict exceptionworldconflict) { + ChunkProviderServer.b.error("Couldn\'t save chunk; already in use by another instance of Minecraft?", exceptionworldconflict); ++ // CraftBukkit end */ + } + + } +@@ -154,6 +262,30 @@ + chunk.n(); + if (this.chunkProvider != null) { + this.chunkProvider.getChunkAt(ichunkprovider, i, j); ++ ++ // CraftBukkit start ++ BlockSand.instaFall = true; ++ Random random = new Random(); ++ random.setSeed(world.getSeed()); ++ long xRand = random.nextLong() / 2L * 2L + 1L; ++ long zRand = random.nextLong() / 2L * 2L + 1L; ++ random.setSeed((long) i * xRand + (long) j * zRand ^ world.getSeed()); ++ ++ org.bukkit.World world = this.world.getWorld(); ++ if (world != null) { ++ this.world.populating = true; ++ try { ++ for (org.bukkit.generator.BlockPopulator populator : world.getPopulators()) { ++ populator.populate(world, random, chunk.bukkitChunk); ++ } ++ } finally { ++ this.world.populating = false; ++ } ++ } ++ BlockSand.instaFall = false; ++ this.world.getServer().getPluginManager().callEvent(new org.bukkit.event.world.ChunkPopulateEvent(chunk.bukkitChunk)); ++ // CraftBukkit end ++ + chunk.e(); + } + } +@@ -173,9 +305,12 @@ + + public boolean saveChunks(boolean flag, IProgressUpdate iprogressupdate) { + int i = 0; +- +- for (int j = 0; j < this.chunkList.size(); ++j) { +- Chunk chunk = (Chunk) this.chunkList.get(j); ++ ++ // CraftBukkit start ++ Iterator iterator = this.chunks.values().iterator(); ++ while (iterator.hasNext()) { ++ Chunk chunk = (Chunk) iterator.next(); ++ // CraftBukkit end + + if (flag) { + this.saveChunkNOP(chunk); +@@ -203,22 +338,42 @@ + + public boolean unloadChunks() { + if (!this.world.savingDisabled) { +- for (int i = 0; i < 100; ++i) { +- if (!this.unloadQueue.isEmpty()) { +- Long olong = (Long) this.unloadQueue.iterator().next(); +- Chunk chunk = (Chunk) this.chunks.getEntry(olong.longValue()); +- ++ // CraftBukkit start ++ Server server = this.world.getServer(); ++ for (int i = 0; i < 100 && !this.unloadQueue.isEmpty(); ++i) { ++ long chunkcoordinates = this.unloadQueue.popFirst(); ++ Chunk chunk = this.chunks.get(chunkcoordinates); ++ if (chunk == null) continue; ++ ++ ChunkUnloadEvent event = new ChunkUnloadEvent(chunk.bukkitChunk); ++ server.getPluginManager().callEvent(event); ++ if (!event.isCancelled()) { + if (chunk != null) { + chunk.removeEntities(); + this.saveChunk(chunk); + this.saveChunkNOP(chunk); +- this.chunks.remove(olong.longValue()); +- this.chunkList.remove(chunk); ++ this.chunks.remove(chunkcoordinates); // CraftBukkit + } + +- this.unloadQueue.remove(olong); ++ // this.unloadQueue.remove(olong); ++ ++ // Update neighbor counts ++ for (int x = -2; x < 3; x++) { ++ for (int z = -2; z < 3; z++) { ++ if (x == 0 && z == 0) { ++ continue; ++ } ++ ++ Chunk neighbor = this.getChunkIfLoaded(chunk.locX + x, chunk.locZ + z); ++ if (neighbor != null) { ++ neighbor.setNeighborUnloaded(-x, -z); ++ chunk.setNeighborUnloaded(x, z); ++ } ++ } ++ } + } +- } ++ } ++ // CraftBukkit end + + if (this.chunkLoader != null) { + this.chunkLoader.a(); +@@ -233,7 +388,8 @@ + } + + public String getName() { +- return "ServerChunkCache: " + this.chunks.count() + " Drop: " + this.unloadQueue.size(); ++ // CraftBukkit - this.chunks.count() -> .size() ++ return "ServerChunkCache: " + this.chunks.size() + " Drop: " + this.unloadQueue.size(); + } + + public List getMobsFor(EnumCreatureType enumcreaturetype, BlockPosition blockposition) { +@@ -245,7 +401,8 @@ + } + + public int getLoadedChunks() { +- return this.chunks.count(); ++ // CraftBukkit - this.chunks.count() -> this.chunks.size() ++ return this.chunks.size(); + } + + public void recreateStructures(Chunk chunk, int i, int j) {} diff --git a/nms-patches/ChunkRegionLoader.patch b/nms-patches/ChunkRegionLoader.patch new file mode 100644 index 00000000..35c373ed --- /dev/null +++ b/nms-patches/ChunkRegionLoader.patch @@ -0,0 +1,131 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ChunkRegionLoader.java 2014-11-27 08:59:46.593422322 +1100 ++++ src/main/java/net/minecraft/server/ChunkRegionLoader.java 2014-11-27 08:42:10.136850942 +1100 +@@ -23,8 +23,40 @@ + public ChunkRegionLoader(File file) { + this.e = file; + } ++ ++ // CraftBukkit start ++ public boolean chunkExists(World world, int i, int j) { ++ ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i, j); ++ ++ synchronized (this.d) { ++ if (this.c.contains(chunkcoordintpair)) { ++ for (int k = 0; k < this.b.size(); ++k) { ++ if (((PendingChunkToSave) this.b.get(k)).a.equals(chunkcoordintpair)) { ++ return true; ++ } ++ } ++ } ++ } ++ ++ return RegionFileCache.a(this.e, i, j).chunkExists(i & 31, j & 31); ++ } ++ // CraftBukkit end + ++ // CraftBukkit start - Add async variant, provide compatibility + public Chunk a(World world, int i, int j) { ++ Object[] data = loadChunk(world, i, j); ++ if (data != null) { ++ Chunk chunk = (Chunk) data[0]; ++ NBTTagCompound nbttagcompound = (NBTTagCompound) data[1]; ++ loadEntities(chunk, nbttagcompound.getCompound("Level"), world); ++ return chunk; ++ } ++ ++ return null; ++ } ++ ++ public Object[] loadChunk(World world, int i, int j) { ++ // CraftBukkit end + NBTTagCompound nbttagcompound = null; + ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i, j); + Object object = this.d; +@@ -53,7 +85,7 @@ + return this.a(world, i, j, nbttagcompound); + } + +- protected Chunk a(World world, int i, int j, NBTTagCompound nbttagcompound) { ++ protected Object[] a(World world, int i, int j, NBTTagCompound nbttagcompound) { // CraftBukkit - return Chunk -> Object[] + if (!nbttagcompound.hasKeyOfType("Level", 10)) { + ChunkRegionLoader.a.error("Chunk file at " + i + "," + j + " is missing level data, skipping"); + return null; +@@ -64,18 +96,42 @@ + Chunk chunk = this.a(world, nbttagcompound.getCompound("Level")); + + if (!chunk.a(i, j)) { +- ChunkRegionLoader.a.error("Chunk file at " + i + "," + j + " is in the wrong location; relocating. (Expected " + i + ", " + j + ", got " + chunk.locX + ", " + chunk.locZ + ")"); +- nbttagcompound.setInt("xPos", i); +- nbttagcompound.setInt("zPos", j); ++ a.error("Chunk file at " + i + "," + j + " is in the wrong location; relocating. (Expected " + i + ", " + j + ", got " + chunk.locX + ", " + chunk.locZ + ")"); ++ nbttagcompound.getCompound("Level").setInt("xPos", i); ++ nbttagcompound.getCompound("Level").setInt("zPos", j); ++ ++ // CraftBukkit start - Have to move tile entities since we don't load them at this stage ++ NBTTagList tileEntities = nbttagcompound.getCompound("Level").getList("TileEntities", 10); ++ if (tileEntities != null) { ++ for (int te = 0; te < tileEntities.size(); te++) { ++ NBTTagCompound tileEntity = (NBTTagCompound) tileEntities.get(te); ++ int x = tileEntity.getInt("x") - chunk.locX * 16; ++ int z = tileEntity.getInt("z") - chunk.locZ * 16; ++ tileEntity.setInt("x", i * 16 + x); ++ tileEntity.setInt("z", j * 16 + z); ++ } ++ } ++ // CraftBukkit end + chunk = this.a(world, nbttagcompound.getCompound("Level")); + } + +- return chunk; ++ // CraftBukkit start ++ Object[] data = new Object[2]; ++ data[0] = chunk; ++ data[1] = nbttagcompound; ++ return data; ++ // CraftBukkit end + } + } + + public void a(World world, Chunk chunk) { +- world.checkSession(); ++ // CraftBukkit start - "handle" exception ++ try { ++ world.checkSession(); ++ } catch (ExceptionWorldConflict ex) { ++ ex.printStackTrace(); ++ } ++ // CraftBukkit end + + try { + NBTTagCompound nbttagcompound = new NBTTagCompound(); +@@ -133,7 +189,7 @@ + return true; + } + +- public void a(PendingChunkToSave pendingchunktosave) { ++ public void a(PendingChunkToSave pendingchunktosave) throws java.io.IOException { // CraftBukkit - added throws + DataOutputStream dataoutputstream = RegionFileCache.d(this.e, pendingchunktosave.a.x, pendingchunktosave.a.z); + + NBTCompressedStreamTools.a(pendingchunktosave.b, (DataOutput) dataoutputstream); +@@ -320,7 +376,13 @@ + if (nbttagcompound.hasKeyOfType("Biomes", 7)) { + chunk.a(nbttagcompound.getByteArray("Biomes")); + } ++ ++ // CraftBukkit start - End this method here and split off entity loading to another method ++ return chunk; ++ } + ++ public void loadEntities(Chunk chunk, NBTTagCompound nbttagcompound, World world) { ++ // CraftBukkit end + NBTTagList nbttaglist1 = nbttagcompound.getList("Entities", 10); + + if (nbttaglist1 != null) { +@@ -379,6 +441,6 @@ + } + } + +- return chunk; ++ // return chunk; // CraftBukkit + } + } diff --git a/nms-patches/ChunkSection.patch b/nms-patches/ChunkSection.patch new file mode 100644 index 00000000..4063d6cc --- /dev/null +++ b/nms-patches/ChunkSection.patch @@ -0,0 +1,21 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ChunkSection.java 2014-11-27 08:59:46.597422305 +1100 ++++ src/main/java/net/minecraft/server/ChunkSection.java 2014-11-27 08:42:10.172850872 +1100 +@@ -19,6 +19,18 @@ + + } + ++ // CraftBukkit start ++ public ChunkSection(int y, boolean flag, char[] blockIds) { ++ this.yPos = y; ++ this.blockIds = blockIds; ++ this.emittedLight = new NibbleArray(); ++ if (flag) { ++ this.skyLight = new NibbleArray(); ++ } ++ recalcBlockCounts(); ++ } ++ // CraftBukkit end ++ + public IBlockData getType(int i, int j, int k) { + IBlockData iblockdata = (IBlockData) Block.d.a(this.blockIds[j << 8 | k << 4 | i]); + diff --git a/nms-patches/CommandBlockListenerAbstract.patch b/nms-patches/CommandBlockListenerAbstract.patch new file mode 100644 index 00000000..c49fad99 --- /dev/null +++ b/nms-patches/CommandBlockListenerAbstract.patch @@ -0,0 +1,162 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/CommandBlockListenerAbstract.java 2014-11-27 08:59:46.597422305 +1100 ++++ src/main/java/net/minecraft/server/CommandBlockListenerAbstract.java 2014-11-27 08:42:10.172850872 +1100 +@@ -4,6 +4,13 @@ + import java.util.Date; + import java.util.concurrent.Callable; + ++// CraftBukkit start ++import java.util.ArrayList; ++import org.apache.logging.log4j.Level; ++import org.bukkit.craftbukkit.command.VanillaCommandWrapper; ++import com.google.common.base.Joiner; ++// CraftBukkit end ++ + public abstract class CommandBlockListenerAbstract implements ICommandListener { + + private static final SimpleDateFormat a = new SimpleDateFormat("HH:mm:ss"); +@@ -13,6 +20,7 @@ + public String e = ""; + private String f = "@"; + private final CommandObjectiveExecutor g = new CommandObjectiveExecutor(); ++ protected org.bukkit.command.CommandSender sender; // CraftBukkit - add sender + + public CommandBlockListenerAbstract() {} + +@@ -79,7 +87,109 @@ + + try { + this.d = null; +- this.b = icommandhandler.a(this, this.e); ++ // this.b = icommandhandler.a(this, this.e); ++ // CraftBukkit start - Handle command block commands using Bukkit dispatcher ++ org.bukkit.command.SimpleCommandMap commandMap = minecraftserver.server.getCommandMap(); ++ Joiner joiner = Joiner.on(" "); ++ String command = this.e; ++ if (this.e.startsWith("/")) { ++ command = this.e.substring(1); ++ } ++ String[] args = command.split(" "); ++ ArrayList<String[]> commands = new ArrayList<String[]>(); ++ ++ // Block disallowed commands ++ if (args[0].equalsIgnoreCase("stop") || args[0].equalsIgnoreCase("kick") || args[0].equalsIgnoreCase("op") || ++ args[0].equalsIgnoreCase("deop") || args[0].equalsIgnoreCase("ban") || args[0].equalsIgnoreCase("ban-ip") || ++ args[0].equalsIgnoreCase("pardon") || args[0].equalsIgnoreCase("pardon-ip") || args[0].equalsIgnoreCase("reload")) { ++ this.b = 0; ++ return; ++ } ++ ++ // If the world has no players don't run ++ if (this.getWorld().players.isEmpty()) { ++ this.b = 0; ++ return; ++ } ++ ++ // Handle vanilla commands; ++ if (minecraftserver.server.getCommandBlockOverride(args[0])) { ++ org.bukkit.command.Command commandBlockCommand = commandMap.getCommand("minecraft:" + args[0]); ++ if (commandBlockCommand instanceof VanillaCommandWrapper) { ++ this.b = ((VanillaCommandWrapper) commandBlockCommand).dispatchVanillaCommandBlock(this, this.e); ++ return; ++ } ++ } ++ ++ // Make sure this is a valid command ++ if (commandMap.getCommand(args[0]) == null) { ++ this.b = 0; ++ return; ++ } ++ ++ // testfor command requires special handling ++ if (args[0].equalsIgnoreCase("testfor")) { ++ if (args.length < 2) { ++ this.b = 0; ++ return; ++ } ++ ++ EntityPlayer[] players = ((java.util.List<EntityPlayer>)PlayerSelector.getPlayers(this, args[1], EntityPlayer.class)).toArray(new EntityPlayer[0]); ++ ++ if (players != null && players.length > 0) { ++ this.b = players.length; ++ return; ++ } else { ++ EntityPlayer player = MinecraftServer.getServer().getPlayerList().getPlayer(args[1]); ++ if (player == null) { ++ this.b = 0; ++ return; ++ } else { ++ this.b = 1; ++ return; ++ } ++ } ++ } ++ ++ commands.add(args); ++ ++ // Find positions of command block syntax, if any ++ ArrayList<String[]> newCommands = new ArrayList<String[]>(); ++ for (int i = 0; i < args.length; i++) { ++ if (PlayerSelector.isPattern(args[i])) { ++ for (int j = 0; j < commands.size(); j++) { ++ newCommands.addAll(this.buildCommands(commands.get(j), i)); ++ } ++ ArrayList<String[]> temp = commands; ++ commands = newCommands; ++ newCommands = temp; ++ newCommands.clear(); ++ } ++ } ++ ++ int completed = 0; ++ ++ // Now dispatch all of the commands we ended up with ++ for (int i = 0; i < commands.size(); i++) { ++ try { ++ if (commandMap.dispatch(sender, joiner.join(java.util.Arrays.asList(commands.get(i))))) { ++ completed++; ++ } ++ } catch (Throwable exception) { ++ if(this instanceof TileEntityCommandListener) { ++ TileEntityCommandListener listener = (TileEntityCommandListener) this; ++ MinecraftServer.getLogger().log(Level.WARN, String.format("CommandBlock at (%d,%d,%d) failed to handle command", listener.getChunkCoordinates().getX(), listener.getChunkCoordinates().getY(), listener.getChunkCoordinates().getZ()), exception); ++ } else if (this instanceof EntityMinecartCommandBlockListener) { ++ EntityMinecartCommandBlockListener listener = (EntityMinecartCommandBlockListener) this; ++ MinecraftServer.getLogger().log(Level.WARN, String.format("MinecartCommandBlock at (%d,%d,%d) failed to handle command", listener.getChunkCoordinates().getX(), listener.getChunkCoordinates().getY(), listener.getChunkCoordinates().getZ()), exception); ++ } else { ++ MinecraftServer.getLogger().log(Level.WARN, String.format("Unknown CommandBlock failed to handle command"), exception); ++ } ++ } ++ } ++ ++ this.b = completed; ++ // CraftBukkit end + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Executing command block"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Command to be executed"); +@@ -91,8 +201,26 @@ + } else { + this.b = 0; + } ++ } ++ ++ // CraftBukkit start ++ private ArrayList<String[]> buildCommands(String[] args, int pos) { ++ ArrayList<String[]> commands = new ArrayList<String[]>(); ++ EntityPlayer[] players = ((java.util.List<EntityPlayer>)PlayerSelector.getPlayers(this, args[pos], EntityPlayer.class)).toArray(new EntityPlayer[0]); ++ if (players != null) { ++ for (EntityPlayer player : players) { ++ if (player.world != this.getWorld()) { ++ continue; ++ } ++ String[] command = args.clone(); ++ command[pos] = player.getName(); ++ commands.add(command); ++ } ++ } + ++ return commands; + } ++ // CraftBukkit end + + public String getName() { + return this.f; diff --git a/nms-patches/Container.patch b/nms-patches/Container.patch new file mode 100644 index 00000000..4891ecc5 --- /dev/null +++ b/nms-patches/Container.patch @@ -0,0 +1,204 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/Container.java 2014-11-27 08:59:46.617422217 +1100 ++++ src/main/java/net/minecraft/server/Container.java 2014-11-27 08:42:10.156850903 +1100 +@@ -7,6 +7,17 @@ + import java.util.List; + import java.util.Set; + ++// CraftBukkit start ++import java.util.HashMap; ++import java.util.Map; ++import org.bukkit.craftbukkit.inventory.CraftInventory; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.Event.Result; ++import org.bukkit.event.inventory.InventoryDragEvent; ++import org.bukkit.event.inventory.InventoryType; ++import org.bukkit.inventory.InventoryView; ++// CraftBukkit end ++ + public abstract class Container { + + public List b = Lists.newArrayList(); +@@ -17,6 +28,18 @@ + private final Set h = Sets.newHashSet(); + protected List listeners = Lists.newArrayList(); + private Set i = Sets.newHashSet(); ++ ++ // CraftBukkit start ++ public boolean checkReachable = true; ++ public abstract InventoryView getBukkitView(); ++ public void transferTo(Container other, org.bukkit.craftbukkit.entity.CraftHumanEntity player) { ++ InventoryView source = this.getBukkitView(), destination = other.getBukkitView(); ++ ((CraftInventory) source.getTopInventory()).getInventory().onClose(player); ++ ((CraftInventory) source.getBottomInventory()).getInventory().onClose(player); ++ ((CraftInventory) destination.getTopInventory()).getInventory().onOpen(player); ++ ((CraftInventory) destination.getBottomInventory()).getInventory().onOpen(player); ++ } ++ // CraftBukkit end + + public Container() {} + +@@ -124,6 +147,7 @@ + l = playerinventory.getCarried().count; + Iterator iterator = this.h.iterator(); + ++ Map<Integer, ItemStack> draggedSlots = new HashMap<Integer, ItemStack>(); // CraftBukkit - Store slots from drag in map (raw slot id -> new stack) + while (iterator.hasNext()) { + Slot slot1 = (Slot) iterator.next(); + +@@ -141,16 +165,49 @@ + } + + l -= itemstack2.count - j1; +- slot1.set(itemstack2); ++ // slot1.set(itemstack2); ++ draggedSlots.put(slot1.rawSlotIndex, itemstack2); // CraftBukkit - Put in map instead of setting + } + } ++ ++ // CraftBukkit start - InventoryDragEvent ++ InventoryView view = getBukkitView(); ++ org.bukkit.inventory.ItemStack newcursor = CraftItemStack.asCraftMirror(itemstack1); ++ newcursor.setAmount(l); ++ Map<Integer, org.bukkit.inventory.ItemStack> eventmap = new HashMap<Integer, org.bukkit.inventory.ItemStack>(); ++ for (Map.Entry<Integer, ItemStack> ditem : draggedSlots.entrySet()) { ++ eventmap.put(ditem.getKey(), CraftItemStack.asBukkitCopy(ditem.getValue())); ++ } ++ ++ // It's essential that we set the cursor to the new value here to prevent item duplication if a plugin closes the inventory. ++ ItemStack oldCursor = playerinventory.getCarried(); ++ playerinventory.setCarried(CraftItemStack.asNMSCopy(newcursor)); ++ ++ InventoryDragEvent event = new InventoryDragEvent(view, (newcursor.getType() != org.bukkit.Material.AIR ? newcursor : null), CraftItemStack.asBukkitCopy(oldCursor), this.dragType == 1, eventmap); ++ entityhuman.world.getServer().getPluginManager().callEvent(event); ++ ++ // Whether or not a change was made to the inventory that requires an update. ++ boolean needsUpdate = event.getResult() != Result.DEFAULT; ++ ++ if (event.getResult() != Result.DENY) { ++ for (Map.Entry<Integer, ItemStack> dslot : draggedSlots.entrySet()) { ++ view.setItem(dslot.getKey(), CraftItemStack.asBukkitCopy(dslot.getValue())); ++ } ++ // The only time the carried item will be set to null is if the inventory is closed by the server. ++ // If the inventory is closed by the server, then the cursor items are dropped. This is why we change the cursor early. ++ if (playerinventory.getCarried() != null) { ++ playerinventory.setCarried(CraftItemStack.asNMSCopy(event.getCursor())); ++ needsUpdate = true; + +- itemstack1.count = l; +- if (itemstack1.count <= 0) { +- itemstack1 = null; ++ } ++ } else { ++ playerinventory.setCarried(oldCursor); + } + +- playerinventory.setCarried(itemstack1); ++ if (needsUpdate && entityhuman instanceof EntityPlayer) { ++ ((EntityPlayer) entityhuman).updateInventory(this); ++ } ++ // CraftBukkit end + } + + this.d(); +@@ -173,8 +230,14 @@ + } + + if (j == 1) { +- entityhuman.drop(playerinventory.getCarried().a(1), true); +- if (playerinventory.getCarried().count == 0) { ++ // CraftBukkit start - Store a reference ++ ItemStack itemstack4 = playerinventory.getCarried(); ++ if (itemstack4.count > 0) { ++ entityhuman.drop(itemstack4.a(1), true); ++ } ++ ++ if (itemstack4.count == 0) { ++ // CraftBukkit end + playerinventory.setCarried((ItemStack) null); + } + } +@@ -223,7 +286,11 @@ + + if (itemstack4.count == 0) { + playerinventory.setCarried((ItemStack) null); ++ // CraftBukkit start - Update client cursor if we didn't empty it ++ } else if (entityhuman instanceof EntityPlayer) { ++ ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, entityhuman.inventory.getCarried())); + } ++ // CraftBukkit end + } + } else if (slot2.isAllowed(entityhuman)) { + if (itemstack4 == null) { +@@ -249,7 +316,11 @@ + itemstack4.a(k1); + if (itemstack4.count == 0) { + playerinventory.setCarried((ItemStack) null); ++ // CraftBukkit start - Update client cursor if we didn't empty it ++ } else if (entityhuman instanceof EntityPlayer) { ++ ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, entityhuman.inventory.getCarried())); + } ++ // CraftBukkit end + + itemstack1.count += k1; + } else if (itemstack4.count <= slot2.getMaxStackSize(itemstack4)) { +@@ -258,7 +329,9 @@ + } + } else if (itemstack1.getItem() == itemstack4.getItem() && itemstack4.getMaxStackSize() > 1 && (!itemstack1.usesData() || itemstack1.getData() == itemstack4.getData()) && ItemStack.equals(itemstack1, itemstack4)) { + k1 = itemstack1.count; +- if (k1 > 0 && k1 + itemstack4.count <= itemstack4.getMaxStackSize()) { ++ // CraftBukkit start - itemstack4.getMaxStackSize() -> maxStack ++ int maxStack = Math.min(itemstack4.getMaxStackSize(), slot2.getMaxStackSize()); ++ if (k1 > 0 && k1 + itemstack4.count <= maxStack) { + itemstack4.count += k1; + itemstack1 = slot2.a(k1); + if (itemstack1.count == 0) { +@@ -266,11 +339,24 @@ + } + + slot2.a(entityhuman, playerinventory.getCarried()); ++ // CraftBukkit start - Update client cursor if we didn't empty it ++ } else if (entityhuman instanceof EntityPlayer) { ++ ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, entityhuman.inventory.getCarried())); + } ++ // CraftBukkit end + } + } + + slot2.f(); ++ // CraftBukkit start - Make sure the client has the right slot contents ++ if (entityhuman instanceof EntityPlayer && slot2.getMaxStackSize() != 64) { ++ ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutSetSlot(this.windowId, slot2.rawSlotIndex, slot2.getItem())); ++ // Updating a crafting inventory makes the client reset the result slot, have to send it again ++ if (this.getBukkitView().getType() == InventoryType.WORKBENCH || this.getBukkitView().getType() == InventoryType.CRAFTING) { ++ ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutSetSlot(this.windowId, 0, this.getSlot(0).getItem())); ++ } ++ } ++ // CraftBukkit end + } + } + } else if (k == 2 && j >= 0 && j < 9) { +@@ -411,17 +497,20 @@ + if (itemstack1 != null && itemstack1.getItem() == itemstack.getItem() && (!itemstack.usesData() || itemstack.getData() == itemstack1.getData()) && ItemStack.equals(itemstack, itemstack1)) { + int l = itemstack1.count + itemstack.count; + +- if (l <= itemstack.getMaxStackSize()) { ++ // CraftBukkit start - itemstack.getMaxStackSize() -> maxStack ++ int maxStack = Math.min(itemstack.getMaxStackSize(), slot.getMaxStackSize()); ++ if (l <= maxStack) { + itemstack.count = 0; + itemstack1.count = l; + slot.f(); + flag1 = true; +- } else if (itemstack1.count < itemstack.getMaxStackSize()) { +- itemstack.count -= itemstack.getMaxStackSize() - itemstack1.count; +- itemstack1.count = itemstack.getMaxStackSize(); ++ } else if (itemstack1.count < maxStack) { ++ itemstack.count -= maxStack - itemstack1.count; ++ itemstack1.count = maxStack; + slot.f(); + flag1 = true; + } ++ // CraftBukkit end + } + + if (flag) { diff --git a/nms-patches/ContainerAnvil.patch b/nms-patches/ContainerAnvil.patch new file mode 100644 index 00000000..1c0c583a --- /dev/null +++ b/nms-patches/ContainerAnvil.patch @@ -0,0 +1,51 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ContainerAnvil.java 2014-11-27 08:59:46.601422287 +1100 ++++ src/main/java/net/minecraft/server/ContainerAnvil.java 2014-11-27 08:42:10.144850927 +1100 +@@ -6,6 +6,8 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; // CraftBukkit ++ + public class ContainerAnvil extends Container { + + private static final Logger f = LogManager.getLogger(); +@@ -17,8 +19,13 @@ + private int k; + private String l; + private final EntityHuman m; ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private PlayerInventory player; ++ // CraftBukkit end + + public ContainerAnvil(PlayerInventory playerinventory, World world, BlockPosition blockposition, EntityHuman entityhuman) { ++ this.player = playerinventory; // CraftBukkit + this.j = blockposition; + this.i = world; + this.m = entityhuman; +@@ -265,6 +272,7 @@ + } + + public boolean a(EntityHuman entityhuman) { ++ if (!this.checkReachable) return true; // CraftBukkit + return this.i.getType(this.j).getBlock() != Blocks.ANVIL ? false : entityhuman.e((double) this.j.getX() + 0.5D, (double) this.j.getY() + 0.5D, (double) this.j.getZ() + 0.5D) <= 64.0D; + } + +@@ -328,4 +336,17 @@ + static int b(ContainerAnvil containeranvil) { + return containeranvil.k; + } ++ ++ // CraftBukkit start ++ @Override ++ public CraftInventoryView getBukkitView() { ++ if (bukkitEntity != null) { ++ return bukkitEntity; ++ } ++ ++ org.bukkit.craftbukkit.inventory.CraftInventory inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryAnvil(this.h, this.g); ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/ContainerAnvilInventory.patch b/nms-patches/ContainerAnvilInventory.patch new file mode 100644 index 00000000..16649334 --- /dev/null +++ b/nms-patches/ContainerAnvilInventory.patch @@ -0,0 +1,58 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ContainerAnvilInventory.java 2014-11-27 08:59:46.597422305 +1100 ++++ src/main/java/net/minecraft/server/ContainerAnvilInventory.java 2014-11-27 08:42:10.168850880 +1100 +@@ -1,8 +1,43 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import java.util.List; ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ + public class ContainerAnvilInventory extends InventorySubcontainer { + + final ContainerAnvil a; ++ ++ // CraftBukkit start ++ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>(); ++ public org.bukkit.entity.Player player; ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() { ++ return this.items; ++ } ++ ++ public void onOpen(CraftHumanEntity who) { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) { ++ transaction.remove(who); ++ } ++ ++ public List<HumanEntity> getViewers() { ++ return transaction; ++ } ++ ++ public org.bukkit.inventory.InventoryHolder getOwner() { ++ return this.player; ++ } ++ ++ public void setMaxStackSize(int size) { ++ maxStack = size; ++ } + + ContainerAnvilInventory(ContainerAnvil containeranvil, String s, boolean flag, int i) { + super(s, flag, i); +@@ -13,4 +48,11 @@ + super.update(); + this.a.a((IInventory) this); + } ++ ++ // CraftBukkit start - override inherited maxStack from InventorySubcontainer ++ @Override ++ public int getMaxStackSize() { ++ return maxStack; ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/ContainerBeacon.patch b/nms-patches/ContainerBeacon.patch new file mode 100644 index 00000000..178e143c --- /dev/null +++ b/nms-patches/ContainerBeacon.patch @@ -0,0 +1,47 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ContainerBeacon.java 2014-11-27 08:59:46.601422287 +1100 ++++ src/main/java/net/minecraft/server/ContainerBeacon.java 2014-11-27 08:42:10.156850903 +1100 +@@ -1,11 +1,18 @@ + package net.minecraft.server; + ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; // CraftBukkit ++ + public class ContainerBeacon extends Container { + + private IInventory a; + private final SlotBeacon f; ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private PlayerInventory player; ++ // CraftBukkit end + + public ContainerBeacon(IInventory iinventory, IInventory iinventory1) { ++ player = (PlayerInventory) iinventory; // CraftBukkit - TODO: check this + this.a = iinventory1; + this.a((Slot) (this.f = new SlotBeacon(this, iinventory1, 0, 136, 110))); + byte b0 = 36; +@@ -35,6 +42,7 @@ + } + + public boolean a(EntityHuman entityhuman) { ++ if (!this.checkReachable) return true; // CraftBukkit + return this.a.a(entityhuman); + } + +@@ -83,4 +91,17 @@ + + return itemstack; + } ++ ++ // CraftBukkit start ++ @Override ++ public CraftInventoryView getBukkitView() { ++ if (bukkitEntity != null) { ++ return bukkitEntity; ++ } ++ ++ org.bukkit.craftbukkit.inventory.CraftInventory inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryBeacon((TileEntityBeacon) this.a); // TODO - check this ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/ContainerBrewingStand.patch b/nms-patches/ContainerBrewingStand.patch new file mode 100644 index 00000000..b7aba135 --- /dev/null +++ b/nms-patches/ContainerBrewingStand.patch @@ -0,0 +1,52 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ContainerBrewingStand.java 2014-11-27 08:59:46.605422269 +1100 ++++ src/main/java/net/minecraft/server/ContainerBrewingStand.java 2014-11-27 08:42:10.172850872 +1100 +@@ -1,12 +1,23 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftInventoryBrewer; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++// CraftBukkit end ++ + public class ContainerBrewingStand extends Container { + + private IInventory brewingStand; + private final Slot f; + private int g; ++ ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private PlayerInventory player; ++ // CraftBukkit end + + public ContainerBrewingStand(PlayerInventory playerinventory, IInventory iinventory) { ++ player = playerinventory; // CraftBukkit + this.brewingStand = iinventory; + this.a((Slot) (new SlotPotionBottle(playerinventory.player, iinventory, 0, 56, 46))); + this.a((Slot) (new SlotPotionBottle(playerinventory.player, iinventory, 1, 79, 53))); +@@ -47,6 +58,7 @@ + } + + public boolean a(EntityHuman entityhuman) { ++ if (!this.checkReachable) return true; // CraftBukkit + return this.brewingStand.a(entityhuman); + } + +@@ -101,4 +113,17 @@ + + return itemstack; + } ++ ++ // CraftBukkit start ++ @Override ++ public CraftInventoryView getBukkitView() { ++ if (bukkitEntity != null) { ++ return bukkitEntity; ++ } ++ ++ CraftInventoryBrewer inventory = new CraftInventoryBrewer(this.brewingStand); ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/ContainerChest.patch b/nms-patches/ContainerChest.patch new file mode 100644 index 00000000..7021c61b --- /dev/null +++ b/nms-patches/ContainerChest.patch @@ -0,0 +1,59 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ContainerChest.java 2014-11-27 08:59:46.605422269 +1100 ++++ src/main/java/net/minecraft/server/ContainerChest.java 2014-11-27 08:42:10.172850872 +1100 +@@ -1,15 +1,48 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftInventory; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++// CraftBukkit end ++ + public class ContainerChest extends Container { + + public IInventory container; + private int f; ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private PlayerInventory player; ++ ++ @Override ++ public CraftInventoryView getBukkitView() { ++ if (bukkitEntity != null) { ++ return bukkitEntity; ++ } ++ ++ CraftInventory inventory; ++ if (this.container instanceof PlayerInventory) { ++ inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryPlayer((PlayerInventory) this.container); ++ } else if (this.container instanceof InventoryLargeChest) { ++ inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryDoubleChest((InventoryLargeChest) this.container); ++ } else { ++ inventory = new CraftInventory(this.container); ++ } ++ ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end + + public ContainerChest(IInventory iinventory, IInventory iinventory1, EntityHuman entityhuman) { + this.container = iinventory1; + this.f = iinventory1.getSize() / 9; + iinventory1.startOpen(entityhuman); + int i = (this.f - 4) * 18; ++ ++ // CraftBukkit start - Save player ++ // TODO: Should we check to make sure it really is an InventoryPlayer? ++ this.player = (PlayerInventory) iinventory; ++ // CraftBukkit end + + int j; + int k; +@@ -33,6 +66,7 @@ + } + + public boolean a(EntityHuman entityhuman) { ++ if (!this.checkReachable) return true; // CraftBukkit + return this.container.a(entityhuman); + } + diff --git a/nms-patches/ContainerDispenser.patch b/nms-patches/ContainerDispenser.patch new file mode 100644 index 00000000..cee54779 --- /dev/null +++ b/nms-patches/ContainerDispenser.patch @@ -0,0 +1,54 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ContainerDispenser.java 2014-11-27 08:59:46.605422269 +1100 ++++ src/main/java/net/minecraft/server/ContainerDispenser.java 2014-11-27 08:42:10.148850918 +1100 +@@ -1,11 +1,24 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftInventory; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++// CraftBukkit end ++ + public class ContainerDispenser extends Container { + +- private IInventory items; ++ public IInventory items; // CraftBukkit - private -> public ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private PlayerInventory player; ++ // CraftBukkit end + + public ContainerDispenser(IInventory iinventory, IInventory iinventory1) { + this.items = iinventory1; ++ // CraftBukkit start - Save player ++ // TODO: Should we check to make sure it really is an InventoryPlayer? ++ this.player = (PlayerInventory)iinventory; ++ // CraftBukkit end + + int i; + int j; +@@ -29,6 +42,7 @@ + } + + public boolean a(EntityHuman entityhuman) { ++ if (!this.checkReachable) return true; // CraftBukkit + return this.items.a(entityhuman); + } + +@@ -63,4 +77,17 @@ + + return itemstack; + } ++ ++ // CraftBukkit start ++ @Override ++ public CraftInventoryView getBukkitView() { ++ if (bukkitEntity != null) { ++ return bukkitEntity; ++ } ++ ++ CraftInventory inventory = new CraftInventory(this.items); ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/ContainerEnchantTable.patch b/nms-patches/ContainerEnchantTable.patch new file mode 100644 index 00000000..f2d866d8 --- /dev/null +++ b/nms-patches/ContainerEnchantTable.patch @@ -0,0 +1,170 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ContainerEnchantTable.java 2014-11-27 08:59:46.609422252 +1100 ++++ src/main/java/net/minecraft/server/ContainerEnchantTable.java 2014-11-27 08:42:10.120850973 +1100 +@@ -3,15 +3,31 @@ + import java.util.List; + import java.util.Random; + ++// CraftBukkit start ++import java.util.Map; ++ ++import org.bukkit.craftbukkit.inventory.CraftInventoryEnchanting; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.enchantment.EnchantItemEvent; ++import org.bukkit.event.enchantment.PrepareItemEnchantEvent; ++import org.bukkit.entity.Player; ++// CraftBukkit end ++ + public class ContainerEnchantTable extends Container { + +- public IInventory enchantSlots = new ContainerEnchantTableInventory(this, "Enchant", true, 2); ++ // CraftBukkit - make type specific (changed from IInventory) ++ public ContainerEnchantTableInventory enchantSlots = new ContainerEnchantTableInventory(this, "Enchant", true, 2); + private World world; + private BlockPosition position; + private Random k = new Random(); + public int f; + public int[] costs = new int[3]; + public int[] h = new int[] { -1, -1, -1}; ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private Player player; ++ // CraftBukkit end + + public ContainerEnchantTable(PlayerInventory playerinventory, World world, BlockPosition blockposition) { + this.world = world; +@@ -31,7 +47,11 @@ + for (i = 0; i < 9; ++i) { + this.a(new Slot(playerinventory, i, 8 + i * 18, 142)); + } +- ++ ++ // CraftBukkit start ++ player = (Player) playerinventory.player.getBukkitEntity(); ++ enchantSlots.player = player; ++ // CraftBukkit end + } + + public void addSlotListener(ICrafting icrafting) { +@@ -67,7 +87,7 @@ + ItemStack itemstack = iinventory.getItem(0); + int i; + +- if (itemstack != null && itemstack.v()) { ++ if (itemstack != null) { // CraftBukkit - relax condition + if (!this.world.isStatic) { + i = 0; + +@@ -114,6 +134,20 @@ + this.costs[j] = 0; + } + } ++ ++ // CraftBukkit start ++ CraftItemStack item = CraftItemStack.asCraftMirror(itemstack); ++ PrepareItemEnchantEvent event = new PrepareItemEnchantEvent(player, this.getBukkitView(), this.world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()), item, this.costs, i); ++ event.setCancelled(!itemstack.v()); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ for (i = 0; i < 3; ++i) { ++ this.costs[i] = 0; ++ } ++ return; ++ } ++ // CraftBukkit end + + for (j = 0; j < 3; ++j) { + if (this.costs[j] > 0) { +@@ -149,24 +183,56 @@ + } else if (this.costs[i] > 0 && itemstack != null && (entityhuman.expLevel >= j && entityhuman.expLevel >= this.costs[i] || entityhuman.abilities.canInstantlyBuild)) { + if (!this.world.isStatic) { + List list = this.a(itemstack, i, this.costs[i]); ++ // CraftBukkit start - Provide an empty enchantment list ++ if (list == null) { ++ list = new java.util.ArrayList<WeightedRandomEnchant>(); ++ } ++ // CraftBukkit end + boolean flag = itemstack.getItem() == Items.BOOK; + + if (list != null) { +- entityhuman.b(j); ++ // CraftBukkit start ++ Map<org.bukkit.enchantments.Enchantment, Integer> enchants = new java.util.HashMap<org.bukkit.enchantments.Enchantment, Integer>(); ++ for (Object obj : list) { ++ WeightedRandomEnchant instance = (WeightedRandomEnchant) obj; ++ enchants.put(org.bukkit.enchantments.Enchantment.getById(instance.enchantment.id), instance.level); ++ } ++ CraftItemStack item = CraftItemStack.asCraftMirror(itemstack); ++ ++ EnchantItemEvent event = new EnchantItemEvent((Player) entityhuman.getBukkitEntity(), this.getBukkitView(), this.world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()), item, this.costs[i], enchants, i); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ int level = event.getExpLevelCost(); ++ if (event.isCancelled() || (level > entityhuman.expLevel && !entityhuman.abilities.canInstantlyBuild) || event.getEnchantsToAdd().isEmpty()) { ++ return false; ++ } ++ + if (flag) { + itemstack.setItem(Items.ENCHANTED_BOOK); + } ++ ++ for (Map.Entry<org.bukkit.enchantments.Enchantment, Integer> entry : event.getEnchantsToAdd().entrySet()) { ++ try { ++ if (flag) { ++ int enchantId = entry.getKey().getId(); ++ if (Enchantment.getById(enchantId) == null) { ++ continue; ++ } + +- for (int k = 0; k < list.size(); ++k) { +- WeightedRandomEnchant weightedrandomenchant = (WeightedRandomEnchant) list.get(k); +- +- if (flag) { +- Items.ENCHANTED_BOOK.a(itemstack, weightedrandomenchant); +- } else { +- itemstack.addEnchantment(weightedrandomenchant.enchantment, weightedrandomenchant.level); ++ WeightedRandomEnchant enchantment = new WeightedRandomEnchant(Enchantment.getById(enchantId), entry.getValue()); ++ Items.ENCHANTED_BOOK.a(itemstack, enchantment); ++ } else { ++ item.addUnsafeEnchantment(entry.getKey(), entry.getValue()); ++ } ++ } catch (IllegalArgumentException e) { ++ /* Just swallow invalid enchantments */ + } + } ++ ++ entityhuman.b(level); ++ // CraftBukkit end + ++ // CraftBukkit - TODO: let plugins change this + if (!entityhuman.abilities.canInstantlyBuild) { + itemstack1.count -= j; + if (itemstack1.count <= 0) { +@@ -212,6 +278,7 @@ + } + + public boolean a(EntityHuman entityhuman) { ++ if (!this.checkReachable) return true; // CraftBukkit + return this.world.getType(this.position).getBlock() != Blocks.ENCHANTING_TABLE ? false : entityhuman.e((double) this.position.getX() + 0.5D, (double) this.position.getY() + 0.5D, (double) this.position.getZ() + 0.5D) <= 64.0D; + } + +@@ -263,5 +330,18 @@ + } + + return itemstack; ++ } ++ ++ // CraftBukkit start ++ @Override ++ public CraftInventoryView getBukkitView() { ++ if (bukkitEntity != null) { ++ return bukkitEntity; ++ } ++ ++ CraftInventoryEnchanting inventory = new CraftInventoryEnchanting(this.enchantSlots); ++ bukkitEntity = new CraftInventoryView(this.player, inventory, this); ++ return bukkitEntity; + } ++ // CraftBukkit end + } diff --git a/nms-patches/ContainerEnchantTableInventory.patch b/nms-patches/ContainerEnchantTableInventory.patch new file mode 100644 index 00000000..f2f80b3e --- /dev/null +++ b/nms-patches/ContainerEnchantTableInventory.patch @@ -0,0 +1,58 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ContainerEnchantTableInventory.java 2014-11-27 08:59:46.609422252 +1100 ++++ src/main/java/net/minecraft/server/ContainerEnchantTableInventory.java 2014-11-27 08:42:10.088851036 +1100 +@@ -1,8 +1,45 @@ + package net.minecraft.server; + +-class ContainerEnchantTableInventory extends InventorySubcontainer { ++// CraftBukkit start ++import java.util.List; ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ ++// CraftBukkit -> public ++public class ContainerEnchantTableInventory extends InventorySubcontainer { + + final ContainerEnchantTable enchantTable; ++ ++ // CraftBukkit start ++ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>(); ++ public org.bukkit.entity.Player player; ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() { ++ return this.items; ++ } ++ ++ public void onOpen(CraftHumanEntity who) { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) { ++ transaction.remove(who); ++ } ++ ++ public List<HumanEntity> getViewers() { ++ return transaction; ++ } ++ ++ public org.bukkit.inventory.InventoryHolder getOwner() { ++ return this.player; ++ } ++ ++ public void setMaxStackSize(int size) { ++ maxStack = size; ++ } ++ // CraftBukkit end + + ContainerEnchantTableInventory(ContainerEnchantTable containerenchanttable, String s, boolean flag, int i) { + super(s, flag, i); +@@ -10,7 +47,7 @@ + } + + public int getMaxStackSize() { +- return 64; ++ return maxStack; // CraftBukkit + } + + public void update() { diff --git a/nms-patches/ContainerFurnace.patch b/nms-patches/ContainerFurnace.patch new file mode 100644 index 00000000..b299ee20 --- /dev/null +++ b/nms-patches/ContainerFurnace.patch @@ -0,0 +1,51 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ContainerFurnace.java 2014-11-27 08:59:46.613422234 +1100 ++++ src/main/java/net/minecraft/server/ContainerFurnace.java 2014-11-27 08:42:10.116850981 +1100 +@@ -1,5 +1,10 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftInventoryFurnace; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++// CraftBukkit end ++ + public class ContainerFurnace extends Container { + + private final IInventory furnace; +@@ -7,12 +12,29 @@ + private int g; + private int h; + private int i; ++ ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private PlayerInventory player; ++ ++ @Override ++ public CraftInventoryView getBukkitView() { ++ if (bukkitEntity != null) { ++ return bukkitEntity; ++ } ++ ++ CraftInventoryFurnace inventory = new CraftInventoryFurnace((TileEntityFurnace) this.furnace); ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end + + public ContainerFurnace(PlayerInventory playerinventory, IInventory iinventory) { + this.furnace = iinventory; + this.a(new Slot(iinventory, 0, 56, 17)); + this.a((Slot) (new SlotFurnaceFuel(iinventory, 1, 56, 53))); + this.a((Slot) (new SlotFurnaceResult(playerinventory.player, iinventory, 2, 116, 35))); ++ this.player = playerinventory; // CraftBukkit - save player + + int i; + +@@ -63,6 +85,7 @@ + } + + public boolean a(EntityHuman entityhuman) { ++ if (!this.checkReachable) return true; // CraftBukkit + return this.furnace.a(entityhuman); + } + diff --git a/nms-patches/ContainerHopper.patch b/nms-patches/ContainerHopper.patch new file mode 100644 index 00000000..4654d33a --- /dev/null +++ b/nms-patches/ContainerHopper.patch @@ -0,0 +1,44 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ContainerHopper.java 2014-11-27 08:59:46.613422234 +1100 ++++ src/main/java/net/minecraft/server/ContainerHopper.java 2014-11-27 08:42:10.168850880 +1100 +@@ -1,11 +1,33 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftInventory; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++// CraftBukkit end ++ + public class ContainerHopper extends Container { + + private final IInventory hopper; ++ ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private PlayerInventory player; ++ ++ @Override ++ public CraftInventoryView getBukkitView() { ++ if (bukkitEntity != null) { ++ return bukkitEntity; ++ } ++ ++ CraftInventory inventory = new CraftInventory(this.hopper); ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end + + public ContainerHopper(PlayerInventory playerinventory, IInventory iinventory, EntityHuman entityhuman) { + this.hopper = iinventory; ++ this.player = playerinventory; // CraftBukkit - save player + iinventory.startOpen(entityhuman); + byte b0 = 51; + +@@ -28,6 +50,7 @@ + } + + public boolean a(EntityHuman entityhuman) { ++ if (!this.checkReachable) return true; // CraftBukkit + return this.hopper.a(entityhuman); + } + diff --git a/nms-patches/ContainerHorse.patch b/nms-patches/ContainerHorse.patch new file mode 100644 index 00000000..9c9fa57a --- /dev/null +++ b/nms-patches/ContainerHorse.patch @@ -0,0 +1,36 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ContainerHorse.java 2014-11-27 08:59:46.613422234 +1100 ++++ src/main/java/net/minecraft/server/ContainerHorse.java 2014-11-27 08:42:10.124850965 +1100 +@@ -1,11 +1,33 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftInventory; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++import org.bukkit.inventory.InventoryView; ++// CraftBukkit end ++ + public class ContainerHorse extends Container { + + private IInventory a; + private EntityHorse f; ++ ++ // CraftBukkit start ++ org.bukkit.craftbukkit.inventory.CraftInventoryView bukkitEntity; ++ PlayerInventory player; ++ ++ @Override ++ public InventoryView getBukkitView() { ++ if (bukkitEntity != null) { ++ return bukkitEntity; ++ } ++ ++ CraftInventory inventory = new org.bukkit.craftbukkit.inventory.CraftInventoryHorse(this.a); ++ return bukkitEntity = new CraftInventoryView(player.player.getBukkitEntity(), inventory, this); ++ } + + public ContainerHorse(IInventory iinventory, IInventory iinventory1, EntityHorse entityhorse, EntityHuman entityhuman) { ++ player = (PlayerInventory) iinventory; ++ // CraftBukkit end + this.a = iinventory1; + this.f = entityhorse; + byte b0 = 3; diff --git a/nms-patches/ContainerMerchant.patch b/nms-patches/ContainerMerchant.patch new file mode 100644 index 00000000..d62ffe9e --- /dev/null +++ b/nms-patches/ContainerMerchant.patch @@ -0,0 +1,36 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ContainerMerchant.java 2014-11-27 08:59:46.617422217 +1100 ++++ src/main/java/net/minecraft/server/ContainerMerchant.java 2014-11-27 08:42:10.136850942 +1100 +@@ -1,10 +1,25 @@ + package net.minecraft.server; + ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; // CraftBukkit ++ + public class ContainerMerchant extends Container { + + private IMerchant merchant; + private InventoryMerchant f; + private final World g; ++ ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private PlayerInventory player; ++ ++ @Override ++ public CraftInventoryView getBukkitView() { ++ if (bukkitEntity == null) { ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), new org.bukkit.craftbukkit.inventory.CraftInventoryMerchant((InventoryMerchant) f), this); ++ } ++ return bukkitEntity; ++ } ++ // CraftBukkit end + + public ContainerMerchant(PlayerInventory playerinventory, IMerchant imerchant, World world) { + this.merchant = imerchant; +@@ -13,6 +28,7 @@ + this.a(new Slot(this.f, 0, 36, 53)); + this.a(new Slot(this.f, 1, 62, 53)); + this.a((Slot) (new SlotMerchantResult(playerinventory.player, imerchant, this.f, 2, 120, 53))); ++ this.player = playerinventory; // CraftBukkit - save player + + int i; + diff --git a/nms-patches/ContainerPlayer.patch b/nms-patches/ContainerPlayer.patch new file mode 100644 index 00000000..8b2c347b --- /dev/null +++ b/nms-patches/ContainerPlayer.patch @@ -0,0 +1,74 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ContainerPlayer.java 2014-11-27 08:59:46.621422199 +1100 ++++ src/main/java/net/minecraft/server/ContainerPlayer.java 2014-11-27 08:42:10.104851005 +1100 +@@ -1,15 +1,28 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftInventoryCrafting; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++// CraftBukkit end ++ + public class ContainerPlayer extends Container { + + public InventoryCrafting craftInventory = new InventoryCrafting(this, 2, 2); + public IInventory resultInventory = new InventoryCraftResult(); + public boolean g; + private final EntityHuman h; ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private PlayerInventory player; ++ // CraftBukkit end + + public ContainerPlayer(PlayerInventory playerinventory, boolean flag, EntityHuman entityhuman) { + this.g = flag; + this.h = entityhuman; ++ this.resultInventory = new InventoryCraftResult(); // CraftBukkit - moved to before InventoryCrafting construction ++ this.craftInventory = new InventoryCrafting(this, 2, 2, playerinventory.player); // CraftBukkit - pass player ++ this.craftInventory.resultInventory = this.resultInventory; // CraftBukkit - let InventoryCrafting know about its result slot ++ this.player = playerinventory; // CraftBukkit - save player + this.a((Slot) (new SlotResult(playerinventory.player, this.craftInventory, this.resultInventory, 0, 144, 36))); + + int i; +@@ -35,11 +48,22 @@ + this.a(new Slot(playerinventory, i, 8 + i * 18, 142)); + } + +- this.a((IInventory) this.craftInventory); ++ // this.a((IInventory) this.craftInventory); // CraftBukkit - unneeded since it just sets result slot to empty + } + + public void a(IInventory iinventory) { +- this.resultInventory.setItem(0, CraftingManager.getInstance().craft(this.craftInventory, this.h.world)); ++ // this.resultInventory.setItem(0, CraftingManager.getInstance().craft(this.craftInventory, this.h.world)); ++ // CraftBukkit start (Note: the following line would cause an error if called during construction) ++ CraftingManager.getInstance().lastCraftView = getBukkitView(); ++ ItemStack craftResult = CraftingManager.getInstance().craft(this.craftInventory, this.h.world); ++ this.resultInventory.setItem(0, craftResult); ++ if (super.listeners.size() < 1) { ++ return; ++ } ++ ++ EntityPlayer player = (EntityPlayer) super.listeners.get(0); // TODO: Is this _always_ correct? Seems like it. ++ player.playerConnection.sendPacket(new PacketPlayOutSetSlot(player.activeContainer.windowId, 0, craftResult)); ++ // CraftBukkit end + } + + public void b(EntityHuman entityhuman) { +@@ -119,4 +143,17 @@ + public boolean a(ItemStack itemstack, Slot slot) { + return slot.inventory != this.resultInventory && super.a(itemstack, slot); + } ++ ++ // CraftBukkit start ++ @Override ++ public CraftInventoryView getBukkitView() { ++ if (bukkitEntity != null) { ++ return bukkitEntity; ++ } ++ ++ CraftInventoryCrafting inventory = new CraftInventoryCrafting(this.craftInventory, this.resultInventory); ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/ContainerWorkbench.patch b/nms-patches/ContainerWorkbench.patch new file mode 100644 index 00000000..045167c1 --- /dev/null +++ b/nms-patches/ContainerWorkbench.patch @@ -0,0 +1,79 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ContainerWorkbench.java 2014-11-27 08:59:46.621422199 +1100 ++++ src/main/java/net/minecraft/server/ContainerWorkbench.java 2014-11-27 08:42:10.156850903 +1100 +@@ -1,13 +1,28 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftInventoryCrafting; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++// CraftBukkit end ++ + public class ContainerWorkbench extends Container { + +- public InventoryCrafting craftInventory = new InventoryCrafting(this, 3, 3); +- public IInventory resultInventory = new InventoryCraftResult(); ++ public InventoryCrafting craftInventory; // CraftBukkit - move initialization into constructor ++ public IInventory resultInventory; // CraftBukkit - move initialization into constructor + private World g; + private BlockPosition h; ++ // CraftBukkit start ++ private CraftInventoryView bukkitEntity = null; ++ private PlayerInventory player; ++ // CraftBukkit end + + public ContainerWorkbench(PlayerInventory playerinventory, World world, BlockPosition blockposition) { ++ // CraftBukkit start - Switched order of IInventory construction and stored player ++ this.resultInventory = new InventoryCraftResult(); ++ this.craftInventory = new InventoryCrafting(this, 3, 3, playerinventory.player); // CraftBukkit - pass player ++ this.craftInventory.resultInventory = this.resultInventory; ++ this.player = playerinventory; ++ // CraftBukkit end + this.g = world; + this.h = blockposition; + this.a((Slot) (new SlotResult(playerinventory.player, this.craftInventory, this.resultInventory, 0, 124, 35))); +@@ -35,7 +50,18 @@ + } + + public void a(IInventory iinventory) { +- this.resultInventory.setItem(0, CraftingManager.getInstance().craft(this.craftInventory, this.g)); ++ // this.resultInventory.setItem(0, CraftingManager.getInstance().craft(this.craftInventory, this.g)); ++ // CraftBukkit start ++ CraftingManager.getInstance().lastCraftView = getBukkitView(); ++ ItemStack craftResult = CraftingManager.getInstance().craft(this.craftInventory, this.g); ++ this.resultInventory.setItem(0, craftResult); ++ if (super.listeners.size() < 1) { ++ return; ++ } ++ ++ EntityPlayer player = (EntityPlayer) super.listeners.get(0); // TODO: Is this _always_ correct? Seems like it. ++ player.playerConnection.sendPacket(new PacketPlayOutSetSlot(player.activeContainer.windowId, 0, craftResult)); ++ // CraftBukkit end + } + + public void b(EntityHuman entityhuman) { +@@ -53,6 +79,7 @@ + } + + public boolean a(EntityHuman entityhuman) { ++ if (!this.checkReachable) return true; // CraftBukkit + return this.g.getType(this.h).getBlock() != Blocks.CRAFTING_TABLE ? false : entityhuman.e((double) this.h.getX() + 0.5D, (double) this.h.getY() + 0.5D, (double) this.h.getZ() + 0.5D) <= 64.0D; + } + +@@ -101,4 +128,17 @@ + public boolean a(ItemStack itemstack, Slot slot) { + return slot.inventory != this.resultInventory && super.a(itemstack, slot); + } ++ ++ // CraftBukkit start ++ @Override ++ public CraftInventoryView getBukkitView() { ++ if (bukkitEntity != null) { ++ return bukkitEntity; ++ } ++ ++ CraftInventoryCrafting inventory = new CraftInventoryCrafting(this.craftInventory, this.resultInventory); ++ bukkitEntity = new CraftInventoryView(this.player.player.getBukkitEntity(), inventory, this); ++ return bukkitEntity; ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/ControllerLook.patch b/nms-patches/ControllerLook.patch new file mode 100644 index 00000000..212d063e --- /dev/null +++ b/nms-patches/ControllerLook.patch @@ -0,0 +1,23 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ControllerLook.java 2014-11-27 08:59:46.621422199 +1100 ++++ src/main/java/net/minecraft/server/ControllerLook.java 2014-11-27 08:42:10.172850872 +1100 +@@ -1,5 +1,7 @@ + package net.minecraft.server; + ++import org.bukkit.craftbukkit.TrigMath; // CraftBukkit ++ + public class ControllerLook { + + private EntityInsentient a; +@@ -45,8 +47,10 @@ + double d1 = this.f - (this.a.locY + (double) this.a.getHeadHeight()); + double d2 = this.g - this.a.locZ; + double d3 = (double) MathHelper.sqrt(d0 * d0 + d2 * d2); +- float f = (float) (Math.atan2(d2, d0) * 180.0D / 3.1415927410125732D) - 90.0F; +- float f1 = (float) (-(Math.atan2(d1, d3) * 180.0D / 3.1415927410125732D)); ++ // CraftBukkit start - Math -> TrigMath ++ float f = (float) (TrigMath.atan2(d2, d0) * 180.0D / 3.1415927410125732D) - 90.0F; ++ float f1 = (float) (-(TrigMath.atan2(d1, d3) * 180.0D / 3.1415927410125732D)); ++ // CraftBukkit end + + this.a.pitch = this.a(this.a.pitch, f1, this.c); + this.a.aI = this.a(this.a.aI, f, this.b); diff --git a/nms-patches/ControllerMove.patch b/nms-patches/ControllerMove.patch new file mode 100644 index 00000000..9963db77 --- /dev/null +++ b/nms-patches/ControllerMove.patch @@ -0,0 +1,12 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ControllerMove.java 2014-11-27 08:59:46.625422182 +1100 ++++ src/main/java/net/minecraft/server/ControllerMove.java 2014-11-27 08:42:10.164850887 +1100 +@@ -43,7 +43,8 @@ + double d3 = d0 * d0 + d2 * d2 + d1 * d1; + + if (d3 >= 2.500000277905201E-7D) { +- float f = (float) (Math.atan2(d1, d0) * 180.0D / 3.1415927410125732D) - 90.0F; ++ // CraftBukkit - Math -> TrigMath ++ float f = (float) (org.bukkit.craftbukkit.TrigMath.atan2(d1, d0) * 180.0D / 3.1415927410125732D) - 90.0F; + + this.a.yaw = this.a(this.a.yaw, f, 30.0F); + this.a.j((float) (this.e * this.a.getAttributeInstance(GenericAttributes.d).getValue())); diff --git a/nms-patches/CraftingManager.patch b/nms-patches/CraftingManager.patch new file mode 100644 index 00000000..8d0f4bde --- /dev/null +++ b/nms-patches/CraftingManager.patch @@ -0,0 +1,57 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/CraftingManager.java 2014-11-27 08:59:46.625422182 +1100 ++++ src/main/java/net/minecraft/server/CraftingManager.java 2014-11-27 08:42:10.112850989 +1100 +@@ -8,10 +8,16 @@ + import java.util.Iterator; + import java.util.List; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class CraftingManager { + + private static final CraftingManager a = new CraftingManager(); + public List recipes = Lists.newArrayList(); ++ // CraftBukkit start ++ public IRecipe lastRecipe; ++ public org.bukkit.inventory.InventoryView lastCraftView; ++ // CraftBukkit end + + public static CraftingManager getInstance() { + return CraftingManager.a; +@@ -166,8 +172,15 @@ + this.registerShapedRecipe(new ItemStack(Blocks.DAYLIGHT_DETECTOR), new Object[] { "GGG", "QQQ", "WWW", Character.valueOf('G'), Blocks.GLASS, Character.valueOf('Q'), Items.QUARTZ, Character.valueOf('W'), Blocks.WOODEN_SLAB}); + this.registerShapedRecipe(new ItemStack(Blocks.HOPPER), new Object[] { "I I", "ICI", " I ", Character.valueOf('I'), Items.IRON_INGOT, Character.valueOf('C'), Blocks.CHEST}); + this.registerShapedRecipe(new ItemStack(Items.ARMOR_STAND, 1), new Object[] { "///", " / ", "/_/", Character.valueOf('/'), Items.STICK, Character.valueOf('_'), new ItemStack(Blocks.STONE_SLAB, 1, EnumStoneSlabVariant.STONE.a())}); ++ // Collections.sort(this.recipes, new RecipeSorter(this)); // CraftBukkit - moved below ++ sort(); ++ } ++ ++ // CraftBukkit start ++ public void sort() { + Collections.sort(this.recipes, new RecipeSorter(this)); + } ++ // CraftBukkit end + + public ShapedRecipes registerShapedRecipe(ItemStack itemstack, Object... aobject) { + String s = ""; +@@ -265,13 +278,18 @@ + + do { + if (!iterator.hasNext()) { ++ inventorycrafting.currentRecipe = null; // CraftBukkit - Clear recipe when no recipe is found + return null; + } + + irecipe = (IRecipe) iterator.next(); +- } while (!irecipe.a(inventorycrafting, world)); +- +- return irecipe.a(inventorycrafting); ++ } while (!irecipe.a(inventorycrafting, world)); ++ ++ // CraftBukkit start - INVENTORY_PRE_CRAFT event ++ inventorycrafting.currentRecipe = irecipe; ++ ItemStack result = irecipe.a(inventorycrafting); ++ return CraftEventFactory.callPreCraftEvent(inventorycrafting, result, lastCraftView, false); ++ // CraftBukkit end + } + + public ItemStack[] b(InventoryCrafting inventorycrafting, World world) { diff --git a/nms-patches/CrashReport.patch b/nms-patches/CrashReport.patch new file mode 100644 index 00000000..3518bd3e --- /dev/null +++ b/nms-patches/CrashReport.patch @@ -0,0 +1,10 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/CrashReport.java 2014-11-27 08:59:46.629422164 +1100 ++++ src/main/java/net/minecraft/server/CrashReport.java 2014-11-27 08:42:10.172850872 +1100 +@@ -40,6 +40,7 @@ + this.d.a("Memory", (Callable) (new CrashReportMemory(this))); + this.d.a("JVM Flags", (Callable) (new CrashReportJVMFlags(this))); + this.d.a("IntCache", (Callable) (new CrashReportIntCacheSize(this))); ++ this.d.a("CraftBukkit Information", (Callable) (new org.bukkit.craftbukkit.CraftCrashReport())); // CraftBukkit + } + + public String a() { diff --git a/nms-patches/DedicatedServer.patch b/nms-patches/DedicatedServer.patch new file mode 100644 index 00000000..814b166b --- /dev/null +++ b/nms-patches/DedicatedServer.patch @@ -0,0 +1,162 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/DedicatedServer.java 2014-11-27 08:59:46.629422164 +1100 ++++ src/main/java/net/minecraft/server/DedicatedServer.java 2014-11-27 08:42:10.172850872 +1100 +@@ -13,6 +13,14 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// CraftBukkit start ++import java.io.PrintStream; ++import org.apache.logging.log4j.Level; ++ ++import org.bukkit.craftbukkit.LoggerOutputStream; ++import org.bukkit.event.server.ServerCommandEvent; ++// CraftBukkit end ++ + public class DedicatedServer extends MinecraftServer implements IMinecraftServer { + + private static final Logger LOGGER = LogManager.getLogger(); +@@ -25,23 +33,48 @@ + private EnumGamemode q; + private boolean r; + +- public DedicatedServer(File file) { +- super(file, Proxy.NO_PROXY, DedicatedServer.a); ++ // CraftBukkit start - Signature changed ++ public DedicatedServer(joptsimple.OptionSet options) { ++ super(options, Proxy.NO_PROXY, a); ++ // super(file, Proxy.NO_PROXY, a); ++ // CraftBukkit end + new ThreadSleepForever(this, "Server Infinisleeper"); + } + +- protected boolean init() { ++ protected boolean init() throws java.net.UnknownHostException { // CraftBukkit - throws UnknownHostException + ThreadCommandReader threadcommandreader = new ThreadCommandReader(this, "Server console handler"); + + threadcommandreader.setDaemon(true); + threadcommandreader.start(); ++ ++ // CraftBukkit start - TODO: handle command-line logging arguments ++ java.util.logging.Logger global = java.util.logging.Logger.getLogger(""); ++ global.setUseParentHandlers(false); ++ for (java.util.logging.Handler handler : global.getHandlers()) { ++ global.removeHandler(handler); ++ } ++ global.addHandler(new org.bukkit.craftbukkit.util.ForwardLogHandler()); ++ ++ final org.apache.logging.log4j.core.Logger logger = ((org.apache.logging.log4j.core.Logger) LogManager.getRootLogger()); ++ for (org.apache.logging.log4j.core.Appender appender : logger.getAppenders().values()) { ++ if (appender instanceof org.apache.logging.log4j.core.appender.ConsoleAppender) { ++ logger.removeAppender(appender); ++ } ++ } ++ ++ new Thread(new org.bukkit.craftbukkit.util.TerminalConsoleWriterThread(System.out, this.reader)).start(); ++ ++ System.setOut(new PrintStream(new LoggerOutputStream(logger, Level.INFO), true)); ++ System.setErr(new PrintStream(new LoggerOutputStream(logger, Level.WARN), true)); ++ // CraftBukkit end ++ + DedicatedServer.LOGGER.info("Starting minecraft server version 1.8"); + if (Runtime.getRuntime().maxMemory() / 1024L / 1024L < 512L) { + DedicatedServer.LOGGER.warn("To start the server with more ram, launch it as \"java -Xmx1024M -Xms1024M -jar minecraft_server.jar\""); + } + + DedicatedServer.LOGGER.info("Loading properties"); +- this.propertyManager = new PropertyManager(new File("server.properties")); ++ this.propertyManager = new PropertyManager(this.options); // CraftBukkit - CLI argument support + this.o = new EULA(new File("eula.txt")); + if (!this.o.a()) { + DedicatedServer.LOGGER.info("You need to agree to the EULA in order to run the server. Go to eula.txt for more info."); +@@ -90,13 +123,15 @@ + + try { + this.ao().a(inetaddress, this.Q()); +- } catch (IOException ioexception) { ++ } catch (Throwable ioexception) { // CraftBukkit - IOException -> Throwable + DedicatedServer.LOGGER.warn("**** FAILED TO BIND TO PORT!"); + DedicatedServer.LOGGER.warn("The exception was: {}", new Object[] { ioexception.toString()}); + DedicatedServer.LOGGER.warn("Perhaps a server is already running on that port?"); + return false; + } + ++ this.a((PlayerList) (new DedicatedPlayerList(this))); // CraftBukkit ++ + if (!this.getOnlineMode()) { + DedicatedServer.LOGGER.warn("**** SERVER IS RUNNING IN OFFLINE/INSECURE MODE!"); + DedicatedServer.LOGGER.warn("The server will make no attempt to authenticate usernames. Beware."); +@@ -111,7 +146,8 @@ + if (!NameReferencingFileConverter.a(this.propertyManager)) { + return false; + } else { +- this.a((PlayerList) (new DedicatedPlayerList(this))); ++ // this.a((PlayerList) (new DedicatedPlayerList(this))); // CraftBukkit - moved up ++ this.convertable = new WorldLoaderServer(server.getWorldContainer()); // CraftBukkit - moved from MinecraftServer constructor + long j = System.nanoTime(); + + if (this.T() == null) { +@@ -166,7 +202,18 @@ + DedicatedServer.LOGGER.info("Starting remote control listener"); + this.m = new RemoteControlListener(this); + this.m.a(); ++ this.remoteConsole = new org.bukkit.craftbukkit.command.CraftRemoteConsoleCommandSender(); // CraftBukkit ++ } ++ ++ // CraftBukkit start ++ if (this.server.getBukkitSpawnRadius() > -1) { ++ DedicatedServer.LOGGER.info("'settings.spawn-radius' in bukkit.yml has been moved to 'spawn-protection' in server.properties. I will move your config for you."); ++ this.propertyManager.properties.remove("spawn-protection"); ++ this.propertyManager.getInt("spawn-protection", this.server.getBukkitSpawnRadius()); ++ this.server.removeBukkitSpawnRadius(); ++ this.propertyManager.savePropertiesFile(); + } ++ // CraftBukkit end + + if (this.aQ() > 0L) { + Thread thread = new Thread(new ThreadWatchdog(this)); +@@ -181,6 +228,12 @@ + } + } + ++ // CraftBukkit start ++ public PropertyManager getPropertyManager() { ++ return this.propertyManager; ++ } ++ // CraftBukkit end ++ + public void setGamemode(EnumGamemode enumgamemode) { + super.setGamemode(enumgamemode); + this.q = enumgamemode; +@@ -203,6 +256,7 @@ + } + + protected void a(CrashReport crashreport) { ++ /* CraftBukkit start - not sure why you would want to continue running commands once the server crashed + while (this.isRunning()) { + this.aM(); + +@@ -212,7 +266,7 @@ + ; + } + } +- ++ // CraftBukkit end */ + } + + public CrashReport b(CrashReport crashreport) { +@@ -257,7 +311,14 @@ + while (!this.k.isEmpty()) { + ServerCommand servercommand = (ServerCommand) this.k.remove(0); + +- this.getCommandHandler().a(servercommand.source, servercommand.command); ++ // CraftBukkit start - ServerCommand for preprocessing ++ ServerCommandEvent event = new ServerCommandEvent(console, servercommand.command); ++ server.getPluginManager().callEvent(event); ++ servercommand = new ServerCommand(event.getCommand(), servercommand.source); ++ ++ // this.getCommandHandler().a(servercommand.source, servercommand.command); // Called in dispatchServerCommand ++ server.dispatchServerCommand(console, servercommand); ++ // CraftBukkit end + } + + } diff --git a/nms-patches/DispenseBehaviorArmor.patch b/nms-patches/DispenseBehaviorArmor.patch new file mode 100644 index 00000000..6c420e65 --- /dev/null +++ b/nms-patches/DispenseBehaviorArmor.patch @@ -0,0 +1,59 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/DispenseBehaviorArmor.java 2014-11-27 08:59:46.629422164 +1100 ++++ src/main/java/net/minecraft/server/DispenseBehaviorArmor.java 2014-11-27 08:42:10.144850927 +1100 +@@ -3,6 +3,11 @@ + import com.google.common.base.Predicates; + import java.util.List; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.block.BlockDispenseEvent; ++// CraftBukkit end ++ + final class DispenseBehaviorArmor extends DispenseBehaviorItem { + + DispenseBehaviorArmor() {} +@@ -19,15 +24,42 @@ + EntityLiving entityliving = (EntityLiving) list.get(0); + int l = entityliving instanceof EntityHuman ? 1 : 0; + int i1 = EntityInsentient.c(itemstack); +- ItemStack itemstack1 = itemstack.cloneItemStack(); ++ ++ // CraftBukkit start ++ ItemStack itemstack1 = itemstack.a(1); ++ World world = isourceblock.i(); ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); ++ ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); ++ if (!BlockDispenser.eventFired) { ++ world.getServer().getPluginManager().callEvent(event); ++ } + ++ if (event.isCancelled()) { ++ itemstack.count++; ++ return itemstack; ++ } ++ ++ if (!event.getItem().equals(craftItem)) { ++ itemstack.count++; ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.M.get(eventStack.getItem()); ++ if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { ++ idispensebehavior.a(isourceblock, eventStack); ++ return itemstack; ++ } ++ } ++ // CraftBukkit end ++ + itemstack1.count = 1; + entityliving.setEquipment(i1 - l, itemstack1); + if (entityliving instanceof EntityInsentient) { + ((EntityInsentient) entityliving).a(i1, 2.0F); + } + +- --itemstack.count; ++ // --itemstack.count; // CraftBukkit - handled above + return itemstack; + } else { + return super.b(isourceblock, itemstack); diff --git a/nms-patches/DispenseBehaviorBoat.patch b/nms-patches/DispenseBehaviorBoat.patch new file mode 100644 index 00000000..19daf555 --- /dev/null +++ b/nms-patches/DispenseBehaviorBoat.patch @@ -0,0 +1,54 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/DispenseBehaviorBoat.java 2014-11-27 08:59:46.633422146 +1100 ++++ src/main/java/net/minecraft/server/DispenseBehaviorBoat.java 2014-11-27 08:42:10.164850887 +1100 +@@ -1,5 +1,10 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.block.BlockDispenseEvent; ++// CraftBukkit end ++ + final class DispenseBehaviorBoat extends DispenseBehaviorItem { + + private final DispenseBehaviorItem b = new DispenseBehaviorItem(); +@@ -26,10 +31,38 @@ + d3 = 0.0D; + } + +- EntityBoat entityboat = new EntityBoat(world, d0, d1 + d3, d2); ++ // EntityBoat entityboat = new EntityBoat(world, d0, d1 + d3, d2); ++ // CraftBukkit start ++ ItemStack itemstack1 = itemstack.a(1); ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); ++ ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1 + d3, d2)); ++ if (!BlockDispenser.eventFired) { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) { ++ itemstack.count++; ++ return itemstack; ++ } ++ ++ if (!event.getItem().equals(craftItem)) { ++ itemstack.count++; ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.M.get(eventStack.getItem()); ++ if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { ++ idispensebehavior.a(isourceblock, eventStack); ++ return itemstack; ++ } ++ } ++ ++ EntityBoat entityboat = new EntityBoat(world, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ()); ++ // CraftBukkit end + + world.addEntity(entityboat); +- itemstack.a(1); ++ // itemstack.a(1); // CraftBukkit - handled during event processing + return itemstack; + } + diff --git a/nms-patches/DispenseBehaviorBonemeal.patch b/nms-patches/DispenseBehaviorBonemeal.patch new file mode 100644 index 00000000..32cea5b7 --- /dev/null +++ b/nms-patches/DispenseBehaviorBonemeal.patch @@ -0,0 +1,44 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/DispenseBehaviorBonemeal.java 2014-11-27 08:59:46.633422146 +1100 ++++ src/main/java/net/minecraft/server/DispenseBehaviorBonemeal.java 2014-11-27 08:42:10.120850973 +1100 +@@ -1,5 +1,10 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.block.BlockDispenseEvent; ++// CraftBukkit end ++ + final class DispenseBehaviorBonemeal extends DispenseBehaviorItem { + + private boolean b = true; +@@ -10,6 +15,30 @@ + if (EnumColor.WHITE == EnumColor.fromInvColorIndex(itemstack.getData())) { + World world = isourceblock.i(); + BlockPosition blockposition = isourceblock.getBlockPosition().shift(BlockDispenser.b(isourceblock.f())); ++ ++ // CraftBukkit start ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); ++ CraftItemStack craftItem = CraftItemStack.asNewCraftStack(itemstack.getItem()); ++ ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); ++ if (!BlockDispenser.eventFired) { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) { ++ return itemstack; ++ } ++ ++ if (!event.getItem().equals(craftItem)) { ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.M.get(eventStack.getItem()); ++ if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { ++ idispensebehavior.a(isourceblock, eventStack); ++ return itemstack; ++ } ++ } ++ // CraftBukkit end + + if (ItemDye.a(itemstack, world, blockposition)) { + if (!world.isStatic) { diff --git a/nms-patches/DispenseBehaviorEmptyBucket.patch b/nms-patches/DispenseBehaviorEmptyBucket.patch new file mode 100644 index 00000000..9727c367 --- /dev/null +++ b/nms-patches/DispenseBehaviorEmptyBucket.patch @@ -0,0 +1,44 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/DispenseBehaviorEmptyBucket.java 2014-11-27 08:59:46.633422146 +1100 ++++ src/main/java/net/minecraft/server/DispenseBehaviorEmptyBucket.java 2014-11-27 08:42:10.124850965 +1100 +@@ -1,5 +1,10 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.block.BlockDispenseEvent; ++// CraftBukkit end ++ + final class DispenseBehaviorEmptyBucket extends DispenseBehaviorItem { + + private final DispenseBehaviorItem b = new DispenseBehaviorItem(); +@@ -23,6 +28,30 @@ + + item = Items.LAVA_BUCKET; + } ++ ++ // CraftBukkit start ++ org.bukkit.block.Block bukkitBlock = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack); ++ ++ BlockDispenseEvent event = new BlockDispenseEvent(bukkitBlock, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX(), blockposition.getY(), blockposition.getZ())); ++ if (!BlockDispenser.eventFired) { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) { ++ return itemstack; ++ } ++ ++ if (!event.getItem().equals(craftItem)) { ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.M.get(eventStack.getItem()); ++ if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { ++ idispensebehavior.a(isourceblock, eventStack); ++ return itemstack; ++ } ++ } ++ // CraftBukkit end + + world.setAir(blockposition); + if (--itemstack.count == 0) { diff --git a/nms-patches/DispenseBehaviorFilledBucket.patch b/nms-patches/DispenseBehaviorFilledBucket.patch new file mode 100644 index 00000000..e3bcc62e --- /dev/null +++ b/nms-patches/DispenseBehaviorFilledBucket.patch @@ -0,0 +1,65 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/DispenseBehaviorFilledBucket.java 2014-11-27 08:59:46.637422129 +1100 ++++ src/main/java/net/minecraft/server/DispenseBehaviorFilledBucket.java 2014-11-27 08:42:10.088851036 +1100 +@@ -1,5 +1,10 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.block.BlockDispenseEvent; ++// CraftBukkit end ++ + final class DispenseBehaviorFilledBucket extends DispenseBehaviorItem { + + private final DispenseBehaviorItem b = new DispenseBehaviorItem(); +@@ -9,10 +14,49 @@ + public ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) { + ItemBucket itembucket = (ItemBucket) itemstack.getItem(); + BlockPosition blockposition = isourceblock.getBlockPosition().shift(BlockDispenser.b(isourceblock.f())); ++ ++ // CraftBukkit start ++ World world = isourceblock.i(); ++ int x = blockposition.getX(); ++ int y = blockposition.getY(); ++ int z = blockposition.getZ(); ++ if (world.isEmpty(blockposition) || !world.getType(blockposition).getBlock().getMaterial().isBuildable()) { ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack); ++ ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(x, y, z)); ++ if (!BlockDispenser.eventFired) { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) { ++ return itemstack; ++ } ++ ++ if (!event.getItem().equals(craftItem)) { ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.M.get(eventStack.getItem()); ++ if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { ++ idispensebehavior.a(isourceblock, eventStack); ++ return itemstack; ++ } ++ } ++ ++ itembucket = (ItemBucket) CraftItemStack.asNMSCopy(event.getItem()).getItem(); ++ } ++ // CraftBukkit end + + if (itembucket.a(isourceblock.i(), blockposition)) { +- itemstack.setItem(Items.BUCKET); +- itemstack.count = 1; ++ // CraftBukkit start - Handle stacked buckets ++ Item item = Items.BUCKET; ++ if (--itemstack.count == 0) { ++ itemstack.setItem(Items.BUCKET); ++ itemstack.count = 1; ++ } else if (((TileEntityDispenser) isourceblock.getTileEntity()).addItem(new ItemStack(item)) < 0) { ++ this.b.a(isourceblock, new ItemStack(item)); ++ } ++ // CraftBukkit end + return itemstack; + } else { + return this.b.a(isourceblock, itemstack); diff --git a/nms-patches/DispenseBehaviorFireball.patch b/nms-patches/DispenseBehaviorFireball.patch new file mode 100644 index 00000000..1d21873f --- /dev/null +++ b/nms-patches/DispenseBehaviorFireball.patch @@ -0,0 +1,55 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/DispenseBehaviorFireball.java 2014-11-27 08:59:46.637422129 +1100 ++++ src/main/java/net/minecraft/server/DispenseBehaviorFireball.java 2014-11-27 08:42:10.172850872 +1100 +@@ -2,6 +2,11 @@ + + import java.util.Random; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.block.BlockDispenseEvent; ++// CraftBukkit end ++ + final class DispenseBehaviorFireball extends DispenseBehaviorItem { + + DispenseBehaviorFireball() {} +@@ -18,8 +23,38 @@ + double d4 = random.nextGaussian() * 0.05D + (double) enumdirection.getAdjacentY(); + double d5 = random.nextGaussian() * 0.05D + (double) enumdirection.getAdjacentZ(); + +- world.addEntity(new EntitySmallFireball(world, d0, d1, d2, d3, d4, d5)); +- itemstack.a(1); ++ // CraftBukkit start ++ ItemStack itemstack1 = itemstack.a(1); ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); ++ ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d3, d4, d5)); ++ if (!BlockDispenser.eventFired) { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) { ++ itemstack.count++; ++ return itemstack; ++ } ++ ++ if (!event.getItem().equals(craftItem)) { ++ itemstack.count++; ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.M.get(eventStack.getItem()); ++ if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { ++ idispensebehavior.a(isourceblock, eventStack); ++ return itemstack; ++ } ++ } ++ ++ EntitySmallFireball entitysmallfireball = new EntitySmallFireball(world, d0, d1, d2, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ()); ++ entitysmallfireball.projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource((TileEntityDispenser) isourceblock.getTileEntity()); ++ ++ world.addEntity(entitysmallfireball); ++ // itemstack.a(1); // Handled during event processing ++ // CraftBukkit end + return itemstack; + } + diff --git a/nms-patches/DispenseBehaviorFlintAndSteel.patch b/nms-patches/DispenseBehaviorFlintAndSteel.patch new file mode 100644 index 00000000..26ab2df0 --- /dev/null +++ b/nms-patches/DispenseBehaviorFlintAndSteel.patch @@ -0,0 +1,57 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/DispenseBehaviorFlintAndSteel.java 2014-11-27 08:59:46.641422111 +1100 ++++ src/main/java/net/minecraft/server/DispenseBehaviorFlintAndSteel.java 2014-11-27 08:42:10.168850880 +1100 +@@ -1,5 +1,10 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.block.BlockDispenseEvent; ++// CraftBukkit end ++ + final class DispenseBehaviorFlintAndSteel extends DispenseBehaviorItem { + + private boolean b = true; +@@ -10,11 +15,39 @@ + World world = isourceblock.i(); + BlockPosition blockposition = isourceblock.getBlockPosition().shift(BlockDispenser.b(isourceblock.f())); + +- if (world.isEmpty(blockposition)) { +- world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData()); +- if (itemstack.isDamaged(1, world.random)) { +- itemstack.count = 0; ++ // CraftBukkit start ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack); ++ ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(0, 0, 0)); ++ if (!BlockDispenser.eventFired) { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) { ++ return itemstack; ++ } ++ ++ if (!event.getItem().equals(craftItem)) { ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.M.get(eventStack.getItem()); ++ if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { ++ idispensebehavior.a(isourceblock, eventStack); ++ return itemstack; ++ } ++ } ++ // CraftBukkit end ++ ++ if (world.isEmpty(blockposition)) { ++ // CraftBukkit start - Ignition by dispensing flint and steel ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()).isCancelled()) { ++ world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData()); ++ if (itemstack.isDamaged(1, world.random)) { ++ itemstack.count = 0; ++ } + } ++ // CraftBukkit end + } else if (world.getType(blockposition).getBlock() == Blocks.TNT) { + Blocks.TNT.postBreak(world, blockposition, Blocks.TNT.getBlockData().set(BlockTNT.EXPLODE, Boolean.valueOf(true))); + world.setAir(blockposition); diff --git a/nms-patches/DispenseBehaviorItem.patch b/nms-patches/DispenseBehaviorItem.patch new file mode 100644 index 00000000..b902b835 --- /dev/null +++ b/nms-patches/DispenseBehaviorItem.patch @@ -0,0 +1,76 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/DispenseBehaviorItem.java 2014-11-27 08:59:46.641422111 +1100 ++++ src/main/java/net/minecraft/server/DispenseBehaviorItem.java 2014-11-27 08:42:10.168850880 +1100 +@@ -1,5 +1,10 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.block.BlockDispenseEvent; ++// CraftBukkit end ++ + public class DispenseBehaviorItem implements IDispenseBehavior { + + public DispenseBehaviorItem() {} +@@ -17,11 +22,18 @@ + IPosition iposition = BlockDispenser.a(isourceblock); + ItemStack itemstack1 = itemstack.a(1); + +- a(isourceblock.i(), itemstack1, 6, enumdirection, iposition); ++ // CraftBukkit start ++ if (!a(isourceblock.i(), itemstack1, 6, enumdirection, isourceblock)) { ++ itemstack.count++; ++ } ++ // CraftBukkit end + return itemstack; + } + +- public static void a(World world, ItemStack itemstack, int i, EnumDirection enumdirection, IPosition iposition) { ++ // CraftBukkit start - void -> boolean return, IPosition -> ISourceBlock last argument ++ public static boolean a(World world, ItemStack itemstack, int i, EnumDirection enumdirection, ISourceBlock isourceblock) { ++ IPosition iposition = BlockDispenser.a(isourceblock); ++ // CraftBukkit end + double d0 = iposition.getX(); + double d1 = iposition.getY(); + double d2 = iposition.getZ(); +@@ -41,7 +53,41 @@ + entityitem.motX += world.random.nextGaussian() * 0.007499999832361937D * (double) i; + entityitem.motY += world.random.nextGaussian() * 0.007499999832361937D * (double) i; + entityitem.motZ += world.random.nextGaussian() * 0.007499999832361937D * (double) i; ++ ++ // CraftBukkit start ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack); ++ ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(entityitem.motX, entityitem.motY, entityitem.motZ)); ++ if (!BlockDispenser.eventFired) { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) { ++ return false; ++ } ++ ++ entityitem.setItemStack(CraftItemStack.asNMSCopy(event.getItem())); ++ entityitem.motX = event.getVelocity().getX(); ++ entityitem.motY = event.getVelocity().getY(); ++ entityitem.motZ = event.getVelocity().getZ(); ++ ++ if (!event.getItem().equals(craftItem)) { ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.M.get(eventStack.getItem()); ++ if (idispensebehavior != IDispenseBehavior.a && idispensebehavior.getClass() != DispenseBehaviorItem.class) { ++ idispensebehavior.a(isourceblock, eventStack); ++ } else { ++ world.addEntity(entityitem); ++ } ++ return false; ++ } ++ + world.addEntity(entityitem); ++ ++ return true; ++ // CraftBukkit end + } + + protected void a(ISourceBlock isourceblock) { diff --git a/nms-patches/DispenseBehaviorMinecart.patch b/nms-patches/DispenseBehaviorMinecart.patch new file mode 100644 index 00000000..454c15b7 --- /dev/null +++ b/nms-patches/DispenseBehaviorMinecart.patch @@ -0,0 +1,58 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/DispenseBehaviorMinecart.java 2014-11-27 08:59:46.641422111 +1100 ++++ src/main/java/net/minecraft/server/DispenseBehaviorMinecart.java 2014-11-27 08:42:10.084851043 +1100 +@@ -1,5 +1,10 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.block.BlockDispenseEvent; ++// CraftBukkit end ++ + final class DispenseBehaviorMinecart extends DispenseBehaviorItem { + + private final DispenseBehaviorItem b = new DispenseBehaviorItem(); +@@ -38,14 +43,42 @@ + } + } + +- EntityMinecartAbstract entityminecartabstract = EntityMinecartAbstract.a(world, d0, d1 + d3, d2, ItemMinecart.a((ItemMinecart) itemstack.getItem())); ++ // CraftBukkit start ++ // EntityMinecartAbstract entityminecartabstract = EntityMinecartAbstract.a(world, d0, d1 + d3, d2, ItemMinecart.a((ItemMinecart) itemstack.getItem())); ++ ItemStack itemstack1 = itemstack.a(1); ++ org.bukkit.block.Block block2 = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); ++ ++ BlockDispenseEvent event = new BlockDispenseEvent(block2, craftItem.clone(), new org.bukkit.util.Vector(d0, d1 + d3, d2)); ++ if (!BlockDispenser.eventFired) { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) { ++ itemstack.count++; ++ return itemstack; ++ } ++ ++ if (!event.getItem().equals(craftItem)) { ++ itemstack.count++; ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.M.get(eventStack.getItem()); ++ if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { ++ idispensebehavior.a(isourceblock, eventStack); ++ return itemstack; ++ } ++ } ++ ++ itemstack1 = CraftItemStack.asNMSCopy(event.getItem()); ++ EntityMinecartAbstract entityminecartabstract = EntityMinecartAbstract.a(world, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), ItemMinecart.a((ItemMinecart) itemstack1.getItem())); + + if (itemstack.hasName()) { + entityminecartabstract.setCustomName(itemstack.getName()); + } + + world.addEntity(entityminecartabstract); +- itemstack.a(1); ++ // itemstack.a(1); // CraftBukkit - handled during event processing + return itemstack; + } + diff --git a/nms-patches/DispenseBehaviorMonsterEgg.patch b/nms-patches/DispenseBehaviorMonsterEgg.patch new file mode 100644 index 00000000..56a05a54 --- /dev/null +++ b/nms-patches/DispenseBehaviorMonsterEgg.patch @@ -0,0 +1,61 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/DispenseBehaviorMonsterEgg.java 2014-11-27 08:59:46.645422093 +1100 ++++ src/main/java/net/minecraft/server/DispenseBehaviorMonsterEgg.java 2014-11-27 08:42:10.096851020 +1100 +@@ -1,5 +1,10 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.block.BlockDispenseEvent; ++// CraftBukkit end ++ + final class DispenseBehaviorMonsterEgg extends DispenseBehaviorItem { + + DispenseBehaviorMonsterEgg() {} +@@ -9,13 +14,45 @@ + double d0 = isourceblock.getX() + (double) enumdirection.getAdjacentX(); + double d1 = (double) ((float) isourceblock.getBlockPosition().getY() + 0.2F); + double d2 = isourceblock.getZ() + (double) enumdirection.getAdjacentZ(); +- Entity entity = ItemMonsterEgg.a(isourceblock.i(), itemstack.getData(), d0, d1, d2); ++ // Entity entity = ItemMonsterEgg.a(isourceblock.i(), itemstack.getData(), d0, d1, d2); ++ ++ // CraftBukkit start ++ World world = isourceblock.i(); ++ ItemStack itemstack1 = itemstack.a(1); ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); ++ ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(d0, d1, d2)); ++ if (!BlockDispenser.eventFired) { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) { ++ itemstack.count++; ++ return itemstack; ++ } ++ ++ if (!event.getItem().equals(craftItem)) { ++ itemstack.count++; ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.M.get(eventStack.getItem()); ++ if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { ++ idispensebehavior.a(isourceblock, eventStack); ++ return itemstack; ++ } ++ } ++ ++ itemstack1 = CraftItemStack.asNMSCopy(event.getItem()); ++ ++ Entity entity = ItemMonsterEgg.spawnCreature(isourceblock.i(), itemstack.getData(), event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DISPENSE_EGG); + + if (entity instanceof EntityLiving && itemstack.hasName()) { + ((EntityInsentient) entity).setCustomName(itemstack.getName()); + } + +- itemstack.a(1); ++ // itemstack.a(1); // Handled during event processing ++ // CraftBukkit end + return itemstack; + } + } diff --git a/nms-patches/DispenseBehaviorProjectile.patch b/nms-patches/DispenseBehaviorProjectile.patch new file mode 100644 index 00000000..b4e7083b --- /dev/null +++ b/nms-patches/DispenseBehaviorProjectile.patch @@ -0,0 +1,54 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/DispenseBehaviorProjectile.java 2014-11-27 08:59:46.645422093 +1100 ++++ src/main/java/net/minecraft/server/DispenseBehaviorProjectile.java 2014-11-27 08:42:10.152850911 +1100 +@@ -1,5 +1,10 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.block.BlockDispenseEvent; ++// CraftBukkit end ++ + public abstract class DispenseBehaviorProjectile extends DispenseBehaviorItem { + + public DispenseBehaviorProjectile() {} +@@ -10,9 +15,38 @@ + EnumDirection enumdirection = BlockDispenser.b(isourceblock.f()); + IProjectile iprojectile = this.a(world, iposition); + +- iprojectile.shoot((double) enumdirection.getAdjacentX(), (double) ((float) enumdirection.getAdjacentY() + 0.1F), (double) enumdirection.getAdjacentZ(), this.b(), this.a()); ++ // iprojectile.shoot((double) enumdirection.getAdjacentX(), (double) ((float) enumdirection.getAdjacentY() + 0.1F), (double) enumdirection.getAdjacentZ(), this.b(), this.a()); ++ // CraftBukkit start ++ ItemStack itemstack1 = itemstack.a(1); ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); ++ ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector((double) enumdirection.getAdjacentX(), (double) ((float) enumdirection.getAdjacentY() + 0.1F), (double) enumdirection.getAdjacentZ())); ++ if (!BlockDispenser.eventFired) { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) { ++ itemstack.count++; ++ return itemstack; ++ } ++ ++ if (!event.getItem().equals(craftItem)) { ++ itemstack.count++; ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.M.get(eventStack.getItem()); ++ if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { ++ idispensebehavior.a(isourceblock, eventStack); ++ return itemstack; ++ } ++ } ++ ++ iprojectile.shoot(event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), this.b(), this.a()); ++ ((Entity) iprojectile).projectileSource = new org.bukkit.craftbukkit.projectiles.CraftBlockProjectileSource((TileEntityDispenser) isourceblock.getTileEntity()); ++ // CraftBukkit end + world.addEntity((Entity) iprojectile); +- itemstack.a(1); ++ // itemstack.a(1); // CraftBukkit - Handled during event processing + return itemstack; + } + diff --git a/nms-patches/DispenseBehaviorTNT.patch b/nms-patches/DispenseBehaviorTNT.patch new file mode 100644 index 00000000..1dd1d42c --- /dev/null +++ b/nms-patches/DispenseBehaviorTNT.patch @@ -0,0 +1,56 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/DispenseBehaviorTNT.java 2014-11-27 08:59:46.645422093 +1100 ++++ src/main/java/net/minecraft/server/DispenseBehaviorTNT.java 2014-11-27 08:42:10.140850934 +1100 +@@ -1,5 +1,10 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.block.BlockDispenseEvent; ++// CraftBukkit end ++ + final class DispenseBehaviorTNT extends DispenseBehaviorItem { + + DispenseBehaviorTNT() {} +@@ -7,11 +12,40 @@ + protected ItemStack b(ISourceBlock isourceblock, ItemStack itemstack) { + World world = isourceblock.i(); + BlockPosition blockposition = isourceblock.getBlockPosition().shift(BlockDispenser.b(isourceblock.f())); +- EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(world, (double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, (EntityLiving) null); ++ // EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(world, (double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, (EntityLiving) null); ++ ++ // CraftBukkit start ++ ItemStack itemstack1 = itemstack.a(1); ++ org.bukkit.block.Block block = world.getWorld().getBlockAt(isourceblock.getBlockPosition().getX(), isourceblock.getBlockPosition().getY(), isourceblock.getBlockPosition().getZ()); ++ CraftItemStack craftItem = CraftItemStack.asCraftMirror(itemstack1); ++ ++ BlockDispenseEvent event = new BlockDispenseEvent(block, craftItem.clone(), new org.bukkit.util.Vector(blockposition.getX() + 0.5, blockposition.getY() + 0.5, blockposition.getZ() + 0.5)); ++ if (!BlockDispenser.eventFired) { ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) { ++ itemstack.count++; ++ return itemstack; ++ } ++ ++ if (!event.getItem().equals(craftItem)) { ++ itemstack.count++; ++ // Chain to handler for new item ++ ItemStack eventStack = CraftItemStack.asNMSCopy(event.getItem()); ++ IDispenseBehavior idispensebehavior = (IDispenseBehavior) BlockDispenser.M.get(eventStack.getItem()); ++ if (idispensebehavior != IDispenseBehavior.a && idispensebehavior != this) { ++ idispensebehavior.a(isourceblock, eventStack); ++ return itemstack; ++ } ++ } ++ ++ EntityTNTPrimed entitytntprimed = new EntityTNTPrimed(world, event.getVelocity().getX(), event.getVelocity().getY(), event.getVelocity().getZ(), (EntityLiving) null); ++ // CraftBukkit end + + world.addEntity(entitytntprimed); + world.makeSound(entitytntprimed, "game.tnt.primed", 1.0F, 1.0F); +- --itemstack.count; ++ // --itemstack.count; // CraftBukkit - handled above + return itemstack; + } + } diff --git a/nms-patches/Enchantment.patch b/nms-patches/Enchantment.patch new file mode 100644 index 00000000..8b09c515 --- /dev/null +++ b/nms-patches/Enchantment.patch @@ -0,0 +1,19 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/Enchantment.java 2014-11-27 08:59:46.649422075 +1100 ++++ src/main/java/net/minecraft/server/Enchantment.java 2014-11-27 08:42:10.096851020 +1100 +@@ -8,6 +8,7 @@ + + public abstract class Enchantment { + ++ // CraftBukkit - update CraftEnchant.getName(i) if this changes + private static final Enchantment[] byId = new Enchantment[256]; + public static final Enchantment[] b; + private static final Map E = Maps.newHashMap(); +@@ -55,6 +56,8 @@ + Enchantment.byId[i] = this; + Enchantment.E.put(minecraftkey, this); + } ++ ++ org.bukkit.enchantments.Enchantment.registerEnchantment(new org.bukkit.craftbukkit.enchantments.CraftEnchantment(this)); // CraftBukkit + } + + public static Enchantment getByName(String s) { diff --git a/nms-patches/Entity.patch b/nms-patches/Entity.patch new file mode 100644 index 00000000..95a7509b --- /dev/null +++ b/nms-patches/Entity.patch @@ -0,0 +1,578 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/Entity.java 2014-11-27 08:59:46.697421864 +1100 ++++ src/main/java/net/minecraft/server/Entity.java 2014-11-27 08:42:10.176850864 +1100 +@@ -6,8 +6,40 @@ + import java.util.UUID; + import java.util.concurrent.Callable; + ++// CraftBukkit start ++import org.bukkit.Bukkit; ++import org.bukkit.Location; ++import org.bukkit.Server; ++import org.bukkit.TravelAgent; ++import org.bukkit.block.BlockFace; ++import org.bukkit.entity.Hanging; ++import org.bukkit.entity.LivingEntity; ++import org.bukkit.entity.Painting; ++import org.bukkit.entity.Vehicle; ++import org.bukkit.event.entity.EntityCombustByEntityEvent; ++import org.bukkit.event.hanging.HangingBreakByEntityEvent; ++import org.bukkit.event.painting.PaintingBreakByEntityEvent; ++import org.bukkit.event.vehicle.VehicleBlockCollisionEvent; ++import org.bukkit.event.vehicle.VehicleEnterEvent; ++import org.bukkit.event.vehicle.VehicleExitEvent; ++import org.bukkit.craftbukkit.CraftWorld; ++import org.bukkit.craftbukkit.entity.CraftEntity; ++import org.bukkit.craftbukkit.entity.CraftPlayer; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.EntityCombustEvent; ++import org.bukkit.event.entity.EntityPortalEvent; ++import org.bukkit.plugin.PluginManager; ++// CraftBukkit end ++ + public abstract class Entity implements ICommandListener { + ++ // CraftBukkit start ++ private static final int CURRENT_LEVEL = 2; ++ static boolean isLevelAtLeast(NBTTagCompound tag, int level) { ++ return tag.hasKey("Bukkit.updateLevel") && tag.getInt("Bukkit.updateLevel") >= level; ++ } ++ // CraftBukikt end ++ + private static final AxisAlignedBB a = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D); + private static int entityCount; + private int id; +@@ -77,6 +109,8 @@ + private boolean invulnerable; + public UUID uniqueID; + private final CommandObjectiveExecutor as; ++ public boolean valid; // CraftBukkit ++ public org.bukkit.projectiles.ProjectileSource projectileSource; // CraftBukkit - For projectiles only + + public int getId() { + return this.id; +@@ -150,6 +184,33 @@ + } + + protected void setYawPitch(float f, float f1) { ++ // CraftBukkit start - yaw was sometimes set to NaN, so we need to set it back to 0 ++ if (Float.isNaN(f)) { ++ f = 0; ++ } ++ ++ if (f == Float.POSITIVE_INFINITY || f == Float.NEGATIVE_INFINITY) { ++ if (this instanceof EntityPlayer) { ++ this.world.getServer().getLogger().warning(this.getName() + " was caught trying to crash the server with an invalid yaw"); ++ ((CraftPlayer) this.getBukkitEntity()).kickPlayer("Nope"); ++ } ++ f = 0; ++ } ++ ++ // pitch was sometimes set to NaN, so we need to set it back to 0 ++ if (Float.isNaN(f1)) { ++ f1 = 0; ++ } ++ ++ if (f1 == Float.POSITIVE_INFINITY || f1 == Float.NEGATIVE_INFINITY) { ++ if (this instanceof EntityPlayer) { ++ this.world.getServer().getLogger().warning(this.getName() + " was caught trying to crash the server with an invalid pitch"); ++ ((CraftPlayer) this.getBukkitEntity()).kickPlayer("Nope"); ++ } ++ f1 = 0; ++ } ++ // CraftBukkit end ++ + this.yaw = f % 360.0F; + this.pitch = f1 % 360.0F; + } +@@ -186,7 +247,7 @@ + int i = this.L(); + + if (this.ak) { +- if (minecraftserver.getAllowNether()) { ++ if (true || minecraftserver.getAllowNether()) { // CraftBukkit + if (this.vehicle == null && this.al++ >= i) { + this.al = i; + this.portalCooldown = this.ar(); +@@ -263,6 +324,27 @@ + protected void burnFromLava() { + if (!this.fireProof) { + this.damageEntity(DamageSource.LAVA, 4.0F); ++ ++ // CraftBukkit start - Fallen in lava TODO: this event spams! ++ if (this instanceof EntityLiving) { ++ if (fireTicks <= 0) { ++ // not on fire yet ++ // TODO: shouldn't be sending null for the block ++ org.bukkit.block.Block damager = null; // ((WorldServer) this.l).getWorld().getBlockAt(i, j, k); ++ org.bukkit.entity.Entity damagee = this.getBukkitEntity(); ++ EntityCombustEvent combustEvent = new org.bukkit.event.entity.EntityCombustByBlockEvent(damager, damagee, 15); ++ this.world.getServer().getPluginManager().callEvent(combustEvent); ++ ++ if (!combustEvent.isCancelled()) { ++ this.setOnFire(combustEvent.getDuration()); ++ } ++ } else { ++ // This will be called every single tick the entity is in lava, so don't throw an event ++ this.setOnFire(15); ++ } ++ return; ++ } ++ // CraftBukkit end - we also don't throw an event unless the object in lava is living, to save on some event calls + this.setOnFire(15); + } + } +@@ -300,6 +382,22 @@ + this.a(this.getBoundingBox().c(d0, d1, d2)); + this.recalcPosition(); + } else { ++ // CraftBukkit start - Don't do anything if we aren't moving ++ // We need to do this regardless of whether or not we are moving thanks to portals ++ try { ++ this.checkBlockCollisions(); ++ } catch (Throwable throwable) { ++ CrashReport crashreport = CrashReport.a(throwable, "Checking entity block collision"); ++ CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being checked for collision"); ++ ++ this.appendEntityCrashDetails(crashreportsystemdetails); ++ throw new ReportedException(crashreport); ++ } ++ // Check if we're moving ++ if (d0 == 0 && d1 == 0 && d2 == 0 && this.vehicle == null && this.passenger == null) { ++ return; ++ } ++ // CraftBukkit end + this.world.methodProfiler.a("move"); + double d3 = this.locX; + double d4 = this.locY; +@@ -520,6 +618,28 @@ + block.a(this.world, this); + } + ++ // CraftBukkit start ++ if (positionChanged && getBukkitEntity() instanceof Vehicle) { ++ Vehicle vehicle = (Vehicle) this.getBukkitEntity(); ++ org.bukkit.block.Block bl = this.world.getWorld().getBlockAt(MathHelper.floor(this.locX), MathHelper.floor(this.locY - (double) this.getHeadHeight()), MathHelper.floor(this.locZ)); ++ ++ // PAIL: using local vars may break between updates, name them above? ++ ++ if (d6 > d0) { ++ bl = bl.getRelative(BlockFace.EAST); ++ } else if (d6 < d0) { ++ bl = bl.getRelative(BlockFace.WEST); ++ } else if (d8 > d2) { ++ bl = bl.getRelative(BlockFace.SOUTH); ++ } else if (d8 < d2) { ++ bl = bl.getRelative(BlockFace.NORTH); ++ } ++ ++ VehicleBlockCollisionEvent event = new VehicleBlockCollisionEvent(vehicle, bl); ++ world.getServer().getPluginManager().callEvent(event); ++ } ++ // CraftBukkit end ++ + if (this.r_() && !flag && this.vehicle == null) { + double d21 = this.locX - d3; + double d22 = this.locY - d4; +@@ -530,7 +650,7 @@ + } + + if (block != null && this.onGround) { +- block.a(this.world, blockposition, this); ++ // block.a(this.world, blockposition, this); // CraftBukkit removed down + } + + this.M = (float) ((double) this.M + (double) MathHelper.sqrt(d21 * d21 + d23 * d23) * 0.6D); +@@ -548,9 +668,12 @@ + } + + this.a(blockposition, block); ++ block.a(this.world, blockposition, this); // CraftBukkit - moved from above + } + } + ++ // CraftBukkit start - Move to the top of the method ++ /* + try { + this.checkBlockCollisions(); + } catch (Throwable throwable) { +@@ -560,6 +683,8 @@ + this.appendEntityCrashDetails(crashreportsystemdetails); + throw new ReportedException(crashreport); + } ++ */ ++ // CraftBukkit end + + boolean flag2 = this.U(); + +@@ -567,7 +692,16 @@ + this.burn(1); + if (!flag2) { + ++this.fireTicks; +- if (this.fireTicks == 0) { ++ // CraftBukkit start - Not on fire yet ++ if (this.fireTicks <= 0) { // Only throw events on the first combust, otherwise it spams ++ EntityCombustEvent event = new EntityCombustEvent(getBukkitEntity(), 8); ++ world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ setOnFire(event.getDuration()); ++ } ++ } else { ++ // CraftBukkit end + this.setOnFire(8); + } + } +@@ -673,7 +807,7 @@ + return null; + } + +- protected void burn(int i) { ++ protected void burn(float i) { // CraftBukkit - int -> float + if (!this.fireProof) { + this.damageEntity(DamageSource.FIRE, (float) i); + } +@@ -823,6 +957,13 @@ + } + + public void spawnIn(World world) { ++ // CraftBukkit start ++ if (world == null) { ++ die(); ++ this.world = ((CraftWorld) Bukkit.getServer().getWorlds().get(0)).getHandle(); ++ return; ++ } ++ // CraftBukkit end + this.world = world; + } + +@@ -1015,6 +1156,18 @@ + try { + nbttagcompound.set("Pos", this.a(new double[] { this.locX, this.locY, this.locZ})); + nbttagcompound.set("Motion", this.a(new double[] { this.motX, this.motY, this.motZ})); ++ ++ // CraftBukkit start - Checking for NaN pitch/yaw and resetting to zero ++ // TODO: make sure this is the best way to address this. ++ if (Float.isNaN(this.yaw)) { ++ this.yaw = 0; ++ } ++ ++ if (Float.isNaN(this.pitch)) { ++ this.pitch = 0; ++ } ++ // CraftBukkit end ++ + nbttagcompound.set("Rotation", this.a(new float[] { this.yaw, this.pitch})); + nbttagcompound.setFloat("FallDistance", this.fallDistance); + nbttagcompound.setShort("Fire", (short) this.fireTicks); +@@ -1025,6 +1178,11 @@ + nbttagcompound.setInt("PortalCooldown", this.portalCooldown); + nbttagcompound.setLong("UUIDMost", this.getUniqueID().getMostSignificantBits()); + nbttagcompound.setLong("UUIDLeast", this.getUniqueID().getLeastSignificantBits()); ++ // CraftBukkit start ++ nbttagcompound.setLong("WorldUUIDLeast", this.world.getDataManager().getUUID().getLeastSignificantBits()); ++ nbttagcompound.setLong("WorldUUIDMost", this.world.getDataManager().getUUID().getMostSignificantBits()); ++ nbttagcompound.setInt("Bukkit.updateLevel", CURRENT_LEVEL); ++ // CraftBukkit end + if (this.getCustomName() != null && this.getCustomName().length() > 0) { + nbttagcompound.setString("CustomName", this.getCustomName()); + nbttagcompound.setBoolean("CustomNameVisible", this.getCustomNameVisible()); +@@ -1062,6 +1220,7 @@ + this.motX = nbttaglist1.d(0); + this.motY = nbttaglist1.d(1); + this.motZ = nbttaglist1.d(2); ++ /* CraftBukkit start - Moved section down + if (Math.abs(this.motX) > 10.0D) { + this.motX = 0.0D; + } +@@ -1073,6 +1232,7 @@ + if (Math.abs(this.motZ) > 10.0D) { + this.motZ = 0.0D; + } ++ // CraftBukkit end */ + + this.lastX = this.P = this.locX = nbttaglist.d(0); + this.lastY = this.Q = this.locY = nbttaglist.d(1); +@@ -1105,7 +1265,57 @@ + if (this.af()) { + this.setPosition(this.locX, this.locY, this.locZ); + } ++ // CraftBukkit start ++ if (this instanceof EntityLiving) { ++ EntityLiving entity = (EntityLiving) this; ++ ++ // Reset the persistence for tamed animals ++ if (entity instanceof EntityTameableAnimal && !isLevelAtLeast(nbttagcompound, 2) && !nbttagcompound.getBoolean("PersistenceRequired")) { ++ EntityInsentient entityinsentient = (EntityInsentient) entity; ++ entityinsentient.persistent = !entityinsentient.isTypeNotPersistent(); ++ } ++ } ++ // CraftBukkit end ++ ++ // CraftBukkit start - Exempt Vehicles from notch's sanity check ++ if (!(getBukkitEntity() instanceof Vehicle)) { ++ if (Math.abs(this.motX) > 10.0D) { ++ this.motX = 0.0D; ++ } ++ ++ if (Math.abs(this.motY) > 10.0D) { ++ this.motY = 0.0D; ++ } ++ ++ if (Math.abs(this.motZ) > 10.0D) { ++ this.motZ = 0.0D; ++ } ++ } ++ // CraftBukkit end ++ ++ // CraftBukkit start - Reset world ++ if (this instanceof EntityPlayer) { ++ Server server = Bukkit.getServer(); ++ org.bukkit.World bworld = null; + ++ // TODO: Remove World related checks, replaced with WorldUID ++ String worldName = nbttagcompound.getString("world"); ++ ++ if (nbttagcompound.hasKey("WorldUUIDMost") && nbttagcompound.hasKey("WorldUUIDLeast")) { ++ UUID uid = new UUID(nbttagcompound.getLong("WorldUUIDMost"), nbttagcompound.getLong("WorldUUIDLeast")); ++ bworld = server.getWorld(uid); ++ } else { ++ bworld = server.getWorld(worldName); ++ } ++ ++ if (bworld == null) { ++ EntityPlayer entityPlayer = (EntityPlayer) this; ++ bworld = ((org.bukkit.craftbukkit.CraftServer) server).getServer().getWorldServer(entityPlayer.dimension).getWorld(); ++ } ++ ++ spawnIn(bworld == null? null : ((CraftWorld) bworld).getHandle()); ++ } ++ // CraftBukkit end + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Loading entity NBT"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being loaded"); +@@ -1167,6 +1377,12 @@ + + public EntityItem a(ItemStack itemstack, float f) { + if (itemstack.count != 0 && itemstack.getItem() != null) { ++ // CraftBukkit start - Capture drops for death event ++ if (this instanceof EntityLiving && ((EntityLiving) this).drops != null) { ++ ((EntityLiving) this).drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemstack)); ++ return null; ++ } ++ // CraftBukkit end + EntityItem entityitem = new EntityItem(this.world, this.locX, this.locY + (double) f, this.locZ, itemstack); + + entityitem.p(); +@@ -1276,16 +1492,76 @@ + } + + public void mount(Entity entity) { ++ // CraftBukkit start ++ setPassengerOf(entity); ++ } ++ ++ protected CraftEntity bukkitEntity; ++ ++ public CraftEntity getBukkitEntity() { ++ if (bukkitEntity == null) { ++ bukkitEntity = CraftEntity.getEntity(world.getServer(), this); ++ } ++ return bukkitEntity; ++ } ++ ++ public void setPassengerOf(Entity entity) { ++ // b(null) doesn't really fly for overloaded methods, ++ // so this method is needed ++ ++ Entity originalVehicle = this.vehicle; ++ Entity originalPassenger = this.vehicle == null ? null : this.vehicle.passenger; ++ PluginManager pluginManager = Bukkit.getPluginManager(); ++ getBukkitEntity(); // make sure bukkitEntity is initialised ++ // CraftBukkit end + this.ap = 0.0D; + this.aq = 0.0D; + if (entity == null) { + if (this.vehicle != null) { ++ // CraftBukkit start ++ if ((this.bukkitEntity instanceof LivingEntity) && (this.vehicle.getBukkitEntity() instanceof Vehicle)) { ++ VehicleExitEvent event = new VehicleExitEvent((Vehicle) this.vehicle.getBukkitEntity(), (LivingEntity) this.bukkitEntity); ++ pluginManager.callEvent(event); ++ ++ if (event.isCancelled() || vehicle != originalVehicle) { ++ return; ++ } ++ } ++ // CraftBukkit end + this.setPositionRotation(this.vehicle.locX, this.vehicle.getBoundingBox().b + (double) this.vehicle.length, this.vehicle.locZ, this.yaw, this.pitch); + this.vehicle.passenger = null; + } + + this.vehicle = null; + } else { ++ // CraftBukkit start ++ if ((this.bukkitEntity instanceof LivingEntity) && (entity.getBukkitEntity() instanceof Vehicle) && entity.world.isChunkLoaded((int) entity.locX >> 4, (int) entity.locZ >> 4, true)) { ++ // It's possible to move from one vehicle to another. We need to check if they're already in a vehicle, and fire an exit event if they are. ++ VehicleExitEvent exitEvent = null; ++ if (this.vehicle != null && this.vehicle.getBukkitEntity() instanceof Vehicle) { ++ exitEvent = new VehicleExitEvent((Vehicle) this.vehicle.getBukkitEntity(), (LivingEntity) this.bukkitEntity); ++ pluginManager.callEvent(exitEvent); ++ ++ if (exitEvent.isCancelled() || this.vehicle != originalVehicle || (this.vehicle != null && this.vehicle.passenger != originalPassenger)) { ++ return; ++ } ++ } ++ ++ VehicleEnterEvent event = new VehicleEnterEvent((Vehicle) entity.getBukkitEntity(), this.bukkitEntity); ++ pluginManager.callEvent(event); ++ ++ // If a plugin messes with the vehicle or the vehicle's passenger ++ if (event.isCancelled() || this.vehicle != originalVehicle || (this.vehicle != null && this.vehicle.passenger != originalPassenger)) { ++ // If we only cancelled the enterevent then we need to put the player in a decent position. ++ if (exitEvent != null && this.vehicle == originalVehicle && this.vehicle != null && this.vehicle.passenger == originalPassenger) { ++ this.setPositionRotation(this.vehicle.locX, this.vehicle.boundingBox.b + (double) this.vehicle.length, this.vehicle.locZ, this.yaw, this.pitch); ++ this.vehicle.passenger = null; ++ this.vehicle = null; ++ } ++ return; ++ } ++ } ++ // CraftBukkit end + if (this.vehicle != null) { + this.vehicle.passenger = null; + } +@@ -1406,10 +1682,50 @@ + } + + public void onLightningStrike(EntityLightning entitylightning) { +- this.damageEntity(DamageSource.LIGHTNING, 5.0F); ++ // CraftBukkit start ++ final org.bukkit.entity.Entity thisBukkitEntity = this.getBukkitEntity(); ++ final org.bukkit.entity.Entity stormBukkitEntity = entitylightning.getBukkitEntity(); ++ final PluginManager pluginManager = Bukkit.getPluginManager(); ++ ++ if (thisBukkitEntity instanceof Hanging) { ++ HangingBreakByEntityEvent hangingEvent = new HangingBreakByEntityEvent((Hanging) thisBukkitEntity, stormBukkitEntity); ++ PaintingBreakByEntityEvent paintingEvent = null; ++ ++ if (thisBukkitEntity instanceof Painting) { ++ paintingEvent = new PaintingBreakByEntityEvent((Painting) thisBukkitEntity, stormBukkitEntity); ++ } ++ ++ pluginManager.callEvent(hangingEvent); ++ ++ if (paintingEvent != null) { ++ paintingEvent.setCancelled(hangingEvent.isCancelled()); ++ pluginManager.callEvent(paintingEvent); ++ } ++ ++ if (hangingEvent.isCancelled() || (paintingEvent != null && paintingEvent.isCancelled())) { ++ return; ++ } ++ } ++ ++ if (this.fireProof) { ++ return; ++ } ++ CraftEventFactory.entityDamage = entitylightning; ++ if (!this.damageEntity(DamageSource.LIGHTNING, 5.0F)) { ++ CraftEventFactory.entityDamage = null; ++ return; ++ } ++ // CraftBukkit end + ++this.fireTicks; + if (this.fireTicks == 0) { + this.setOnFire(8); ++ // CraftBukkit start - Call a combust event when lightning strikes ++ EntityCombustByEntityEvent entityCombustEvent = new EntityCombustByEntityEvent(stormBukkitEntity, thisBukkitEntity, 8); ++ pluginManager.callEvent(entityCombustEvent); ++ if (!entityCombustEvent.isCancelled()) { ++ this.setOnFire(entityCombustEvent.getDuration()); ++ } ++ // CraftBukkit end + } + + } +@@ -1546,32 +1862,78 @@ + if (!this.world.isStatic && !this.dead) { + this.world.methodProfiler.a("changeDimension"); + MinecraftServer minecraftserver = MinecraftServer.getServer(); +- int j = this.dimension; +- WorldServer worldserver = minecraftserver.getWorldServer(j); +- WorldServer worldserver1 = minecraftserver.getWorldServer(i); ++ // CraftBukkit start - Move logic into new function "teleportToLocation" ++ // int j = this.dimension; ++ // WorldServer worldserver = minecraftserver.getWorldServer(j); ++ // WorldServer worldserver1 = minecraftserver.getWorldServer(i); ++ WorldServer exitWorld = null; ++ if (this.dimension < CraftWorld.CUSTOM_DIMENSION_OFFSET) { // Plugins must specify exit from custom Bukkit worlds ++ // Only target existing worlds (compensate for allow-nether/allow-end as false) ++ for (WorldServer world : minecraftserver.worlds) { ++ if (world.dimension == i) { ++ exitWorld = world; ++ } ++ } ++ } ++ ++ Location enter = this.getBukkitEntity().getLocation(); ++ Location exit = exitWorld != null ? minecraftserver.getPlayerList().calculateTarget(enter, minecraftserver.getWorldServer(i)) : null; ++ boolean useTravelAgent = exitWorld != null && !(this.dimension == 1 && exitWorld.dimension == 1); // don't use agent for custom worlds or return from THE_END ++ ++ TravelAgent agent = exit != null ? (TravelAgent) ((CraftWorld) exit.getWorld()).getHandle().getTravelAgent() : org.bukkit.craftbukkit.CraftTravelAgent.DEFAULT; // return arbitrary TA to compensate for implementation dependent plugins ++ EntityPortalEvent event = new EntityPortalEvent(this.getBukkitEntity(), enter, exit, agent); ++ event.useTravelAgent(useTravelAgent); ++ event.getEntity().getServer().getPluginManager().callEvent(event); ++ if (event.isCancelled() || event.getTo() == null || event.getTo().getWorld() == null || !this.isAlive()) { ++ return; ++ } ++ exit = event.useTravelAgent() ? event.getPortalTravelAgent().findOrCreate(event.getTo()) : event.getTo(); ++ this.teleportTo(exit, true); ++ } ++ } ++ ++ public void teleportTo(Location exit, boolean portal) { ++ if (true) { ++ WorldServer worldserver = ((CraftWorld) getBukkitEntity().getLocation().getWorld()).getHandle(); ++ WorldServer worldserver1 = ((CraftWorld) exit.getWorld()).getHandle(); ++ int i = worldserver1.dimension; ++ // CraftBukkit end + + this.dimension = i; ++ /* CraftBukkit start - TODO: Check if we need this + if (j == 1 && i == 1) { + worldserver1 = minecraftserver.getWorldServer(0); + this.dimension = 0; + } ++ // CraftBukkit end */ + + this.world.kill(this); + this.dead = false; + this.world.methodProfiler.a("reposition"); +- minecraftserver.getPlayerList().changeWorld(this, j, worldserver, worldserver1); ++ // CraftBukkit start - Ensure chunks are loaded in case TravelAgent is not used which would initially cause chunks to load during find/create ++ // minecraftserver.getPlayerList().changeWorld(this, j, worldserver, worldserver1); ++ boolean before = worldserver1.chunkProviderServer.forceChunkLoad; ++ worldserver1.chunkProviderServer.forceChunkLoad = true; ++ worldserver1.getMinecraftServer().getPlayerList().repositionEntity(this, exit, portal); ++ worldserver1.chunkProviderServer.forceChunkLoad = before; ++ // CraftBukkit end + this.world.methodProfiler.c("reloading"); + Entity entity = EntityTypes.createEntityByName(EntityTypes.b(this), worldserver1); + + if (entity != null) { + entity.n(this); ++ /* CraftBukkit start - We need to do this... + if (j == 1 && i == 1) { + BlockPosition blockposition = this.world.r(worldserver1.getSpawn()); + + entity.setPositionRotation(blockposition, entity.yaw, entity.pitch); + } +- ++ // CraftBukkit end */ + worldserver1.addEntity(entity); ++ // CraftBukkit start - Forward the CraftEntity to the new entity ++ this.getBukkitEntity().setHandle(entity); ++ entity.bukkitEntity = this.getBukkitEntity(); ++ // CraftBukkit end + } + + this.dead = true; diff --git a/nms-patches/EntityAgeable.patch b/nms-patches/EntityAgeable.patch new file mode 100644 index 00000000..f1fe109b --- /dev/null +++ b/nms-patches/EntityAgeable.patch @@ -0,0 +1,48 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityAgeable.java 2014-11-27 08:59:46.649422075 +1100 ++++ src/main/java/net/minecraft/server/EntityAgeable.java 2014-11-27 08:42:10.144850927 +1100 +@@ -7,6 +7,7 @@ + protected int c; + private float bk = -1.0F; + private float bl; ++ public boolean ageLocked = false; // CraftBukkit + + public EntityAgeable(World world) { + super(world); +@@ -27,14 +28,14 @@ + if (entityageable != null) { + entityageable.setAgeRaw(-24000); + entityageable.setPositionRotation(this.locX, this.locY, this.locZ, 0.0F, 0.0F); +- this.world.addEntity(entityageable); ++ this.world.addEntity(entityageable, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER_EGG); // CraftBukkit + if (itemstack.hasName()) { + entityageable.setCustomName(itemstack.getName()); + } + + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; +- if (itemstack.count <= 0) { ++ if (itemstack.count == 0) { // CraftBukkit - allow less than 0 stacks as "infinite" + entityhuman.inventory.setItem(entityhuman.inventory.itemInHandIndex, (ItemStack) null); + } + } +@@ -99,17 +100,19 @@ + super.b(nbttagcompound); + nbttagcompound.setInt("Age", this.getAge()); + nbttagcompound.setInt("ForcedAge", this.b); ++ nbttagcompound.setBoolean("AgeLocked", this.ageLocked); // CraftBukkit + } + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + this.setAgeRaw(nbttagcompound.getInt("Age")); + this.b = nbttagcompound.getInt("ForcedAge"); ++ this.ageLocked = nbttagcompound.getBoolean("AgeLocked"); // CraftBukkit + } + + public void m() { + super.m(); +- if (this.world.isStatic) { ++ if (this.world.isStatic || ageLocked) { // CraftBukkit + if (this.c > 0) { + if (this.c % 4 == 0) { + this.world.addParticle(EnumParticle.VILLAGER_HAPPY, this.locX + (double) (this.random.nextFloat() * this.width * 2.0F) - (double) this.width, this.locY + 0.5D + (double) (this.random.nextFloat() * this.length), this.locZ + (double) (this.random.nextFloat() * this.width * 2.0F) - (double) this.width, 0.0D, 0.0D, 0.0D, new int[0]); diff --git a/nms-patches/EntityArrow.patch b/nms-patches/EntityArrow.patch new file mode 100644 index 00000000..2912f97b --- /dev/null +++ b/nms-patches/EntityArrow.patch @@ -0,0 +1,106 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityArrow.java 2014-11-27 08:59:46.653422058 +1100 ++++ src/main/java/net/minecraft/server/EntityArrow.java 2014-11-27 08:42:10.100851012 +1100 +@@ -2,6 +2,12 @@ + + import java.util.List; + ++// CraftBukkit start ++import org.bukkit.entity.LivingEntity; ++import org.bukkit.event.entity.EntityCombustByEntityEvent; ++import org.bukkit.event.player.PlayerPickupItemEvent; ++// CraftBukkit end ++ + public class EntityArrow extends Entity implements IProjectile { + + private int d = -1; +@@ -35,6 +41,7 @@ + super(world); + this.j = 10.0D; + this.shooter = entityliving; ++ this.projectileSource = (LivingEntity) entityliving.getBukkitEntity(); // CraftBukkit + if (entityliving instanceof EntityHuman) { + this.fromPlayer = 1; + } +@@ -62,6 +69,7 @@ + super(world); + this.j = 10.0D; + this.shooter = entityliving; ++ this.projectileSource = (LivingEntity) entityliving.getBukkitEntity(); // CraftBukkit + if (entityliving instanceof EntityHuman) { + this.fromPlayer = 1; + } +@@ -175,7 +183,7 @@ + MovingObjectPosition movingobjectposition1 = axisalignedbb1.a(vec3d, vec3d1); + + if (movingobjectposition1 != null) { +- double d1 = vec3d.f(movingobjectposition1.pos); ++ double d1 = vec3d.distanceSquared(movingobjectposition1.pos); // CraftBukkit + + if (d1 < d0 || d0 == 0.0D) { + entity = entity1; +@@ -202,6 +210,8 @@ + float f4; + + if (movingobjectposition != null) { ++ org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this); // CraftBukkit - Call event ++ + if (movingobjectposition.entity != null) { + f2 = MathHelper.sqrt(this.motX * this.motX + this.motY * this.motY + this.motZ * this.motZ); + int k = MathHelper.f((double) f2 * this.damage); +@@ -217,12 +227,20 @@ + } else { + damagesource = DamageSource.arrow(this, this.shooter); + } ++ ++ // CraftBukkit start - Moved damage call ++ if (movingobjectposition.entity.damageEntity(damagesource, (float) k)) { ++ if (this.isBurning() && !(movingobjectposition.entity instanceof EntityEnderman) && (!(movingobjectposition.entity instanceof EntityPlayer) || !(this.shooter instanceof EntityPlayer) || this.world.pvpMode)) { // CraftBukkit - abide by pvp setting if destination is a player ++ EntityCombustByEntityEvent combustEvent = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), 5); ++ org.bukkit.Bukkit.getPluginManager().callEvent(combustEvent); + +- if (this.isBurning() && !(movingobjectposition.entity instanceof EntityEnderman)) { +- movingobjectposition.entity.setOnFire(5); ++ if (!combustEvent.isCancelled()) { ++ movingobjectposition.entity.setOnFire(combustEvent.getDuration()); ++ } ++ // CraftBukkit end + } + +- if (movingobjectposition.entity.damageEntity(damagesource, (float) k)) { ++ // if (movingobjectposition.entity.damageEntity(damagesource, (float) k)) { // CraftBukkit - moved up + if (movingobjectposition.entity instanceof EntityLiving) { + EntityLiving entityliving = (EntityLiving) movingobjectposition.entity; + +@@ -382,6 +400,21 @@ + + public void d(EntityHuman entityhuman) { + if (!this.world.isStatic && this.inGround && this.shake <= 0) { ++ // CraftBukkit start ++ ItemStack itemstack = new ItemStack(Items.ARROW); ++ if (this.fromPlayer == 1 && entityhuman.inventory.canHold(itemstack) > 0) { ++ EntityItem item = new EntityItem(this.world, this.locX, this.locY, this.locZ, itemstack); ++ ++ PlayerPickupItemEvent event = new PlayerPickupItemEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), new org.bukkit.craftbukkit.entity.CraftItem(this.world.getServer(), this, item), 0); ++ // event.setCancelled(!entityhuman.canPickUpLoot); TODO ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ return; ++ } ++ } ++ // CraftBukkit end ++ + boolean flag = this.fromPlayer == 1 || this.fromPlayer == 2 && entityhuman.abilities.canInstantlyBuild; + + if (this.fromPlayer == 1 && !entityhuman.inventory.pickup(new ItemStack(Items.ARROW, 1))) { +@@ -433,4 +466,10 @@ + + return (b0 & 1) != 0; + } ++ ++ // CraftBukkit start ++ public boolean isInGround() { ++ return inGround; ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/EntityBoat.patch b/nms-patches/EntityBoat.patch new file mode 100644 index 00000000..6ab202bf --- /dev/null +++ b/nms-patches/EntityBoat.patch @@ -0,0 +1,249 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityBoat.java 2014-11-27 08:59:46.653422058 +1100 ++++ src/main/java/net/minecraft/server/EntityBoat.java 2014-11-27 08:42:10.172850872 +1100 +@@ -2,6 +2,16 @@ + + import java.util.List; + ++// CraftBukkit start ++import org.bukkit.Location; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.entity.Vehicle; ++import org.bukkit.event.vehicle.VehicleDamageEvent; ++import org.bukkit.event.vehicle.VehicleDestroyEvent; ++import org.bukkit.event.vehicle.VehicleEntityCollisionEvent; ++import org.bukkit.event.vehicle.VehicleMoveEvent; ++// CraftBukkit end ++ + public class EntityBoat extends Entity { + + private boolean a; +@@ -12,6 +22,27 @@ + private double f; + private double g; + private double h; ++ ++ // CraftBukkit start ++ public double maxSpeed = 0.4D; ++ public double occupiedDeceleration = 0.2D; ++ public double unoccupiedDeceleration = -1; ++ public boolean landBoats = false; ++ ++ @Override ++ public void collide(Entity entity) { ++ org.bukkit.entity.Entity hitEntity = (entity == null) ? null : entity.getBukkitEntity(); ++ ++ VehicleEntityCollisionEvent event = new VehicleEntityCollisionEvent((Vehicle) this.getBukkitEntity(), hitEntity); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ return; ++ } ++ ++ super.collide(entity); ++ } ++ // CraftBukkit end + + public EntityBoat(World world) { + super(world); +@@ -52,6 +83,8 @@ + this.lastX = d0; + this.lastY = d1; + this.lastZ = d2; ++ ++ this.world.getServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleCreateEvent((Vehicle) this.getBukkitEntity())); // CraftBukkit + } + + public double an() { +@@ -65,6 +98,19 @@ + if (this.passenger != null && this.passenger == damagesource.getEntity() && damagesource instanceof EntityDamageSourceIndirect) { + return false; + } else { ++ // CraftBukkit start ++ Vehicle vehicle = (Vehicle) this.getBukkitEntity(); ++ org.bukkit.entity.Entity attacker = (damagesource.getEntity() == null) ? null : damagesource.getEntity().getBukkitEntity(); ++ ++ VehicleDamageEvent event = new VehicleDamageEvent(vehicle, attacker, (double) f); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ return true; ++ } ++ // f = event.getDamage(); // TODO Why don't we do this? ++ // CraftBukkit end ++ + this.b(-this.m()); + this.a(10); + this.setDamage(this.j() + f * 10.0F); +@@ -72,6 +118,15 @@ + boolean flag = damagesource.getEntity() instanceof EntityHuman && ((EntityHuman) damagesource.getEntity()).abilities.canInstantlyBuild; + + if (flag || this.j() > 40.0F) { ++ // CraftBukkit start ++ VehicleDestroyEvent destroyEvent = new VehicleDestroyEvent(vehicle, attacker); ++ this.world.getServer().getPluginManager().callEvent(destroyEvent); ++ ++ if (destroyEvent.isCancelled()) { ++ this.setDamage(40F); // Maximize damage so this doesn't get triggered again right away ++ return true; ++ } ++ // CraftBukkit end + if (this.passenger != null) { + this.passenger.mount(this); + } +@@ -95,6 +150,13 @@ + } + + public void s_() { ++ // CraftBukkit start ++ double prevX = this.locX; ++ double prevY = this.locY; ++ double prevZ = this.locZ; ++ float prevYaw = this.yaw; ++ float prevPitch = this.pitch; ++ // CraftBukkit end + super.s_(); + if (this.l() > 0) { + this.a(this.l() - 1); +@@ -196,6 +258,19 @@ + this.motX += -Math.sin((double) (f * 3.1415927F / 180.0F)) * this.b * (double) entityliving.aY * 0.05000000074505806D; + this.motZ += Math.cos((double) (f * 3.1415927F / 180.0F)) * this.b * (double) entityliving.aY * 0.05000000074505806D; + } ++ // CraftBukkit start - Support unoccupied deceleration ++ else if (unoccupiedDeceleration >= 0) { ++ this.motX *= unoccupiedDeceleration; ++ this.motZ *= unoccupiedDeceleration; ++ // Kill lingering speed ++ if (motX <= 0.00001) { ++ motX = 0; ++ } ++ if (motZ <= 0.00001) { ++ motZ = 0; ++ } ++ } ++ // CraftBukkit end + + d4 = Math.sqrt(this.motX * this.motX + this.motZ * this.motZ); + if (d4 > 0.35D) { +@@ -230,16 +305,26 @@ + Block block = this.world.getType(blockposition).getBlock(); + + if (block == Blocks.SNOW_LAYER) { ++ // CraftBukkit start ++ if (CraftEventFactory.callEntityChangeBlockEvent(this, l, j1, j, Blocks.AIR, 0).isCancelled()) { ++ continue; ++ } ++ // CraftBukkit end + this.world.setAir(blockposition); + this.positionChanged = false; + } else if (block == Blocks.WATERLILY) { ++ // CraftBukkit start ++ if (CraftEventFactory.callEntityChangeBlockEvent(this, l, j1, j, Blocks.AIR, 0).isCancelled()) { ++ continue; ++ } ++ // CraftBukkit end + this.world.setAir(blockposition, true); + this.positionChanged = false; + } + } + } + +- if (this.onGround) { ++ if (this.onGround && !this.landBoats) { // CraftBukkit + this.motX *= 0.5D; + this.motY *= 0.5D; + this.motZ *= 0.5D; +@@ -247,16 +332,23 @@ + + this.move(this.motX, this.motY, this.motZ); + if (this.positionChanged && d3 > 0.2D) { +- if (!this.world.isStatic && !this.dead) { +- this.die(); ++ if (!this.world.isStatic && !this.dead) { ++ // CraftBukkit start ++ Vehicle vehicle = (Vehicle) this.getBukkitEntity(); ++ VehicleDestroyEvent destroyEvent = new VehicleDestroyEvent(vehicle, null); ++ this.world.getServer().getPluginManager().callEvent(destroyEvent); ++ if (!destroyEvent.isCancelled()) { ++ this.die(); + +- for (k = 0; k < 3; ++k) { +- this.a(Item.getItemOf(Blocks.PLANKS), 1, 0.0F); +- } ++ for (k = 0; k < 3; ++k) { ++ this.a(Item.getItemOf(Blocks.PLANKS), 1, 0.0F); ++ } + +- for (k = 0; k < 2; ++k) { +- this.a(Items.STICK, 1, 0.0F); ++ for (k = 0; k < 2; ++k) { ++ this.a(Items.STICK, 1, 0.0F); ++ } + } ++ // CraftBukkit end + } + } else { + this.motX *= 0.9900000095367432D; +@@ -284,6 +376,23 @@ + + this.yaw = (float) ((double) this.yaw + d12); + this.setYawPitch(this.yaw, this.pitch); ++ ++ // CraftBukkit start ++ org.bukkit.Server server = this.world.getServer(); ++ org.bukkit.World bworld = this.world.getWorld(); ++ ++ Location from = new Location(bworld, prevX, prevY, prevZ, prevYaw, prevPitch); ++ Location to = new Location(bworld, this.locX, this.locY, this.locZ, this.yaw, this.pitch); ++ Vehicle vehicle = (Vehicle) this.getBukkitEntity(); ++ ++ server.getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleUpdateEvent(vehicle)); ++ ++ if (!from.equals(to)) { ++ VehicleMoveEvent event = new VehicleMoveEvent(vehicle, from, to); ++ server.getPluginManager().callEvent(event); ++ } ++ // CraftBukkit end ++ + if (!this.world.isStatic) { + List list = this.world.getEntities(this, this.getBoundingBox().grow(0.20000000298023224D, 0.0D, 0.20000000298023224D)); + +@@ -298,6 +407,7 @@ + } + + if (this.passenger != null && this.passenger.dead) { ++ this.passenger.vehicle = null; // CraftBukkit + this.passenger = null; + } + +@@ -335,17 +445,24 @@ + if (this.fallDistance > 3.0F) { + this.e(this.fallDistance, 1.0F); + if (!this.world.isStatic && !this.dead) { +- this.die(); ++ // CraftBukkit start ++ Vehicle vehicle = (Vehicle) this.getBukkitEntity(); ++ VehicleDestroyEvent destroyEvent = new VehicleDestroyEvent(vehicle, null); ++ this.world.getServer().getPluginManager().callEvent(destroyEvent); ++ if (!destroyEvent.isCancelled()) { ++ this.die(); + +- int i; ++ int i; + +- for (i = 0; i < 3; ++i) { +- this.a(Item.getItemOf(Blocks.PLANKS), 1, 0.0F); +- } ++ for (i = 0; i < 3; ++i) { ++ this.a(Item.getItemOf(Blocks.PLANKS), 1, 0.0F); ++ } + +- for (i = 0; i < 2; ++i) { +- this.a(Items.STICK, 1, 0.0F); ++ for (i = 0; i < 2; ++i) { ++ this.a(Items.STICK, 1, 0.0F); ++ } + } ++ // CraftBukkit end + } + + this.fallDistance = 0.0F; diff --git a/nms-patches/EntityChicken.patch b/nms-patches/EntityChicken.patch new file mode 100644 index 00000000..07602829 --- /dev/null +++ b/nms-patches/EntityChicken.patch @@ -0,0 +1,14 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityChicken.java 2014-11-27 08:59:46.657422040 +1100 ++++ src/main/java/net/minecraft/server/EntityChicken.java 2014-11-27 08:42:10.104851005 +1100 +@@ -35,6 +35,11 @@ + } + + public void m() { ++ // CraftBukkit start ++ if (this.isChickenJockey()) { ++ this.persistent = !this.isTypeNotPersistent(); ++ } ++ // CraftBukkit end + super.m(); + this.bo = this.bk; + this.bn = this.bm; diff --git a/nms-patches/EntityCow.patch b/nms-patches/EntityCow.patch new file mode 100644 index 00000000..c8aafbcc --- /dev/null +++ b/nms-patches/EntityCow.patch @@ -0,0 +1,42 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityCow.java 2014-11-27 08:59:46.657422040 +1100 ++++ src/main/java/net/minecraft/server/EntityCow.java 2014-11-27 08:42:10.168850880 +1100 +@@ -1,5 +1,10 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++// CraftBukkit end ++ + public class EntityCow extends EntityAnimal { + + public EntityCow(World world) { +@@ -69,13 +74,23 @@ + + public boolean a(EntityHuman entityhuman) { + ItemStack itemstack = entityhuman.inventory.getItemInHand(); +- ++ + if (itemstack != null && itemstack.getItem() == Items.BUCKET && !entityhuman.abilities.canInstantlyBuild) { +- if (itemstack.count-- == 1) { +- entityhuman.inventory.setItem(entityhuman.inventory.itemInHandIndex, new ItemStack(Items.MILK_BUCKET)); +- } else if (!entityhuman.inventory.pickup(new ItemStack(Items.MILK_BUCKET))) { +- entityhuman.drop(new ItemStack(Items.MILK_BUCKET, 1, 0), false); ++ // CraftBukkit start - Got milk? ++ org.bukkit.Location loc = this.getBukkitEntity().getLocation(); ++ org.bukkit.event.player.PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent(entityhuman, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), null, itemstack, Items.MILK_BUCKET); ++ ++ if (event.isCancelled()) { ++ return false; ++ } ++ ++ ItemStack result = CraftItemStack.asNMSCopy(event.getItemStack()); ++ if (--itemstack.count <= 0) { ++ entityhuman.inventory.setItem(entityhuman.inventory.itemInHandIndex, result); ++ } else if (!entityhuman.inventory.pickup(result)) { ++ entityhuman.drop(result, false); + } ++ // CraftBukkit end + + return true; + } else { diff --git a/nms-patches/EntityCreature.patch b/nms-patches/EntityCreature.patch new file mode 100644 index 00000000..f9d70d5e --- /dev/null +++ b/nms-patches/EntityCreature.patch @@ -0,0 +1,31 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityCreature.java 2014-11-27 08:59:46.657422040 +1100 ++++ src/main/java/net/minecraft/server/EntityCreature.java 2014-11-27 08:42:10.172850872 +1100 +@@ -2,6 +2,12 @@ + + import java.util.UUID; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.entity.CraftEntity; ++import org.bukkit.event.entity.EntityTargetEvent; ++import org.bukkit.event.entity.EntityUnleashEvent; ++// CraftBukkit end ++ + public abstract class EntityCreature extends EntityInsentient { + + public static final UUID bi = UUID.fromString("E199AD21-BA8A-4C53-8D13-6182D5C69D3A"); +@@ -69,6 +75,7 @@ + + if (this instanceof EntityTameableAnimal && ((EntityTameableAnimal) this).isSitting()) { + if (f > 10.0F) { ++ this.world.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), EntityUnleashEvent.UnleashReason.DISTANCE)); // CraftBukkit + this.unleash(true, true); + } + +@@ -100,6 +107,7 @@ + } + + if (f > 10.0F) { ++ this.world.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), EntityUnleashEvent.UnleashReason.DISTANCE)); // CraftBukkit + this.unleash(true, true); + } + } else if (!this.cb() && this.bk) { diff --git a/nms-patches/EntityCreeper.patch b/nms-patches/EntityCreeper.patch new file mode 100644 index 00000000..47e72258 --- /dev/null +++ b/nms-patches/EntityCreeper.patch @@ -0,0 +1,104 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityCreeper.java 2014-11-27 08:59:46.661422023 +1100 ++++ src/main/java/net/minecraft/server/EntityCreeper.java 2014-11-27 08:42:10.168850880 +1100 +@@ -1,5 +1,10 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.ExplosionPrimeEvent; ++// CraftBukkit end ++ + public class EntityCreeper extends EntityMonster { + + private int b; +@@ -7,6 +12,7 @@ + private int maxFuseTicks = 30; + private int explosionRadius = 3; + private int bm = 0; ++ private int record = -1; // CraftBukkit + + public EntityCreeper(World world) { + super(world); +@@ -111,19 +117,36 @@ + } + + public void die(DamageSource damagesource) { +- super.die(damagesource); ++ // super.die(damagesource); // CraftBukkit - Moved to end + if (damagesource.getEntity() instanceof EntitySkeleton) { + int i = Item.getId(Items.RECORD_13); + int j = Item.getId(Items.RECORD_WAIT); + int k = i + this.random.nextInt(j - i + 1); + +- this.a(Item.getById(k), 1); ++ // CraftBukkit start - Store record for now, drop in dropDeathLoot ++ // this.a(Item.getById(k), 1); ++ this.record = k; ++ // CraftBukkit end + } else if (damagesource.getEntity() instanceof EntityCreeper && damagesource.getEntity() != this && ((EntityCreeper) damagesource.getEntity()).isPowered() && ((EntityCreeper) damagesource.getEntity()).cn()) { + ((EntityCreeper) damagesource.getEntity()).co(); + this.a(new ItemStack(Items.SKULL, 1, 4), 0.0F); + } +- ++ ++ super.die(damagesource); // CraftBukkit - Moved from above ++ } ++ ++ // CraftBukkit start - Whole method ++ @Override ++ protected void dropDeathLoot(boolean flag, int i) { ++ super.dropDeathLoot(flag, i); ++ ++ // Drop a music disc? ++ if (this.record != -1) { ++ this.a(Item.getById(this.record), 1); ++ this.record = -1; ++ } + } ++ // CraftBukkit end + + public boolean r(Entity entity) { + return true; +@@ -147,7 +170,21 @@ + + public void onLightningStrike(EntityLightning entitylightning) { + super.onLightningStrike(entitylightning); +- this.datawatcher.watch(17, Byte.valueOf((byte) 1)); ++ // CraftBukkit start ++ if (CraftEventFactory.callCreeperPowerEvent(this, entitylightning, org.bukkit.event.entity.CreeperPowerEvent.PowerCause.LIGHTNING).isCancelled()) { ++ return; ++ } ++ ++ this.setPowered(true); ++ } ++ ++ public void setPowered(boolean powered) { ++ if (!powered) { ++ this.datawatcher.watch(17, Byte.valueOf((byte) 0)); ++ } else { ++ this.datawatcher.watch(17, Byte.valueOf((byte) 1)); ++ } ++ // CraftBukkit end + } + + protected boolean a(EntityHuman entityhuman) { +@@ -170,9 +207,16 @@ + if (!this.world.isStatic) { + boolean flag = this.world.getGameRules().getBoolean("mobGriefing"); + float f = this.isPowered() ? 2.0F : 1.0F; +- +- this.world.explode(this, this.locX, this.locY, this.locZ, (float) this.explosionRadius * f, flag); +- this.die(); ++ ++ ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), this.explosionRadius * f, false); ++ this.world.getServer().getPluginManager().callEvent(event); ++ if (!event.isCancelled()) { ++ this.world.createExplosion(this, this.locX, this.locY, this.locZ, event.getRadius(), event.getFire(), flag); ++ this.die(); ++ } else { ++ fuseTicks = 0; ++ } ++ // CraftBukkit end + } + + } diff --git a/nms-patches/EntityDamageSourceIndirect.patch b/nms-patches/EntityDamageSourceIndirect.patch new file mode 100644 index 00000000..efcfd591 --- /dev/null +++ b/nms-patches/EntityDamageSourceIndirect.patch @@ -0,0 +1,14 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityDamageSourceIndirect.java 2014-11-27 08:59:46.661422023 +1100 ++++ src/main/java/net/minecraft/server/EntityDamageSourceIndirect.java 2014-11-27 08:42:10.164850887 +1100 +@@ -24,5 +24,11 @@ + String s1 = s + ".item"; + + return itemstack != null && itemstack.hasName() && LocaleI18n.c(s1) ? new ChatMessage(s1, new Object[] { entityliving.getScoreboardDisplayName(), ichatbasecomponent, itemstack.C()}) : new ChatMessage(s, new Object[] { entityliving.getScoreboardDisplayName(), ichatbasecomponent}); ++ } ++ ++ // CraftBukkit start ++ public Entity getProximateDamageSource() { ++ return super.getEntity(); + } ++ // CraftBukkit end + } diff --git a/nms-patches/EntityEgg.patch b/nms-patches/EntityEgg.patch new file mode 100644 index 00000000..9b51e766 --- /dev/null +++ b/nms-patches/EntityEgg.patch @@ -0,0 +1,65 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityEgg.java 2014-11-27 08:59:46.665422005 +1100 ++++ src/main/java/net/minecraft/server/EntityEgg.java 2014-11-27 08:42:10.112850989 +1100 +@@ -1,5 +1,12 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.entity.Ageable; ++import org.bukkit.entity.EntityType; ++import org.bukkit.entity.Player; ++import org.bukkit.event.player.PlayerEggThrowEvent; ++// CraftBukkit end ++ + public class EntityEgg extends EntityProjectile { + + public EntityEgg(World world) { +@@ -19,21 +26,36 @@ + movingobjectposition.entity.damageEntity(DamageSource.projectile(this, this.getShooter()), 0.0F); + } + +- if (!this.world.isStatic && this.random.nextInt(8) == 0) { +- byte b0 = 1; ++ // CraftBukkit start - Fire PlayerEggThrowEvent ++ boolean hatching = !this.world.isStatic && this.random.nextInt(8) == 0; ++ int numHatching = (this.random.nextInt(32) == 0) ? 4 : 1; ++ if (!hatching) { ++ numHatching = 0; ++ } ++ ++ EntityType hatchingType = EntityType.CHICKEN; + +- if (this.random.nextInt(32) == 0) { +- b0 = 4; +- } +- +- for (int i = 0; i < b0; ++i) { +- EntityChicken entitychicken = new EntityChicken(this.world); +- +- entitychicken.setAgeRaw(-24000); +- entitychicken.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, 0.0F); +- this.world.addEntity(entitychicken); +- } ++ Entity shooter = this.getShooter(); ++ if (shooter instanceof EntityPlayer) { ++ Player player = (shooter == null) ? null : (Player) shooter.getBukkitEntity(); ++ ++ PlayerEggThrowEvent event = new PlayerEggThrowEvent(player, (org.bukkit.entity.Egg) this.getBukkitEntity(), hatching, (byte) numHatching, hatchingType); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ hatching = event.isHatching(); ++ numHatching = event.getNumHatches(); ++ hatchingType = event.getHatchingType(); ++ } ++ ++ if (hatching) { ++ for (int k = 0; k < numHatching; k++) { ++ org.bukkit.entity.Entity entity = world.getWorld().spawn(new org.bukkit.Location(world.getWorld(), this.locX, this.locY, this.locZ, this.yaw, 0.0F), hatchingType.getEntityClass(), org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.EGG); ++ if (entity instanceof Ageable) { ++ ((Ageable) entity).setBaby(); ++ } ++ } + } ++ // CraftBukkit end + + double d0 = 0.08D; + diff --git a/nms-patches/EntityEnderCrystal.patch b/nms-patches/EntityEnderCrystal.patch new file mode 100644 index 00000000..0bf41baa --- /dev/null +++ b/nms-patches/EntityEnderCrystal.patch @@ -0,0 +1,43 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityEnderCrystal.java 2014-11-27 08:59:46.665422005 +1100 ++++ src/main/java/net/minecraft/server/EntityEnderCrystal.java 2014-11-27 08:42:10.172850872 +1100 +@@ -1,5 +1,10 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.ExplosionPrimeEvent; ++// CraftBukkit end ++ + public class EntityEnderCrystal extends Entity { + + public int a; +@@ -32,7 +37,11 @@ + int k = MathHelper.floor(this.locZ); + + if (this.world.worldProvider instanceof WorldProviderTheEnd && this.world.getType(new BlockPosition(i, j, k)).getBlock() != Blocks.FIRE) { +- this.world.setTypeUpdate(new BlockPosition(i, j, k), Blocks.FIRE.getBlockData()); ++ // CraftBukkit start ++ if (!CraftEventFactory.callBlockIgniteEvent(this.world, i, j, k, this).isCancelled()) { ++ this.world.setTypeUpdate(new BlockPosition(i, j, k), Blocks.FIRE.getBlockData()); ++ } ++ // CraftBukkit end + } + + } +@@ -54,7 +63,15 @@ + if (this.b <= 0) { + this.die(); + if (!this.world.isStatic) { +- this.world.explode((Entity) null, this.locX, this.locY, this.locZ, 6.0F, true); ++ // CraftBukkit start ++ ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), 6.0F, false); ++ this.world.getServer().getPluginManager().callEvent(event); ++ if (event.isCancelled()) { ++ this.dead = false; ++ return false; ++ } ++ this.world.createExplosion(this, this.locX, this.locY, this.locZ, event.getRadius(), event.getFire(), true); ++ // CraftBukkit end + } + } + } diff --git a/nms-patches/EntityEnderDragon.patch b/nms-patches/EntityEnderDragon.patch new file mode 100644 index 00000000..a58d2f8f --- /dev/null +++ b/nms-patches/EntityEnderDragon.patch @@ -0,0 +1,330 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityEnderDragon.java 2014-11-27 08:59:46.665422005 +1100 ++++ src/main/java/net/minecraft/server/EntityEnderDragon.java 2014-11-27 08:42:10.116850981 +1100 +@@ -5,6 +5,17 @@ + import java.util.Iterator; + import java.util.List; + ++// CraftBukkit start ++import org.bukkit.block.BlockState; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.util.BlockStateListPopulator; ++import org.bukkit.event.entity.EntityCreatePortalEvent; ++import org.bukkit.event.entity.EntityExplodeEvent; ++import org.bukkit.event.entity.EntityRegainHealthEvent; ++import org.bukkit.event.entity.EntityTargetEvent; ++import org.bukkit.Bukkit; ++// CraftBukkit end ++ + public class EntityEnderDragon extends EntityInsentient implements IComplex, IMonster { + + public double a; +@@ -27,6 +38,7 @@ + private Entity by; + public int bw; + public EntityEnderCrystal bx; ++ private Explosion explosionSource = new Explosion(null, this, Double.NaN, Double.NaN, Double.NaN, Float.NaN, true, true); // CraftBukkit - reusable source for CraftTNTPrimed.getSource() + + public EntityEnderDragon(World world) { + super(world); +@@ -120,21 +132,21 @@ + + if (this.world.isStatic) { + if (this.ba > 0) { +- d3 = this.locX + (this.bb - this.locX) / (double) this.ba; +- d0 = this.locY + (this.bc - this.locY) / (double) this.ba; +- d1 = this.locZ + (this.bd - this.locZ) / (double) this.ba; +- d2 = MathHelper.g(this.be - (double) this.yaw); +- this.yaw = (float) ((double) this.yaw + d2 / (double) this.ba); ++ d0 = this.locX + (this.bb - this.locX) / (double) this.ba; ++ d1 = this.locY + (this.bc - this.locY) / (double) this.ba; ++ d2 = this.locZ + (this.bd - this.locZ) / (double) this.ba; ++ d3 = MathHelper.g(this.be - (double) this.yaw); ++ this.yaw = (float) ((double) this.yaw + d3 / (double) this.ba); + this.pitch = (float) ((double) this.pitch + (this.bf - (double) this.pitch) / (double) this.ba); + --this.ba; +- this.setPosition(d3, d0, d1); ++ this.setPosition(d0, d1, d2); + this.setYawPitch(this.yaw, this.pitch); + } + } else { +- d3 = this.a - this.locX; +- d0 = this.b - this.locY; +- d1 = this.c - this.locZ; +- d2 = d3 * d3 + d0 * d0 + d1 * d1; ++ d0 = this.a - this.locX; ++ d1 = this.b - this.locY; ++ d2 = this.c - this.locZ; ++ d3 = d0 * d0 + d1 * d1 + d2 * d2; + double d4; + + if (this.by != null) { +@@ -155,16 +167,16 @@ + this.c += this.random.nextGaussian() * 2.0D; + } + +- if (this.bu || d2 < 100.0D || d2 > 22500.0D || this.positionChanged || this.E) { ++ if (this.bu || d3 < 100.0D || d3 > 22500.0D || this.positionChanged || this.E) { + this.cd(); + } + +- d0 /= (double) MathHelper.sqrt(d3 * d3 + d1 * d1); ++ d1 /= (double) MathHelper.sqrt(d0 * d0 + d2 * d2); + f3 = 0.6F; +- d0 = MathHelper.a(d0, (double) (-f3), (double) f3); +- this.motY += d0 * 0.10000000149011612D; ++ d1 = MathHelper.a(d1, (double) (-f3), (double) f3); ++ this.motY += d1 * 0.10000000149011612D; + this.yaw = MathHelper.g(this.yaw); +- double d8 = 180.0D - Math.atan2(d3, d1) * 180.0D / 3.1415927410125732D; ++ double d8 = 180.0D - Math.atan2(d0, d2) * 180.0D / 3.1415927410125732D; + double d9 = MathHelper.g(d8 - (double) this.yaw); + + if (d9 > 50.0D) { +@@ -290,12 +302,21 @@ + if (this.bx != null) { + if (this.bx.dead) { + if (!this.world.isStatic) { ++ CraftEventFactory.entityDamage = this.bx; // CraftBukkit + this.a(this.bl, DamageSource.explosion((Explosion) null), 10.0F); ++ CraftEventFactory.entityDamage = null; // CraftBukkit + } + + this.bx = null; + } else if (this.ticksLived % 10 == 0 && this.getHealth() < this.getMaxHealth()) { +- this.setHealth(this.getHealth() + 1.0F); ++ // CraftBukkit start ++ EntityRegainHealthEvent event = new EntityRegainHealthEvent(this.getBukkitEntity(), 1.0D, EntityRegainHealthEvent.RegainReason.ENDER_CRYSTAL); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ this.setHealth((float) (this.getHealth() + event.getAmount())); ++ } ++ // CraftBukkit end + } + } + +@@ -364,7 +385,19 @@ + } + + if (this.random.nextInt(2) == 0 && !arraylist.isEmpty()) { +- this.by = (Entity) arraylist.get(this.random.nextInt(arraylist.size())); ++ // CraftBukkit start ++ Entity target = (Entity) this.world.players.get(this.random.nextInt(this.world.players.size())); ++ EntityTargetEvent event = new EntityTargetEvent(this.getBukkitEntity(), target.getBukkitEntity(), EntityTargetEvent.TargetReason.RANDOM_TARGET); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ if (event.getTarget() == null) { ++ this.by = null; ++ } else { ++ this.by = ((org.bukkit.craftbukkit.entity.CraftEntity) event.getTarget()).getHandle(); ++ } ++ } ++ // CraftBukkit end + } else { + boolean flag; + +@@ -399,6 +432,11 @@ + int j1 = MathHelper.floor(axisalignedbb.f); + boolean flag = false; + boolean flag1 = false; ++ ++ // CraftBukkit start - Create a list to hold all the destroyed blocks ++ List<org.bukkit.block.Block> destroyedBlocks = new java.util.ArrayList<org.bukkit.block.Block>(); ++ org.bukkit.craftbukkit.CraftWorld craftWorld = this.world.getWorld(); ++ // CraftBukkit end + + for (int k1 = i; k1 <= l; ++k1) { + for (int l1 = j; l1 <= i1; ++l1) { +@@ -407,7 +445,11 @@ + + if (block.getMaterial() != Material.AIR) { + if (block != Blocks.BARRIER && block != Blocks.OBSIDIAN && block != Blocks.END_STONE && block != Blocks.BEDROCK && block != Blocks.COMMAND_BLOCK && this.world.getGameRules().getBoolean("mobGriefing")) { +- flag1 = this.world.setAir(new BlockPosition(k1, l1, i2)) || flag1; ++ // CraftBukkit start - Add blocks to list rather than destroying them ++ // flag1 = this.world.setAir(new BlockPosition(k1, l1, i2)) || flag1; ++ flag1 = true; ++ destroyedBlocks.add(craftWorld.getBlockAt(k1, l1, i2)); ++ // CraftBukkit end + } else { + flag = true; + } +@@ -417,6 +459,40 @@ + } + + if (flag1) { ++ // CraftBukkit start - Set off an EntityExplodeEvent for the dragon exploding all these blocks ++ org.bukkit.entity.Entity bukkitEntity = this.getBukkitEntity(); ++ EntityExplodeEvent event = new EntityExplodeEvent(bukkitEntity, bukkitEntity.getLocation(), destroyedBlocks, 0F); ++ Bukkit.getPluginManager().callEvent(event); ++ if (event.isCancelled()) { ++ // This flag literally means 'Dragon hit something hard' (Obsidian, White Stone or Bedrock) and will cause the dragon to slow down. ++ // We should consider adding an event extension for it, or perhaps returning true if the event is cancelled. ++ return flag; ++ } else if (event.getYield() == 0F) { ++ // Yield zero ==> no drops ++ for (org.bukkit.block.Block block : event.blockList()) { ++ this.world.setAir(new BlockPosition(block.getX(), block.getY(), block.getZ())); ++ } ++ } else { ++ for (org.bukkit.block.Block block : event.blockList()) { ++ org.bukkit.Material blockId = block.getType(); ++ if (blockId == org.bukkit.Material.AIR) { ++ continue; ++ } ++ ++ int blockX = block.getX(); ++ int blockY = block.getY(); ++ int blockZ = block.getZ(); ++ ++ Block nmsBlock = org.bukkit.craftbukkit.util.CraftMagicNumbers.getBlock(blockId); ++ if (nmsBlock.a(explosionSource)) { ++ nmsBlock.dropNaturally(this.world, new BlockPosition(blockX, blockY, blockZ), nmsBlock.fromLegacyData(block.getData()), event.getYield(), 0); ++ } ++ nmsBlock.wasExploded(world, new BlockPosition(blockX, blockY, blockZ), explosionSource); ++ ++ this.world.setAir(new BlockPosition(blockX, blockY, blockZ)); ++ } ++ } ++ // CraftBukkit end + double d0 = axisalignedbb.a + (axisalignedbb.d - axisalignedbb.a) * (double) this.random.nextFloat(); + double d1 = axisalignedbb.b + (axisalignedbb.e - axisalignedbb.b) * (double) this.random.nextFloat(); + double d2 = axisalignedbb.c + (axisalignedbb.f - axisalignedbb.c) * (double) this.random.nextFloat(); +@@ -464,6 +540,7 @@ + } + + protected void aY() { ++ if (this.dead) return; // CraftBukkit - can't kill what's already dead + ++this.bw; + if (this.bw >= 180 && this.bw <= 200) { + float f = (this.random.nextFloat() - 0.5F) * 8.0F; +@@ -478,7 +555,7 @@ + + if (!this.world.isStatic) { + if (this.bw > 150 && this.bw % 5 == 0 && this.world.getGameRules().getBoolean("doMobLoot")) { +- i = 1000; ++ i = this.expToDrop / 12; // CraftBukkit - drop experience as dragon falls from sky. use experience drop from death event. This is now set in getExpReward() + + while (i > 0) { + j = EntityExperienceOrb.getOrbValue(i); +@@ -488,14 +565,30 @@ + } + + if (this.bw == 1) { +- this.world.a(1018, new BlockPosition(this), 0); ++ // CraftBukkit start - Use relative location for far away sounds ++ // this.world.a(1018, new BlockPosition(this), 0); ++ int viewDistance = ((WorldServer) this.world).getServer().getViewDistance() * 16; ++ for (EntityPlayer player : (List<EntityPlayer>) this.world.players) { ++ double deltaX = this.locX - player.locX; ++ double deltaZ = this.locZ - player.locZ; ++ double distanceSquared = deltaX * deltaX + deltaZ * deltaZ; ++ if (distanceSquared > viewDistance * viewDistance) { ++ double deltaLength = Math.sqrt(distanceSquared); ++ double relativeX = player.locX + (deltaX / deltaLength) * viewDistance; ++ double relativeZ = player.locZ + (deltaZ / deltaLength) * viewDistance; ++ player.playerConnection.sendPacket(new PacketPlayOutWorldEvent(1018, new BlockPosition((int) relativeX, (int) this.locY, (int) relativeZ), 0, true)); ++ } else { ++ player.playerConnection.sendPacket(new PacketPlayOutWorldEvent(1018, new BlockPosition((int) this.locX, (int) this.locY, (int) this.locZ), 0, true)); ++ } ++ } ++ // CraftBukkit end + } + } + + this.move(0.0D, 0.10000000149011612D, 0.0D); + this.aG = this.yaw += 20.0F; + if (this.bw == 200 && !this.world.isStatic) { +- i = 2000; ++ i = this.expToDrop - (10 * this.expToDrop / 12); // CraftBukkit - drop the remaining experience + + while (i > 0) { + j = EntityExperienceOrb.getOrbValue(i); +@@ -513,6 +606,9 @@ + boolean flag = true; + double d0 = 12.25D; + double d1 = 6.25D; ++ ++ // CraftBukkit start - Replace any "this.world" in the following with just "world"! ++ BlockStateListPopulator world = new BlockStateListPopulator(this.world.getWorld()); + + for (int i = -1; i <= 32; ++i) { + for (int j = -4; j <= 4; ++j) { +@@ -524,31 +620,51 @@ + + if (i < 0) { + if (d2 <= 6.25D) { +- this.world.setTypeUpdate(blockposition1, Blocks.BEDROCK.getBlockData()); ++ world.setTypeUpdate(blockposition1, Blocks.BEDROCK.getBlockData()); + } + } else if (i > 0) { +- this.world.setTypeUpdate(blockposition1, Blocks.AIR.getBlockData()); ++ world.setTypeUpdate(blockposition1, Blocks.AIR.getBlockData()); + } else if (d2 > 6.25D) { +- this.world.setTypeUpdate(blockposition1, Blocks.BEDROCK.getBlockData()); ++ world.setTypeUpdate(blockposition1, Blocks.BEDROCK.getBlockData()); + } else { +- this.world.setTypeUpdate(blockposition1, Blocks.END_PORTAL.getBlockData()); ++ world.setTypeUpdate(blockposition1, Blocks.END_PORTAL.getBlockData()); + } + } + } + } + } + +- this.world.setTypeUpdate(blockposition, Blocks.BEDROCK.getBlockData()); +- this.world.setTypeUpdate(blockposition.up(), Blocks.BEDROCK.getBlockData()); ++ world.setTypeUpdate(blockposition, Blocks.BEDROCK.getBlockData()); ++ world.setTypeUpdate(blockposition.up(), Blocks.BEDROCK.getBlockData()); + BlockPosition blockposition2 = blockposition.up(2); + +- this.world.setTypeUpdate(blockposition2, Blocks.BEDROCK.getBlockData()); +- this.world.setTypeUpdate(blockposition2.west(), Blocks.TORCH.getBlockData().set(BlockTorch.FACING, EnumDirection.EAST)); +- this.world.setTypeUpdate(blockposition2.east(), Blocks.TORCH.getBlockData().set(BlockTorch.FACING, EnumDirection.WEST)); +- this.world.setTypeUpdate(blockposition2.north(), Blocks.TORCH.getBlockData().set(BlockTorch.FACING, EnumDirection.SOUTH)); +- this.world.setTypeUpdate(blockposition2.south(), Blocks.TORCH.getBlockData().set(BlockTorch.FACING, EnumDirection.NORTH)); +- this.world.setTypeUpdate(blockposition.up(3), Blocks.BEDROCK.getBlockData()); +- this.world.setTypeUpdate(blockposition.up(4), Blocks.DRAGON_EGG.getBlockData()); ++ world.setTypeUpdate(blockposition2, Blocks.BEDROCK.getBlockData()); ++ world.setTypeUpdate(blockposition2.west(), Blocks.TORCH.getBlockData().set(BlockTorch.FACING, EnumDirection.EAST)); ++ world.setTypeUpdate(blockposition2.east(), Blocks.TORCH.getBlockData().set(BlockTorch.FACING, EnumDirection.WEST)); ++ world.setTypeUpdate(blockposition2.north(), Blocks.TORCH.getBlockData().set(BlockTorch.FACING, EnumDirection.SOUTH)); ++ world.setTypeUpdate(blockposition2.south(), Blocks.TORCH.getBlockData().set(BlockTorch.FACING, EnumDirection.NORTH)); ++ world.setTypeUpdate(blockposition.up(3), Blocks.BEDROCK.getBlockData()); ++ world.setTypeUpdate(blockposition.up(4), Blocks.DRAGON_EGG.getBlockData()); ++ ++ EntityCreatePortalEvent event = new EntityCreatePortalEvent((org.bukkit.entity.LivingEntity) this.getBukkitEntity(), java.util.Collections.unmodifiableList(world.getList()), org.bukkit.PortalType.ENDER); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ for (BlockState state : event.getBlocks()) { ++ state.update(true); ++ } ++ } else { ++ for (BlockState state : event.getBlocks()) { ++ PacketPlayOutBlockChange packet = new PacketPlayOutBlockChange(this.world, new BlockPosition(state.getX(), state.getY(), state.getZ())); ++ for (Iterator it = this.world.players.iterator(); it.hasNext();) { ++ EntityHuman entity = (EntityHuman) it.next(); ++ if (entity instanceof EntityPlayer) { ++ ((EntityPlayer) entity).playerConnection.sendPacket(packet); ++ } ++ } ++ } ++ } ++ // CraftBukkit end + } + + protected void D() {} +@@ -576,4 +692,12 @@ + protected float bA() { + return 5.0F; + } ++ ++ // CraftBukkit start ++ public int getExpReward() { ++ // This value is equal to the amount of experience dropped while falling from the sky (10 * 1000) ++ // plus what is dropped when the dragon hits the ground (2000) ++ return 12000; ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/EntityEnderPearl.patch b/nms-patches/EntityEnderPearl.patch new file mode 100644 index 00000000..de13eefd --- /dev/null +++ b/nms-patches/EntityEnderPearl.patch @@ -0,0 +1,50 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityEnderPearl.java 2014-11-27 08:59:46.669421987 +1100 ++++ src/main/java/net/minecraft/server/EntityEnderPearl.java 2014-11-27 08:42:10.124850965 +1100 +@@ -1,5 +1,11 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.Bukkit; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.player.PlayerTeleportEvent; ++// CraftBukkit end ++ + public class EntityEnderPearl extends EntityProjectile { + + public EntityEnderPearl(World world, EntityLiving entityliving) { +@@ -29,14 +35,28 @@ + entityendermite.setPositionRotation(entityliving.locX, entityliving.locY, entityliving.locZ, entityliving.yaw, entityliving.pitch); + this.world.addEntity(entityendermite); + } +- +- if (entityliving.av()) { +- entityliving.mount((Entity) null); ++ ++ // CraftBukkit start - Fire PlayerTeleportEvent ++ org.bukkit.craftbukkit.entity.CraftPlayer player = entityplayer.getBukkitEntity(); ++ org.bukkit.Location location = getBukkitEntity().getLocation(); ++ location.setPitch(player.getLocation().getPitch()); ++ location.setYaw(player.getLocation().getYaw()); ++ ++ PlayerTeleportEvent teleEvent = new PlayerTeleportEvent(player, player.getLocation(), location, PlayerTeleportEvent.TeleportCause.ENDER_PEARL); ++ Bukkit.getPluginManager().callEvent(teleEvent); ++ ++ if (!teleEvent.isCancelled() && !entityplayer.playerConnection.isDisconnected()) { ++ if (entityliving.av()) { ++ entityliving.mount((Entity) null); ++ } ++ ++ entityplayer.playerConnection.teleport(teleEvent.getTo()); ++ entityliving.fallDistance = 0.0F; ++ CraftEventFactory.entityDamage = this; ++ entityliving.damageEntity(DamageSource.FALL, 5.0F); ++ CraftEventFactory.entityDamage = null; + } +- +- entityliving.enderTeleportTo(this.locX, this.locY, this.locZ); +- entityliving.fallDistance = 0.0F; +- entityliving.damageEntity(DamageSource.FALL, 5.0F); ++ // CraftBukkit end + } + } + diff --git a/nms-patches/EntityEnderman.patch b/nms-patches/EntityEnderman.patch new file mode 100644 index 00000000..1fb9b606 --- /dev/null +++ b/nms-patches/EntityEnderman.patch @@ -0,0 +1,34 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityEnderman.java 2014-11-27 08:59:46.669421987 +1100 ++++ src/main/java/net/minecraft/server/EntityEnderman.java 2014-11-27 08:42:10.140850934 +1100 +@@ -4,6 +4,12 @@ + import java.util.Set; + import java.util.UUID; + ++// CraftBukkit start ++import org.bukkit.Location; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.EntityTeleportEvent; ++// CraftBukkit end ++ + public class EntityEnderman extends EntityMonster { + + private static final UUID b = UUID.fromString("020E0DFB-87AE-4653-9556-831010E291A0"); +@@ -165,7 +171,17 @@ + } + + if (flag1) { +- super.enderTeleportTo(this.locX, this.locY, this.locZ); ++ // CraftBukkit start - Teleport event ++ // super.enderTeleportTo(this.locX, this.locY, this.locZ); ++ EntityTeleportEvent teleport = new EntityTeleportEvent(this.getBukkitEntity(), new Location(this.world.getWorld(), d3, d4, d5), new Location(this.world.getWorld(), this.locX, this.locY, this.locZ)); ++ this.world.getServer().getPluginManager().callEvent(teleport); ++ if (teleport.isCancelled()) { ++ return false; ++ } ++ ++ Location to = teleport.getTo(); ++ this.enderTeleportTo(to.getX(), to.getY(), to.getZ()); ++ // CraftBukkit end + if (this.world.getCubes(this, this.getBoundingBox()).isEmpty() && !this.world.containsLiquid(this.getBoundingBox())) { + flag = true; + } diff --git a/nms-patches/EntityExperienceOrb.patch b/nms-patches/EntityExperienceOrb.patch new file mode 100644 index 00000000..0f389895 --- /dev/null +++ b/nms-patches/EntityExperienceOrb.patch @@ -0,0 +1,82 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityExperienceOrb.java 2014-11-27 08:59:46.673421970 +1100 ++++ src/main/java/net/minecraft/server/EntityExperienceOrb.java 2014-11-27 08:42:10.100851012 +1100 +@@ -1,5 +1,11 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.EntityTargetLivingEntityEvent; ++import org.bukkit.event.entity.EntityTargetEvent; ++// CraftBukkit end ++ + public class EntityExperienceOrb extends Entity { + + public int a; +@@ -34,6 +40,7 @@ + + public void s_() { + super.s_(); ++ EntityHuman prevTarget = this.targetPlayer;// CraftBukkit - store old target + if (this.c > 0) { + --this.c; + } +@@ -65,6 +72,16 @@ + } + + if (this.targetPlayer != null) { ++ // CraftBukkit start ++ boolean cancelled = false; ++ if (this.targetPlayer != prevTarget) { ++ EntityTargetLivingEntityEvent event = CraftEventFactory.callEntityTargetLivingEvent(this, targetPlayer, EntityTargetEvent.TargetReason.CLOSEST_PLAYER); ++ EntityLiving target = event.getTarget() == null ? null : ((org.bukkit.craftbukkit.entity.CraftLivingEntity) event.getTarget()).getHandle(); ++ targetPlayer = target instanceof EntityHuman ? (EntityHuman) target : null; ++ cancelled = event.isCancelled(); ++ } ++ ++ if (!cancelled && targetPlayer != null) { + double d1 = (this.targetPlayer.locX - this.locX) / d0; + double d2 = (this.targetPlayer.locY + (double) this.targetPlayer.getHeadHeight() - this.locY) / d0; + double d3 = (this.targetPlayer.locZ - this.locZ) / d0; +@@ -77,6 +94,8 @@ + this.motY += d2 / d4 * d5 * 0.1D; + this.motZ += d3 / d4 * d5 * 0.1D; + } ++ } ++ // CraftBukkit end + } + + this.move(this.motX, this.motY, this.motZ); +@@ -141,7 +160,7 @@ + entityhuman.bn = 2; + this.world.makeSound(entityhuman, "random.orb", 0.1F, 0.5F * ((this.random.nextFloat() - this.random.nextFloat()) * 0.7F + 1.8F)); + entityhuman.receive(this, 1); +- entityhuman.giveExp(this.value); ++ entityhuman.giveExp(CraftEventFactory.callPlayerExpChangeEvent(entityhuman, this.value).getAmount()); // CraftBukkit - this.value -> event.getAmount() + this.die(); + } + +@@ -153,6 +172,24 @@ + } + + public static int getOrbValue(int i) { ++ // CraftBukkit start ++ if (i > 162670129) return i - 100000; ++ if (i > 81335063) return 81335063; ++ if (i > 40667527) return 40667527; ++ if (i > 20333759) return 20333759; ++ if (i > 10166857) return 10166857; ++ if (i > 5083423) return 5083423; ++ if (i > 2541701) return 2541701; ++ if (i > 1270849) return 1270849; ++ if (i > 635413) return 635413; ++ if (i > 317701) return 317701; ++ if (i > 158849) return 158849; ++ if (i > 79423) return 79423; ++ if (i > 39709) return 39709; ++ if (i > 19853) return 19853; ++ if (i > 9923) return 9923; ++ if (i > 4957) return 4957; ++ // CraftBukkit end + return i >= 2477 ? 2477 : (i >= 1237 ? 1237 : (i >= 617 ? 617 : (i >= 307 ? 307 : (i >= 149 ? 149 : (i >= 73 ? 73 : (i >= 37 ? 37 : (i >= 17 ? 17 : (i >= 7 ? 7 : (i >= 3 ? 3 : 1))))))))); + } + diff --git a/nms-patches/EntityFallingBlock.patch b/nms-patches/EntityFallingBlock.patch new file mode 100644 index 00000000..2b53c264 --- /dev/null +++ b/nms-patches/EntityFallingBlock.patch @@ -0,0 +1,44 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityFallingBlock.java 2014-11-27 08:59:46.673421970 +1100 ++++ src/main/java/net/minecraft/server/EntityFallingBlock.java 2014-11-27 08:42:10.132850949 +1100 +@@ -4,6 +4,8 @@ + import java.util.ArrayList; + import java.util.Iterator; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class EntityFallingBlock extends Entity { + + public IBlockData block; +@@ -56,7 +58,7 @@ + + if (this.ticksLived++ == 0) { + blockposition = new BlockPosition(this); +- if (this.world.getType(blockposition).getBlock() == block) { ++ if (this.world.getType(blockposition).getBlock() == block && !CraftEventFactory.callEntityChangeBlockEvent(this, blockposition.getX(), blockposition.getY(), blockposition.getZ(), Blocks.AIR, 0).isCancelled()) { + this.world.setAir(blockposition); + } else if (!this.world.isStatic) { + this.die(); +@@ -77,7 +79,12 @@ + this.motY *= -0.5D; + if (this.world.getType(blockposition).getBlock() != Blocks.PISTON_EXTENSION) { + this.die(); +- if (!this.e && this.world.a(block, blockposition, true, EnumDirection.UP, (Entity) null, (ItemStack) null) && !BlockFalling.canFall(this.world, blockposition.down()) && this.world.setTypeAndData(blockposition, this.block, 3)) { ++ if (!this.e && this.world.a(block, blockposition, true, EnumDirection.UP, (Entity) null, (ItemStack) null) && !BlockFalling.canFall(this.world, blockposition.down()) /* mimic the false conditions of setTypeIdAndData */ && blockposition.getX() >= -30000000 && blockposition.getZ() >= -30000000 && blockposition.getX() < 30000000 && blockposition.getZ() < 30000000 && blockposition.getY() >= 0 && blockposition.getY() < 256 && this.world.getType(blockposition) != this.block) { ++ if (CraftEventFactory.callEntityChangeBlockEvent(this, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this.block.getBlock(), this.block.getBlock().toLegacyData(this.block)).isCancelled()) { ++ return; ++ } ++ this.world.setTypeAndData(blockposition, this.block, 3); ++ // CraftBukkit end + if (block instanceof BlockFalling) { + ((BlockFalling) block).a_(this.world, blockposition); + } +@@ -135,7 +142,9 @@ + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + ++ CraftEventFactory.entityDamage = this; // CraftBukkit + entity.damageEntity(damagesource, (float) Math.min(MathHelper.d((float) i * this.fallHurtAmount), this.fallHurtMax)); ++ CraftEventFactory.entityDamage = null; // CraftBukkit + } + + if (flag && (double) this.random.nextFloat() < 0.05000000074505806D + (double) i * 0.05D) { diff --git a/nms-patches/EntityFireball.patch b/nms-patches/EntityFireball.patch new file mode 100644 index 00000000..67022e16 --- /dev/null +++ b/nms-patches/EntityFireball.patch @@ -0,0 +1,111 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityFireball.java 2014-11-27 08:59:46.677421952 +1100 ++++ src/main/java/net/minecraft/server/EntityFireball.java 2014-11-27 08:42:10.144850927 +1100 +@@ -2,6 +2,8 @@ + + import java.util.List; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public abstract class EntityFireball extends Entity { + + private int e = -1; +@@ -15,6 +17,8 @@ + public double dirX; + public double dirY; + public double dirZ; ++ public float bukkitYield = 1; // CraftBukkit ++ public boolean isIncendiary = true; // CraftBukkit + + public EntityFireball(World world) { + super(world); +@@ -38,10 +42,17 @@ + public EntityFireball(World world, EntityLiving entityliving, double d0, double d1, double d2) { + super(world); + this.shooter = entityliving; ++ this.projectileSource = (org.bukkit.entity.LivingEntity) entityliving.getBukkitEntity(); // CraftBukkit + this.a(1.0F, 1.0F); + this.setPositionRotation(entityliving.locX, entityliving.locY, entityliving.locZ, entityliving.yaw, entityliving.pitch); + this.setPosition(this.locX, this.locY, this.locZ); + this.motX = this.motY = this.motZ = 0.0D; ++ // CraftBukkit start - Added setDirection method ++ this.setDirection(d0, d1, d2); ++ } ++ ++ public void setDirection(double d0, double d1, double d2) { ++ // CraftBukkit end + d0 += this.random.nextGaussian() * 0.4D; + d1 += this.random.nextGaussian() * 0.4D; + d2 += this.random.nextGaussian() * 0.4D; +@@ -101,7 +112,7 @@ + MovingObjectPosition movingobjectposition1 = axisalignedbb.a(vec3d, vec3d1); + + if (movingobjectposition1 != null) { +- double d1 = vec3d.f(movingobjectposition1.pos); ++ double d1 = vec3d.distanceSquared(movingobjectposition1.pos); // CraftBukkit - distance efficiency + + if (d1 < d0 || d0 == 0.0D) { + entity = entity1; +@@ -117,6 +128,12 @@ + + if (movingobjectposition != null) { + this.a(movingobjectposition); ++ ++ // CraftBukkit start - Fire ProjectileHitEvent ++ if (this.dead) { ++ CraftEventFactory.callProjectileHitEvent(this); ++ } ++ // CraftBukkit end + } + + this.locX += this.motX; +@@ -181,7 +198,8 @@ + + nbttagcompound.setString("inTile", minecraftkey == null ? "" : minecraftkey.toString()); + nbttagcompound.setByte("inGround", (byte) (this.i ? 1 : 0)); +- nbttagcompound.set("direction", this.a(new double[] { this.motX, this.motY, this.motZ})); ++ // CraftBukkit - Fix direction being mismapped to invalid variables ++ nbttagcompound.set("power", this.a(new double[] { this.dirX, this.dirY, this.dirZ})); + } + + public void a(NBTTagCompound nbttagcompound) { +@@ -195,12 +213,14 @@ + } + + this.i = nbttagcompound.getByte("inGround") == 1; +- if (nbttagcompound.hasKeyOfType("direction", 9)) { +- NBTTagList nbttaglist = nbttagcompound.getList("direction", 6); +- +- this.motX = nbttaglist.d(0); +- this.motY = nbttaglist.d(1); +- this.motZ = nbttaglist.d(2); ++ // CraftBukkit start - direction -> power ++ if (nbttagcompound.hasKeyOfType("power", 9)) { ++ NBTTagList nbttaglist = nbttagcompound.getList("power", 6); ++ ++ this.dirX = nbttaglist.d(0); ++ this.dirY = nbttaglist.d(1); ++ this.dirZ = nbttaglist.d(2); ++ // CraftBukkit end + } else { + this.die(); + } +@@ -221,6 +241,11 @@ + } else { + this.ac(); + if (damagesource.getEntity() != null) { ++ // CraftBukkit start ++ if (CraftEventFactory.handleNonLivingEntityDamageEvent(this, damagesource, f)) { ++ return false; ++ } ++ // CraftBukkit end + Vec3D vec3d = damagesource.getEntity().ap(); + + if (vec3d != null) { +@@ -234,6 +259,7 @@ + + if (damagesource.getEntity() instanceof EntityLiving) { + this.shooter = (EntityLiving) damagesource.getEntity(); ++ this.projectileSource = (org.bukkit.projectiles.ProjectileSource) this.shooter.getBukkitEntity(); + } + + return true; diff --git a/nms-patches/EntityFishingHook.patch b/nms-patches/EntityFishingHook.patch new file mode 100644 index 00000000..b2a015b8 --- /dev/null +++ b/nms-patches/EntityFishingHook.patch @@ -0,0 +1,159 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityFishingHook.java 2014-11-27 08:59:46.677421952 +1100 ++++ src/main/java/net/minecraft/server/EntityFishingHook.java 2014-11-27 08:42:10.104851005 +1100 +@@ -3,6 +3,12 @@ + import java.util.Arrays; + 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 List d = Arrays.asList(new PossibleFishingResult[] { (new PossibleFishingResult(new ItemStack(Items.LEATHER_BOOTS), 10)).a(0.9F), new PossibleFishingResult(new ItemStack(Items.LEATHER), 10), new PossibleFishingResult(new ItemStack(Items.BONE), 10), new PossibleFishingResult(new ItemStack(Items.POTION), 10), new PossibleFishingResult(new ItemStack(Items.STRING), 5), (new PossibleFishingResult(new ItemStack(Items.FISHING_ROD), 2)).a(0.9F), new PossibleFishingResult(new ItemStack(Items.BOWL), 10), new PossibleFishingResult(new ItemStack(Items.STICK), 5), new PossibleFishingResult(new ItemStack(Items.DYE, 10, EnumColor.BLACK.getInvColorIndex()), 1), new PossibleFishingResult(new ItemStack(Blocks.TRIPWIRE_HOOK), 10), new PossibleFishingResult(new ItemStack(Items.ROTTEN_FLESH), 10)}); +@@ -168,7 +174,7 @@ + MovingObjectPosition movingobjectposition1 = axisalignedbb.a(vec3d, vec3d1); + + if (movingobjectposition1 != null) { +- d6 = vec3d.f(movingobjectposition1.pos); ++ d6 = vec3d.distanceSquared(movingobjectposition1.pos); // CraftBukkit - distance efficiency + if (d6 < d5 || d5 == 0.0D) { + entity = entity1; + d5 = d6; +@@ -182,6 +188,7 @@ + } + + if (movingobjectposition != null) { ++ org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this); // Craftbukkit - Call event + if (movingobjectposition.entity != null) { + if (movingobjectposition.entity.damageEntity(DamageSource.projectile(this, this.owner), 0.0F)) { + this.hooked = movingobjectposition.entity; +@@ -261,8 +268,8 @@ + } else { + float f3; + float f4; +- double d11; + float f5; ++ double d11; + double d12; + + if (this.av > 0) { +@@ -277,20 +284,20 @@ + } else { + this.aw = (float) ((double) this.aw + this.random.nextGaussian() * 4.0D); + f3 = this.aw * 0.017453292F; +- f5 = MathHelper.sin(f3); +- f4 = MathHelper.cos(f3); +- d8 = this.locX + (double) (f5 * (float) this.av * 0.1F); +- d12 = (double) ((float) MathHelper.floor(this.getBoundingBox().b) + 1.0F); +- d11 = this.locZ + (double) (f4 * (float) this.av * 0.1F); ++ f4 = MathHelper.sin(f3); ++ f5 = MathHelper.cos(f3); ++ d8 = this.locX + (double) (f4 * (float) this.av * 0.1F); ++ d11 = (double) ((float) MathHelper.floor(this.getBoundingBox().b) + 1.0F); ++ d12 = this.locZ + (double) (f5 * (float) this.av * 0.1F); + if (this.random.nextFloat() < 0.15F) { +- worldserver.a(EnumParticle.WATER_BUBBLE, d8, d12 - 0.10000000149011612D, d11, 1, (double) f5, 0.1D, (double) f4, 0.0D, new int[0]); ++ worldserver.a(EnumParticle.WATER_BUBBLE, d8, d11 - 0.10000000149011612D, d12, 1, (double) f4, 0.1D, (double) f5, 0.0D, new int[0]); + } + +- float f6 = f5 * 0.04F; +- float f7 = f4 * 0.04F; ++ float f6 = f4 * 0.04F; ++ float f7 = f5 * 0.04F; + +- worldserver.a(EnumParticle.WATER_WAKE, d8, d12, d11, 0, (double) f7, 0.01D, (double) (-f6), 1.0D, new int[0]); +- worldserver.a(EnumParticle.WATER_WAKE, d8, d12, d11, 0, (double) (-f7), 0.01D, (double) f6, 1.0D, new int[0]); ++ worldserver.a(EnumParticle.WATER_WAKE, d8, d11, d12, 0, (double) f7, 0.01D, (double) (-f6), 1.0D, new int[0]); ++ worldserver.a(EnumParticle.WATER_WAKE, d8, d11, d12, 0, (double) (-f7), 0.01D, (double) f6, 1.0D, new int[0]); + } + } else if (this.au > 0) { + this.au -= k; +@@ -304,12 +311,12 @@ + } + + if (this.random.nextFloat() < f3) { +- f5 = MathHelper.a(this.random, 0.0F, 360.0F) * 0.017453292F; +- f4 = MathHelper.a(this.random, 25.0F, 60.0F); +- d8 = this.locX + (double) (MathHelper.sin(f5) * f4 * 0.1F); +- d12 = (double) ((float) MathHelper.floor(this.getBoundingBox().b) + 1.0F); +- d11 = this.locZ + (double) (MathHelper.cos(f5) * f4 * 0.1F); +- worldserver.a(EnumParticle.WATER_SPLASH, d8, d12, d11, 2 + this.random.nextInt(2), 0.10000000149011612D, 0.0D, 0.10000000149011612D, 0.0D, new int[0]); ++ f4 = MathHelper.a(this.random, 0.0F, 360.0F) * 0.017453292F; ++ f5 = MathHelper.a(this.random, 25.0F, 60.0F); ++ d8 = this.locX + (double) (MathHelper.sin(f4) * f5 * 0.1F); ++ d11 = (double) ((float) MathHelper.floor(this.getBoundingBox().b) + 1.0F); ++ d12 = this.locZ + (double) (MathHelper.cos(f4) * f5 * 0.1F); ++ worldserver.a(EnumParticle.WATER_SPLASH, d8, d11, d12, 2 + this.random.nextInt(2), 0.10000000149011612D, 0.0D, 0.10000000149011612D, 0.0D, new int[0]); + } + + if (this.au <= 0) { +@@ -374,6 +381,15 @@ + byte b0 = 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 ++ + double d0 = this.owner.locX - this.locX; + double d1 = this.owner.locY - this.locY; + double d2 = this.owner.locZ - this.locZ; +@@ -386,6 +402,16 @@ + b0 = 3; + } else if (this.at > 0) { + EntityItem entityitem = new EntityItem(this.world, this.locX, this.locY, this.locZ, this.m()); ++ // 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 d5 = this.owner.locX - this.locX; + double d6 = this.owner.locY - this.locY; + double d7 = this.owner.locZ - this.locZ; +@@ -396,13 +422,32 @@ + entityitem.motY = d6 * d9 + (double) MathHelper.sqrt(d8) * 0.08D; + entityitem.motZ = d7 * d9; + this.world.addEntity(entityitem); +- this.owner.world.addEntity(new EntityExperienceOrb(this.owner.world, this.owner.locX, this.owner.locY + 0.5D, this.owner.locZ + 0.5D, this.random.nextInt(6) + 1)); ++ // CraftBukkit - this.random.nextInt(6) + 1 -> playerFishEvent.getExpToDrop() ++ this.owner.world.addEntity(new EntityExperienceOrb(this.owner.world, this.owner.locX, this.owner.locY + 0.5D, this.owner.locZ + 0.5D, playerFishEvent.getExpToDrop())); + b0 = 1; + } + + if (this.aq) { ++ // 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 + b0 = 2; + } ++ ++ // CraftBukkit start ++ if (b0 == 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(); + this.owner.hookedFish = null; diff --git a/nms-patches/EntityHanging.patch b/nms-patches/EntityHanging.patch new file mode 100644 index 00000000..d65c6dd9 --- /dev/null +++ b/nms-patches/EntityHanging.patch @@ -0,0 +1,111 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityHanging.java 2014-11-27 08:59:46.681421935 +1100 ++++ src/main/java/net/minecraft/server/EntityHanging.java 2014-11-27 08:42:10.168850880 +1100 +@@ -4,6 +4,13 @@ + import java.util.List; + import org.apache.commons.lang3.Validate; + ++// CraftBukkit start ++import org.bukkit.entity.Hanging; ++import org.bukkit.entity.Painting; ++import org.bukkit.event.hanging.HangingBreakEvent; ++import org.bukkit.event.painting.PaintingBreakEvent; ++// CraftBukkit end ++ + public abstract class EntityHanging extends Entity { + + private int c; +@@ -77,6 +84,33 @@ + if (this.c++ == 100 && !this.world.isStatic) { + this.c = 0; + if (!this.dead && !this.survives()) { ++ // CraftBukkit start - fire break events ++ Material material = this.world.getType(new BlockPosition(this)).getBlock().getMaterial(); ++ HangingBreakEvent.RemoveCause cause; ++ ++ if (!material.equals(Material.AIR)) { ++ // TODO: This feels insufficient to catch 100% of suffocation cases ++ cause = HangingBreakEvent.RemoveCause.OBSTRUCTION; ++ } else { ++ cause = HangingBreakEvent.RemoveCause.PHYSICS; ++ } ++ ++ HangingBreakEvent event = new HangingBreakEvent((Hanging) this.getBukkitEntity(), cause); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ PaintingBreakEvent paintingEvent = null; ++ if (this instanceof EntityPainting) { ++ // Fire old painting event until it can be removed ++ paintingEvent = new PaintingBreakEvent((Painting) this.getBukkitEntity(), PaintingBreakEvent.RemoveCause.valueOf(cause.name())); ++ paintingEvent.setCancelled(event.isCancelled()); ++ this.world.getServer().getPluginManager().callEvent(paintingEvent); ++ } ++ ++ if (dead || event.isCancelled() || (paintingEvent != null && paintingEvent.isCancelled())) { ++ return; ++ } ++ // CraftBukkit end ++ + this.die(); + this.b((Entity) null); + } +@@ -138,6 +172,32 @@ + return false; + } else { + if (!this.dead && !this.world.isStatic) { ++ // CraftBukkit start - fire break events ++ HangingBreakEvent event = new HangingBreakEvent((Hanging) this.getBukkitEntity(), HangingBreakEvent.RemoveCause.DEFAULT); ++ PaintingBreakEvent paintingEvent = null; ++ if (damagesource.getEntity() != null) { ++ event = new org.bukkit.event.hanging.HangingBreakByEntityEvent((Hanging) this.getBukkitEntity(), damagesource.getEntity() == null ? null : damagesource.getEntity().getBukkitEntity()); ++ ++ if (this instanceof EntityPainting) { ++ // Fire old painting event until it can be removed ++ paintingEvent = new org.bukkit.event.painting.PaintingBreakByEntityEvent((Painting) this.getBukkitEntity(), damagesource.getEntity() == null ? null : damagesource.getEntity().getBukkitEntity()); ++ } ++ } else if (damagesource.isExplosion()) { ++ event = new HangingBreakEvent((Hanging) this.getBukkitEntity(), HangingBreakEvent.RemoveCause.EXPLOSION); ++ } ++ ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (paintingEvent != null) { ++ paintingEvent.setCancelled(event.isCancelled()); ++ this.world.getServer().getPluginManager().callEvent(paintingEvent); ++ } ++ ++ if (this.dead || event.isCancelled() || (paintingEvent != null && paintingEvent.isCancelled())) { ++ return true; ++ } ++ // CraftBukkit end ++ + this.die(); + this.ac(); + this.b(damagesource.getEntity()); +@@ -149,6 +209,18 @@ + + public void move(double d0, double d1, double d2) { + if (!this.world.isStatic && !this.dead && d0 * d0 + d1 * d1 + d2 * d2 > 0.0D) { ++ if (this.dead) return; // CraftBukkit ++ ++ // CraftBukkit start - fire break events ++ // TODO - Does this need its own cause? Seems to only be triggered by pistons ++ HangingBreakEvent event = new HangingBreakEvent((Hanging) this.getBukkitEntity(), HangingBreakEvent.RemoveCause.PHYSICS); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (this.dead || event.isCancelled()) { ++ return; ++ } ++ // CraftBukkit end ++ + this.die(); + this.b((Entity) null); + } +@@ -156,7 +228,7 @@ + } + + public void g(double d0, double d1, double d2) { +- if (!this.world.isStatic && !this.dead && d0 * d0 + d1 * d1 + d2 * d2 > 0.0D) { ++ if (false && !this.world.isStatic && !this.dead && d0 * d0 + d1 * d1 + d2 * d2 > 0.0D) { // CraftBukkit - not needed + this.die(); + this.b((Entity) null); + } diff --git a/nms-patches/EntityHorse.patch b/nms-patches/EntityHorse.patch new file mode 100644 index 00000000..f4ff42d0 --- /dev/null +++ b/nms-patches/EntityHorse.patch @@ -0,0 +1,140 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityHorse.java 2014-11-27 08:59:46.681421935 +1100 ++++ src/main/java/net/minecraft/server/EntityHorse.java 2014-11-27 08:42:10.148850918 +1100 +@@ -4,6 +4,8 @@ + import java.util.Iterator; + import java.util.List; + ++import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason; // CraftBukkit ++ + public class EntityHorse extends EntityAnimal implements IInventoryListener { + + private static final Predicate bq = new EntitySelectorHorse(); +@@ -36,6 +38,7 @@ + private String bM; + private String[] bN = new String[3]; + private boolean bO = false; ++ public int maxDomestication = 100; // CraftBukkit - store max domestication value + + public EntityHorse(World world) { + super(world); +@@ -314,13 +317,13 @@ + private int cX() { + int i = this.getType(); + +- return this.hasChest() && (i == 1 || i == 2) ? 17 : 2; ++ return this.hasChest() /* && (i == 1 || i == 2) */ ? 17 : 2; // CraftBukkit - Remove type check + } + + public void loadChest() { + InventoryHorseChest inventoryhorsechest = this.inventoryChest; + +- this.inventoryChest = new InventoryHorseChest("HorseChest", this.cX()); ++ this.inventoryChest = new InventoryHorseChest("HorseChest", this.cX(), this); // CraftBukkit - add this horse + this.inventoryChest.a(this.getName()); + if (inventoryhorsechest != null) { + inventoryhorsechest.b(this); +@@ -485,7 +488,7 @@ + } + + public int getMaxDomestication() { +- return 100; ++ return this.maxDomestication; // CraftBukkit - return stored max domestication instead of 100 + } + + protected float bA() { +@@ -585,7 +588,7 @@ + } + + if (this.getHealth() < this.getMaxHealth() && f > 0.0F) { +- this.heal(f); ++ this.heal(f, RegainReason.EATING); // CraftBukkit + flag = true; + } + +@@ -692,11 +695,24 @@ + + public void die(DamageSource damagesource) { + super.die(damagesource); ++ /* CraftBukkit start - Handle chest dropping in dropDeathLoot below + if (!this.world.isStatic) { + this.dropChest(); + } ++ // CraftBukkit end */ ++ } + ++ // CraftBukkit start - Add method ++ @Override ++ protected void dropDeathLoot(boolean flag, int i) { ++ super.dropDeathLoot(flag, i); ++ ++ // Moved from die method above ++ if (!this.world.isStatic) { ++ this.dropChest(); ++ } + } ++ // CraftBukkit end + + public void m() { + if (this.random.nextInt(200) == 0) { +@@ -706,7 +722,7 @@ + super.m(); + if (!this.world.isStatic) { + if (this.random.nextInt(900) == 0 && this.deathTicks == 0) { +- this.heal(1.0F); ++ this.heal(1.0F, RegainReason.REGEN); // CraftBukkit + } + + if (!this.cw() && this.passenger == null && this.random.nextInt(300) == 0 && this.world.getType(new BlockPosition(MathHelper.floor(this.locX), MathHelper.floor(this.locY) - 1, MathHelper.floor(this.locZ))).getBlock() == Blocks.GRASS) { +@@ -949,6 +965,7 @@ + nbttagcompound.setInt("Temper", this.getTemper()); + nbttagcompound.setBoolean("Tame", this.isTame()); + nbttagcompound.setString("OwnerUUID", this.getOwnerUUID()); ++ nbttagcompound.setInt("Bukkit.MaxDomestication", this.maxDomestication); // CraftBukkit + if (this.hasChest()) { + NBTTagList nbttaglist = new NBTTagList(); + +@@ -1001,6 +1018,12 @@ + this.setOwnerUUID(s); + } + ++ // CraftBukkit start ++ if (nbttagcompound.hasKey("Bukkit.MaxDomestication")) { ++ this.maxDomestication = nbttagcompound.getInt("Bukkit.MaxDomestication"); ++ } ++ // CraftBukkit end ++ + AttributeInstance attributeinstance = this.getAttributeMap().a("Speed"); + + if (attributeinstance != null) { +@@ -1166,18 +1189,25 @@ + + public void v(int i) { + if (this.cE()) { ++ // CraftBukkit start - fire HorseJumpEvent, use event power + if (i < 0) { + i = 0; +- } else { +- this.bE = true; +- this.df(); + } +- ++ ++ float power; + if (i >= 90) { +- this.bp = 1.0F; ++ power = 1.0F; + } else { +- this.bp = 0.4F + 0.4F * (float) i / 90.0F; ++ power = 0.4F + 0.4F * (float) i / 90.0F; ++ } ++ ++ org.bukkit.event.entity.HorseJumpEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callHorseJumpEvent(this, power); ++ if (!event.isCancelled()) { ++ this.bE = true; ++ this.df(); ++ this.bp = power; + } ++ // CraftBukkit end + } + + } diff --git a/nms-patches/EntityHuman.patch b/nms-patches/EntityHuman.patch new file mode 100644 index 00000000..185b52b4 --- /dev/null +++ b/nms-patches/EntityHuman.patch @@ -0,0 +1,383 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityHuman.java 2014-11-27 08:59:46.685421917 +1100 ++++ src/main/java/net/minecraft/server/EntityHuman.java 2014-11-27 08:42:10.120850973 +1100 +@@ -8,13 +8,25 @@ + import java.util.List; + import java.util.UUID; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.craftbukkit.entity.CraftItem; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.entity.Player; ++import org.bukkit.event.entity.EntityCombustByEntityEvent; ++import org.bukkit.event.player.PlayerBedEnterEvent; ++import org.bukkit.event.player.PlayerBedLeaveEvent; ++import org.bukkit.event.player.PlayerDropItemEvent; ++import org.bukkit.event.player.PlayerItemConsumeEvent; ++// CraftBukkit end ++ + public abstract class EntityHuman extends EntityLiving { + + public PlayerInventory inventory = new PlayerInventory(this); + private InventoryEnderChest enderChest = new InventoryEnderChest(); + public Container defaultContainer; + public Container activeContainer; +- protected FoodMetaData foodData = new FoodMetaData(); ++ protected FoodMetaData foodData = new FoodMetaData(this); // CraftBukkit - add "this" to constructor + protected int bk; + public float bl; + public float bm; +@@ -34,6 +46,7 @@ + private boolean d; + private BlockPosition e; + public PlayerAbilities abilities = new PlayerAbilities(); ++ public int oldLevel = -1; // CraftBukkit - add field + public int expLevel; + public int expTotal; + public float exp; +@@ -46,6 +59,16 @@ + private final GameProfile bF; + private boolean bG = false; + public EntityFishingHook hookedFish; ++ ++ // CraftBukkit start ++ public boolean fauxSleeping; ++ public String spawnWorld = ""; ++ ++ @Override ++ public CraftHumanEntity getBukkitEntity() { ++ return (CraftHumanEntity) super.getBukkitEntity(); ++ } ++ // CraftBukkit end + + public EntityHuman(World world, GameProfile gameprofile) { + super(world); +@@ -265,6 +288,32 @@ + if (this.g != null) { + this.b(this.g, 16); + int i = this.g.count; ++ ++ // CraftBukkit start - fire PlayerItemConsumeEvent ++ org.bukkit.inventory.ItemStack craftItem = CraftItemStack.asBukkitCopy(this.g); ++ PlayerItemConsumeEvent event = new PlayerItemConsumeEvent((Player) this.getBukkitEntity(), craftItem); ++ world.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ // Update client ++ if (this instanceof EntityPlayer) { ++ ((EntityPlayer) this).playerConnection.sendPacket(new PacketPlayOutSetSlot((byte) 0, activeContainer.getSlot((IInventory) this.inventory, this.inventory.itemInHandIndex).index, this.g)); ++ } ++ return; ++ } ++ ++ // Plugin modified the item, process it but don't remove it ++ if (!craftItem.equals(event.getItem())) { ++ CraftItemStack.asNMSCopy(event.getItem()).b(this.world, this); ++ ++ // Update client ++ if (this instanceof EntityPlayer) { ++ ((EntityPlayer) this).playerConnection.sendPacket(new PacketPlayOutSetSlot((byte) 0, activeContainer.getSlot((IInventory) this.inventory, this.inventory.itemInHandIndex).index, this.g)); ++ } ++ return; ++ } ++ // CraftBukkit end ++ + ItemStack itemstack = this.g.b(this.world, this); + + if (itemstack != this.g || itemstack != null && itemstack.count != i) { +@@ -324,7 +373,8 @@ + + if (this.world.getDifficulty() == EnumDifficulty.PEACEFUL && this.world.getGameRules().getBoolean("naturalRegeneration")) { + if (this.getHealth() < this.getMaxHealth() && this.ticksLived % 20 == 0) { +- this.heal(1.0F); ++ // CraftBukkit - added regain reason of "REGEN" for filtering purposes. ++ this.heal(1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.REGEN); + } + + if (this.foodData.c() && this.ticksLived % 10 == 0) { +@@ -348,7 +398,7 @@ + + this.j((float) attributeinstance.getValue()); + float f = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); +- float f1 = (float) (Math.atan(-this.motY * 0.20000000298023224D) * 15.0D); ++ float f1 = (float) ( org.bukkit.craftbukkit.TrigMath.atan(-this.motY * 0.20000000298023224D) * 15.0D); // CraftBukkit + + if (f > 0.1F) { + f = 0.1F; +@@ -438,11 +488,14 @@ + + public void b(Entity entity, int i) { + this.addScore(i); +- Collection collection = this.getScoreboard().getObjectivesForCriteria(IScoreboardCriteria.f); ++ // CraftBukkit - Get our scores instead ++ Collection<ScoreboardScore> collection = this.world.getServer().getScoreboardManager().getScoreboardScores(IScoreboardCriteria.e, this.getName(), new java.util.ArrayList<ScoreboardScore>()); ++ + + if (entity instanceof EntityHuman) { + this.b(StatisticList.B); +- collection.addAll(this.getScoreboard().getObjectivesForCriteria(IScoreboardCriteria.e)); ++ // CraftBukkit - Get our scores instead ++ this.world.getServer().getScoreboardManager().getScoreboardScores(IScoreboardCriteria.d, this.getName(), collection); + collection.addAll(this.e(entity)); + } else { + this.b(StatisticList.z); +@@ -451,8 +504,7 @@ + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { +- ScoreboardObjective scoreboardobjective = (ScoreboardObjective) iterator.next(); +- ScoreboardScore scoreboardscore = this.getScoreboard().getPlayerScoreForObjective(this.getName(), scoreboardobjective); ++ ScoreboardScore scoreboardscore = (ScoreboardScore) iterator.next(); // CraftBukkit - Use our scores instead + + scoreboardscore.incrementScore(); + } +@@ -491,6 +543,7 @@ + } + + public EntityItem a(boolean flag) { ++ // Called only when dropped by Q or CTRL-Q + return this.a(this.inventory.splitStack(this.inventory.itemInHandIndex, flag && this.inventory.getItemInHand() != null ? this.inventory.getItemInHand().count : 1), false, true); + } + +@@ -532,6 +585,30 @@ + entityitem.motY += (double) ((this.random.nextFloat() - this.random.nextFloat()) * 0.1F); + entityitem.motZ += Math.sin((double) f1) * (double) f; + } ++ ++ // CraftBukkit start - fire PlayerDropItemEvent ++ Player player = (Player) this.getBukkitEntity(); ++ CraftItem drop = new CraftItem(this.world.getServer(), entityitem); ++ ++ PlayerDropItemEvent event = new PlayerDropItemEvent(player, drop); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ org.bukkit.inventory.ItemStack cur = player.getInventory().getItemInHand(); ++ if (flag1 && (cur == null || cur.getAmount() == 0)) { ++ // The complete stack was dropped ++ player.getInventory().setItemInHand(drop.getItemStack()); ++ } else if (flag1 && cur.isSimilar(drop.getItemStack()) && drop.getItemStack().getAmount() == 1) { ++ // Only one item is dropped ++ cur.setAmount(cur.getAmount() + 1); ++ player.getInventory().setItemInHand(cur); ++ } else { ++ // Fallback ++ player.getInventory().addItem(drop.getItemStack()); ++ } ++ return null; ++ } ++ // CraftBukkit end + + this.a(entityitem); + if (flag1) { +@@ -623,10 +700,18 @@ + this.bv = new BlockPosition(this); + this.a(true, true, false); + } ++ ++ // CraftBukkit start ++ this.spawnWorld = nbttagcompound.getString("SpawnWorld"); ++ if ("".equals(spawnWorld)) { ++ this.spawnWorld = this.world.getServer().getWorlds().get(0).getName(); ++ } ++ // CraftBukkit end + + if (nbttagcompound.hasKeyOfType("SpawnX", 99) && nbttagcompound.hasKeyOfType("SpawnY", 99) && nbttagcompound.hasKeyOfType("SpawnZ", 99)) { + this.c = new BlockPosition(nbttagcompound.getInt("SpawnX"), nbttagcompound.getInt("SpawnY"), nbttagcompound.getInt("SpawnZ")); + this.d = nbttagcompound.getBoolean("SpawnForced"); ++ nbttagcompound.setString("SpawnWorld", spawnWorld); // CraftBukkit - fixes bed spawns for multiworld worlds + } + + this.foodData.a(nbttagcompound); +@@ -684,7 +769,7 @@ + + if (damagesource.r()) { + if (this.world.getDifficulty() == EnumDifficulty.PEACEFUL) { +- f = 0.0F; ++ return false; // CraftBukkit - f = 0.0f -> return false + } + + if (this.world.getDifficulty() == EnumDifficulty.EASY) { +@@ -696,7 +781,7 @@ + } + } + +- if (f == 0.0F) { ++ if (false && f == 0.0F) { // CraftBukkit - Don't filter out 0 damage + return false; + } else { + Entity entity = damagesource.getEntity(); +@@ -712,10 +797,29 @@ + } + + public boolean a(EntityHuman entityhuman) { +- ScoreboardTeamBase scoreboardteambase = this.getScoreboardTeam(); +- ScoreboardTeamBase scoreboardteambase1 = entityhuman.getScoreboardTeam(); ++ // CraftBukkit start - Change to check OTHER player's scoreboard team according to API ++ // To summarize this method's logic, it's "Can parameter hurt this" ++ org.bukkit.scoreboard.Team team; ++ if (entityhuman instanceof EntityPlayer) { ++ EntityPlayer thatPlayer = (EntityPlayer) entityhuman; ++ team = thatPlayer.getBukkitEntity().getScoreboard().getPlayerTeam(thatPlayer.getBukkitEntity()); ++ if (team == null || team.allowFriendlyFire()) { ++ return true; ++ } ++ } else { ++ // This should never be called, but is implemented anyway ++ org.bukkit.OfflinePlayer thisPlayer = entityhuman.world.getServer().getOfflinePlayer(entityhuman.getName()); ++ team = entityhuman.world.getServer().getScoreboardManager().getMainScoreboard().getPlayerTeam(thisPlayer); ++ if (team == null || team.allowFriendlyFire()) { ++ return true; ++ } ++ } + +- return scoreboardteambase == null ? true : (!scoreboardteambase.isAlly(scoreboardteambase1) ? true : scoreboardteambase.allowFriendlyFire()); ++ if (this instanceof EntityPlayer) { ++ return !team.hasPlayer(((EntityPlayer) this).getBukkitEntity()); ++ } ++ return !team.hasPlayer(this.world.getServer().getOfflinePlayer(this.getName())); ++ // CraftBukkit end + } + + protected void damageArmor(float f) { +@@ -742,7 +846,12 @@ + return (float) i / (float) this.inventory.armor.length; + } + +- protected void d(DamageSource damagesource, float f) { ++ // CraftBukkit start ++ protected boolean d(DamageSource damagesource, float f) { // void -> boolean ++ if (true) { ++ return super.d(damagesource, f); ++ } ++ // CraftBukkit end + if (!this.isInvulnerable(damagesource)) { + if (!damagesource.ignoresArmor() && this.isBlocking() && f > 0.0F) { + f = (1.0F + f) * 0.5F; +@@ -766,6 +875,7 @@ + + } + } ++ return false; // CraftBukkit + } + + public void openSign(TileEntitySign tileentitysign) {} +@@ -800,7 +910,8 @@ + } + + if (itemstack.a(this, (EntityLiving) entity)) { +- if (itemstack.count <= 0 && !this.abilities.canInstantlyBuild) { ++ // CraftBukkit - bypass infinite items; <= 0 -> == 0 ++ if (itemstack.count == 0 && !this.abilities.canInstantlyBuild) { + this.bZ(); + } + +@@ -866,8 +977,15 @@ + int j = EnchantmentManager.getFireAspectEnchantmentLevel(this); + + if (entity instanceof EntityLiving && j > 0 && !entity.isBurning()) { +- flag1 = true; +- entity.setOnFire(1); ++ // CraftBukkit start - Call a combust event when somebody hits with a fire enchanted item ++ EntityCombustByEntityEvent combustEvent = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), 1); ++ org.bukkit.Bukkit.getPluginManager().callEvent(combustEvent); ++ ++ if (!combustEvent.isCancelled()) { ++ flag1 = true; ++ entity.setOnFire(combustEvent.getDuration()); ++ } ++ // CraftBukkit end + } + + double d0 = entity.motX; +@@ -922,7 +1040,8 @@ + + if (itemstack != null && object instanceof EntityLiving) { + itemstack.a((EntityLiving) object, this); +- if (itemstack.count <= 0) { ++ // CraftBukkit - bypass infinite items; <= 0 -> == 0 ++ if (itemstack.count == 0) { + this.bZ(); + } + } +@@ -930,7 +1049,14 @@ + if (entity instanceof EntityLiving) { + this.a(StatisticList.w, Math.round(f * 10.0F)); + if (j > 0) { +- entity.setOnFire(j * 4); ++ // CraftBukkit start - Call a combust event when somebody hits with a fire enchanted item ++ EntityCombustByEntityEvent combustEvent = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), j * 4); ++ org.bukkit.Bukkit.getPluginManager().callEvent(combustEvent); ++ ++ if (!combustEvent.isCancelled()) { ++ entity.setOnFire(combustEvent.getDuration()); ++ } ++ // CraftBukkit end + } + } + +@@ -995,6 +1121,20 @@ + if (this.av()) { + this.mount((Entity) null); + } ++ ++ // CraftBukkit start - fire PlayerBedEnterEvent ++ if (this.getBukkitEntity() instanceof Player) { ++ Player player = (Player) this.getBukkitEntity(); ++ org.bukkit.block.Block bed = this.world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ ++ PlayerBedEnterEvent event = new PlayerBedEnterEvent(player, bed); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ return EnumBedResult.OTHER_PROBLEM; ++ } ++ } ++ // CraftBukkit end + + this.a(0.2F, 0.2F); + if (this.world.isLoaded(blockposition)) { +@@ -1077,6 +1217,23 @@ + if (!this.world.isStatic && flag1) { + this.world.everyoneSleeping(); + } ++ ++ // CraftBukkit start - fire PlayerBedLeaveEvent ++ if (this.getBukkitEntity() instanceof Player) { ++ Player player = (Player) this.getBukkitEntity(); ++ ++ org.bukkit.block.Block bed; ++ BlockPosition blockposition = this.bv; ++ if (blockposition != null) { ++ bed = this.world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ } else { ++ bed = this.world.getWorld().getBlockAt(player.getLocation()); ++ } ++ ++ PlayerBedLeaveEvent event = new PlayerBedLeaveEvent(player, bed); ++ this.world.getServer().getPluginManager().callEvent(event); ++ } ++ // CraftBukkit end + + this.sleepTicks = flag ? 0 : 100; + if (flag2) { +@@ -1128,9 +1285,11 @@ + if (blockposition != null) { + this.c = blockposition; + this.d = flag; ++ this.spawnWorld = this.world.worldData.getName(); // CraftBukkit + } else { + this.c = null; + this.d = false; ++ this.spawnWorld = ""; // CraftBukkit + } + + } +@@ -1477,6 +1636,7 @@ + } + + public IChatBaseComponent getScoreboardDisplayName() { ++ // CraftBukkit - todo: fun + ChatComponentText chatcomponenttext = new ChatComponentText(ScoreboardTeam.getPlayerDisplayName(this.getScoreboardTeam(), this.getName())); + + chatcomponenttext.getChatModifier().setChatClickable(new ChatClickable(EnumClickAction.SUGGEST_COMMAND, "/msg " + this.getName() + " ")); diff --git a/nms-patches/EntityInsentient.patch b/nms-patches/EntityInsentient.patch new file mode 100644 index 00000000..17867b8d --- /dev/null +++ b/nms-patches/EntityInsentient.patch @@ -0,0 +1,164 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityInsentient.java 2014-11-27 08:59:46.689421900 +1100 ++++ src/main/java/net/minecraft/server/EntityInsentient.java 2014-11-27 08:42:10.156850903 +1100 +@@ -4,6 +4,15 @@ + import java.util.List; + import java.util.UUID; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.entity.CraftLivingEntity; ++import org.bukkit.event.entity.EntityTargetLivingEntityEvent; ++import org.bukkit.event.entity.EntityTargetEvent; ++import org.bukkit.event.entity.EntityUnleashEvent; ++import org.bukkit.event.entity.EntityUnleashEvent.UnleashReason; ++// CraftBukkit end ++ + public abstract class EntityInsentient extends EntityLiving { + + public int a_; +@@ -39,7 +48,9 @@ + for (int i = 0; i < this.dropChances.length; ++i) { + this.dropChances[i] = 0.085F; + } +- ++ // CraftBukkit start - default persistance to type's persistance value ++ this.persistent = !isTypeNotPersistent(); ++ // CraftBukkit end + } + + protected void aW() { +@@ -76,7 +87,37 @@ + } + + public void setGoalTarget(EntityLiving entityliving) { ++ // CraftBukkit start - fire event ++ setGoalTarget(entityliving, EntityTargetEvent.TargetReason.UNKNOWN, true); ++ } ++ ++ public void setGoalTarget(EntityLiving entityliving, EntityTargetEvent.TargetReason reason, boolean fireEvent) { ++ if (getGoalTarget() == entityliving) return; ++ if (fireEvent) { ++ if (reason == EntityTargetEvent.TargetReason.UNKNOWN && getGoalTarget() != null && entityliving == null) { ++ reason = getGoalTarget().isAlive() ? EntityTargetEvent.TargetReason.FORGOT_TARGET : EntityTargetEvent.TargetReason.TARGET_DIED; ++ } ++ if (reason == EntityTargetEvent.TargetReason.UNKNOWN) { ++ world.getServer().getLogger().log(java.util.logging.Level.WARNING, "Unknown target reason, please report on the issue tracker", new Exception()); ++ } ++ CraftLivingEntity ctarget = null; ++ if (entityliving != null) { ++ ctarget = (CraftLivingEntity) entityliving.getBukkitEntity(); ++ } ++ EntityTargetLivingEntityEvent event = new EntityTargetLivingEntityEvent(this.getBukkitEntity(), ctarget, reason); ++ world.getServer().getPluginManager().callEvent(event); ++ if (event.isCancelled()) { ++ return; ++ } ++ ++ if (event.getTarget() != null) { ++ entityliving = ((CraftLivingEntity) event.getTarget()).getHandle(); ++ } else { ++ entityliving = null; ++ } ++ } + this.goalTarget = entityliving; ++ // CraftBukkit end + } + + public boolean a(Class oclass) { +@@ -235,11 +276,21 @@ + + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); ++ ++ // CraftBukkit start - If looting or persistence is false only use it if it was set after we started using it + if (nbttagcompound.hasKeyOfType("CanPickUpLoot", 1)) { +- this.j(nbttagcompound.getBoolean("CanPickUpLoot")); ++ boolean data = nbttagcompound.getBoolean("CanPickUpLoot"); ++ if (isLevelAtLeast(nbttagcompound, 1) || data) { ++ this.j(data); ++ } + } + +- this.persistent = nbttagcompound.getBoolean("PersistenceRequired"); ++ boolean data = nbttagcompound.getBoolean("PersistenceRequired"); ++ if (isLevelAtLeast(nbttagcompound, 1) || data) { ++ this.persistent = data; ++ } ++ // CraftBukkit end ++ + NBTTagList nbttaglist; + int i; + +@@ -380,11 +431,11 @@ + double d2 = entityhuman.locZ - this.locZ; + double d3 = d0 * d0 + d1 * d1 + d2 * d2; + +- if (this.isTypeNotPersistent() && d3 > 16384.0D) { ++ if (d3 > 16384.0D) { // CraftBukkit - remove isTypeNotPersistent() check + this.die(); + } + +- if (this.aO > 600 && this.random.nextInt(800) == 0 && d3 > 1024.0D && this.isTypeNotPersistent()) { ++ if (this.aO > 600 && this.random.nextInt(800) == 0 && d3 > 1024.0D) { // CraftBukkit - remove isTypeNotPersistent() check + this.die(); + } else if (d3 < 1024.0D) { + this.aO = 0; +@@ -707,6 +758,12 @@ + + public final boolean e(EntityHuman entityhuman) { + if (this.cb() && this.getLeashHolder() == entityhuman) { ++ // CraftBukkit start - fire PlayerUnleashEntityEvent ++ if (CraftEventFactory.callPlayerUnleashEntityEvent(this, entityhuman).isCancelled()) { ++ ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutAttachEntity(1, this, this.getLeashHolder())); ++ return false; ++ } ++ // CraftBukkit end + this.unleash(true, !entityhuman.abilities.canInstantlyBuild); + return true; + } else { +@@ -714,12 +771,24 @@ + + if (itemstack != null && itemstack.getItem() == Items.LEAD && this.ca()) { + if (!(this instanceof EntityTameableAnimal) || !((EntityTameableAnimal) this).isTamed()) { ++ // CraftBukkit start - fire PlayerLeashEntityEvent ++ if (CraftEventFactory.callPlayerLeashEntityEvent(this, entityhuman, entityhuman).isCancelled()) { ++ ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutAttachEntity(1, this, this.getLeashHolder())); ++ return false; ++ } ++ // CraftBukkit end + this.setLeashHolder(entityhuman, true); + --itemstack.count; + return true; + } + + if (((EntityTameableAnimal) this).e((EntityLiving) entityhuman)) { ++ // CraftBukkit start - fire PlayerLeashEntityEvent ++ if (CraftEventFactory.callPlayerLeashEntityEvent(this, entityhuman, entityhuman).isCancelled()) { ++ ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutAttachEntity(1, this, this.getLeashHolder())); ++ return false; ++ } ++ // CraftBukkit end + this.setLeashHolder(entityhuman, true); + --itemstack.count; + return true; +@@ -741,10 +810,12 @@ + + if (this.bm) { + if (!this.isAlive()) { ++ this.world.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), UnleashReason.PLAYER_UNLEASH)); // CraftBukkit + this.unleash(true, true); + } + + if (this.bn == null || this.bn.dead) { ++ this.world.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), UnleashReason.HOLDER_GONE)); // CraftBukkit + this.unleash(true, true); + } + } +@@ -811,6 +882,7 @@ + + this.bn = entityleash; + } else { ++ this.world.getServer().getPluginManager().callEvent(new EntityUnleashEvent(this.getBukkitEntity(), UnleashReason.UNKNOWN)); // CraftBukkit + this.unleash(false, true); + } + } diff --git a/nms-patches/EntityIronGolem.patch b/nms-patches/EntityIronGolem.patch new file mode 100644 index 00000000..e71334e1 --- /dev/null +++ b/nms-patches/EntityIronGolem.patch @@ -0,0 +1,11 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityIronGolem.java 2014-11-27 08:59:46.689421900 +1100 ++++ src/main/java/net/minecraft/server/EntityIronGolem.java 2014-11-27 08:42:10.100851012 +1100 +@@ -57,7 +57,7 @@ + + protected void s(Entity entity) { + if (entity instanceof IMonster && this.bb().nextInt(20) == 0) { +- this.setGoalTarget((EntityLiving) entity); ++ this.setGoalTarget((EntityLiving) entity, org.bukkit.event.entity.EntityTargetLivingEntityEvent.TargetReason.COLLISION, true); // CraftBukkit - set reason + } + + super.s(entity); diff --git a/nms-patches/EntityItem.patch b/nms-patches/EntityItem.patch new file mode 100644 index 00000000..0873e363 --- /dev/null +++ b/nms-patches/EntityItem.patch @@ -0,0 +1,115 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityItem.java 2014-11-27 08:59:46.693421882 +1100 ++++ src/main/java/net/minecraft/server/EntityItem.java 2014-11-27 08:42:10.104851005 +1100 +@@ -4,6 +4,8 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++import org.bukkit.event.player.PlayerPickupItemEvent; // CraftBukkit ++ + public class EntityItem extends Entity { + + private static final Logger b = LogManager.getLogger(); +@@ -13,6 +15,7 @@ + private String f; + private String g; + public float a; ++ private int lastTick = MinecraftServer.currentTick; // CraftBukkit + + public EntityItem(World world, double d0, double d1, double d2) { + super(world); +@@ -28,6 +31,11 @@ + + public EntityItem(World world, double d0, double d1, double d2, ItemStack itemstack) { + this(world, d0, d1, d2); ++ // CraftBukkit start - Can't set null items in the datawatcher ++ if (itemstack == null || itemstack.getItem() == null) { ++ return; ++ } ++ // CraftBukkit end + this.setItemStack(itemstack); + } + +@@ -52,9 +60,12 @@ + this.die(); + } else { + super.s_(); +- if (this.pickupDelay > 0 && this.pickupDelay != 32767) { +- --this.pickupDelay; +- } ++ // CraftBukkit start - Use wall time for pickup and despawn timers ++ int elapsedTicks = MinecraftServer.currentTick - this.lastTick; ++ this.pickupDelay -= elapsedTicks; ++ this.age += elapsedTicks; ++ this.lastTick = MinecraftServer.currentTick; ++ // CraftBukkit end + + this.lastX = this.locX; + this.lastY = this.locY; +@@ -90,12 +101,20 @@ + this.motY *= -0.5D; + } + ++ /* Craftbukkit start - moved up + if (this.age != -32768) { + ++this.age; + } ++ // Craftbukkit end */ + + this.W(); + if (!this.world.isStatic && this.age >= 6000) { ++ // CraftBukkit start - fire ItemDespawnEvent ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemDespawnEvent(this).isCancelled()) { ++ this.age = 0; ++ return; ++ } ++ // CraftBukkit end + this.die(); + } + +@@ -228,7 +247,18 @@ + + NBTTagCompound nbttagcompound1 = nbttagcompound.getCompound("Item"); + +- this.setItemStack(ItemStack.createStack(nbttagcompound1)); ++ // CraftBukkit start - Handle missing "Item" compounds ++ if (nbttagcompound1 != null) { ++ ItemStack itemstack = ItemStack.createStack(nbttagcompound1); ++ if (itemstack != null) { ++ this.setItemStack(itemstack); ++ } else { ++ this.die(); ++ } ++ } else { ++ this.die(); ++ } ++ // CraftBukkit end + if (this.getItemStack() == null) { + this.die(); + } +@@ -239,6 +269,26 @@ + if (!this.world.isStatic) { + ItemStack itemstack = this.getItemStack(); + int i = itemstack.count; ++ ++ // CraftBukkit start - fire PlayerPickupItemEvent ++ int canHold = entityhuman.inventory.canHold(itemstack); ++ int remaining = itemstack.count - canHold; ++ ++ if (this.pickupDelay <= 0 && canHold > 0) { ++ itemstack.count = canHold; ++ PlayerPickupItemEvent event = new PlayerPickupItemEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining); ++ // event.setCancelled(!entityhuman.canPickUpLoot); TODO ++ this.world.getServer().getPluginManager().callEvent(event); ++ itemstack.count = canHold + remaining; ++ ++ if (event.isCancelled()) { ++ return; ++ } ++ ++ // Possibly < 0; fix here so we do not have to modify code below ++ this.pickupDelay = 0; ++ } ++ // CraftBukkit end + + if (this.pickupDelay == 0 && (this.g == null || 6000 - this.age <= 200 || this.g.equals(entityhuman.getName())) && entityhuman.inventory.pickup(itemstack)) { + if (itemstack.getItem() == Item.getItemOf(Blocks.LOG)) { diff --git a/nms-patches/EntityItemFrame.patch b/nms-patches/EntityItemFrame.patch new file mode 100644 index 00000000..1ae52fd2 --- /dev/null +++ b/nms-patches/EntityItemFrame.patch @@ -0,0 +1,15 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityItemFrame.java 2014-11-27 08:59:46.693421882 +1100 ++++ src/main/java/net/minecraft/server/EntityItemFrame.java 2014-11-27 08:42:10.176850864 +1100 +@@ -27,6 +27,12 @@ + return false; + } else if (!damagesource.isExplosion() && this.getItem() != null) { + if (!this.world.isStatic) { ++ // CraftBukkit start - fire EntityDamageEvent ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.handleNonLivingEntityDamageEvent(this, damagesource, f, false) || this.dead) { ++ return true; ++ } ++ // CraftBukkit end ++ + this.a(damagesource.getEntity(), false); + this.setItem((ItemStack) null); + } diff --git a/nms-patches/EntityLargeFireball.patch b/nms-patches/EntityLargeFireball.patch new file mode 100644 index 00000000..2e14fc4d --- /dev/null +++ b/nms-patches/EntityLargeFireball.patch @@ -0,0 +1,38 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityLargeFireball.java 2014-11-27 08:59:46.697421864 +1100 ++++ src/main/java/net/minecraft/server/EntityLargeFireball.java 2014-11-27 08:42:10.152850911 +1100 +@@ -1,5 +1,7 @@ + package net.minecraft.server; + ++import org.bukkit.event.entity.ExplosionPrimeEvent; // CraftBukkit ++ + public class EntityLargeFireball extends EntityFireball { + + public int yield = 1; +@@ -21,7 +23,16 @@ + + boolean flag = this.world.getGameRules().getBoolean("mobGriefing"); + +- this.world.createExplosion((Entity) null, this.locX, this.locY, this.locZ, (float) this.yield, flag, flag); ++ // CraftBukkit start - fire ExplosionPrimeEvent ++ ExplosionPrimeEvent event = new ExplosionPrimeEvent((org.bukkit.entity.Explosive) org.bukkit.craftbukkit.entity.CraftEntity.getEntity(this.world.getServer(), this)); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ // give 'this' instead of (Entity) null so we know what causes the damage ++ this.world.createExplosion(this, this.locX, this.locY, this.locZ, event.getRadius(), event.getFire(), flag); ++ } ++ // CraftBukkit end ++ + this.die(); + } + +@@ -35,7 +46,8 @@ + public void a(NBTTagCompound nbttagcompound) { + super.a(nbttagcompound); + if (nbttagcompound.hasKeyOfType("ExplosionPower", 99)) { +- this.yield = nbttagcompound.getInt("ExplosionPower"); ++ // CraftBukkit - set bukkitYield when setting explosionpower ++ bukkitYield = this.yield = nbttagcompound.getInt("ExplosionPower"); + } + + } diff --git a/nms-patches/EntityLeash.patch b/nms-patches/EntityLeash.patch new file mode 100644 index 00000000..65d979ac --- /dev/null +++ b/nms-patches/EntityLeash.patch @@ -0,0 +1,61 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityLeash.java 2014-11-27 08:59:46.701421847 +1100 ++++ src/main/java/net/minecraft/server/EntityLeash.java 2014-11-27 08:42:10.136850942 +1100 +@@ -3,6 +3,8 @@ + import java.util.Iterator; + import java.util.List; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class EntityLeash extends EntityHanging { + + public EntityLeash(World world) { +@@ -63,6 +65,12 @@ + while (iterator.hasNext()) { + entityinsentient = (EntityInsentient) iterator.next(); + if (entityinsentient.cb() && entityinsentient.getLeashHolder() == entityhuman) { ++ // CraftBukkit start ++ if (CraftEventFactory.callPlayerLeashEntityEvent(entityinsentient, this, entityhuman).isCancelled()) { ++ ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutAttachEntity(1, entityinsentient, entityinsentient.getLeashHolder())); ++ continue; ++ } ++ // CraftBukkit end + entityinsentient.setLeashHolder(this, true); + flag = true; + } +@@ -70,8 +78,11 @@ + } + + if (!this.world.isStatic && !flag) { +- this.die(); +- if (entityhuman.abilities.canInstantlyBuild) { ++ // CraftBukkit start - Move below ++ // this.die(); ++ boolean die = true; ++ // CraftBukkit end ++ if (true || entityhuman.abilities.canInstantlyBuild) { // CraftBukkit - Process for non-creative as well + d0 = 7.0D; + list = this.world.a(EntityInsentient.class, new AxisAlignedBB(this.locX - d0, this.locY - d0, this.locZ - d0, this.locX + d0, this.locY + d0, this.locZ + d0)); + iterator = list.iterator(); +@@ -79,10 +90,21 @@ + while (iterator.hasNext()) { + entityinsentient = (EntityInsentient) iterator.next(); + if (entityinsentient.cb() && entityinsentient.getLeashHolder() == this) { +- entityinsentient.unleash(true, false); ++ // CraftBukkit start ++ if (CraftEventFactory.callPlayerUnleashEntityEvent(entityinsentient, entityhuman).isCancelled()) { ++ die = false; ++ continue; ++ } ++ entityinsentient.unleash(true, !entityhuman.abilities.canInstantlyBuild); // false -> survival mode boolean ++ // CraftBukkit end + } + } + } ++ // CraftBukkit start ++ if (die) { ++ this.die(); ++ } ++ // CraftBukkit end + } + + return true; diff --git a/nms-patches/EntityLightning.patch b/nms-patches/EntityLightning.patch new file mode 100644 index 00000000..7e8633d2 --- /dev/null +++ b/nms-patches/EntityLightning.patch @@ -0,0 +1,111 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityLightning.java 2014-11-27 08:59:46.701421847 +1100 ++++ src/main/java/net/minecraft/server/EntityLightning.java 2014-11-27 08:42:10.116850981 +1100 +@@ -2,30 +2,54 @@ + + import java.util.List; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class EntityLightning extends EntityWeather { + + private int lifeTicks; + public long a; + private int c; ++ ++ // CraftBukkit start ++ public boolean isEffect = false; + + public EntityLightning(World world, double d0, double d1, double d2) { ++ this(world, d0, d1, d2, false); ++ } ++ ++ public EntityLightning(World world, double d0, double d1, double d2, boolean isEffect) { ++ // CraftBukkit end ++ + super(world); ++ ++ // CraftBukkit - Set isEffect ++ this.isEffect = isEffect; ++ + this.setPositionRotation(d0, d1, d2, 0.0F, 0.0F); + this.lifeTicks = 2; + this.a = this.random.nextLong(); + this.c = this.random.nextInt(3) + 1; +- if (!world.isStatic && world.getGameRules().getBoolean("doFireTick") && (world.getDifficulty() == EnumDifficulty.NORMAL || world.getDifficulty() == EnumDifficulty.HARD) && world.areChunksLoaded(new BlockPosition(this), 10)) { ++ // CraftBukkit - add "!isEffect" ++ if (!isEffect && !world.isStatic && world.getGameRules().getBoolean("doFireTick") && (world.getDifficulty() == EnumDifficulty.NORMAL || world.getDifficulty() == EnumDifficulty.HARD) && world.areChunksLoaded(new BlockPosition(this), 10)) { + BlockPosition blockposition = new BlockPosition(this); + +- if (world.getType(blockposition).getBlock().getMaterial() == Material.AIR && Blocks.FIRE.canPlace(world, blockposition)) { +- world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData()); ++ if (world.getType(blockposition).getBlock().getMaterial() == Material.AIR && Blocks.FIRE.canPlace(world, blockposition)) { ++ // CraftBukkit start ++ if (!CraftEventFactory.callBlockIgniteEvent(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this).isCancelled()) { ++ world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData()); ++ } ++ // CraftBukkit end + } + + for (int i = 0; i < 4; ++i) { + BlockPosition blockposition1 = blockposition.a(this.random.nextInt(3) - 1, this.random.nextInt(3) - 1, this.random.nextInt(3) - 1); + +- if (world.getType(blockposition1).getBlock().getMaterial() == Material.AIR && Blocks.FIRE.canPlace(world, blockposition1)) { +- world.setTypeUpdate(blockposition1, Blocks.FIRE.getBlockData()); ++ if (world.getType(blockposition1).getBlock().getMaterial() == Material.AIR && Blocks.FIRE.canPlace(world, blockposition1)) { ++ // CraftBukkit start ++ if (!CraftEventFactory.callBlockIgniteEvent(world, blockposition1.getX(), blockposition1.getY(), blockposition1.getZ(), this).isCancelled()) { ++ world.setTypeUpdate(blockposition1, Blocks.FIRE.getBlockData()); ++ } ++ // CraftBukkit end + } + } + } +@@ -35,7 +59,24 @@ + public void s_() { + super.s_(); + if (this.lifeTicks == 2) { +- this.world.makeSound(this.locX, this.locY, this.locZ, "ambient.weather.thunder", 10000.0F, 0.8F + this.random.nextFloat() * 0.2F); ++ // CraftBukkit start - Use relative location for far away sounds ++ //this.world.makeSound(this.locX, this.locY, this.locZ, "ambient.weather.thunder", 10000.0F, 0.8F + this.random.nextFloat() * 0.2F); ++ float pitch = 0.8F + this.random.nextFloat() * 0.2F; ++ int viewDistance = ((WorldServer) this.world).getServer().getViewDistance() * 16; ++ for (EntityPlayer player : (List<EntityPlayer>) this.world.players) { ++ double deltaX = this.locX - player.locX; ++ double deltaZ = this.locZ - player.locZ; ++ double distanceSquared = deltaX * deltaX + deltaZ * deltaZ; ++ if (distanceSquared > viewDistance * viewDistance) { ++ double deltaLength = Math.sqrt(distanceSquared); ++ double relativeX = player.locX + (deltaX / deltaLength) * viewDistance; ++ double relativeZ = player.locZ + (deltaZ / deltaLength) * viewDistance; ++ player.playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect("ambient.weather.thunder", relativeX, this.locY, relativeZ, 10000.0F, pitch)); ++ } else { ++ player.playerConnection.sendPacket(new PacketPlayOutNamedSoundEffect("ambient.weather.thunder", this.locX, this.locY, this.locZ, 10000.0F, pitch)); ++ } ++ } ++ // CraftBukkit end + this.world.makeSound(this.locX, this.locY, this.locZ, "random.explode", 2.0F, 0.5F + this.random.nextFloat() * 0.2F); + } + +@@ -48,14 +89,18 @@ + this.lifeTicks = 1; + this.a = this.random.nextLong(); + BlockPosition blockposition = new BlockPosition(this); +- +- if (!this.world.isStatic && this.world.getGameRules().getBoolean("doFireTick") && this.world.areChunksLoaded(blockposition, 10) && this.world.getType(blockposition).getBlock().getMaterial() == Material.AIR && Blocks.FIRE.canPlace(this.world, blockposition)) { +- this.world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData()); ++ // CraftBukkit - add "!isEffect" ++ if (!isEffect && !this.world.isStatic && this.world.getGameRules().getBoolean("doFireTick") && this.world.areChunksLoaded(blockposition, 10) && this.world.getType(blockposition).getBlock().getMaterial() == Material.AIR && Blocks.FIRE.canPlace(this.world, blockposition)) { ++ // CraftBukkit start ++ if (!CraftEventFactory.callBlockIgniteEvent(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this).isCancelled()) { ++ this.world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData()); ++ } ++ // CraftBukkit end + } + } + } + +- if (this.lifeTicks >= 0) { ++ if (this.lifeTicks >= 0 && !this.isEffect) { // CraftBukkit - add !this.isEffect + if (this.world.isStatic) { + this.world.c(2); + } else { diff --git a/nms-patches/EntityLiving.patch b/nms-patches/EntityLiving.patch new file mode 100644 index 00000000..880a5b32 --- /dev/null +++ b/nms-patches/EntityLiving.patch @@ -0,0 +1,440 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityLiving.java 2014-11-27 08:59:46.705421829 +1100 ++++ src/main/java/net/minecraft/server/EntityLiving.java 2014-11-27 08:42:10.160850895 +1100 +@@ -8,6 +8,15 @@ + import java.util.Random; + import java.util.UUID; + ++// CraftBukkit start ++import java.util.ArrayList; ++import com.google.common.base.Function; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.EntityDamageEvent; ++import org.bukkit.event.entity.EntityDamageEvent.DamageModifier; ++import org.bukkit.event.entity.EntityRegainHealthEvent; ++// CraftBukkit end ++ + public abstract class EntityLiving extends Entity { + + private static final UUID a = UUID.fromString("662A6B8D-DA3E-4C1C-8813-96EA6097278D"); +@@ -67,6 +76,11 @@ + private float bk; + private int bl; + private float bm; ++ // CraftBukkit start ++ public int expToDrop; ++ public int maxAirTicks = 300; ++ ArrayList<org.bukkit.inventory.ItemStack> drops = null; ++ // CraftBukkit end + + public void G() { + this.damageEntity(DamageSource.OUT_OF_WORLD, Float.MAX_VALUE); +@@ -75,7 +89,8 @@ + public EntityLiving(World world) { + super(world); + this.aW(); +- this.setHealth(this.getMaxHealth()); ++ // CraftBukkit - setHealth(getMaxHealth()) inlined and simplified to skip the instanceof check for EntityPlayer, as getBukkitEntity() is not initialized in constructor ++ this.datawatcher.watch(6, (float) this.getAttributeInstance(GenericAttributes.maxHealth).getValue()); + this.k = true; + this.aF = (float) ((Math.random() + 1.0D) * 0.009999999776482582D); + this.setPosition(this.locX, this.locY, this.locZ); +@@ -116,8 +131,14 @@ + } + + int i = (int) (150.0D * d1); +- +- ((WorldServer) this.world).a(EnumParticle.BLOCK_DUST, this.locX, this.locY, this.locZ, i, 0.0D, 0.0D, 0.0D, 0.15000000596046448D, new int[] { Block.getCombinedId(iblockdata)}); ++ ++ // CraftBukkit start - visiblity api ++ if (this instanceof EntityPlayer) { ++ ((WorldServer) this.world).sendParticles((EntityPlayer) this, EnumParticle.BLOCK_DUST, false, this.locX, this.locY, this.locZ, i, 0.0D, 0.0D, 0.0D, 0.15000000596046448D, new int[] { Block.getCombinedId(iblockdata)}); ++ } else { ++ ((WorldServer) this.world).a(EnumParticle.BLOCK_DUST, this.locX, this.locY, this.locZ, i, 0.0D, 0.0D, 0.0D, 0.15000000596046448D, new int[] { Block.getCombinedId(iblockdata)}); ++ } ++ // CraftBukkit end + } + } + +@@ -174,7 +195,11 @@ + this.mount((Entity) null); + } + } else { +- this.setAirTicks(300); ++ // CraftBukkit start - Only set if needed to work around a DataWatcher inefficiency ++ if (this.getAirTicks() != 300) { ++ this.setAirTicks(maxAirTicks); ++ } ++ // CraftBukkit end + } + + if (this.isAlive() && this.U()) { +@@ -220,6 +245,18 @@ + this.lastPitch = this.pitch; + this.world.methodProfiler.b(); + } ++ ++ // CraftBukkit start ++ public int getExpReward() { ++ int exp = this.getExpValue(this.killer); ++ ++ if (!this.world.isStatic && (this.lastDamageByPlayerTime > 0 || this.alwaysGivesExp()) && this.aZ() && this.world.getGameRules().getBoolean("doMobLoot")) { ++ return exp; ++ } else { ++ return 0; ++ } ++ } ++ // CraftBukkit end + + public boolean isBaby() { + return false; +@@ -227,19 +264,18 @@ + + protected void aY() { + ++this.deathTicks; +- if (this.deathTicks == 20) { ++ if (this.deathTicks >= 20 && !this.dead) { // CraftBukkit - (this.deathTicks == 20) -> (this.deathTicks >= 20 && !this.dead) + int i; + +- if (!this.world.isStatic && (this.lastDamageByPlayerTime > 0 || this.alwaysGivesExp()) && this.aZ() && this.world.getGameRules().getBoolean("doMobLoot")) { +- i = this.getExpValue(this.killer); +- +- while (i > 0) { +- int j = EntityExperienceOrb.getOrbValue(i); +- +- i -= j; +- this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY, this.locZ, j)); +- } ++ // CraftBukkit start - Update getExpReward() above if the removed if() changes! ++ i = this.expToDrop; ++ while (i > 0) { ++ int j = EntityExperienceOrb.getOrbValue(i); ++ i -= j; ++ this.world.addEntity(new EntityExperienceOrb(this.world, this.locX, this.locY, this.locZ, j)); + } ++ this.expToDrop = 0; ++ // CraftBukkit end + + this.die(); + +@@ -375,6 +411,17 @@ + } + } + } ++ ++ // CraftBukkit start ++ if (nbttagcompound.hasKey("Bukkit.MaxHealth")) { ++ NBTBase nbtbase = nbttagcompound.get("Bukkit.MaxHealth"); ++ if (nbtbase.getTypeId() == 5) { ++ this.getAttributeInstance(GenericAttributes.maxHealth).setValue((double) ((NBTTagFloat) nbtbase).c()); ++ } else if (nbtbase.getTypeId() == 3) { ++ this.getAttributeInstance(GenericAttributes.maxHealth).setValue((double) ((NBTTagInt) nbtbase).d()); ++ } ++ } ++ // CraftBukkit end + + if (nbttagcompound.hasKeyOfType("HealF", 99)) { + this.setHealth(nbttagcompound.getFloat("HealF")); +@@ -486,7 +533,8 @@ + } + + public boolean hasEffect(int i) { +- return this.effects.containsKey(Integer.valueOf(i)); ++ // CraftBukkit - Add size check for efficiency ++ return this.effects.size() != 0 && this.effects.containsKey(Integer.valueOf(i)); + } + + public boolean hasEffect(MobEffectList mobeffectlist) { +@@ -560,20 +608,52 @@ + + } + ++ // CraftBukkit start - Delegate so we can handle providing a reason for health being regained + public void heal(float f) { ++ heal(f, EntityRegainHealthEvent.RegainReason.CUSTOM); ++ } ++ ++ public void heal(float f, EntityRegainHealthEvent.RegainReason regainReason) { + float f1 = this.getHealth(); + + if (f1 > 0.0F) { +- this.setHealth(f1 + f); ++ EntityRegainHealthEvent event = new EntityRegainHealthEvent(this.getBukkitEntity(), f, regainReason); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ this.setHealth((float) (this.getHealth() + event.getAmount())); ++ } ++ // CraftBukkit end + } + + } + + public final float getHealth() { ++ // CraftBukkit start - Use unscaled health ++ if (this instanceof EntityPlayer) { ++ return (float) ((EntityPlayer) this).getBukkitEntity().getHealth(); ++ } ++ // CraftBukkit end + return this.datawatcher.getFloat(6); + } + + public void setHealth(float f) { ++ // CraftBukkit start - Handle scaled health ++ if (this instanceof EntityPlayer) { ++ org.bukkit.craftbukkit.entity.CraftPlayer player = ((EntityPlayer) this).getBukkitEntity(); ++ // Squeeze ++ if (f < 0.0F) { ++ player.setRealHealth(0.0D); ++ } else if (f > player.getMaxHealth()) { ++ player.setRealHealth(player.getMaxHealth()); ++ } else { ++ player.setRealHealth(f); ++ } ++ ++ this.datawatcher.watch(6, Float.valueOf(player.getScaledHealth())); ++ return; ++ } ++ // CraftBukkit end + this.datawatcher.watch(6, Float.valueOf(MathHelper.a(f, 0.0F, this.getMaxHealth()))); + } + +@@ -589,7 +669,8 @@ + } else if (damagesource.o() && this.hasEffect(MobEffectList.FIRE_RESISTANCE)) { + return false; + } else { +- if ((damagesource == DamageSource.ANVIL || damagesource == DamageSource.FALLING_BLOCK) && this.getEquipment(4) != null) { ++ // CraftBukkit - Moved into d(DamageSource, float) ++ if (false && (damagesource == DamageSource.ANVIL || damagesource == DamageSource.FALLING_BLOCK) && this.getEquipment(4) != null) { + this.getEquipment(4).damage((int) (f * 4.0F + this.random.nextFloat() * f * 2.0F), this); + f *= 0.75F; + } +@@ -602,13 +683,22 @@ + return false; + } + +- this.d(damagesource, f - this.lastDamage); ++ // CraftBukkit start ++ if (!this.d(damagesource, f - this.lastDamage)) { ++ return false; ++ } ++ // CraftBukkit end + this.lastDamage = f; + flag = false; + } else { ++ // CraftBukkit start ++ float previousHealth = this.getHealth(); ++ if (!this.d(damagesource, f)) { ++ return false; ++ } + this.lastDamage = f; + this.noDamageTicks = this.maxNoDamageTicks; +- this.d(damagesource, f); ++ // CraftBukkit end + this.hurtTicks = this.at = 10; + } + +@@ -717,11 +807,19 @@ + } + + if (this.aZ() && this.world.getGameRules().getBoolean("doMobLoot")) { ++ this.drops = new ArrayList<org.bukkit.inventory.ItemStack>(); // CraftBukkit - Setup drop capture ++ + this.dropDeathLoot(this.lastDamageByPlayerTime > 0, i); + this.dropEquipment(this.lastDamageByPlayerTime > 0, i); + if (this.lastDamageByPlayerTime > 0 && this.random.nextFloat() < 0.025F + (float) i * 0.01F) { + this.getRareDrop(); +- } ++ } ++ // CraftBukkit start - Call death event ++ CraftEventFactory.callEntityDeathEvent(this, this.drops); ++ this.drops = null; ++ } else { ++ CraftEventFactory.callEntityDeathEvent(this); ++ // CraftBukkit end + } + } + +@@ -781,8 +879,13 @@ + int i = MathHelper.f((f - 3.0F - f2) * f1); + + if (i > 0) { ++ // CraftBukkit start ++ if (!this.damageEntity(DamageSource.FALL, (float) i)) { ++ return; ++ } ++ // CraftBukkit end + this.makeSound(this.n(i), 1.0F, 1.0F); +- this.damageEntity(DamageSource.FALL, (float) i); ++ // this.damageEntity(DamageSource.FALL, (float) i); // CraftBukkit - moved up + int j = MathHelper.floor(this.locX); + int k = MathHelper.floor(this.locY - 0.20000000298023224D); + int l = MathHelper.floor(this.locZ); +@@ -826,7 +929,7 @@ + int i = 25 - this.bq(); + float f1 = f * (float) i; + +- this.damageArmor(f); ++ // this.damageArmor(f); // CraftBukkit - Moved into d(DamageSource, float) + f = f1 / 25.0F; + } + +@@ -840,8 +943,9 @@ + int i; + int j; + float f1; +- +- if (this.hasEffect(MobEffectList.RESISTANCE) && damagesource != DamageSource.OUT_OF_WORLD) { ++ ++ // CraftBukkit - Moved to d(DamageSource, float) ++ if (false && this.hasEffect(MobEffectList.RESISTANCE) && damagesource != DamageSource.OUT_OF_WORLD) { + i = (this.getEffect(MobEffectList.RESISTANCE).getAmplifier() + 1) * 5; + j = 25 - i; + f1 = f * (float) j; +@@ -867,22 +971,117 @@ + } + } + +- protected void d(DamageSource damagesource, float f) { ++ // CraftBukkit start ++ protected boolean d(final DamageSource damagesource, float f) { // void -> boolean, add final + if (!this.isInvulnerable(damagesource)) { +- f = this.applyArmorModifier(damagesource, f); +- f = this.applyMagicModifier(damagesource, f); +- float f1 = f; ++ final boolean human = this instanceof EntityHuman; ++ float originalDamage = f; ++ Function<Double, Double> hardHat = new Function<Double, Double>() { ++ @Override ++ public Double apply(Double f) { ++ if ((damagesource == DamageSource.ANVIL || damagesource == DamageSource.FALLING_BLOCK) && EntityLiving.this.getEquipment(4) != null) { ++ return -(f - (f * 0.75F)); ++ } ++ return -0.0; ++ } ++ }; ++ float hardHatModifier = hardHat.apply((double) f).floatValue(); ++ f += hardHatModifier; ++ ++ Function<Double, Double> blocking = new Function<Double, Double>() { ++ @Override ++ public Double apply(Double f) { ++ if (human) { ++ if (!damagesource.ignoresArmor() && ((EntityHuman) EntityLiving.this).isBlocking() && f > 0.0F) { ++ return -(f - ((1.0F + f) * 0.5F)); ++ } ++ } ++ return -0.0; ++ } ++ }; ++ float blockingModifier = blocking.apply((double) f).floatValue(); ++ f += blockingModifier; ++ ++ Function<Double, Double> armor = new Function<Double, Double>() { ++ @Override ++ public Double apply(Double f) { ++ return -(f - EntityLiving.this.applyArmorModifier(damagesource, f.floatValue())); ++ } ++ }; ++ float armorModifier = armor.apply((double) f).floatValue(); ++ f += armorModifier; ++ ++ Function<Double, Double> resistance = new Function<Double, Double>() { ++ @Override ++ public Double apply(Double f) { ++ if (!damagesource.isStarvation() && EntityLiving.this.hasEffect(MobEffectList.RESISTANCE) && damagesource != DamageSource.OUT_OF_WORLD) { ++ int i = (EntityLiving.this.getEffect(MobEffectList.RESISTANCE).getAmplifier() + 1) * 5; ++ int j = 25 - i; ++ float f1 = f.floatValue() * (float) j; ++ return -(f - (f1 / 25.0F)); ++ } ++ return -0.0; ++ } ++ }; ++ float resistanceModifier = resistance.apply((double) f).floatValue(); ++ f += resistanceModifier; ++ ++ Function<Double, Double> magic = new Function<Double, Double>() { ++ @Override ++ public Double apply(Double f) { ++ return -(f - EntityLiving.this.applyMagicModifier(damagesource, f.floatValue())); ++ } ++ }; ++ float magicModifier = magic.apply((double) f).floatValue(); ++ f += magicModifier; ++ ++ Function<Double, Double> absorption = new Function<Double, Double>() { ++ @Override ++ public Double apply(Double f) { ++ return -(Math.max(f - Math.max(f - EntityLiving.this.getAbsorptionHearts(), 0.0F), 0.0F)); ++ } ++ }; ++ float absorptionModifier = absorption.apply((double) f).floatValue(); ++ ++ EntityDamageEvent event = CraftEventFactory.handleLivingEntityDamageEvent(this, damagesource, originalDamage, hardHatModifier, blockingModifier, armorModifier, resistanceModifier, magicModifier, absorptionModifier, hardHat, blocking, armor, resistance, magic, absorption); ++ if (event.isCancelled()) { ++ return false; ++ } ++ ++ f = (float) event.getFinalDamage(); + +- f = Math.max(f - this.getAbsorptionHearts(), 0.0F); +- this.setAbsorptionHearts(this.getAbsorptionHearts() - (f1 - f)); ++ // Apply damage to helmet ++ if ((damagesource == DamageSource.ANVIL || damagesource == DamageSource.FALLING_BLOCK) && this.getEquipment(4) != null) { ++ this.getEquipment(4).damage((int) (event.getDamage() * 4.0F + this.random.nextFloat() * event.getDamage() * 2.0F), this); ++ } ++ ++ // Apply damage to armor ++ if (!damagesource.ignoresArmor()) { ++ float armorDamage = (float) (event.getDamage() + event.getDamage(DamageModifier.BLOCKING) + event.getDamage(DamageModifier.HARD_HAT)); ++ this.damageArmor(armorDamage); ++ } ++ ++ absorptionModifier = (float) -event.getDamage(DamageModifier.ABSORPTION); ++ this.setAbsorptionHearts(Math.max(this.getAbsorptionHearts() - absorptionModifier, 0.0F)); + if (f != 0.0F) { ++ if (human) { ++ ((EntityHuman) this).applyExhaustion(damagesource.getExhaustionCost()); ++ } ++ // CraftBukkit end + float f2 = this.getHealth(); + + this.setHealth(f2 - f); + this.br().a(damagesource, f2, f); ++ // CraftBukkit start ++ if (human) { ++ return true; ++ } ++ // CraftBukkit end + this.setAbsorptionHearts(this.getAbsorptionHearts() - f); + } ++ return true; // CraftBukkit + } ++ return false; // CraftBukkit + } + + public CombatTracker br() { +@@ -1236,7 +1435,8 @@ + if (f > 0.0025000002F) { + f3 = 1.0F; + f2 = (float) Math.sqrt((double) f) * 3.0F; +- f1 = (float) Math.atan2(d1, d0) * 180.0F / 3.1415927F - 90.0F; ++ // CraftBukkit - Math -> TrigMath ++ f1 = (float) org.bukkit.craftbukkit.TrigMath.atan2(d1, d0) * 180.0F / 3.1415927F - 90.0F; + } + + if (this.ax > 0.0F) { +@@ -1400,6 +1600,13 @@ + if (list != null && !list.isEmpty()) { + for (int i = 0; i < list.size(); ++i) { + Entity entity = (Entity) list.get(i); ++ ++ // TODO better check now? ++ // CraftBukkit start - Only handle mob (non-player) collisions every other tick ++ if (entity instanceof EntityLiving && !(this instanceof EntityPlayer) && this.ticksLived % 2 == 0) { ++ continue; ++ } ++ // CraftBukkit end + + if (entity.ae()) { + this.s(entity); diff --git a/nms-patches/EntityMinecartAbstract.patch b/nms-patches/EntityMinecartAbstract.patch new file mode 100644 index 00000000..c8ed3d56 --- /dev/null +++ b/nms-patches/EntityMinecartAbstract.patch @@ -0,0 +1,231 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityMinecartAbstract.java 2014-11-27 08:59:46.705421829 +1100 ++++ src/main/java/net/minecraft/server/EntityMinecartAbstract.java 2014-11-27 08:42:10.136850942 +1100 +@@ -2,6 +2,15 @@ + + import java.util.Iterator; + ++// CraftBukkit start ++import org.bukkit.Location; ++import org.bukkit.entity.Vehicle; ++import org.bukkit.event.vehicle.VehicleDamageEvent; ++import org.bukkit.event.vehicle.VehicleDestroyEvent; ++import org.bukkit.event.vehicle.VehicleEntityCollisionEvent; ++import org.bukkit.util.Vector; ++// CraftBukkit end ++ + public abstract class EntityMinecartAbstract extends Entity implements INamableTileEntity { + + private boolean a; +@@ -13,6 +22,17 @@ + private double g; + private double h; + private double i; ++ ++ // CraftBukkit start ++ public boolean slowWhenEmpty = true; ++ private double derailedX = 0.5; ++ private double derailedY = 0.5; ++ private double derailedZ = 0.5; ++ private double flyingX = 0.95; ++ private double flyingY = 0.95; ++ private double flyingZ = 0.95; ++ public double maxSpeed = 0.4D; ++ // CraftBukkit end + + public EntityMinecartAbstract(World world) { + super(world); +@@ -79,6 +99,8 @@ + this.lastX = d0; + this.lastY = d1; + this.lastZ = d2; ++ ++ this.world.getServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleCreateEvent((Vehicle) this.getBukkitEntity())); // CraftBukkit + } + + public double an() { +@@ -90,16 +112,39 @@ + if (this.isInvulnerable(damagesource)) { + return false; + } else { ++ // CraftBukkit start - fire VehicleDamageEvent ++ Vehicle vehicle = (Vehicle) this.getBukkitEntity(); ++ org.bukkit.entity.Entity passenger = (damagesource.getEntity() == null) ? null : damagesource.getEntity().getBukkitEntity(); ++ ++ VehicleDamageEvent event = new VehicleDamageEvent(vehicle, passenger, f); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ return true; ++ } ++ ++ f = (float) event.getDamage(); ++ // CraftBukkit end ++ + this.k(-this.r()); + this.j(10); + this.ac(); + this.setDamage(this.getDamage() + f * 10.0F); + boolean flag = damagesource.getEntity() instanceof EntityHuman && ((EntityHuman) damagesource.getEntity()).abilities.canInstantlyBuild; + +- if (flag || this.getDamage() > 40.0F) { ++ if (flag || this.getDamage() > 40.0F) { // CraftBukkit - multi-world should still allow teleport even if default vanilla nether disabled + if (this.passenger != null) { + this.passenger.mount((Entity) null); + } ++ // CraftBukkit start ++ VehicleDestroyEvent destroyEvent = new VehicleDestroyEvent(vehicle, passenger); ++ this.world.getServer().getPluginManager().callEvent(destroyEvent); ++ ++ if (destroyEvent.isCancelled()) { ++ this.setDamage(40); // Maximize damage so this doesn't get triggered again right away ++ return true; ++ } ++ // CraftBukkit end + + if (flag && !this.hasCustomName()) { + this.die(); +@@ -135,6 +180,14 @@ + } + + public void s_() { ++ // CraftBukkit start ++ double prevX = this.locX; ++ double prevY = this.locY; ++ double prevZ = this.locZ; ++ float prevYaw = this.yaw; ++ float prevPitch = this.pitch; ++ // CraftBukkit end ++ + if (this.getType() > 0) { + this.j(this.getType() - 1); + } +@@ -155,7 +208,7 @@ + + i = this.L(); + if (this.ak) { +- if (minecraftserver.getAllowNether()) { ++ if (true || minecraftserver.getAllowNether()) { + if (this.vehicle == null && this.al++ >= i) { + this.al = i; + this.portalCooldown = this.ar(); +@@ -252,6 +305,20 @@ + } + + this.setYawPitch(this.yaw, this.pitch); ++ ++ // CraftBukkit start ++ org.bukkit.World bworld = this.world.getWorld(); ++ Location from = new Location(bworld, prevX, prevY, prevZ, prevYaw, prevPitch); ++ Location to = new Location(bworld, this.locX, this.locY, this.locZ, this.yaw, this.pitch); ++ Vehicle vehicle = (Vehicle) this.getBukkitEntity(); ++ ++ this.world.getServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleUpdateEvent(vehicle)); ++ ++ if (!from.equals(to)) { ++ this.world.getServer().getPluginManager().callEvent(new org.bukkit.event.vehicle.VehicleMoveEvent(vehicle, from, to)); ++ } ++ // CraftBukkit end ++ + Iterator iterator = this.world.getEntities(this, this.getBoundingBox().grow(0.20000000298023224D, 0.0D, 0.20000000298023224D)).iterator(); + + while (iterator.hasNext()) { +@@ -275,7 +342,7 @@ + } + + protected double m() { +- return 0.4D; ++ return this.maxSpeed; // CraftBukkit + } + + public void a(int i, int j, int k, boolean flag) {} +@@ -286,16 +353,20 @@ + this.motX = MathHelper.a(this.motX, -d0, d0); + this.motZ = MathHelper.a(this.motZ, -d0, d0); + if (this.onGround) { +- this.motX *= 0.5D; +- this.motY *= 0.5D; +- this.motZ *= 0.5D; ++ // CraftBukkit start - replace magic numbers with our variables ++ this.motX *= this.derailedX; ++ this.motY *= this.derailedY; ++ this.motZ *= this.derailedZ; ++ // CraftBukkit end + } + + this.move(this.motX, this.motY, this.motZ); + if (!this.onGround) { +- this.motX *= 0.949999988079071D; +- this.motY *= 0.949999988079071D; +- this.motZ *= 0.949999988079071D; ++ // CraftBukkit start - replace magic numbers with our variables ++ this.motX *= this.flyingX; ++ this.motY *= this.flyingY; ++ this.motZ *= this.flyingZ; ++ // CraftBukkit end + } + + } +@@ -483,7 +554,7 @@ + } + + protected void o() { +- if (this.passenger != null) { ++ if (this.passenger != null || !this.slowWhenEmpty) { // CraftBukkit - add !this.slowWhenEmpty + this.motX *= 0.996999979019165D; + this.motY *= 0.0D; + this.motZ *= 0.996999979019165D; +@@ -611,6 +682,17 @@ + if (!this.world.isStatic) { + if (!entity.T && !this.T) { + if (entity != this.passenger) { ++ // CraftBukkit start ++ Vehicle vehicle = (Vehicle) this.getBukkitEntity(); ++ org.bukkit.entity.Entity hitEntity = (entity == null) ? null : entity.getBukkitEntity(); ++ ++ VehicleEntityCollisionEvent collisionEvent = new VehicleEntityCollisionEvent(vehicle, hitEntity); ++ this.world.getServer().getPluginManager().callEvent(collisionEvent); ++ ++ if (collisionEvent.isCancelled()) { ++ return; ++ } ++ // CraftBukkit end + if (entity instanceof EntityLiving && !(entity instanceof EntityHuman) && !(entity instanceof EntityIronGolem) && this.s() == EnumMinecartType.RIDEABLE && this.motX * this.motX + this.motZ * this.motZ > 0.01D && this.passenger == null && entity.vehicle == null) { + entity.mount(this); + } +@@ -619,7 +701,8 @@ + double d1 = entity.locZ - this.locZ; + double d2 = d0 * d0 + d1 * d1; + +- if (d2 >= 9.999999747378752E-5D) { ++ // CraftBukkit - collision ++ if (d2 >= 9.999999747378752E-5D && !collisionEvent.isCollisionCancelled()) { + d2 = (double) MathHelper.sqrt(d2); + d0 /= d2; + d1 /= d2; +@@ -775,4 +858,26 @@ + return chatmessage; + } + } ++ ++ // CraftBukkit start - Methods for getting and setting flying and derailed velocity modifiers ++ public Vector getFlyingVelocityMod() { ++ return new Vector(flyingX, flyingY, flyingZ); ++ } ++ ++ public void setFlyingVelocityMod(Vector flying) { ++ flyingX = flying.getX(); ++ flyingY = flying.getY(); ++ flyingZ = flying.getZ(); ++ } ++ ++ public Vector getDerailedVelocityMod() { ++ return new Vector(derailedX, derailedY, derailedZ); ++ } ++ ++ public void setDerailedVelocityMod(Vector derailed) { ++ derailedX = derailed.getX(); ++ derailedY = derailed.getY(); ++ derailedZ = derailed.getZ(); ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/EntityMinecartCommandBlockListener.patch b/nms-patches/EntityMinecartCommandBlockListener.patch new file mode 100644 index 00000000..ccfa4973 --- /dev/null +++ b/nms-patches/EntityMinecartCommandBlockListener.patch @@ -0,0 +1,10 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityMinecartCommandBlockListener.java 2014-11-27 08:59:46.709421812 +1100 ++++ src/main/java/net/minecraft/server/EntityMinecartCommandBlockListener.java 2014-11-27 08:42:10.088851036 +1100 +@@ -6,6 +6,7 @@ + + EntityMinecartCommandBlockListener(EntityMinecartCommandBlock entityminecartcommandblock) { + this.a = entityminecartcommandblock; ++ this.sender = (org.bukkit.craftbukkit.entity.CraftMinecartCommand) entityminecartcommandblock.getBukkitEntity(); // CraftBukkit - Set the sender + } + + public void h() { diff --git a/nms-patches/EntityMinecartContainer.patch b/nms-patches/EntityMinecartContainer.patch new file mode 100644 index 00000000..3a7c0c58 --- /dev/null +++ b/nms-patches/EntityMinecartContainer.patch @@ -0,0 +1,61 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityMinecartContainer.java 2014-11-27 08:59:46.709421812 +1100 ++++ src/main/java/net/minecraft/server/EntityMinecartContainer.java 2014-11-27 08:42:10.120850973 +1100 +@@ -1,9 +1,48 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import java.util.List; ++ ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++import org.bukkit.inventory.InventoryHolder; ++// CraftBukkit end ++ + public abstract class EntityMinecartContainer extends EntityMinecartAbstract implements ITileInventory { + +- private ItemStack[] items = new ItemStack[36]; ++ private ItemStack[] items = new ItemStack[27]; // CraftBukkit - 36 -> 27 + private boolean b = true; ++ ++ // CraftBukkit start ++ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>(); ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() { ++ return this.items; ++ } ++ ++ public void onOpen(CraftHumanEntity who) { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) { ++ transaction.remove(who); ++ } ++ ++ public List<HumanEntity> getViewers() { ++ return transaction; ++ } ++ ++ public InventoryHolder getOwner() { ++ org.bukkit.entity.Entity cart = getBukkitEntity(); ++ if(cart instanceof InventoryHolder) return (InventoryHolder) cart; ++ return null; ++ } ++ ++ public void setMaxStackSize(int size) { ++ maxStack = size; ++ } ++ // CraftBukkit end + + public EntityMinecartContainer(World world) { + super(world); +@@ -81,7 +120,7 @@ + } + + public int getMaxStackSize() { +- return 64; ++ return maxStack; // CraftBukkit + } + + public void c(int i) { diff --git a/nms-patches/EntityMonster.patch b/nms-patches/EntityMonster.patch new file mode 100644 index 00000000..f5500fac --- /dev/null +++ b/nms-patches/EntityMonster.patch @@ -0,0 +1,26 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityMonster.java 2014-11-27 08:59:46.709421812 +1100 ++++ src/main/java/net/minecraft/server/EntityMonster.java 2014-11-27 08:42:10.164850887 +1100 +@@ -1,5 +1,7 @@ + package net.minecraft.server; + ++import org.bukkit.event.entity.EntityCombustByEntityEvent; // CraftBukkit ++ + public abstract class EntityMonster extends EntityCreature implements IMonster { + + protected final PathfinderGoal a = new PathfinderGoalAvoidTarget(this, new EntitySelectorExplodingCreeper(this), 4.0F, 1.0D, 2.0D); +@@ -81,7 +83,14 @@ + int j = EnchantmentManager.getFireAspectEnchantmentLevel(this); + + if (j > 0) { +- entity.setOnFire(j * 4); ++ // CraftBukkit start - Call a combust event when somebody hits with a fire enchanted item ++ EntityCombustByEntityEvent combustEvent = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), j * 4); ++ org.bukkit.Bukkit.getPluginManager().callEvent(combustEvent); ++ ++ if (!combustEvent.isCancelled()) { ++ entity.setOnFire(combustEvent.getDuration()); ++ } ++ // CraftBukkit end + } + + this.a((EntityLiving) this, entity); diff --git a/nms-patches/EntityMushroomCow.patch b/nms-patches/EntityMushroomCow.patch new file mode 100644 index 00000000..2d2507ed --- /dev/null +++ b/nms-patches/EntityMushroomCow.patch @@ -0,0 +1,26 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityMushroomCow.java 2014-11-27 08:59:46.713421793 +1100 ++++ src/main/java/net/minecraft/server/EntityMushroomCow.java 2014-11-27 08:42:10.084851043 +1100 +@@ -1,5 +1,7 @@ + package net.minecraft.server; + ++import org.bukkit.event.player.PlayerShearEntityEvent; // CraftBukkit ++ + public class EntityMushroomCow extends EntityCow { + + public EntityMushroomCow(World world) { +@@ -24,6 +26,15 @@ + } + + if (itemstack != null && itemstack.getItem() == Items.SHEARS && this.getAge() >= 0) { ++ // 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.isStatic) { diff --git a/nms-patches/EntityOcelot.patch b/nms-patches/EntityOcelot.patch new file mode 100644 index 00000000..1abf343c --- /dev/null +++ b/nms-patches/EntityOcelot.patch @@ -0,0 +1,30 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityOcelot.java 2014-11-27 08:59:46.713421793 +1100 ++++ src/main/java/net/minecraft/server/EntityOcelot.java 2014-11-27 08:42:10.160850895 +1100 +@@ -51,7 +51,7 @@ + } + + protected boolean isTypeNotPersistent() { +- return !this.isTamed() && this.ticksLived > 2400; ++ return !this.isTamed() /*&& this.ticksLived > 2400*/; // CraftBukkit + } + + protected void aW() { +@@ -124,7 +124,8 @@ + } + + if (!this.world.isStatic) { +- if (this.random.nextInt(3) == 0) { ++ // CraftBukkit - added event call and isCancelled check ++ if (this.random.nextInt(3) == 0 && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this, entityhuman).isCancelled()) { + this.setTamed(true); + this.setCatType(1 + this.world.random.nextInt(3)); + this.setOwnerUUID(entityhuman.getUniqueID().toString()); +@@ -231,7 +232,7 @@ + + entityocelot.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, 0.0F); + entityocelot.setAgeRaw(-24000); +- this.world.addEntity(entityocelot); ++ this.world.addEntity(entityocelot, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.OCELOT_BABY); // CraftBukkit - add SpawnReason + } + } + diff --git a/nms-patches/EntityPainting.patch b/nms-patches/EntityPainting.patch new file mode 100644 index 00000000..7c698b4f --- /dev/null +++ b/nms-patches/EntityPainting.patch @@ -0,0 +1,10 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityPainting.java 2014-11-27 08:59:46.717421776 +1100 ++++ src/main/java/net/minecraft/server/EntityPainting.java 2014-11-27 08:42:10.132850949 +1100 +@@ -9,6 +9,7 @@ + + public EntityPainting(World world) { + super(world); ++ this.art = EnumArt.values()[this.random.nextInt(EnumArt.values().length)]; // CraftBukkit - generate a non-null painting + } + + public EntityPainting(World world, BlockPosition blockposition, EnumDirection enumdirection) { diff --git a/nms-patches/EntityPig.patch b/nms-patches/EntityPig.patch new file mode 100644 index 00000000..064655a7 --- /dev/null +++ b/nms-patches/EntityPig.patch @@ -0,0 +1,29 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityPig.java 2014-11-27 08:59:46.717421776 +1100 ++++ src/main/java/net/minecraft/server/EntityPig.java 2014-11-27 08:42:10.140850934 +1100 +@@ -1,5 +1,7 @@ + package net.minecraft.server; + ++import org.bukkit.craftbukkit.event.CraftEventFactory; // CraftBukkit ++ + public class EntityPig extends EntityAnimal { + + private final PathfinderGoalPassengerCarrotStick bk; +@@ -111,10 +113,17 @@ + public void onLightningStrike(EntityLightning entitylightning) { + if (!this.world.isStatic) { + EntityPigZombie entitypigzombie = new EntityPigZombie(this.world); ++ ++ // CraftBukkit start ++ if (CraftEventFactory.callPigZapEvent(this, entitylightning, entitypigzombie).isCancelled()) { ++ return; ++ } ++ // CraftBukkit end + + entitypigzombie.setEquipment(0, new ItemStack(Items.GOLDEN_SWORD)); + entitypigzombie.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, this.pitch); +- this.world.addEntity(entitypigzombie); ++ // CraftBukkit - added a reason for spawning this creature ++ this.world.addEntity(entitypigzombie, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.LIGHTNING); + this.die(); + } + } diff --git a/nms-patches/EntityPlayer.patch b/nms-patches/EntityPlayer.patch new file mode 100644 index 00000000..e63348a0 --- /dev/null +++ b/nms-patches/EntityPlayer.patch @@ -0,0 +1,542 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityPlayer.java 2014-11-27 08:59:46.721421758 +1100 ++++ src/main/java/net/minecraft/server/EntityPlayer.java 2014-11-27 08:42:10.164850887 +1100 +@@ -13,6 +13,17 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// CraftBukkit start ++import org.bukkit.Bukkit; ++import org.bukkit.WeatherType; ++import org.bukkit.craftbukkit.CraftWorld; ++import org.bukkit.craftbukkit.entity.CraftPlayer; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.inventory.InventoryType; ++import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; ++// CraftBukkit end ++ + public class EntityPlayer extends EntityHuman implements ICrafting { + + private static final Logger bF = LogManager.getLogger(); +@@ -39,6 +50,18 @@ + public boolean g; + public int ping; + public boolean viewingCredits; ++ ++ // CraftBukkit start ++ public String displayName; ++ public IChatBaseComponent listName; ++ public org.bukkit.Location compassTarget; ++ public int newExp = 0; ++ public int newLevel = 0; ++ public int newTotalExp = 0; ++ public boolean keepLevel = false; ++ public double maxHealthCache; ++ public boolean joining = true; ++ // CraftBukkit end + + public EntityPlayer(MinecraftServer minecraftserver, WorldServer worldserver, GameProfile gameprofile, PlayerInteractManager playerinteractmanager) { + super(worldserver, gameprofile); +@@ -69,7 +92,11 @@ + while (!worldserver.getCubes(this, this.getBoundingBox()).isEmpty() && this.locY < 255.0D) { + this.setPosition(this.locX, this.locY + 1.0D, this.locZ); + } +- ++ // CraftBukkit start ++ this.displayName = this.getName(); ++ // this.canPickUpLoot = true; TODO ++ this.maxHealthCache = this.getMaxHealth(); ++ // CraftBukkit end + } + + public void a(NBTTagCompound nbttagcompound) { +@@ -81,13 +108,39 @@ + this.playerInteractManager.setGameMode(EnumGamemode.getById(nbttagcompound.getInt("playerGameType"))); + } + } +- ++ this.getBukkitEntity().readExtraData(nbttagcompound); // CraftBukkit + } + + public void b(NBTTagCompound nbttagcompound) { + super.b(nbttagcompound); + nbttagcompound.setInt("playerGameType", this.playerInteractManager.getGameMode().getId()); ++ this.getBukkitEntity().setExtraData(nbttagcompound); // CraftBukkit ++ } ++ ++ // CraftBukkit start - World fallback code, either respawn location or global spawn ++ public void spawnIn(World world) { ++ super.spawnIn(world); ++ if (world == null) { ++ this.dead = false; ++ BlockPosition position = null; ++ if (this.spawnWorld != null && !this.spawnWorld.equals("")) { ++ CraftWorld cworld = (CraftWorld) Bukkit.getServer().getWorld(this.spawnWorld); ++ if (cworld != null && this.getBed() != null) { ++ world = cworld.getHandle(); ++ position = EntityHuman.getBed(cworld.getHandle(), this.getBed(), false); ++ } ++ } ++ if (world == null || position == null) { ++ world = ((CraftWorld) Bukkit.getServer().getWorlds().get(0)).getHandle(); ++ position = world.getSpawn(); ++ } ++ this.world = world; ++ this.setPosition(position.getX() + 0.5, position.getY(), position.getZ() + 0.5); ++ } ++ this.dimension = ((WorldServer) this.world).dimension; ++ this.playerInteractManager.a((WorldServer) world); + } ++ // CraftBukkit end + + public void levelDown(int i) { + super.levelDown(i); +@@ -114,6 +167,11 @@ + } + + public void s_() { ++ // CraftBukkit start ++ if (this.joining) { ++ this.joining = false; ++ } ++ // CraftBukkit end + this.playerInteractManager.a(); + --this.invulnerableTicks; + if (this.noDamageTicks > 0) { +@@ -155,7 +213,7 @@ + chunk = this.world.getChunkAt(chunkcoordintpair.x, chunkcoordintpair.z); + if (chunk.isReady()) { + arraylist.add(chunk); +- arraylist1.addAll(((WorldServer) this.world).getTileEntities(chunkcoordintpair.x * 16, 0, chunkcoordintpair.z * 16, chunkcoordintpair.x * 16 + 16, 256, chunkcoordintpair.z * 16 + 16)); ++ arraylist1.addAll(chunk.tileEntities.values()); // CraftBukkit - Get tile entities directly from the chunk instead of the world + iterator1.remove(); + } + } +@@ -220,8 +278,9 @@ + } + } + ++ // CraftBukkit - Optionally scale health + if (this.getHealth() != this.bK || this.bL != this.foodData.getFoodLevel() || this.foodData.getSaturationLevel() == 0.0F != this.bM) { +- this.playerConnection.sendPacket(new PacketPlayOutUpdateHealth(this.getHealth(), this.foodData.getFoodLevel(), this.foodData.getSaturationLevel())); ++ this.playerConnection.sendPacket(new PacketPlayOutUpdateHealth(this.getBukkitEntity().getScaledHealth(), this.foodData.getFoodLevel(), this.foodData.getSaturationLevel())); + this.bK = this.getHealth(); + this.bL = this.foodData.getFoodLevel(); + this.bM = this.foodData.getSaturationLevel() == 0.0F; +@@ -229,15 +288,14 @@ + + if (this.getHealth() + this.getAbsorptionHearts() != this.bJ) { + this.bJ = this.getHealth() + this.getAbsorptionHearts(); +- Collection collection = this.getScoreboard().getObjectivesForCriteria(IScoreboardCriteria.g); +- Iterator iterator = collection.iterator(); +- +- while (iterator.hasNext()) { +- ScoreboardObjective scoreboardobjective = (ScoreboardObjective) iterator.next(); +- +- this.getScoreboard().getPlayerScoreForObjective(this.getName(), scoreboardobjective).updateForList(Arrays.asList(new EntityHuman[] { this})); +- } ++ // CraftBukkit - Update ALL the scores! ++ this.world.getServer().getScoreboardManager().updateAllScoresForList(IScoreboardCriteria.f, this.getName(), com.google.common.collect.ImmutableList.of(this)); ++ } ++ // CraftBukkit start - Force max health updates ++ if (this.maxHealthCache != this.getMaxHealth()) { ++ this.getBukkitEntity().updateScaledHealth(); + } ++ // CraftBukkit end + + if (this.expTotal != this.lastSentExp) { + this.lastSentExp = this.expTotal; +@@ -247,7 +305,17 @@ + if (this.ticksLived % 20 * 5 == 0 && !this.getStatisticManager().hasAchievement(AchievementList.L)) { + this.h_(); + } ++ ++ // CraftBukkit start - initialize oldLevel and fire PlayerLevelChangeEvent ++ if (this.oldLevel == -1) { ++ this.oldLevel = this.expLevel; ++ } + ++ if (this.oldLevel != this.expLevel) { ++ CraftEventFactory.callPlayerLevelChangeEvent(this.world.getServer().getPlayer((EntityPlayer) this), this.oldLevel, this.expLevel); ++ this.oldLevel = this.expLevel; ++ } ++ // CraftBukkit end + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Ticking player"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Player being ticked"); +@@ -296,30 +364,64 @@ + } + + public void die(DamageSource damagesource) { +- if (this.world.getGameRules().getBoolean("showDeathMessages")) { +- ScoreboardTeamBase scoreboardteambase = this.getScoreboardTeam(); ++ // CraftBukkit start - fire PlayerDeathEvent ++ if (this.dead) { ++ return; ++ } ++ ++ java.util.List<org.bukkit.inventory.ItemStack> loot = new java.util.ArrayList<org.bukkit.inventory.ItemStack>(); ++ boolean keepInventory = this.world.getGameRules().getBoolean("keepInventory"); ++ ++ if (!keepInventory) { ++ for (int i = 0; i < this.inventory.items.length; ++i) { ++ if (this.inventory.items[i] != null) { ++ loot.add(CraftItemStack.asCraftMirror(this.inventory.items[i])); ++ } ++ } + +- if (scoreboardteambase != null && scoreboardteambase.j() != EnumNameTagVisibility.ALWAYS) { +- if (scoreboardteambase.j() == EnumNameTagVisibility.HIDE_FOR_OTHER_TEAMS) { +- this.server.getPlayerList().a((EntityHuman) this, this.br().b()); +- } else if (scoreboardteambase.j() == EnumNameTagVisibility.HIDE_FOR_OWN_TEAM) { +- this.server.getPlayerList().b((EntityHuman) this, this.br().b()); ++ for (int i = 0; i < this.inventory.armor.length; ++i) { ++ if (this.inventory.armor[i] != null) { ++ loot.add(CraftItemStack.asCraftMirror(this.inventory.armor[i])); + } ++ } ++ } ++ ++ IChatBaseComponent chatmessage = this.br().b(); ++ ++ String deathmessage = chatmessage.c(); ++ org.bukkit.event.entity.PlayerDeathEvent event = CraftEventFactory.callPlayerDeathEvent(this, loot, deathmessage, keepInventory); ++ ++ String deathMessage = event.getDeathMessage(); ++ ++ if (deathMessage != null && deathMessage.length() > 0 && this.world.getGameRules().getBoolean("showDeathMessages")) { // TODO: allow plugins to override? ++ if (deathMessage.equals(deathmessage)) { ++ this.server.getPlayerList().sendMessage(chatmessage); + } else { +- this.server.getPlayerList().sendMessage(this.br().b()); ++ this.server.getPlayerList().sendMessage(org.bukkit.craftbukkit.util.CraftChatMessage.fromString(deathMessage)); + } + } ++ ++ // we clean the player's inventory after the EntityDeathEvent is called so plugins can get the exact state of the inventory. ++ if (!event.getKeepInventory()) { ++ for (int i = 0; i < this.inventory.items.length; ++i) { ++ this.inventory.items[i] = null; ++ } + +- if (!this.world.getGameRules().getBoolean("keepInventory")) { +- this.inventory.n(); ++ for (int i = 0; i < this.inventory.armor.length; ++i) { ++ this.inventory.armor[i] = null; ++ } + } + +- Collection collection = this.world.getScoreboard().getObjectivesForCriteria(IScoreboardCriteria.d); ++ this.closeInventory(); ++ this.e((Entity) this); // Remove spectated target ++ // CraftBukkit end ++ ++ // CraftBukkit - Get our scores instead ++ Collection collection = this.world.getServer().getScoreboardManager().getScoreboardScores(IScoreboardCriteria.c, this.getName(), new java.util.ArrayList<ScoreboardScore>()); + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { +- ScoreboardObjective scoreboardobjective = (ScoreboardObjective) iterator.next(); +- ScoreboardScore scoreboardscore = this.getScoreboard().getPlayerScoreForObjective(this.getName(), scoreboardobjective); ++ ScoreboardScore scoreboardscore = (ScoreboardScore) iterator.next(); // CraftBukkit - Use our scores instead + + scoreboardscore.incrementScore(); + } +@@ -376,7 +478,8 @@ + } + + private boolean cq() { +- return this.server.getPVP(); ++ // CraftBukkit - this.server.getPvP() -> this.world.pvpMode ++ return this.world.pvpMode; + } + + public void c(int i) { +@@ -388,6 +491,8 @@ + } else { + if (this.dimension == 0 && i == 1) { + this.b((Statistic) AchievementList.C); ++ // CraftBukkit start - Rely on custom portal management ++ /* + BlockPosition blockposition = this.server.getWorldServer(i).getDimensionSpawn(); + + if (blockposition != null) { +@@ -395,11 +500,16 @@ + } + + i = 1; ++ */ ++ // CraftBukkit end + } else { + this.b((Statistic) AchievementList.y); + } + +- this.server.getPlayerList().changeDimension(this, i); ++ // CraftBukkit start ++ TeleportCause cause = (this.dimension == 1 || i == 1) ? TeleportCause.END_PORTAL : TeleportCause.NETHER_PORTAL; ++ this.server.getPlayerList().changeDimension(this, i, cause); ++ // CraftBukkit end + this.lastSentExp = -1; + this.bK = -1.0F; + this.bL = -1; +@@ -442,6 +552,8 @@ + } + + public void a(boolean flag, boolean flag1, boolean flag2) { ++ if (!this.sleeping) return; // CraftBukkit - Can't leave bed if not in one! ++ + if (this.isSleeping()) { + this.u().getTracker().sendPacketToEntity(this, new PacketPlayOutAnimation(this, 2)); + } +@@ -454,14 +566,23 @@ + } + + public void mount(Entity entity) { +- Entity entity1 = this.vehicle; ++ // CraftBukkit start ++ this.setPassengerOf(entity); ++ } ++ ++ public void setPassengerOf(Entity entity) { ++ // mount(null) doesn't really fly for overloaded methods, ++ // so this method is needed ++ Entity currentVehicle = this.vehicle; ++ ++ super.setPassengerOf(entity); + +- super.mount(entity); +- if (entity != entity1) { ++ // Check if the vehicle actually changed. ++ if (currentVehicle != this.vehicle) { + this.playerConnection.sendPacket(new PacketPlayOutAttachEntity(0, this, this.vehicle)); + this.playerConnection.a(this.locX, this.locY, this.locZ, this.yaw, this.pitch); + } +- ++ // CraftBukkit end + } + + protected void a(double d0, boolean flag, Block block, BlockPosition blockposition) {} +@@ -490,19 +611,38 @@ + this.playerConnection.sendPacket(new PacketPlayOutOpenSignEditor(tileentitysign.getPosition())); + } + +- public void nextContainerCounter() { ++ public int nextContainerCounter() { // CraftBukkit - private void -> public int + this.containerCounter = this.containerCounter % 100 + 1; ++ return containerCounter; // CraftBukkit + } + + public void openTileEntity(ITileEntityContainer itileentitycontainer) { ++ // CraftBukkit start - Inventory open hook ++ Container container = CraftEventFactory.callInventoryOpenEvent(this, itileentitycontainer.createContainer(this.inventory, this)); ++ if (container == null) { ++ return; ++ } ++ // CraftBukkit end + this.nextContainerCounter(); + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, itileentitycontainer.getContainerName(), itileentitycontainer.getScoreboardDisplayName())); +- this.activeContainer = itileentitycontainer.createContainer(this.inventory, this); ++ this.activeContainer = container; // CraftBukkit + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + } + + public void openContainer(IInventory iinventory) { ++ // CraftBukkit start - Inventory open hook ++ Container container; ++ if (iinventory instanceof ITileEntityContainer) { ++ container = ((ITileEntityContainer)iinventory).createContainer(this.inventory, this); ++ } else { ++ container = new ContainerChest(this.inventory, iinventory, this); ++ } ++ container = CraftEventFactory.callInventoryOpenEvent(this, container); ++ if (container == null) { ++ return; ++ } ++ // CraftBukkit end + if (this.activeContainer != this.defaultContainer) { + this.closeInventory(); + } +@@ -520,10 +660,10 @@ + this.nextContainerCounter(); + if (iinventory instanceof ITileEntityContainer) { + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, ((ITileEntityContainer) iinventory).getContainerName(), iinventory.getScoreboardDisplayName(), iinventory.getSize())); +- this.activeContainer = ((ITileEntityContainer) iinventory).createContainer(this.inventory, this); ++ this.activeContainer = container; // CraftBukkit + } else { + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, "minecraft:container", iinventory.getScoreboardDisplayName(), iinventory.getSize())); +- this.activeContainer = new ContainerChest(this.inventory, iinventory, this); ++ this.activeContainer = container; // CraftBukkit + } + + this.activeContainer.windowId = this.containerCounter; +@@ -531,8 +671,14 @@ + } + + public void openTrade(IMerchant imerchant) { ++ // CraftBukkit start - Inventory open hook ++ Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerMerchant(this.inventory, imerchant, this.world)); ++ if (container == null) { ++ return; ++ } ++ // CraftBukkit end + this.nextContainerCounter(); +- this.activeContainer = new ContainerMerchant(this.inventory, imerchant, this.world); ++ this.activeContainer = container; // CraftBukkit + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + InventoryMerchant inventorymerchant = ((ContainerMerchant) this.activeContainer).e(); +@@ -552,13 +698,20 @@ + } + + public void openHorseInventory(EntityHorse entityhorse, IInventory iinventory) { ++ // CraftBukkit start - Inventory open hook ++ Container container = CraftEventFactory.callInventoryOpenEvent(this, new ContainerHorse(this.inventory, iinventory, entityhorse, this)); ++ if (container == null) { ++ iinventory.closeContainer(this); ++ return; ++ } ++ // CraftBukkit end + if (this.activeContainer != this.defaultContainer) { + this.closeInventory(); + } + + this.nextContainerCounter(); + this.playerConnection.sendPacket(new PacketPlayOutOpenWindow(this.containerCounter, "EntityHorse", iinventory.getScoreboardDisplayName(), iinventory.getSize(), entityhorse.getId())); +- this.activeContainer = new ContainerHorse(this.inventory, iinventory, entityhorse, this); ++ this.activeContainer = container; + this.activeContainer.windowId = this.containerCounter; + this.activeContainer.addSlotListener(this); + } +@@ -587,6 +740,11 @@ + public void a(Container container, List list) { + this.playerConnection.sendPacket(new PacketPlayOutWindowItems(container.windowId, list)); + this.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.inventory.getCarried())); ++ // CraftBukkit start - Send a Set Slot to update the crafting result slot ++ if (java.util.EnumSet.of(InventoryType.CRAFTING,InventoryType.WORKBENCH).contains(container.getBukkitView().getType())) { ++ this.playerConnection.sendPacket(new PacketPlayOutSetSlot(container.windowId, 0, container.getSlot(0).getItem())); ++ } ++ // CraftBukkit end + } + + public void setContainerData(Container container, int i, int j) { +@@ -601,6 +759,7 @@ + } + + public void closeInventory() { ++ CraftEventFactory.handleInventoryCloseEvent(this); // CraftBukkit + this.playerConnection.sendPacket(new PacketPlayOutCloseWindow(this.activeContainer.windowId)); + this.p(); + } +@@ -681,7 +840,16 @@ + + public void triggerHealthUpdate() { + this.bK = -1.0E8F; ++ this.lastSentExp = -1; // CraftBukkit - Added to reset ++ } ++ ++ // CraftBukkit start - Support multi-line messages ++ public void sendMessage(IChatBaseComponent[] ichatbasecomponent) { ++ for (IChatBaseComponent component : ichatbasecomponent) { ++ this.sendMessage(component); ++ } + } ++ // CraftBukkit end + + public void b(IChatBaseComponent ichatbasecomponent) { + this.playerConnection.sendPacket(new PacketPlayOutChat(ichatbasecomponent)); +@@ -867,6 +1035,93 @@ + } + + public IChatBaseComponent getPlayerListName() { +- return null; ++ return listName; // CraftBukkit ++ } ++ ++ // CraftBukkit start - Add per-player time and weather. ++ public long timeOffset = 0; ++ public boolean relativeTime = true; ++ ++ public long getPlayerTime() { ++ if (this.relativeTime) { ++ // Adds timeOffset to the current server time. ++ return this.world.getDayTime() + this.timeOffset; ++ } else { ++ // Adds timeOffset to the beginning of this day. ++ return this.world.getDayTime() - (this.world.getDayTime() % 24000) + this.timeOffset; ++ } ++ } ++ ++ public WeatherType weather = null; ++ ++ public WeatherType getPlayerWeather() { ++ return this.weather; ++ } ++ ++ public void setPlayerWeather(WeatherType type, boolean plugin) { ++ if (!plugin && this.weather != null) { ++ return; ++ } ++ ++ if (plugin) { ++ this.weather = type; ++ } ++ ++ if (type == WeatherType.DOWNFALL) { ++ this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(2, 0)); ++ // this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(7, this.world.j(1.0F))); ++ // this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(8, this.world.h(1.0F))); ++ } else { ++ this.playerConnection.sendPacket(new PacketPlayOutGameStateChange(1, 0)); ++ } ++ } ++ ++ public void resetPlayerWeather() { ++ this.weather = null; ++ this.setPlayerWeather(this.world.getWorldData().hasStorm() ? WeatherType.DOWNFALL : WeatherType.CLEAR, false); ++ } ++ ++ @Override ++ public String toString() { ++ return super.toString() + "(" + this.getName() + " at " + this.locX + "," + this.locY + "," + this.locZ + ")"; ++ } ++ ++ public void reset() { ++ float exp = 0; ++ boolean keepInventory = this.world.getGameRules().getBoolean("keepInventory"); ++ ++ if (this.keepLevel || keepInventory) { ++ exp = this.exp; ++ this.newTotalExp = this.expTotal; ++ this.newLevel = this.expLevel; ++ } ++ ++ this.setHealth(this.getMaxHealth()); ++ this.fireTicks = 0; ++ this.fallDistance = 0; ++ this.foodData = new FoodMetaData(this); ++ this.expLevel = this.newLevel; ++ this.expTotal = this.newTotalExp; ++ this.exp = 0; ++ this.deathTicks = 0; ++ this.removeAllEffects(); ++ this.updateEffects = true; ++ this.activeContainer = this.defaultContainer; ++ this.killer = null; ++ this.lastDamager = null; ++ this.combatTracker = new CombatTracker(this); ++ this.lastSentExp = -1; ++ if (this.keepLevel || keepInventory) { ++ this.exp = exp; ++ } else { ++ this.giveExp(this.newExp); ++ } ++ this.keepLevel = false; ++ } ++ ++ @Override ++ public CraftPlayer getBukkitEntity() { ++ return (CraftPlayer) super.getBukkitEntity(); + } ++ // CraftBukkit end + } diff --git a/nms-patches/EntityPotion.patch b/nms-patches/EntityPotion.patch new file mode 100644 index 00000000..fd9e7d33 --- /dev/null +++ b/nms-patches/EntityPotion.patch @@ -0,0 +1,70 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityPotion.java 2014-11-27 08:59:46.721421758 +1100 ++++ src/main/java/net/minecraft/server/EntityPotion.java 2014-11-27 08:42:10.112850989 +1100 +@@ -3,6 +3,13 @@ + import java.util.Iterator; + import java.util.List; + ++// CraftBukkit start ++import java.util.HashMap; ++ ++import org.bukkit.craftbukkit.entity.CraftLivingEntity; ++import org.bukkit.entity.LivingEntity; ++// CraftBukkit end ++ + public class EntityPotion extends EntityProjectile { + + public ItemStack item; +@@ -57,12 +64,15 @@ + if (!this.world.isStatic) { + List list = Items.POTION.h(this.item); + +- if (list != null && !list.isEmpty()) { ++ if (true || list != null && !list.isEmpty()) { // CraftBukkit - Call event even if no effects to apply + AxisAlignedBB axisalignedbb = this.getBoundingBox().grow(4.0D, 2.0D, 4.0D); + List list1 = this.world.a(EntityLiving.class, axisalignedbb); + +- if (!list1.isEmpty()) { ++ if (true || !list1.isEmpty()) { // CraftBukkit - Run code even if there are no entities around + Iterator iterator = list1.iterator(); ++ ++ // CraftBukkit ++ HashMap<LivingEntity, Double> affected = new HashMap<LivingEntity, Double>(); + + while (iterator.hasNext()) { + EntityLiving entityliving = (EntityLiving) iterator.next(); +@@ -74,12 +84,35 @@ + if (entityliving == movingobjectposition.entity) { + d1 = 1.0D; + } ++ ++ // CraftBukkit start ++ affected.put((LivingEntity) entityliving.getBukkitEntity(), d1); ++ } ++ } ++ ++ org.bukkit.event.entity.PotionSplashEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPotionSplashEvent(this, affected); ++ if (!event.isCancelled() && list != null && !list.isEmpty()) { // do not process effects if there are no effects to process ++ for (LivingEntity victim : event.getAffectedEntities()) { ++ if (!(victim instanceof CraftLivingEntity)) { ++ continue; ++ } ++ ++ EntityLiving entityliving = ((CraftLivingEntity) victim).getHandle(); ++ double d1 = event.getIntensity(victim); ++ // CraftBukkit end + + Iterator iterator1 = list.iterator(); + + while (iterator1.hasNext()) { + MobEffect mobeffect = (MobEffect) iterator1.next(); + int i = mobeffect.getEffectId(); ++ ++ // CraftBukkit start - Abide by PVP settings - for players only! ++ if (!this.world.pvpMode && this.getShooter() instanceof EntityPlayer && entityliving instanceof EntityPlayer && entityliving != this.getShooter()) { ++ // Block SLOWER_MOVEMENT, SLOWER_DIG, HARM, BLINDNESS, HUNGER, WEAKNESS and POISON potions ++ if (i == 2 || i == 4 || i == 7 || i == 15 || i == 17 || i == 18 || i == 19) continue; ++ } ++ // CraftBukkit end + + if (MobEffectList.byId[i].isInstant()) { + MobEffectList.byId[i].applyInstantEffect(this, this.getShooter(), entityliving, mobeffect.getAmplifier(), d1); diff --git a/nms-patches/EntityProjectile.patch b/nms-patches/EntityProjectile.patch new file mode 100644 index 00000000..bd4789e6 --- /dev/null +++ b/nms-patches/EntityProjectile.patch @@ -0,0 +1,31 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityProjectile.java 2014-11-27 08:59:46.725421741 +1100 ++++ src/main/java/net/minecraft/server/EntityProjectile.java 2014-11-27 08:42:10.140850934 +1100 +@@ -25,6 +25,7 @@ + public EntityProjectile(World world, EntityLiving entityliving) { + super(world); + this.shooter = entityliving; ++ this.projectileSource = (org.bukkit.entity.LivingEntity) entityliving.getBukkitEntity(); // CraftBukkit + this.a(0.25F, 0.25F); + this.setPositionRotation(entityliving.locX, entityliving.locY + (double) entityliving.getHeadHeight(), entityliving.locZ, entityliving.yaw, entityliving.pitch); + this.locX -= (double) (MathHelper.cos(this.yaw / 180.0F * 3.1415927F) * 0.16F); +@@ -130,7 +131,7 @@ + MovingObjectPosition movingobjectposition1 = axisalignedbb.a(vec3d, vec3d1); + + if (movingobjectposition1 != null) { +- double d1 = vec3d.f(movingobjectposition1.pos); ++ double d1 = vec3d.distanceSquared(movingobjectposition1.pos); // CraftBukkit - distance efficiency + + if (d1 < d0 || d0 == 0.0D) { + entity = entity1; +@@ -150,6 +151,11 @@ + this.aq(); + } else { + this.a(movingobjectposition); ++ // CraftBukkit start ++ if (this.dead) { ++ org.bukkit.craftbukkit.event.CraftEventFactory.callProjectileHitEvent(this); ++ } ++ // CraftBukkit end + } + } + diff --git a/nms-patches/EntitySheep.patch b/nms-patches/EntitySheep.patch new file mode 100644 index 00000000..b1737592 --- /dev/null +++ b/nms-patches/EntitySheep.patch @@ -0,0 +1,54 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntitySheep.java 2014-11-27 08:59:46.725421741 +1100 ++++ src/main/java/net/minecraft/server/EntitySheep.java 2014-11-27 08:42:10.124850965 +1100 +@@ -4,6 +4,11 @@ + import java.util.Map; + import java.util.Random; + ++// CraftBukkit start ++import org.bukkit.event.entity.SheepRegrowWoolEvent; ++import org.bukkit.event.player.PlayerShearEntityEvent; ++// CraftBukkit end ++ + public class EntitySheep extends EntityAnimal { + + private final InventoryCrafting bk = new InventoryCrafting(new ContainerSheepBreed(this), 2, 1); +@@ -30,6 +35,7 @@ + this.goalSelector.a(8, new PathfinderGoalRandomLookaround(this)); + this.bk.setItem(0, new ItemStack(Items.DYE, 1, 0)); + this.bk.setItem(1, new ItemStack(Items.DYE, 1, 0)); ++ this.bk.resultInventory = new InventoryCraftResult(); // CraftBukkit - add result slot for event + } + + protected void E() { +@@ -82,6 +88,15 @@ + + if (itemstack != null && itemstack.getItem() == Items.SHEARS && !this.isSheared() && !this.isBaby()) { + if (!this.world.isStatic) { ++ // 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.setSheared(true); + int i = 1 + this.random.nextInt(3); + +@@ -169,7 +184,14 @@ + } + + public void v() { +- this.setSheared(false); ++ // CraftBukkit start ++ SheepRegrowWoolEvent event = new SheepRegrowWoolEvent((org.bukkit.entity.Sheep) this.getBukkitEntity()); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ this.setSheared(false); ++ } ++ // CraftBukkit end + if (this.isBaby()) { + this.setAge(60); + } diff --git a/nms-patches/EntitySkeleton.patch b/nms-patches/EntitySkeleton.patch new file mode 100644 index 00000000..fe603518 --- /dev/null +++ b/nms-patches/EntitySkeleton.patch @@ -0,0 +1,60 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntitySkeleton.java 2014-11-27 08:59:46.725421741 +1100 ++++ src/main/java/net/minecraft/server/EntitySkeleton.java 2014-11-27 08:42:10.136850942 +1100 +@@ -2,6 +2,8 @@ + + import java.util.Calendar; + ++import org.bukkit.event.entity.EntityCombustEvent; // CraftBukkit ++ + public class EntitySkeleton extends EntityMonster implements IRangedEntity { + + private PathfinderGoalArrowAttack b = new PathfinderGoalArrowAttack(this, 1.0D, 20, 60, 15.0F); +@@ -90,7 +92,14 @@ + } + + if (flag) { +- this.setOnFire(8); ++ // CraftBukkit start ++ EntityCombustEvent event = new EntityCombustEvent(this.getBukkitEntity(), 8); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ this.setOnFire(event.getDuration()); ++ } ++ // CraftBukkit end + } + } + } +@@ -225,11 +234,30 @@ + } + + if (EnchantmentManager.getEnchantmentLevel(Enchantment.ARROW_FIRE.id, this.bz()) > 0 || this.getSkeletonType() == 1) { +- entityarrow.setOnFire(100); ++ // CraftBukkit start - call EntityCombustEvent ++ EntityCombustEvent event = new EntityCombustEvent(entityarrow.getBukkitEntity(), 100); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ entityarrow.setOnFire(event.getDuration()); ++ } ++ // CraftBukkit end ++ } ++ ++ // CraftBukkit start ++ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(this, this.bz(), entityarrow, 0.8F); ++ if (event.isCancelled()) { ++ event.getProjectile().remove(); ++ return; ++ } ++ ++ if (event.getProjectile() == entityarrow.getBukkitEntity()) { ++ world.addEntity(entityarrow); + } ++ // CraftBukkit end + + this.makeSound("random.bow", 1.0F, 1.0F / (this.bb().nextFloat() * 0.4F + 0.8F)); +- this.world.addEntity(entityarrow); ++ // this.world.addEntity(entityarrow); // CraftBukkit - moved up + } + + public int getSkeletonType() { diff --git a/nms-patches/EntitySlime.patch b/nms-patches/EntitySlime.patch new file mode 100644 index 00000000..02d48970 --- /dev/null +++ b/nms-patches/EntitySlime.patch @@ -0,0 +1,40 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntitySlime.java 2014-11-27 08:59:46.729421723 +1100 ++++ src/main/java/net/minecraft/server/EntitySlime.java 2014-11-27 08:42:10.100851012 +1100 +@@ -1,5 +1,9 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.event.entity.SlimeSplitEvent; ++// CraftBukkit end ++ + public class EntitySlime extends EntityInsentient implements IMonster { + + public float a; +@@ -132,6 +136,18 @@ + + if (!this.world.isStatic && i > 1 && this.getHealth() <= 0.0F) { + int j = 2 + this.random.nextInt(3); ++ ++ // CraftBukkit start ++ SlimeSplitEvent event = new SlimeSplitEvent((org.bukkit.entity.Slime) this.getBukkitEntity(), j); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled() && event.getCount() > 0) { ++ j = event.getCount(); ++ } else { ++ super.die(); ++ return; ++ } ++ // CraftBukkit end + + for (int k = 0; k < j; ++k) { + float f = ((float) (k % 2) - 0.5F) * (float) i / 4.0F; +@@ -148,7 +164,7 @@ + + entityslime.setSize(i / 2); + entityslime.setPositionRotation(this.locX + (double) f, this.locY + 0.5D, this.locZ + (double) f1, this.random.nextFloat() * 360.0F, 0.0F); +- this.world.addEntity(entityslime); ++ this.world.addEntity(entityslime, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SLIME_SPLIT); // CraftBukkit - SpawnReason + } + } + diff --git a/nms-patches/EntitySmallFireball.patch b/nms-patches/EntitySmallFireball.patch new file mode 100644 index 00000000..7d2e7589 --- /dev/null +++ b/nms-patches/EntitySmallFireball.patch @@ -0,0 +1,39 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntitySmallFireball.java 2014-11-27 08:59:46.729421723 +1100 ++++ src/main/java/net/minecraft/server/EntitySmallFireball.java 2014-11-27 08:42:10.152850911 +1100 +@@ -1,5 +1,7 @@ + package net.minecraft.server; + ++import org.bukkit.event.entity.EntityCombustByEntityEvent; // CraftBukkit ++ + public class EntitySmallFireball extends EntityFireball { + + public EntitySmallFireball(World world) { +@@ -26,7 +28,14 @@ + if (flag) { + this.a(this.shooter, movingobjectposition.entity); + if (!movingobjectposition.entity.isFireProof()) { +- movingobjectposition.entity.setOnFire(5); ++ // CraftBukkit start - Entity damage by entity event + combust event ++ EntityCombustByEntityEvent event = new EntityCombustByEntityEvent((org.bukkit.entity.Projectile) this.getBukkitEntity(), movingobjectposition.entity.getBukkitEntity(), 5); ++ movingobjectposition.entity.world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ movingobjectposition.entity.setOnFire(event.getDuration()); ++ } ++ // CraftBukkit end + } + } + } else { +@@ -39,7 +48,11 @@ + BlockPosition blockposition = movingobjectposition.a().shift(movingobjectposition.direction); + + if (this.world.isEmpty(blockposition)) { +- this.world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData()); ++ // CraftBukkit start ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this).isCancelled()) { ++ this.world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData()); ++ } ++ // CraftBukkit end + } + } + } diff --git a/nms-patches/EntitySnowman.patch b/nms-patches/EntitySnowman.patch new file mode 100644 index 00000000..67e4371f --- /dev/null +++ b/nms-patches/EntitySnowman.patch @@ -0,0 +1,42 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntitySnowman.java 2014-11-27 08:59:46.733421706 +1100 ++++ src/main/java/net/minecraft/server/EntitySnowman.java 2014-11-27 08:42:10.144850927 +1100 +@@ -1,5 +1,11 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.util.CraftMagicNumbers; ++import org.bukkit.event.block.EntityBlockFormEvent; ++// CraftBukkit end ++ + public class EntitySnowman extends EntityGolem implements IRangedEntity { + + public EntitySnowman(World world) { +@@ -31,7 +37,7 @@ + } + + if (this.world.getBiome(new BlockPosition(i, 0, k)).a(new BlockPosition(i, j, k)) > 1.0F) { +- this.damageEntity(DamageSource.BURN, 1.0F); ++ this.damageEntity(CraftEventFactory.MELTING, 1.0F); // CraftBukkit - DamageSource.BURN -> CraftEventFactory.MELTING + } + + for (int l = 0; l < 4; ++l) { +@@ -39,7 +45,17 @@ + j = MathHelper.floor(this.locY); + k = MathHelper.floor(this.locZ + (double) ((float) (l / 2 % 2 * 2 - 1) * 0.25F)); + if (this.world.getType(new BlockPosition(i, j, k)).getBlock().getMaterial() == Material.AIR && this.world.getBiome(new BlockPosition(i, 0, k)).a(new BlockPosition(i, j, k)) < 0.8F && Blocks.SNOW_LAYER.canPlace(this.world, new BlockPosition(i, j, k))) { +- this.world.setTypeUpdate(new BlockPosition(i, j, k), Blocks.SNOW_LAYER.getBlockData()); ++ // CraftBukkit start ++ org.bukkit.block.BlockState blockState = this.world.getWorld().getBlockAt(i, j, k).getState(); ++ blockState.setType(CraftMagicNumbers.getMaterial(Blocks.SNOW_LAYER)); ++ ++ EntityBlockFormEvent event = new EntityBlockFormEvent(this.getBukkitEntity(), blockState.getBlock(), blockState); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if(!event.isCancelled()) { ++ blockState.update(true); ++ } ++ // CraftBukkit end + } + } + } diff --git a/nms-patches/EntitySpider.patch b/nms-patches/EntitySpider.patch new file mode 100644 index 00000000..c9c70dc8 --- /dev/null +++ b/nms-patches/EntitySpider.patch @@ -0,0 +1,11 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntitySpider.java 2014-11-27 08:59:46.733421706 +1100 ++++ src/main/java/net/minecraft/server/EntitySpider.java 2014-11-27 08:42:10.096851020 +1100 +@@ -107,7 +107,7 @@ + + entityskeleton.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, 0.0F); + entityskeleton.prepare(difficultydamagescaler, (GroupDataEntity) null); +- this.world.addEntity(entityskeleton); ++ this.world.addEntity(entityskeleton, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.JOCKEY); // CraftBukkit - add SpawnReason + entityskeleton.mount(this); + } + diff --git a/nms-patches/EntitySquid.patch b/nms-patches/EntitySquid.patch new file mode 100644 index 00000000..83d481ae --- /dev/null +++ b/nms-patches/EntitySquid.patch @@ -0,0 +1,37 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntitySquid.java 2014-11-27 08:59:46.733421706 +1100 ++++ src/main/java/net/minecraft/server/EntitySquid.java 2014-11-27 08:42:10.156850903 +1100 +@@ -1,5 +1,7 @@ + package net.minecraft.server; + ++import org.bukkit.craftbukkit.TrigMath; // CraftBukkit ++ + public class EntitySquid extends EntityWaterAnimal { + + public float a; +@@ -67,9 +69,11 @@ + + } + ++ /* CraftBukkit start - Delegate to Entity to use existing inWater value + public boolean V() { + return this.world.a(this.getBoundingBox().grow(0.0D, -0.6000000238418579D, 0.0D), Material.WATER, (Entity) this); + } ++ // CraftBukkit end */ + + public void m() { + super.m(); +@@ -116,10 +120,12 @@ + } + + f = MathHelper.sqrt(this.motX * this.motX + this.motZ * this.motZ); +- this.aG += (-((float) Math.atan2(this.motX, this.motZ)) * 180.0F / 3.1415927F - this.aG) * 0.1F; ++ // CraftBukkit - Math -> TrigMath ++ this.aG += (-((float) TrigMath.atan2(this.motX, this.motZ)) * 180.0F / 3.1415927F - this.aG) * 0.1F; + this.yaw = this.aG; + this.c = (float) ((double) this.c + 3.141592653589793D * (double) this.bp * 1.5D); +- this.a += (-((float) Math.atan2((double) f, this.motY)) * 180.0F / 3.1415927F - this.a) * 0.1F; ++ // CraftBukkit - Math -> TrigMath ++ this.a += (-((float) TrigMath.atan2((double) f, this.motY)) * 180.0F / 3.1415927F - this.a) * 0.1F; + } else { + this.bl = MathHelper.e(MathHelper.sin(this.bj)) * 3.1415927F * 0.25F; + if (!this.world.isStatic) { diff --git a/nms-patches/EntityTNTPrimed.patch b/nms-patches/EntityTNTPrimed.patch new file mode 100644 index 00000000..96c3399a --- /dev/null +++ b/nms-patches/EntityTNTPrimed.patch @@ -0,0 +1,52 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityTNTPrimed.java 2014-11-27 08:59:46.737421688 +1100 ++++ src/main/java/net/minecraft/server/EntityTNTPrimed.java 2014-11-27 08:42:10.120850973 +1100 +@@ -1,9 +1,13 @@ + package net.minecraft.server; + ++import org.bukkit.event.entity.ExplosionPrimeEvent; // CraftBukkit ++ + public class EntityTNTPrimed extends Entity { + + public int fuseTicks; + private EntityLiving source; ++ public float yield = 4; // CraftBukkit - add field ++ public boolean isIncendiary = false; // CraftBukkit - add field + + public EntityTNTPrimed(World world) { + super(world); +@@ -52,10 +56,13 @@ + } + + if (this.fuseTicks-- <= 0) { +- this.die(); ++ // CraftBukkit start - Need to reverse the order of the explosion and the entity death so we have a location for the event ++ // this.die(); + if (!this.world.isStatic) { + this.explode(); + } ++ this.die(); ++ // CraftBukkit end + } else { + this.W(); + this.world.addParticle(EnumParticle.SMOKE_NORMAL, this.locX, this.locY + 0.5D, this.locZ, 0.0D, 0.0D, 0.0D, new int[0]); +@@ -64,9 +71,18 @@ + } + + private void explode() { +- float f = 4.0F; ++ // CraftBukkit start ++ // float f = 4.0F; + +- this.world.explode(this, this.locX, this.locY + (double) (this.length / 2.0F), this.locZ, f, true); ++ org.bukkit.craftbukkit.CraftServer server = this.world.getServer(); ++ ++ ExplosionPrimeEvent event = new ExplosionPrimeEvent((org.bukkit.entity.Explosive) org.bukkit.craftbukkit.entity.CraftEntity.getEntity(server, this)); ++ server.getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ this.world.createExplosion(this, this.locX, this.locY + (double) (this.length / 2.0F), this.locZ, event.getRadius(), event.getFire(), true); ++ } ++ // CraftBukkit end + } + + protected void b(NBTTagCompound nbttagcompound) { diff --git a/nms-patches/EntityThrownExpBottle.patch b/nms-patches/EntityThrownExpBottle.patch new file mode 100644 index 00000000..c6b40039 --- /dev/null +++ b/nms-patches/EntityThrownExpBottle.patch @@ -0,0 +1,22 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityThrownExpBottle.java 2014-11-27 08:59:46.737421688 +1100 ++++ src/main/java/net/minecraft/server/EntityThrownExpBottle.java 2014-11-27 08:42:10.112850989 +1100 +@@ -28,8 +28,17 @@ + + protected void a(MovingObjectPosition movingobjectposition) { + if (!this.world.isStatic) { +- this.world.triggerEffect(2002, new BlockPosition(this), 0); +- int i = 3 + this.world.random.nextInt(5) + this.world.random.nextInt(5); ++ // CraftBukkit - moved to after event ++ // this.world.triggerEffect(2002, new BlockPosition(this), 0); ++ int i = 3 + this.world.random.nextInt(5) + this.world.random.nextInt(5); ++ ++ // CraftBukkit start ++ org.bukkit.event.entity.ExpBottleEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callExpBottleEvent(this, i); ++ i = event.getExperience(); ++ if (event.getShowEffect()) { ++ this.world.triggerEffect(2002, new BlockPosition(this), 0); ++ } ++ // CraftBukkit end + + while (i > 0) { + int j = EntityExperienceOrb.getOrbValue(i); diff --git a/nms-patches/EntityTrackerEntry.patch b/nms-patches/EntityTrackerEntry.patch new file mode 100644 index 00000000..9d0f38de --- /dev/null +++ b/nms-patches/EntityTrackerEntry.patch @@ -0,0 +1,176 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityTrackerEntry.java 2014-11-27 08:59:46.741421670 +1100 ++++ src/main/java/net/minecraft/server/EntityTrackerEntry.java 2014-11-27 08:42:10.136850942 +1100 +@@ -8,6 +8,11 @@ + 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 p = LogManager.getLogger(); +@@ -74,13 +79,13 @@ + this.broadcast(new PacketPlayOutAttachEntity(0, this.tracker, this.tracker.vehicle)); + } + +- if (this.tracker instanceof EntityItemFrame && this.m % 10 == 0) { ++ if (this.tracker instanceof EntityItemFrame /*&& this.m % 10 == 0*/) { // CraftBukkit - Moved below, should always enter this block + EntityItemFrame entityitemframe = (EntityItemFrame) this.tracker; + ItemStack itemstack = entityitemframe.getItem(); + +- if (itemstack != null && itemstack.getItem() instanceof ItemWorldMap) { ++ if (this.m % 10 == 0 && itemstack != null && itemstack.getItem() instanceof ItemWorldMap) { // CraftBukkit - Moved this.m % 10 logic here so item frames do not enter the other blocks + WorldMap worldmap = Items.FILLED_MAP.getSavedMap(itemstack, this.tracker.world); +- Iterator iterator = list.iterator(); ++ Iterator iterator = this.trackedPlayers.iterator(); // CraftBukkit + + while (iterator.hasNext()) { + EntityHuman entityhuman = (EntityHuman) iterator.next(); +@@ -115,6 +120,19 @@ + Object object = null; + boolean flag = Math.abs(j1) >= 4 || Math.abs(k1) >= 4 || Math.abs(l1) >= 4 || this.m % 60 == 0; + boolean flag1 = Math.abs(l - this.yRot) >= 4 || Math.abs(i1 - this.xRot) >= 4; ++ ++ // CraftBukkit start - Code moved from below ++ if (flag) { ++ this.xLoc = i; ++ this.yLoc = j; ++ this.zLoc = k; ++ } ++ ++ if (flag1) { ++ this.yRot = l; ++ this.xRot = i1; ++ } ++ // CraftBukkit end + + if (this.m > 0 || this.tracker instanceof EntityArrow) { + if (j1 >= -128 && j1 < 128 && k1 >= -128 && k1 < 128 && l1 >= -128 && l1 < 128 && this.v <= 400 && !this.x && this.y == this.tracker.onGround) { +@@ -128,6 +146,11 @@ + } 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 + object = new PacketPlayOutEntityTeleport(this.tracker.getId(), i, j, k, (byte) l, (byte) i1, this.tracker.onGround); + } + } +@@ -152,6 +175,7 @@ + } + + this.b(); ++ /* CraftBukkit start - Code moved up + if (flag) { + this.xLoc = i; + this.yLoc = j; +@@ -162,6 +186,7 @@ + this.yRot = l; + this.xRot = i1; + } ++ // CraftBukkit end */ + + this.x = false; + } else { +@@ -193,7 +218,27 @@ + + ++this.m; + if (this.tracker.velocityChanged) { +- this.broadcastIncludingSelf(new PacketPlayOutEntityVelocity(this.tracker)); ++ // 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); ++ this.tracker.world.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ cancelled = true; ++ } else if (!velocity.equals(event.getVelocity())) { ++ player.setVelocity(velocity); ++ } ++ } ++ ++ if (!cancelled) { ++ this.broadcastIncludingSelf(new PacketPlayOutEntityVelocity(this.tracker)); ++ } ++ // CraftBukkit end + this.tracker.velocityChanged = false; + } + +@@ -211,6 +256,11 @@ + 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)); + } + +@@ -260,7 +310,17 @@ + public void updatePlayer(EntityPlayer entityplayer) { + if (entityplayer != this.tracker) { + if (this.c(entityplayer)) { +- if (!this.trackedPlayers.contains(entityplayer) && (this.e(entityplayer) || this.tracker.attachedToPlayer)) { ++ 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.trackedPlayers.add(entityplayer); + Packet packet = this.c(); + +@@ -278,6 +338,12 @@ + 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)); +@@ -316,6 +382,11 @@ + entityplayer.playerConnection.sendPacket(new PacketPlayOutBed(entityhuman, new BlockPosition(this.tracker))); + } + } ++ ++ // CraftBukkit start - Fix for nonsensical head yaw ++ this.i = MathHelper.d(this.tracker.getHeadRotation() * 256.0F / 360.0F); ++ this.broadcast(new PacketPlayOutEntityHeadRotation(this.tracker, (byte) i)); ++ // CraftBukkit end + + if (this.tracker instanceof EntityLiving) { + EntityLiving entityliving = (EntityLiving) this.tracker; +@@ -356,7 +427,10 @@ + + private Packet c() { + if (this.tracker.dead) { +- EntityTrackerEntry.p.warn("Fetching addPacket for removed entity"); ++ // CraftBukkit start - Remove useless error spam, just return ++ // EntityTrackerEntry.p.warn("Fetching addPacket for removed entity"); ++ return null; ++ // CraftBukkit end + } + + if (this.tracker instanceof EntityItem) { diff --git a/nms-patches/EntityVillager.patch b/nms-patches/EntityVillager.patch new file mode 100644 index 00000000..e2b327ae --- /dev/null +++ b/nms-patches/EntityVillager.patch @@ -0,0 +1,19 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityVillager.java 2014-11-27 08:59:46.741421670 +1100 ++++ src/main/java/net/minecraft/server/EntityVillager.java 2014-11-27 08:42:10.144850927 +1100 +@@ -1,6 +1,7 @@ + package net.minecraft.server; + + import java.util.Iterator; ++import org.bukkit.craftbukkit.entity.CraftVillager; + + public class EntityVillager extends EntityAgeable implements NPC, IMerchant { + +@@ -28,7 +29,7 @@ + + public EntityVillager(World world, int i) { + super(world); +- this.inventory = new InventorySubcontainer("Items", false, 8); ++ this.inventory = new InventorySubcontainer("Items", false, 8, (CraftVillager) this.getBukkitEntity()); // CraftBukkit add argument + this.setProfession(i); + this.a(0.6F, 1.8F); + ((Navigation) this.getNavigation()).b(true); diff --git a/nms-patches/EntityWither.patch b/nms-patches/EntityWither.patch new file mode 100644 index 00000000..7caa58e9 --- /dev/null +++ b/nms-patches/EntityWither.patch @@ -0,0 +1,78 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityWither.java 2014-11-27 08:59:46.745421653 +1100 ++++ src/main/java/net/minecraft/server/EntityWither.java 2014-11-27 08:42:10.156850903 +1100 +@@ -5,6 +5,12 @@ + import java.util.Iterator; + import java.util.List; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.EntityRegainHealthEvent; ++import org.bukkit.event.entity.ExplosionPrimeEvent; ++// CraftBukkit end ++ + public class EntityWither extends EntityMonster implements IRangedEntity { + + private float[] b = new float[2]; +@@ -160,13 +166,38 @@ + if (this.cj() > 0) { + i = this.cj() - 1; + if (i <= 0) { +- this.world.createExplosion(this, this.locX, this.locY + (double) this.getHeadHeight(), this.locZ, 7.0F, false, this.world.getGameRules().getBoolean("mobGriefing")); +- this.world.a(1013, new BlockPosition(this), 0); ++ // CraftBukkit start ++ // this.world.createExplosion(this, this.locX, this.locY + (double) this.getHeadHeight(), this.locZ, 7.0F, false, this.world.getGameRules().getBoolean("mobGriefing")); ++ ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), 7.0F, false); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ this.world.createExplosion(this, this.locX, this.locY + (double) this.getHeadHeight(), this.locZ, event.getRadius(), event.getFire(), this.world.getGameRules().getBoolean("mobGriefing")); ++ } ++ // CraftBukkit end ++ ++ // CraftBukkit start - Use relative location for far away sounds ++ // this.world.a(1013, new BlockPosition(this), 0); ++ int viewDistance = ((WorldServer) this.world).getServer().getViewDistance() * 16; ++ for (EntityPlayer player : (List<EntityPlayer>) this.world.players) { ++ double deltaX = this.locX - player.locX; ++ double deltaZ = this.locZ - player.locZ; ++ double distanceSquared = deltaX * deltaX + deltaZ * deltaZ; ++ if (distanceSquared > viewDistance * viewDistance) { ++ double deltaLength = Math.sqrt(distanceSquared); ++ double relativeX = player.locX + (deltaX / deltaLength) * viewDistance; ++ double relativeZ = player.locZ + (deltaZ / deltaLength) * viewDistance; ++ player.playerConnection.sendPacket(new PacketPlayOutWorldEvent(1013, new BlockPosition((int) relativeX, (int) this.locY, (int) relativeZ), 0, true)); ++ } else { ++ player.playerConnection.sendPacket(new PacketPlayOutWorldEvent(1013, new BlockPosition((int) this.locX, (int) this.locY, (int) this.locZ), 0, true)); ++ } ++ } ++ // CraftBukkit end + } + + this.r(i); + if (this.ticksLived % 10 == 0) { +- this.heal(10.0F); ++ this.heal(10.0F, EntityRegainHealthEvent.RegainReason.WITHER_SPAWN); // CraftBukkit + } + + } else { +@@ -251,6 +282,11 @@ + Block block = this.world.getType(new BlockPosition(j2, k2, l2)).getBlock(); + + if (block.getMaterial() != Material.AIR && block != Blocks.BEDROCK && block != Blocks.END_PORTAL && block != Blocks.END_PORTAL_FRAME && block != Blocks.COMMAND_BLOCK && block != Blocks.BARRIER) { ++ // CraftBukkit start ++ if (CraftEventFactory.callEntityChangeBlockEvent(this, j2, k2, l2, Blocks.AIR, 0).isCancelled()) { ++ continue; ++ } ++ // CraftBukkit end + flag = this.world.setAir(new BlockPosition(j2, k2, l2), true) || flag; + } + } +@@ -264,7 +300,7 @@ + } + + if (this.ticksLived % 20 == 0) { +- this.heal(1.0F); ++ this.heal(1.0F, EntityRegainHealthEvent.RegainReason.REGEN); // CraftBukkit + } + + } diff --git a/nms-patches/EntityWitherSkull.patch b/nms-patches/EntityWitherSkull.patch new file mode 100644 index 00000000..1428bf51 --- /dev/null +++ b/nms-patches/EntityWitherSkull.patch @@ -0,0 +1,36 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityWitherSkull.java 2014-11-27 08:59:46.745421653 +1100 ++++ src/main/java/net/minecraft/server/EntityWitherSkull.java 2014-11-27 08:42:10.120850973 +1100 +@@ -1,5 +1,7 @@ + package net.minecraft.server; + ++import org.bukkit.event.entity.ExplosionPrimeEvent; // CraftBukkit ++ + public class EntityWitherSkull extends EntityFireball { + + public EntityWitherSkull(World world) { +@@ -36,7 +38,7 @@ + if (this.shooter != null) { + if (movingobjectposition.entity.damageEntity(DamageSource.mobAttack(this.shooter), 8.0F)) { + if (!movingobjectposition.entity.isAlive()) { +- this.shooter.heal(5.0F); ++ this.shooter.heal(5.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.WITHER); // CraftBukkit + } else { + this.a(this.shooter, movingobjectposition.entity); + } +@@ -60,7 +62,15 @@ + } + } + +- this.world.createExplosion(this, this.locX, this.locY, this.locZ, 1.0F, false, this.world.getGameRules().getBoolean("mobGriefing")); ++ // CraftBukkit start ++ // this.world.createExplosion(this, this.locX, this.locY, this.locZ, 1.0F, false, this.world.getGameRules().getBoolean("mobGriefing")); ++ ExplosionPrimeEvent event = new ExplosionPrimeEvent(this.getBukkitEntity(), 1.0F, false); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ this.world.createExplosion(this, this.locX, this.locY, this.locZ, event.getRadius(), event.getFire(), this.world.getGameRules().getBoolean("mobGriefing")); ++ } ++ // CraftBukkit end + this.die(); + } + diff --git a/nms-patches/EntityWolf.patch b/nms-patches/EntityWolf.patch new file mode 100644 index 00000000..1621a484 --- /dev/null +++ b/nms-patches/EntityWolf.patch @@ -0,0 +1,87 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityWolf.java 2014-11-27 08:59:46.749421635 +1100 ++++ src/main/java/net/minecraft/server/EntityWolf.java 2014-11-27 08:42:10.160850895 +1100 +@@ -1,5 +1,10 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.EntityTargetEvent.TargetReason; ++// CraftBukkit end ++ + public class EntityWolf extends EntityTameableAnimal { + + private float bm; +@@ -51,8 +56,19 @@ + } else if (!this.isTamed()) { + this.setAngry(true); + } ++ } + ++ // CraftBukkit - add overriden version ++ @Override ++ public void setGoalTarget(EntityLiving entityliving, org.bukkit.event.entity.EntityTargetEvent.TargetReason reason, boolean fire) { ++ super.setGoalTarget(entityliving, reason, fire); ++ if (entityliving == null) { ++ this.setAngry(false); ++ } else if (!this.isTamed()) { ++ this.setAngry(true); ++ } + } ++ // CraftBukkit end + + protected void E() { + this.datawatcher.watch(18, Float.valueOf(this.getHealth())); +@@ -85,7 +101,8 @@ + } + + protected String z() { +- return this.isAngry() ? "mob.wolf.growl" : (this.random.nextInt(3) == 0 ? (this.isTamed() && this.datawatcher.getFloat(18) < 10.0F ? "mob.wolf.whine" : "mob.wolf.panting") : "mob.wolf.bark"); ++ // CraftBukkit - (getFloat(18) < 10) -> (getFloat(18) < this.getMaxHealth() / 2) ++ return this.isAngry() ? "mob.wolf.growl" : (this.random.nextInt(3) == 0 ? (this.isTamed() && this.datawatcher.getFloat(18) < this.getMaxHealth() / 2 ? "mob.wolf.whine" : "mob.wolf.panting") : "mob.wolf.bark"); + } + + protected String bn() { +@@ -219,7 +236,7 @@ + --itemstack.count; + } + +- this.heal((float) itemfood.getNutrition(itemstack)); ++ this.heal((float) itemfood.getNutrition(itemstack), org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.EATING); // CraftBukkit + if (itemstack.count <= 0) { + entityhuman.inventory.setItem(entityhuman.inventory.itemInHandIndex, (ItemStack) null); + } +@@ -244,7 +261,7 @@ + this.bk.setSitting(!this.isSitting()); + this.aW = false; + this.navigation.n(); +- this.setGoalTarget((EntityLiving) null); ++ this.setGoalTarget((EntityLiving) null, TargetReason.FORGOT_TARGET, true); // CraftBukkit - reason + } + } else if (itemstack != null && itemstack.getItem() == Items.BONE && !this.isAngry()) { + if (!entityhuman.abilities.canInstantlyBuild) { +@@ -256,12 +273,13 @@ + } + + if (!this.world.isStatic) { +- if (this.random.nextInt(3) == 0) { ++ // CraftBukkit - added event call and isCancelled check. ++ if (this.random.nextInt(3) == 0 && !CraftEventFactory.callEntityTameEvent(this, entityhuman).isCancelled()) { + this.setTamed(true); + this.navigation.n(); +- this.setGoalTarget((EntityLiving) null); ++ this.setGoalTarget((EntityLiving) null, TargetReason.FORGOT_TARGET, true); + this.bk.setSitting(true); +- this.setHealth(20.0F); ++ this.setHealth(this.getMaxHealth()); // CraftBukkit - 20.0 -> getMaxHealth() + this.setOwnerUUID(entityhuman.getUniqueID().toString()); + this.l(true); + this.world.broadcastEntityEffect(this, (byte) 7); +@@ -348,7 +366,7 @@ + } + + protected boolean isTypeNotPersistent() { +- return !this.isTamed() && this.ticksLived > 2400; ++ return !this.isTamed() /*&& this.ticksLived > 2400*/; // CraftBukkit + } + + public boolean a(EntityLiving entityliving, EntityLiving entityliving1) { diff --git a/nms-patches/EntityZombie.patch b/nms-patches/EntityZombie.patch new file mode 100644 index 00000000..634ca939 --- /dev/null +++ b/nms-patches/EntityZombie.patch @@ -0,0 +1,108 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/EntityZombie.java 2014-11-27 08:59:46.749421635 +1100 ++++ src/main/java/net/minecraft/server/EntityZombie.java 2014-11-27 08:42:10.144850927 +1100 +@@ -4,6 +4,14 @@ + import java.util.List; + import java.util.UUID; + ++//CraftBukkit start ++import org.bukkit.craftbukkit.entity.CraftLivingEntity; ++import org.bukkit.event.entity.CreatureSpawnEvent; ++import org.bukkit.event.entity.EntityCombustByEntityEvent; ++import org.bukkit.event.entity.EntityCombustEvent; ++import org.bukkit.event.entity.EntityTargetEvent; ++//CraftBukkit end ++ + public class EntityZombie extends EntityMonster { + + protected static final IAttribute b = (new AttributeRanged((IAttribute) null, "zombie.spawnReinforcements", 0.0D, 0.0D, 1.0D)).a("Spawn Reinforcements Chance"); +@@ -14,6 +22,7 @@ + private boolean bn = false; + private float bo = -1.0F; + private float bp; ++ private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field + + public EntityZombie(World world) { + super(world); +@@ -136,7 +145,14 @@ + } + + if (flag) { +- this.setOnFire(8); ++ // CraftBukkit start ++ EntityCombustEvent event = new EntityCombustEvent(this.getBukkitEntity(), 8); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ this.setOnFire(event.getDuration()); ++ } ++ // CraftBukkit end + } + } + } +@@ -170,8 +186,8 @@ + if (World.a((IBlockAccess) this.world, new BlockPosition(i1, j1 - 1, k1)) && this.world.getLightLevel(new BlockPosition(i1, j1, k1)) < 10) { + entityzombie.setPosition((double) i1, (double) j1, (double) k1); + if (!this.world.isPlayerNearby((double) i1, (double) j1, (double) k1, 7.0D) && this.world.a(entityzombie.getBoundingBox(), (Entity) entityzombie) && this.world.getCubes(entityzombie, entityzombie.getBoundingBox()).isEmpty() && !this.world.containsLiquid(entityzombie.getBoundingBox())) { +- this.world.addEntity(entityzombie); +- entityzombie.setGoalTarget(entityliving); ++ this.world.addEntity(entityzombie, CreatureSpawnEvent.SpawnReason.REINFORCEMENTS); // CraftBukkit ++ entityzombie.setGoalTarget(entityliving, EntityTargetEvent.TargetReason.REINFORCEMENT_TARGET, true); + entityzombie.prepare(this.world.E(new BlockPosition(entityzombie)), (GroupDataEntity) null); + this.getAttributeInstance(EntityZombie.b).b(new AttributeModifier("Zombie reinforcement caller charge", -0.05000000074505806D, 0)); + entityzombie.getAttributeInstance(EntityZombie.b).b(new AttributeModifier("Zombie reinforcement callee charge", -0.05000000074505806D, 0)); +@@ -190,6 +206,12 @@ + public void s_() { + if (!this.world.isStatic && this.cn()) { + int i = this.cp(); ++ ++ // CraftBukkit start - Use wall time instead of ticks for villager conversion ++ int elapsedTicks = MinecraftServer.currentTick - this.lastTick; ++ this.lastTick = MinecraftServer.currentTick; ++ i *= elapsedTicks; ++ // CraftBukkit end + + this.bm -= i; + if (this.bm <= 0) { +@@ -207,7 +229,14 @@ + int i = this.world.getDifficulty().a(); + + if (this.bz() == null && this.isBurning() && this.random.nextFloat() < (float) i * 0.3F) { +- entity.setOnFire(2 * i); ++ // CraftBukkit start ++ EntityCombustByEntityEvent event = new EntityCombustByEntityEvent(this.getBukkitEntity(), entity.getBukkitEntity(), 2 * i); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ entity.setOnFire(event.getDuration()); ++ } ++ // CraftBukkit end + } + } + +@@ -316,7 +345,7 @@ + entityzombie.setBaby(true); + } + +- this.world.addEntity(entityzombie); ++ this.world.addEntity(entityzombie, CreatureSpawnEvent.SpawnReason.INFECTION); // CraftBukkit - add SpawnReason + this.world.a((EntityHuman) null, 1016, new BlockPosition((int) this.locX, (int) this.locY, (int) this.locZ), 0); + } + +@@ -369,7 +398,7 @@ + entitychicken1.setPositionRotation(this.locX, this.locY, this.locZ, this.yaw, 0.0F); + entitychicken1.prepare(difficultydamagescaler, (GroupDataEntity) null); + entitychicken1.l(true); +- this.world.addEntity(entitychicken1); ++ this.world.addEntity(entitychicken1, CreatureSpawnEvent.SpawnReason.MOUNT); + this.mount(entitychicken1); + } + } +@@ -452,7 +481,7 @@ + } + + this.world.kill(this); +- this.world.addEntity(entityvillager); ++ this.world.addEntity(entityvillager, CreatureSpawnEvent.SpawnReason.CURED); // CraftBukkit - add SpawnReason + entityvillager.addEffect(new MobEffect(MobEffectList.CONFUSION.id, 200, 0)); + this.world.a((EntityHuman) null, 1017, new BlockPosition((int) this.locX, (int) this.locY, (int) this.locZ), 0); + } diff --git a/nms-patches/ExpirableListEntry.patch b/nms-patches/ExpirableListEntry.patch new file mode 100644 index 00000000..6bb3d391 --- /dev/null +++ b/nms-patches/ExpirableListEntry.patch @@ -0,0 +1,42 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ExpirableListEntry.java 2014-11-27 08:59:46.749421635 +1100 ++++ src/main/java/net/minecraft/server/ExpirableListEntry.java 2014-11-27 08:42:10.096851020 +1100 +@@ -22,7 +22,7 @@ + } + + protected ExpirableListEntry(Object object, JsonObject jsonobject) { +- super(object, jsonobject); ++ super(checkExpiry(object, jsonobject), jsonobject); + + Date date; + +@@ -65,4 +65,30 @@ + jsonobject.addProperty("expires", this.d == null ? "forever" : ExpirableListEntry.a.format(this.d)); + jsonobject.addProperty("reason", this.e); + } ++ ++ // CraftBukkit start ++ public String getSource() { ++ return this.c; ++ } ++ ++ public Date getCreated() { ++ return this.b; ++ } ++ ++ private static Object checkExpiry(Object object, JsonObject jsonobject) { ++ Date expires = null; ++ ++ try { ++ expires = jsonobject.has("expires") ? a.parse(jsonobject.get("expires").getAsString()) : null; ++ } catch (ParseException ex) { ++ // Guess we don't have a date ++ } ++ ++ if (expires == null || expires.after(new Date())) { ++ return object; ++ } else { ++ return null; ++ } ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/Explosion.patch b/nms-patches/Explosion.patch new file mode 100644 index 00000000..3d0b5b84 --- /dev/null +++ b/nms-patches/Explosion.patch @@ -0,0 +1,127 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/Explosion.java 2014-11-27 08:59:46.753421618 +1100 ++++ src/main/java/net/minecraft/server/Explosion.java 2014-11-27 08:42:10.160850895 +1100 +@@ -8,6 +8,12 @@ + import java.util.List; + import java.util.Map; + import java.util.Random; ++ ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.EntityExplodeEvent; ++import org.bukkit.Location; ++// CraftBukkit end + + public class Explosion { + +@@ -22,11 +28,12 @@ + private final float size; + private final List blocks = Lists.newArrayList(); + private final Map k = Maps.newHashMap(); ++ public boolean wasCanceled = false; // CraftBukkit - add field + + 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 = f; ++ this.size = (float) Math.max(f, 0.0); // CraftBukkit - clamp bad values + this.posX = d0; + this.posY = d1; + this.posZ = d2; +@@ -35,6 +42,12 @@ + } + + public void a() { ++ // CraftBukkit start ++ if (this.size < 0.1F) { ++ return; ++ } ++ // CraftBukkit end ++ + HashSet hashset = Sets.newHashSet(); + boolean flag = true; + +@@ -68,7 +81,7 @@ + f -= (f2 + 0.3F) * 0.3F; + } + +- if (f > 0.0F && (this.source == null || this.source.a(this, this.world, blockposition, iblockdata, f))) { ++ if (f > 0.0F && (this.source == null || this.source.a(this, this.world, blockposition, iblockdata, f)) && blockposition.getY() < 256 && blockposition.getY() >= 0) { // CraftBukkit - don't wrap explosions + hashset.add(blockposition); + } + +@@ -112,7 +125,14 @@ + double d12 = (double) this.world.a(vec3d, entity.getBoundingBox()); + double d13 = (1.0D - d7) * d12; + ++ // entity.damageEntity(DamageSource.explosion(this), (float) ((int) ((d13 * d13 + d13) / 2.0D * 8.0D * (double) f3 + 1.0D))); ++ ++ // CraftBukkit start ++ CraftEventFactory.entityDamage = source; + entity.damageEntity(DamageSource.explosion(this), (float) ((int) ((d13 * d13 + d13) / 2.0D * 8.0D * (double) f3 + 1.0D))); ++ CraftEventFactory.entityDamage = null; ++ // CraftBukkit end ++ + double d14 = EnchantmentProtection.a(entity, d13); + + entity.motX += d8 * d14; +@@ -140,6 +160,35 @@ + 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<org.bukkit.block.Block> 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); ++ } ++ } ++ ++ EntityExplodeEvent event = new EntityExplodeEvent(explode, location, blockList, 0.3F); ++ this.world.getServer().getPluginManager().callEvent(event); ++ ++ this.blocks.clear(); ++ ++ for (org.bukkit.block.Block bblock : event.blockList()) { ++ BlockPosition coords = new BlockPosition(bblock.getX(), bblock.getY(), bblock.getZ()); ++ blocks.add(coords); ++ } ++ ++ if (event.isCancelled()) { ++ this.wasCanceled = true; ++ return; ++ } ++ // CraftBukkit end + iterator = this.blocks.iterator(); + + while (iterator.hasNext()) { +@@ -170,7 +219,8 @@ + + if (block.getMaterial() != Material.AIR) { + if (block.a(this)) { +- block.dropNaturally(this.world, blockposition, this.world.getType(blockposition), 1.0F / this.size, 0); ++ // CraftBukkit - add yield ++ block.dropNaturally(this.world, blockposition, this.world.getType(blockposition), event.getYield(), 0); + } + + this.world.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 3); +@@ -184,8 +234,12 @@ + + while (iterator.hasNext()) { + blockposition = (BlockPosition) iterator.next(); +- if (this.world.getType(blockposition).getBlock().getMaterial() == Material.AIR && this.world.getType(blockposition.down()).getBlock().m() && this.c.nextInt(3) == 0) { +- this.world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData()); ++ if (this.world.getType(blockposition).getBlock().getMaterial() == Material.AIR && this.world.getType(blockposition.down()).getBlock().m() && this.c.nextInt(3) == 0) { ++ // CraftBukkit start - Ignition by explosion ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(this.world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this).isCancelled()) { ++ this.world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData()); ++ } ++ // CraftBukkit end + } + } + } diff --git a/nms-patches/FoodMetaData.patch b/nms-patches/FoodMetaData.patch new file mode 100644 index 00000000..bfd974b3 --- /dev/null +++ b/nms-patches/FoodMetaData.patch @@ -0,0 +1,66 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/FoodMetaData.java 2014-11-27 08:59:46.753421618 +1100 ++++ src/main/java/net/minecraft/server/FoodMetaData.java 2014-11-27 08:42:10.104851005 +1100 +@@ -6,9 +6,17 @@ + public float saturationLevel = 5.0F; + public float exhaustionLevel; + public int foodTickTimer; ++ private EntityHuman entityhuman; // CraftBukkit + private int e = 20; + +- public FoodMetaData() {} ++ public FoodMetaData() { throw new AssertionError("Whoopsie, we missed the bukkit."); } // CraftBukkit start - throw an error ++ ++ // CraftBukkit start - added EntityHuman constructor ++ public FoodMetaData(EntityHuman entityhuman) { ++ org.apache.commons.lang.Validate.notNull(entityhuman); ++ this.entityhuman = entityhuman; ++ } ++ // CraftBukkit end + + public void eat(int i, float f) { + this.foodLevel = Math.min(i + this.foodLevel, 20); +@@ -16,7 +24,17 @@ + } + + public void a(ItemFood itemfood, ItemStack itemstack) { +- this.eat(itemfood.getNutrition(itemstack), itemfood.getSaturationModifier(itemstack)); ++ // CraftBukkit start ++ int oldFoodLevel = foodLevel; ++ ++ org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(entityhuman, itemfood.getNutrition(itemstack) + oldFoodLevel); ++ ++ if (!event.isCancelled()) { ++ this.eat(event.getFoodLevel() - oldFoodLevel, itemfood.getSaturationModifier(itemstack)); ++ } ++ ++ ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutUpdateHealth(((EntityPlayer) entityhuman).getBukkitEntity().getScaledHealth(), entityhuman.getFoodData().foodLevel, entityhuman.getFoodData().saturationLevel)); ++ // CraftBukkit end + } + + public void a(EntityHuman entityhuman) { +@@ -28,14 +46,23 @@ + if (this.saturationLevel > 0.0F) { + this.saturationLevel = Math.max(this.saturationLevel - 1.0F, 0.0F); + } else if (enumdifficulty != EnumDifficulty.PEACEFUL) { +- this.foodLevel = Math.max(this.foodLevel - 1, 0); ++ // CraftBukkit start ++ org.bukkit.event.entity.FoodLevelChangeEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callFoodLevelChangeEvent(entityhuman, Math.max(this.foodLevel - 1, 0)); ++ ++ if (!event.isCancelled()) { ++ this.foodLevel = event.getFoodLevel(); ++ } ++ ++ ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutUpdateHealth(((EntityPlayer) entityhuman).getBukkitEntity().getScaledHealth(), this.foodLevel, this.saturationLevel)); ++ // CraftBukkit end + } + } + + if (entityhuman.world.getGameRules().getBoolean("naturalRegeneration") && this.foodLevel >= 18 && entityhuman.cl()) { + ++this.foodTickTimer; + if (this.foodTickTimer >= 80) { +- entityhuman.heal(1.0F); ++ // CraftBukkit - added RegainReason ++ entityhuman.heal(1.0F, org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason.SATIATED); + this.a(3.0F); + this.foodTickTimer = 0; + } diff --git a/nms-patches/HandshakeListener.patch b/nms-patches/HandshakeListener.patch new file mode 100644 index 00000000..651b7fee --- /dev/null +++ b/nms-patches/HandshakeListener.patch @@ -0,0 +1,69 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/HandshakeListener.java 2014-11-27 08:59:46.757421600 +1100 ++++ src/main/java/net/minecraft/server/HandshakeListener.java 2014-11-27 08:42:10.100851012 +1100 +@@ -1,6 +1,16 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import java.net.InetAddress; ++import java.util.HashMap; ++// CraftBukkit end ++ + public class HandshakeListener implements PacketHandshakingInListener { ++ ++ // CraftBukkit start - add fields ++ private static final HashMap<InetAddress, Long> throttleTracker = new HashMap<InetAddress, Long>(); ++ private static int throttleCounter = 0; ++ // CraftBukkit end + + private final MinecraftServer a; + private final NetworkManager b; +@@ -15,6 +25,41 @@ + case 1: + this.b.a(EnumProtocol.LOGIN); + ChatComponentText chatcomponenttext; ++ ++ // CraftBukkit start - Connection throttle ++ try { ++ long currentTime = System.currentTimeMillis(); ++ long connectionThrottle = MinecraftServer.getServer().server.getConnectionThrottle(); ++ InetAddress address = ((java.net.InetSocketAddress) this.b.getSocketAddress()).getAddress(); ++ ++ synchronized (throttleTracker) { ++ if (throttleTracker.containsKey(address) && !"127.0.0.1".equals(address.getHostAddress()) && currentTime - throttleTracker.get(address) < connectionThrottle) { ++ throttleTracker.put(address, currentTime); ++ chatcomponenttext = new ChatComponentText("Connection throttled! Please wait before reconnecting."); ++ this.b.handle(new PacketLoginOutDisconnect(chatcomponenttext)); ++ this.b.close(chatcomponenttext); ++ return; ++ } ++ ++ throttleTracker.put(address, currentTime); ++ throttleCounter++; ++ if (throttleCounter > 200) { ++ throttleCounter = 0; ++ ++ // Cleanup stale entries ++ java.util.Iterator iter = throttleTracker.entrySet().iterator(); ++ while (iter.hasNext()) { ++ java.util.Map.Entry<InetAddress, Long> entry = (java.util.Map.Entry) iter.next(); ++ if (entry.getValue() > connectionThrottle) { ++ iter.remove(); ++ } ++ } ++ } ++ } ++ } catch (Throwable t) { ++ org.apache.logging.log4j.LogManager.getLogger().debug("Failed to check connection throttle", t); ++ } ++ // CraftBukkit end + + if (packethandshakinginsetprotocol.b() > 47) { + chatcomponenttext = new ChatComponentText("Outdated server! I\'m still on 1.8"); +@@ -26,6 +71,7 @@ + this.b.close(chatcomponenttext); + } else { + this.b.a((PacketListener) (new LoginListener(this.a, this.b))); ++ ((LoginListener) this.b.getPacketListener()).hostname = packethandshakinginsetprotocol.b + ":" + packethandshakinginsetprotocol.c; // CraftBukkit - set hostname + } + break; + diff --git a/nms-patches/IDataManager.patch b/nms-patches/IDataManager.patch new file mode 100644 index 00000000..0c8a3723 --- /dev/null +++ b/nms-patches/IDataManager.patch @@ -0,0 +1,18 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/IDataManager.java 2014-11-27 08:59:46.757421600 +1100 ++++ src/main/java/net/minecraft/server/IDataManager.java 2014-11-27 08:42:10.104851005 +1100 +@@ -6,7 +6,7 @@ + + WorldData getWorldData(); + +- void checkSession(); ++ void checkSession() throws ExceptionWorldConflict; // CraftBukkit - throws ExceptionWorldConflict + + IChunkLoader createChunkLoader(WorldProvider worldprovider); + +@@ -23,4 +23,6 @@ + File getDataFile(String s); + + String g(); ++ ++ java.util.UUID getUUID(); // CraftBukkit + } diff --git a/nms-patches/IInventory.patch b/nms-patches/IInventory.patch new file mode 100644 index 00000000..d65dcde8 --- /dev/null +++ b/nms-patches/IInventory.patch @@ -0,0 +1,31 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/IInventory.java 2014-11-27 08:59:46.757421600 +1100 ++++ src/main/java/net/minecraft/server/IInventory.java 2014-11-27 08:42:10.168850880 +1100 +@@ -1,5 +1,7 @@ + package net.minecraft.server; + ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; // CraftBukkit ++ + public interface IInventory extends INamableTileEntity { + + int getSize(); +@@ -31,4 +33,20 @@ + int g(); + + void l(); ++ ++ // CraftBukkit start ++ ItemStack[] getContents(); ++ ++ void onOpen(CraftHumanEntity who); ++ ++ void onClose(CraftHumanEntity who); ++ ++ java.util.List<org.bukkit.entity.HumanEntity> getViewers(); ++ ++ org.bukkit.inventory.InventoryHolder getOwner(); ++ ++ void setMaxStackSize(int size); ++ ++ int MAX_STACK = 64; ++ // CraftBukkit end + } diff --git a/nms-patches/IRecipe.patch b/nms-patches/IRecipe.patch new file mode 100644 index 00000000..cdc66801 --- /dev/null +++ b/nms-patches/IRecipe.patch @@ -0,0 +1,9 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/IRecipe.java 2014-11-27 08:59:46.769421547 +1100 ++++ src/main/java/net/minecraft/server/IRecipe.java 2014-11-27 08:42:10.168850880 +1100 +@@ -11,4 +11,6 @@ + ItemStack b(); + + ItemStack[] b(InventoryCrafting inventorycrafting); ++ ++ org.bukkit.inventory.Recipe toBukkitRecipe(); // CraftBukkit + } diff --git a/nms-patches/InventoryCraftResult.patch b/nms-patches/InventoryCraftResult.patch new file mode 100644 index 00000000..af51f97f --- /dev/null +++ b/nms-patches/InventoryCraftResult.patch @@ -0,0 +1,48 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/InventoryCraftResult.java 2014-11-27 08:59:46.761421583 +1100 ++++ src/main/java/net/minecraft/server/InventoryCraftResult.java 2014-11-27 08:42:10.140850934 +1100 +@@ -1,8 +1,36 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ + public class InventoryCraftResult implements IInventory { + + private ItemStack[] items = new ItemStack[1]; ++ ++ // CraftBukkit start ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() { ++ return this.items; ++ } ++ ++ public org.bukkit.inventory.InventoryHolder getOwner() { ++ return null; // Result slots don't get an owner ++ } ++ ++ // Don't need a transaction; the InventoryCrafting keeps track of it for us ++ public void onOpen(CraftHumanEntity who) {} ++ public void onClose(CraftHumanEntity who) {} ++ public java.util.List<HumanEntity> getViewers() { ++ return new java.util.ArrayList<HumanEntity>(); ++ } ++ ++ public void setMaxStackSize(int size) { ++ maxStack = size; ++ } ++ // CraftBukkit end + + public InventoryCraftResult() {} + +@@ -53,7 +81,7 @@ + } + + public int getMaxStackSize() { +- return 64; ++ return maxStack; // CraftBukkit + } + + public void update() {} diff --git a/nms-patches/InventoryCrafting.patch b/nms-patches/InventoryCrafting.patch new file mode 100644 index 00000000..84e27a99 --- /dev/null +++ b/nms-patches/InventoryCrafting.patch @@ -0,0 +1,64 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/InventoryCrafting.java 2014-11-27 08:59:46.761421583 +1100 ++++ src/main/java/net/minecraft/server/InventoryCrafting.java 2014-11-27 08:42:10.152850911 +1100 +@@ -1,11 +1,61 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import java.util.List; ++ ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++import org.bukkit.event.inventory.InventoryType; ++// CraftBukkit end ++ + public class InventoryCrafting implements IInventory { + + private final ItemStack[] items; + private final int b; + private final int c; + private final Container d; ++ ++ // CraftBukkit start - add fields ++ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>(); ++ public IRecipe currentRecipe; ++ public IInventory resultInventory; ++ private EntityHuman owner; ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() { ++ return this.items; ++ } ++ ++ public void onOpen(CraftHumanEntity who) { ++ transaction.add(who); ++ } ++ ++ public InventoryType getInvType() { ++ return items.length == 4 ? InventoryType.CRAFTING : InventoryType.WORKBENCH; ++ } ++ ++ public void onClose(CraftHumanEntity who) { ++ transaction.remove(who); ++ } ++ ++ public List<HumanEntity> getViewers() { ++ return transaction; ++ } ++ ++ public org.bukkit.inventory.InventoryHolder getOwner() { ++ return owner.getBukkitEntity(); ++ } ++ ++ public void setMaxStackSize(int size) { ++ maxStack = size; ++ resultInventory.setMaxStackSize(size); ++ } ++ ++ public InventoryCrafting(Container container, int i, int j, EntityHuman player) { ++ this(container, i, j); ++ this.owner = player; ++ } ++ // CraftBukkit end + + public InventoryCrafting(Container container, int i, int j) { + int k = i * j; diff --git a/nms-patches/InventoryEnderChest.patch b/nms-patches/InventoryEnderChest.patch new file mode 100644 index 00000000..ee181fb3 --- /dev/null +++ b/nms-patches/InventoryEnderChest.patch @@ -0,0 +1,51 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/InventoryEnderChest.java 2014-11-27 08:59:46.761421583 +1100 ++++ src/main/java/net/minecraft/server/InventoryEnderChest.java 2014-11-27 08:42:10.096851020 +1100 +@@ -1,8 +1,48 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import java.util.List; ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ + public class InventoryEnderChest extends InventorySubcontainer { + + private TileEntityEnderChest a; ++ ++ // CraftBukkit start - add fields and methods ++ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>(); ++ public org.bukkit.entity.Player player; ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() { ++ return this.items; ++ } ++ ++ public void onOpen(CraftHumanEntity who) { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) { ++ transaction.remove(who); ++ } ++ ++ public List<HumanEntity> getViewers() { ++ return transaction; ++ } ++ ++ public org.bukkit.inventory.InventoryHolder getOwner() { ++ return this.player; ++ } ++ ++ public void setMaxStackSize(int size) { ++ maxStack = size; ++ } ++ ++ public int getMaxStackSize() { ++ return maxStack; ++ } ++ // CraftBukkit end + + public InventoryEnderChest() { + super("container.enderchest", false, 27); diff --git a/nms-patches/InventoryHorseChest.patch b/nms-patches/InventoryHorseChest.patch new file mode 100644 index 00000000..4146987e --- /dev/null +++ b/nms-patches/InventoryHorseChest.patch @@ -0,0 +1,63 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/InventoryHorseChest.java 2014-11-27 08:59:46.765421565 +1100 ++++ src/main/java/net/minecraft/server/InventoryHorseChest.java 2014-11-27 08:42:10.172850872 +1100 +@@ -1,8 +1,60 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import java.util.List; ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ + public class InventoryHorseChest extends InventorySubcontainer { + + public InventoryHorseChest(String s, int i) { + super(s, false, i); + } ++ ++ // CraftBukkit start - add fields and methods ++ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>(); ++ private EntityHorse horse; ++ private int maxStack = MAX_STACK; ++ ++ public InventoryHorseChest(String s, int i, EntityHorse horse) { ++ super(s, false, i, (org.bukkit.craftbukkit.entity.CraftHorse) horse.getBukkitEntity()); ++ this.horse = horse; ++ } ++ ++ @Override ++ public ItemStack[] getContents() { ++ return this.items; ++ } ++ ++ @Override ++ public void onOpen(CraftHumanEntity who) { ++ transaction.add(who); ++ } ++ ++ @Override ++ public void onClose(CraftHumanEntity who) { ++ transaction.remove(who); ++ } ++ ++ @Override ++ public List<HumanEntity> getViewers() { ++ return transaction; ++ } ++ ++ @Override ++ public org.bukkit.inventory.InventoryHolder getOwner() { ++ return (org.bukkit.entity.Horse) this.horse.getBukkitEntity(); ++ } ++ ++ @Override ++ public void setMaxStackSize(int size) { ++ maxStack = size; ++ } ++ ++ @Override ++ public int getMaxStackSize() { ++ return maxStack; ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/InventoryLargeChest.patch b/nms-patches/InventoryLargeChest.patch new file mode 100644 index 00000000..2e1b2eaa --- /dev/null +++ b/nms-patches/InventoryLargeChest.patch @@ -0,0 +1,66 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/InventoryLargeChest.java 2014-11-27 08:59:46.765421565 +1100 ++++ src/main/java/net/minecraft/server/InventoryLargeChest.java 2014-11-27 08:42:10.164850887 +1100 +@@ -1,10 +1,54 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import java.util.List; ++ ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ + public class InventoryLargeChest implements ITileInventory { + + private String a; + public ITileInventory left; + public ITileInventory right; ++ ++ // CraftBukkit start - add fields and methods ++ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>(); ++ ++ public ItemStack[] getContents() { ++ ItemStack[] result = new ItemStack[this.getSize()]; ++ for (int i = 0; i < result.length; i++) { ++ result[i] = this.getItem(i); ++ } ++ return result; ++ } ++ ++ public void onOpen(CraftHumanEntity who) { ++ this.left.onOpen(who); ++ this.right.onOpen(who); ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) { ++ this.left.onClose(who); ++ this.right.onClose(who); ++ transaction.remove(who); ++ } ++ ++ public List<HumanEntity> getViewers() { ++ return transaction; ++ } ++ ++ public org.bukkit.inventory.InventoryHolder getOwner() { ++ return null; // This method won't be called since CraftInventoryDoubleChest doesn't defer to here ++ } ++ ++ public void setMaxStackSize(int size) { ++ this.left.setMaxStackSize(size); ++ this.right.setMaxStackSize(size); ++ } ++ // CraftBukkit end + + public InventoryLargeChest(String s, ITileInventory itileinventory, ITileInventory itileinventory1) { + this.a = s; +@@ -68,7 +112,7 @@ + } + + public int getMaxStackSize() { +- return this.left.getMaxStackSize(); ++ return Math.min(this.left.getMaxStackSize(), this.right.getMaxStackSize()); // CraftBukkit - check both sides + } + + public void update() { diff --git a/nms-patches/InventoryMerchant.patch b/nms-patches/InventoryMerchant.patch new file mode 100644 index 00000000..bc24b3dc --- /dev/null +++ b/nms-patches/InventoryMerchant.patch @@ -0,0 +1,59 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/InventoryMerchant.java 2014-11-27 08:59:46.769421547 +1100 ++++ src/main/java/net/minecraft/server/InventoryMerchant.java 2014-11-27 08:42:10.136850942 +1100 +@@ -1,5 +1,11 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import java.util.List; ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ + public class InventoryMerchant implements IInventory { + + private final IMerchant merchant; +@@ -8,6 +14,35 @@ + private MerchantRecipe recipe; + private int e; + ++ // CraftBukkit start - add fields and methods ++ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>(); ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() { ++ return this.itemsInSlots; ++ } ++ ++ public void onOpen(CraftHumanEntity who) { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) { ++ transaction.remove(who); ++ } ++ ++ public List<HumanEntity> getViewers() { ++ return transaction; ++ } ++ ++ public void setMaxStackSize(int i) { ++ maxStack = i; ++ } ++ ++ public org.bukkit.inventory.InventoryHolder getOwner() { ++ return player.getBukkitEntity(); ++ } ++ // CraftBukkit end ++ + public InventoryMerchant(EntityHuman entityhuman, IMerchant imerchant) { + this.player = entityhuman; + this.merchant = imerchant; +@@ -94,7 +129,7 @@ + } + + public int getMaxStackSize() { +- return 64; ++ return maxStack; // CraftBukkit + } + + public boolean a(EntityHuman entityhuman) { diff --git a/nms-patches/InventorySubcontainer.patch b/nms-patches/InventorySubcontainer.patch new file mode 100644 index 00000000..ddacf613 --- /dev/null +++ b/nms-patches/InventorySubcontainer.patch @@ -0,0 +1,59 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/InventorySubcontainer.java 2014-11-27 08:59:46.769421547 +1100 ++++ src/main/java/net/minecraft/server/InventorySubcontainer.java 2014-11-27 08:42:10.088851036 +1100 +@@ -3,6 +3,12 @@ + import com.google.common.collect.Lists; + import java.util.List; + ++// CraftBukkit start ++import java.util.List; ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ + public class InventorySubcontainer implements IInventory { + + private String a; +@@ -10,8 +16,43 @@ + public ItemStack[] items; + private List d; + private boolean e; ++ ++ // CraftBukkit start - add fields and methods ++ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>(); ++ private int maxStack = MAX_STACK; ++ protected org.bukkit.inventory.InventoryHolder bukkitOwner; ++ ++ public ItemStack[] getContents() { ++ return this.items; ++ } ++ ++ public void onOpen(CraftHumanEntity who) { ++ transaction.add(who); ++ } + ++ public void onClose(CraftHumanEntity who) { ++ transaction.remove(who); ++ } ++ ++ public List<HumanEntity> getViewers() { ++ return transaction; ++ } ++ ++ public void setMaxStackSize(int i) { ++ maxStack = i; ++ } ++ ++ public org.bukkit.inventory.InventoryHolder getOwner() { ++ return bukkitOwner; ++ } ++ + public InventorySubcontainer(String s, boolean flag, int i) { ++ this(s, flag, i, null); ++ } ++ ++ public InventorySubcontainer(String s, boolean flag, int i, org.bukkit.inventory.InventoryHolder owner) { // Added argument ++ this.bukkitOwner = owner; ++ // CraftBukkit end + this.a = s; + this.e = flag; + this.b = i; diff --git a/nms-patches/ItemBoat.patch b/nms-patches/ItemBoat.patch new file mode 100644 index 00000000..29142514 --- /dev/null +++ b/nms-patches/ItemBoat.patch @@ -0,0 +1,17 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ItemBoat.java 2014-11-27 08:59:46.773421530 +1100 ++++ src/main/java/net/minecraft/server/ItemBoat.java 2014-11-27 08:42:10.156850903 +1100 +@@ -53,6 +53,14 @@ + } else { + if (movingobjectposition.type == EnumMovingObjectType.BLOCK) { + BlockPosition blockposition = movingobjectposition.a(); ++ ++ // CraftBukkit start - Boat placement ++ org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(entityhuman, org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK, blockposition, movingobjectposition.direction, itemstack); ++ ++ if (event.isCancelled()) { ++ return itemstack; ++ } ++ // CraftBukkit end + + if (world.getType(blockposition).getBlock() == Blocks.SNOW_LAYER) { + blockposition = blockposition.down(); diff --git a/nms-patches/ItemBow.patch b/nms-patches/ItemBow.patch new file mode 100644 index 00000000..9f8c9f51 --- /dev/null +++ b/nms-patches/ItemBow.patch @@ -0,0 +1,49 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ItemBow.java 2014-11-27 08:59:46.773421530 +1100 ++++ src/main/java/net/minecraft/server/ItemBow.java 2014-11-27 08:42:10.140850934 +1100 +@@ -1,5 +1,7 @@ + package net.minecraft.server; + ++import org.bukkit.event.entity.EntityCombustEvent; // CraftBukkit ++ + public class ItemBow extends Item { + + public static final String[] a = new String[] { "pulling_0", "pulling_1", "pulling_2"}; +@@ -45,9 +47,28 @@ + } + + if (EnchantmentManager.getEnchantmentLevel(Enchantment.ARROW_FIRE.id, itemstack) > 0) { +- entityarrow.setOnFire(100); ++ // CraftBukkit start - call EntityCombustEvent ++ EntityCombustEvent event = new EntityCombustEvent(entityarrow.getBukkitEntity(), 100); ++ entityarrow.world.getServer().getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ entityarrow.setOnFire(event.getDuration()); ++ } ++ // CraftBukkit end ++ } ++ ++ // CraftBukkit start ++ org.bukkit.event.entity.EntityShootBowEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callEntityShootBowEvent(entityhuman, itemstack, entityarrow, f); ++ if (event.isCancelled()) { ++ event.getProjectile().remove(); ++ return; + } + ++ if (event.getProjectile() == entityarrow.getBukkitEntity()) { ++ world.addEntity(entityarrow); ++ } ++ // CraftBukkit end ++ + itemstack.damage(1, entityhuman); + world.makeSound(entityhuman, "random.bow", 1.0F, 1.0F / (ItemBow.g.nextFloat() * 0.4F + 1.2F) + f * 0.5F); + if (flag) { +@@ -58,7 +79,7 @@ + + entityhuman.b(StatisticList.USE_ITEM_COUNT[Item.getId(this)]); + if (!world.isStatic) { +- world.addEntity(entityarrow); ++ // world.addEntity(entityarrow); // CraftBukkit - moved up + } + } + diff --git a/nms-patches/ItemBucket.patch b/nms-patches/ItemBucket.patch new file mode 100644 index 00000000..d037c57b --- /dev/null +++ b/nms-patches/ItemBucket.patch @@ -0,0 +1,102 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ItemBucket.java 2014-11-27 08:59:46.777421512 +1100 ++++ src/main/java/net/minecraft/server/ItemBucket.java 2014-11-27 08:42:10.156850903 +1100 +@@ -1,5 +1,12 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.event.player.PlayerBucketEmptyEvent; ++import org.bukkit.event.player.PlayerBucketFillEvent; ++// CraftBukkit end ++ + public class ItemBucket extends Item { + + private Block a; +@@ -33,19 +40,41 @@ + Material material = iblockdata.getBlock().getMaterial(); + + if (material == Material.WATER && ((Integer) iblockdata.get(BlockFluids.LEVEL)).intValue() == 0) { ++ // CraftBukkit start ++ PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent(entityhuman, blockposition.getX(), blockposition.getY(), blockposition.getZ(), null, itemstack, Items.WATER_BUCKET); ++ ++ if (event.isCancelled()) { ++ return itemstack; ++ } ++ // CraftBukkit end + world.setAir(blockposition); + entityhuman.b(StatisticList.USE_ITEM_COUNT[Item.getId(this)]); +- return this.a(itemstack, entityhuman, Items.WATER_BUCKET); ++ return this.a(itemstack, entityhuman, Items.WATER_BUCKET, event.getItemStack()); // CraftBukkit - added Event stack + } + + if (material == Material.LAVA && ((Integer) iblockdata.get(BlockFluids.LEVEL)).intValue() == 0) { ++ // CraftBukkit start ++ PlayerBucketFillEvent event = CraftEventFactory.callPlayerBucketFillEvent(entityhuman, blockposition.getX(), blockposition.getY(), blockposition.getZ(), null, itemstack, Items.LAVA_BUCKET); ++ ++ if (event.isCancelled()) { ++ return itemstack; ++ } ++ // CraftBukkit end + world.setAir(blockposition); + entityhuman.b(StatisticList.USE_ITEM_COUNT[Item.getId(this)]); +- return this.a(itemstack, entityhuman, Items.LAVA_BUCKET); ++ return this.a(itemstack, entityhuman, Items.LAVA_BUCKET, event.getItemStack()); // CraftBukkit - added Event stack + } + } else { + if (this.a == Blocks.AIR) { +- return new ItemStack(Items.BUCKET); ++ // CraftBukkit start ++ PlayerBucketEmptyEvent event = CraftEventFactory.callPlayerBucketEmptyEvent(entityhuman, blockposition.getX(), blockposition.getY(), blockposition.getZ(), movingobjectposition.direction, itemstack); ++ ++ if (event.isCancelled()) { ++ return itemstack; ++ } ++ ++ return CraftItemStack.asNMSCopy(event.getItemStack()); ++ // CraftBukkit end + } + + BlockPosition blockposition1 = blockposition.shift(movingobjectposition.direction); +@@ -53,10 +82,18 @@ + if (!entityhuman.a(blockposition1, movingobjectposition.direction, itemstack)) { + return itemstack; + } ++ ++ // CraftBukkit start ++ PlayerBucketEmptyEvent event = CraftEventFactory.callPlayerBucketEmptyEvent(entityhuman, blockposition.getX(), blockposition.getY(), blockposition.getZ(), movingobjectposition.direction, itemstack); ++ ++ if (event.isCancelled()) { ++ return itemstack; ++ } ++ // CraftBukkit end + + if (this.a(world, blockposition1) && !entityhuman.abilities.canInstantlyBuild) { + entityhuman.b(StatisticList.USE_ITEM_COUNT[Item.getId(this)]); +- return new ItemStack(Items.BUCKET); ++ return CraftItemStack.asNMSCopy(event.getItemStack()); // CraftBukkit + } + } + } +@@ -64,15 +101,16 @@ + return itemstack; + } + } +- +- private ItemStack a(ItemStack itemstack, EntityHuman entityhuman, Item item) { ++ ++ // CraftBukkit - added ob.ItemStack result - TODO: Is this... the right way to handle this? ++ private ItemStack a(ItemStack itemstack, EntityHuman entityhuman, Item item, org.bukkit.inventory.ItemStack result) { + if (entityhuman.abilities.canInstantlyBuild) { + return itemstack; + } else if (--itemstack.count <= 0) { +- return new ItemStack(item); ++ return CraftItemStack.asNMSCopy(result); // CraftBukkit + } else { +- if (!entityhuman.inventory.pickup(new ItemStack(item))) { +- entityhuman.drop(new ItemStack(item, 1, 0), false); ++ if (!entityhuman.inventory.pickup(CraftItemStack.asNMSCopy(result))) { ++ entityhuman.drop(CraftItemStack.asNMSCopy(result), false); + } + + return itemstack; diff --git a/nms-patches/ItemDye.patch b/nms-patches/ItemDye.patch new file mode 100644 index 00000000..8586e786 --- /dev/null +++ b/nms-patches/ItemDye.patch @@ -0,0 +1,28 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ItemDye.java 2014-11-27 08:59:46.777421512 +1100 ++++ src/main/java/net/minecraft/server/ItemDye.java 2014-11-27 08:42:10.160850895 +1100 +@@ -1,5 +1,7 @@ + package net.minecraft.server; + ++import org.bukkit.event.entity.SheepDyeWoolEvent; // CraftBukkit ++ + public class ItemDye extends Item { + + public static final int[] a = new int[] { 1973019, 11743532, 3887386, 5320730, 2437522, 8073150, 2651799, 11250603, 4408131, 14188952, 4312372, 14602026, 6719955, 12801229, 15435844, 15790320}; +@@ -89,6 +91,17 @@ + EnumColor enumcolor = EnumColor.fromInvColorIndex(itemstack.getData()); + + if (!entitysheep.isSheared() && entitysheep.getColor() != enumcolor) { ++ // CraftBukkit start ++ byte bColor = (byte) enumcolor.getColorIndex(); ++ SheepDyeWoolEvent event = new SheepDyeWoolEvent((org.bukkit.entity.Sheep) entitysheep.getBukkitEntity(), org.bukkit.DyeColor.getByData(bColor)); ++ entitysheep.world.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ return false; ++ } ++ ++ enumcolor = EnumColor.fromColorIndex((byte) event.getColor().getWoolData()); ++ // CraftBukkit end + entitysheep.setColor(enumcolor); + --itemstack.count; + } diff --git a/nms-patches/ItemFireball.patch b/nms-patches/ItemFireball.patch new file mode 100644 index 00000000..6a754928 --- /dev/null +++ b/nms-patches/ItemFireball.patch @@ -0,0 +1,19 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ItemFireball.java 2014-11-27 08:59:46.777421512 +1100 ++++ src/main/java/net/minecraft/server/ItemFireball.java 2014-11-27 08:42:10.124850965 +1100 +@@ -15,7 +15,15 @@ + return false; + } else { + if (world.getType(blockposition).getBlock().getMaterial() == Material.AIR) { +- world.makeSound((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D, "item.fireCharge.use", 1.0F, (ItemFireball.g.nextFloat() - ItemFireball.g.nextFloat()) * 0.2F + 1.0F); ++ // CraftBukkit start - fire BlockIgniteEvent ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callBlockIgniteEvent(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), org.bukkit.event.block.BlockIgniteEvent.IgniteCause.FIREBALL, entityhuman).isCancelled()) { ++ if (!entityhuman.abilities.canInstantlyBuild) { ++ --itemstack.count; ++ } ++ return false; ++ } ++ // CraftBukkit end ++ world.makeSound((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D, "item.fireCharge.use", 1.0F, (g.nextFloat() - g.nextFloat()) * 0.2F + 1.0F); + world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData()); + } + diff --git a/nms-patches/ItemFishingRod.patch b/nms-patches/ItemFishingRod.patch new file mode 100644 index 00000000..564c24b4 --- /dev/null +++ b/nms-patches/ItemFishingRod.patch @@ -0,0 +1,31 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ItemFishingRod.java 2014-11-27 08:59:46.781421494 +1100 ++++ src/main/java/net/minecraft/server/ItemFishingRod.java 2014-11-27 08:42:10.168850880 +1100 +@@ -1,5 +1,7 @@ + package net.minecraft.server; + ++import org.bukkit.event.player.PlayerFishEvent; // CraftBukkit ++ + public class ItemFishingRod extends Item { + + public ItemFishingRod() { +@@ -15,9 +17,18 @@ + itemstack.damage(i, entityhuman); + entityhuman.bv(); + } else { +- world.makeSound(entityhuman, "random.bow", 0.5F, 0.4F / (ItemFishingRod.g.nextFloat() * 0.4F + 0.8F)); ++ // CraftBukkit start ++ EntityFishingHook hook = new EntityFishingHook(world, entityhuman); ++ PlayerFishEvent playerFishEvent = new PlayerFishEvent((org.bukkit.entity.Player) entityhuman.getBukkitEntity(), null, (org.bukkit.entity.Fish) hook.getBukkitEntity(), PlayerFishEvent.State.FISHING); ++ world.getServer().getPluginManager().callEvent(playerFishEvent); ++ ++ if (playerFishEvent.isCancelled()) { ++ return itemstack; ++ } ++ // CraftBukkit end ++ world.makeSound(entityhuman, "random.bow", 0.5F, 0.4F / (g.nextFloat() * 0.4F + 0.8F)); + if (!world.isStatic) { +- world.addEntity(new EntityFishingHook(world, entityhuman)); ++ world.addEntity(hook); // CraftBukkit - moved creation up + } + + entityhuman.bv(); diff --git a/nms-patches/ItemFlintAndSteel.patch b/nms-patches/ItemFlintAndSteel.patch new file mode 100644 index 00000000..a27cc0b6 --- /dev/null +++ b/nms-patches/ItemFlintAndSteel.patch @@ -0,0 +1,47 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ItemFlintAndSteel.java 2014-11-27 08:59:46.781421494 +1100 ++++ src/main/java/net/minecraft/server/ItemFlintAndSteel.java 2014-11-27 08:42:10.152850911 +1100 +@@ -1,5 +1,10 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.block.CraftBlockState; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++// CraftBukkit end ++ + public class ItemFlintAndSteel extends Item { + + public ItemFlintAndSteel() { +@@ -9,13 +14,32 @@ + } + + public boolean interactWith(ItemStack itemstack, EntityHuman entityhuman, World world, BlockPosition blockposition, EnumDirection enumdirection, float f, float f1, float f2) { ++ BlockPosition clicked = blockposition; // CraftBukkit + blockposition = blockposition.shift(enumdirection); + if (!entityhuman.a(blockposition, enumdirection, itemstack)) { + return false; + } else { + if (world.getType(blockposition).getBlock().getMaterial() == Material.AIR) { +- world.makeSound((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D, "fire.ignite", 1.0F, ItemFlintAndSteel.g.nextFloat() * 0.4F + 0.8F); ++ // CraftBukkit start - Store the clicked block ++ if (CraftEventFactory.callBlockIgniteEvent(world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), org.bukkit.event.block.BlockIgniteEvent.IgniteCause.FLINT_AND_STEEL, entityhuman).isCancelled()) { ++ itemstack.damage(1, entityhuman); ++ return false; ++ } ++ ++ CraftBlockState blockState = CraftBlockState.getBlockState(world, blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ // CraftBukkit end ++ ++ world.makeSound((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D, "fire.ignite", 1.0F, g.nextFloat() * 0.4F + 0.8F); + world.setTypeUpdate(blockposition, Blocks.FIRE.getBlockData()); ++ ++ // CraftBukkit start ++ org.bukkit.event.block.BlockPlaceEvent placeEvent = CraftEventFactory.callBlockPlaceEvent(world, entityhuman, blockState, clicked.getX(), clicked.getY(), clicked.getZ()); ++ ++ if (placeEvent.isCancelled() || !placeEvent.canBuild()) { ++ placeEvent.getBlockPlaced().setTypeIdAndData(0, (byte) 0, false); ++ return false; ++ } ++ // CraftBukkit end + } + + itemstack.damage(1, entityhuman); diff --git a/nms-patches/ItemHanging.patch b/nms-patches/ItemHanging.patch new file mode 100644 index 00000000..0a312a80 --- /dev/null +++ b/nms-patches/ItemHanging.patch @@ -0,0 +1,41 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ItemHanging.java 2014-11-27 08:59:46.785421476 +1100 ++++ src/main/java/net/minecraft/server/ItemHanging.java 2014-11-27 08:42:10.144850927 +1100 +@@ -1,5 +1,11 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.entity.Player; ++import org.bukkit.event.hanging.HangingPlaceEvent; ++import org.bukkit.event.painting.PaintingPlaceEvent; ++// CraftBukkit end ++ + public class ItemHanging extends Item { + + private final Class a; +@@ -24,6 +30,26 @@ + + if (entityhanging != null && entityhanging.survives()) { + if (!world.isStatic) { ++ // CraftBukkit start - fire HangingPlaceEvent ++ Player who = (entityhuman == null) ? null : (Player) entityhuman.getBukkitEntity(); ++ org.bukkit.block.Block blockClicked = world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ org.bukkit.block.BlockFace blockFace = org.bukkit.craftbukkit.block.CraftBlock.notchToBlockFace(enumdirection); ++ ++ HangingPlaceEvent event = new HangingPlaceEvent((org.bukkit.entity.Hanging) entityhanging.getBukkitEntity(), who, blockClicked, blockFace); ++ world.getServer().getPluginManager().callEvent(event); ++ ++ PaintingPlaceEvent paintingEvent = null; ++ if (entityhanging instanceof EntityPainting) { ++ // Fire old painting event until it can be removed ++ paintingEvent = new PaintingPlaceEvent((org.bukkit.entity.Painting) entityhanging.getBukkitEntity(), who, blockClicked, blockFace); ++ paintingEvent.setCancelled(event.isCancelled()); ++ world.getServer().getPluginManager().callEvent(paintingEvent); ++ } ++ ++ if (event.isCancelled() || (paintingEvent != null && paintingEvent.isCancelled())) { ++ return false; ++ } ++ // CraftBukkit end + world.addEntity(entityhanging); + } + diff --git a/nms-patches/ItemLeash.patch b/nms-patches/ItemLeash.patch new file mode 100644 index 00000000..2836ac93 --- /dev/null +++ b/nms-patches/ItemLeash.patch @@ -0,0 +1,35 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ItemLeash.java 2014-11-27 08:59:46.785421476 +1100 ++++ src/main/java/net/minecraft/server/ItemLeash.java 2014-11-27 08:42:10.084851043 +1100 +@@ -3,6 +3,8 @@ + import java.util.Iterator; + import java.util.List; + ++import org.bukkit.event.hanging.HangingPlaceEvent; // CraftBukkit ++ + public class ItemLeash extends Item { + + public ItemLeash() { +@@ -40,7 +42,23 @@ + if (entityinsentient.cb() && entityinsentient.getLeashHolder() == entityhuman) { + if (entityleash == null) { + entityleash = EntityLeash.a(world, blockposition); ++ ++ // CraftBukkit start - fire HangingPlaceEvent ++ HangingPlaceEvent event = new HangingPlaceEvent((org.bukkit.entity.Hanging) entityleash.getBukkitEntity(), entityhuman != null ? (org.bukkit.entity.Player) entityhuman.getBukkitEntity() : null, world.getWorld().getBlockAt(i, j, k), org.bukkit.block.BlockFace.SELF); ++ world.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ entityleash.die(); ++ return false; ++ } ++ // CraftBukkit end ++ } ++ ++ // CraftBukkit start ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerLeashEntityEvent(entityinsentient, entityleash, entityhuman).isCancelled()) { ++ continue; + } ++ // CraftBukkit end + + entityinsentient.setLeashHolder(entityleash, true); + flag = true; diff --git a/nms-patches/ItemMapEmpty.patch b/nms-patches/ItemMapEmpty.patch new file mode 100644 index 00000000..9159a106 --- /dev/null +++ b/nms-patches/ItemMapEmpty.patch @@ -0,0 +1,24 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ItemMapEmpty.java 2014-11-27 08:59:46.785421476 +1100 ++++ src/main/java/net/minecraft/server/ItemMapEmpty.java 2014-11-27 08:42:10.164850887 +1100 +@@ -7,15 +7,19 @@ + } + + public ItemStack a(ItemStack itemstack, World world, EntityHuman entityhuman) { +- ItemStack itemstack1 = new ItemStack(Items.FILLED_MAP, 1, world.b("map")); ++ World worldMain = world.getServer().getServer().worlds.get(0); // CraftBukkit - store reference to primary world ++ ItemStack itemstack1 = new ItemStack(Items.FILLED_MAP, 1, worldMain.b("map")); // CraftBukkit - use primary world for maps + String s = "map_" + itemstack1.getData(); + WorldMap worldmap = new WorldMap(s); + + world.a(s, (PersistentBase) worldmap); + worldmap.scale = 0; + worldmap.a(entityhuman.locX, entityhuman.locZ, worldmap.scale); +- worldmap.map = (byte) world.worldProvider.getDimension(); ++ worldmap.map = (byte) ((WorldServer) world).dimension; // CraftBukkit - use bukkit dimension + worldmap.c(); ++ ++ org.bukkit.craftbukkit.event.CraftEventFactory.callEvent(new org.bukkit.event.server.MapInitializeEvent(worldmap.mapView)); // CraftBukkit ++ + --itemstack.count; + if (itemstack.count <= 0) { + return itemstack1; diff --git a/nms-patches/ItemMinecart.patch b/nms-patches/ItemMinecart.patch new file mode 100644 index 00000000..5cff838f --- /dev/null +++ b/nms-patches/ItemMinecart.patch @@ -0,0 +1,16 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ItemMinecart.java 2014-11-27 08:59:46.789421459 +1100 ++++ src/main/java/net/minecraft/server/ItemMinecart.java 2014-11-27 08:42:10.168850880 +1100 +@@ -23,6 +23,13 @@ + if (enumtrackposition.c()) { + d0 = 0.5D; + } ++ // CraftBukkit start - Minecarts ++ org.bukkit.event.player.PlayerInteractEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerInteractEvent(entityhuman, org.bukkit.event.block.Action.RIGHT_CLICK_BLOCK, blockposition, enumdirection, itemstack); ++ ++ if (event.isCancelled()) { ++ return false; ++ } ++ // CraftBukkit end + + EntityMinecartAbstract entityminecartabstract = EntityMinecartAbstract.a(world, (double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.0625D + d0, (double) blockposition.getZ() + 0.5D, this.b); + diff --git a/nms-patches/ItemMonsterEgg.patch b/nms-patches/ItemMonsterEgg.patch new file mode 100644 index 00000000..3a5fbee6 --- /dev/null +++ b/nms-patches/ItemMonsterEgg.patch @@ -0,0 +1,25 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ItemMonsterEgg.java 2014-11-27 08:59:46.789421459 +1100 ++++ src/main/java/net/minecraft/server/ItemMonsterEgg.java 2014-11-27 08:42:10.172850872 +1100 +@@ -19,7 +19,8 @@ + } + + public boolean interactWith(ItemStack itemstack, EntityHuman entityhuman, World world, BlockPosition blockposition, EnumDirection enumdirection, float f, float f1, float f2) { +- if (world.isStatic) { ++ // CraftBukkit - check ItemStack data ++ if (world.isStatic || itemstack.getData() == 48 || itemstack.getData() == 49 || itemstack.getData() == 63 || itemstack.getData() == 64) { + return true; + } else if (!entityhuman.a(blockposition.shift(enumdirection), enumdirection, itemstack)) { + return false; +@@ -109,6 +110,12 @@ + } + + public static Entity a(World world, int i, double d0, double d1, double d2) { ++ // CraftBukkit start - delegate to spawnCreature ++ return spawnCreature(world, i, d0, d1, d2, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER_EGG); ++ } ++ ++ public static Entity spawnCreature(World world, int i, double d0, double d1, double d2, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) { ++ // CraftBukkit end + if (!EntityTypes.eggInfo.containsKey(Integer.valueOf(i))) { + return null; + } else { diff --git a/nms-patches/ItemStack.patch b/nms-patches/ItemStack.patch new file mode 100644 index 00000000..da50d5cf --- /dev/null +++ b/nms-patches/ItemStack.patch @@ -0,0 +1,219 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ItemStack.java 2014-11-27 08:59:46.789421459 +1100 ++++ src/main/java/net/minecraft/server/ItemStack.java 2014-11-27 08:42:10.156850903 +1100 +@@ -5,6 +5,18 @@ + import java.text.DecimalFormat; + import java.util.Random; + ++// CraftBukkit start ++import java.util.List; ++ ++import org.bukkit.Location; ++import org.bukkit.TreeType; ++import org.bukkit.block.BlockState; ++import org.bukkit.craftbukkit.block.CraftBlockState; ++import org.bukkit.craftbukkit.util.CraftMagicNumbers; ++import org.bukkit.entity.Player; ++import org.bukkit.event.world.StructureGrowEvent; ++// CraftBukkit end ++ + public final class ItemStack { + + public static final DecimalFormat a = new DecimalFormat("#.###"); +@@ -46,11 +58,13 @@ + this.k = false; + this.item = item; + this.count = i; +- this.damage = j; +- if (this.damage < 0) { +- this.damage = 0; +- } +- ++ // CraftBukkit start - Pass to setData to do filtering ++ this.setData(j); ++ //this.damage = j; ++ //if (this.damage < 0) { ++ // this.damage = 0; ++ //} ++ // CraftBukkit end + } + + public static ItemStack createStack(NBTTagCompound nbttagcompound) { +@@ -83,11 +97,96 @@ + } + + public boolean placeItem(EntityHuman entityhuman, World world, BlockPosition blockposition, EnumDirection enumdirection, float f, float f1, float f2) { ++ // CraftBukkit start - handle all block place event logic here ++ int data = this.getData(); ++ int count = this.count; ++ ++ if (!(this.getItem() instanceof ItemBucket)) { // if not bucket ++ world.captureBlockStates = true; ++ // special case bonemeal ++ if (this.getItem() instanceof ItemDye && this.getData() == 15) { ++ Block block = world.getType(blockposition).getBlock(); ++ if (block == Blocks.SAPLING || block instanceof BlockMushroom) { ++ world.captureTreeGeneration = true; ++ } ++ } ++ } + boolean flag = this.getItem().interactWith(this, entityhuman, world, blockposition, enumdirection, f, f1, f2); ++ int newData = this.getData(); ++ int newCount = this.count; ++ this.count = count; ++ this.setData(data); ++ world.captureBlockStates = false; ++ if (flag && world.captureTreeGeneration && world.capturedBlockStates.size() > 0) { ++ world.captureTreeGeneration = false; ++ Location location = new Location(world.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ TreeType treeType = BlockSapling.treeType; ++ BlockSapling.treeType = null; ++ List<BlockState> blocks = (List<BlockState>) world.capturedBlockStates.clone(); ++ world.capturedBlockStates.clear(); ++ StructureGrowEvent event = null; ++ if (treeType != null) { ++ event = new StructureGrowEvent(location, treeType, false, (Player) entityhuman.getBukkitEntity(), blocks); ++ org.bukkit.Bukkit.getPluginManager().callEvent(event); ++ } ++ if (event == null || !event.isCancelled()) { ++ // Change the stack to its new contents if it hasn't been tampered with. ++ if (this.count == count && this.getData() == data) { ++ this.setData(newData); ++ this.count = newCount; ++ } ++ for (BlockState blockstate : blocks) { ++ blockstate.update(true); ++ } ++ } ++ ++ return flag; ++ } ++ world.captureTreeGeneration = false; + + if (flag) { +- entityhuman.b(StatisticList.USE_ITEM_COUNT[Item.getId(this.item)]); ++ org.bukkit.event.block.BlockPlaceEvent placeEvent = null; ++ List<BlockState> blocks = (List<BlockState>) world.capturedBlockStates.clone(); ++ world.capturedBlockStates.clear(); ++ if (blocks.size() > 1) { ++ placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockMultiPlaceEvent(world, entityhuman, blocks, blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ } else if (blocks.size() == 1) { ++ placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent(world, entityhuman, blocks.get(0), blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ } ++ ++ if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) { ++ flag = false; // cancel placement ++ // revert back all captured blocks ++ for (BlockState blockstate : blocks) { ++ blockstate.update(true, false); ++ } ++ } else { ++ // Change the stack to its new contents if it hasn't been tampered with. ++ if (this.count == count && this.getData() == data) { ++ this.setData(newData); ++ this.count = newCount; ++ } ++ for (BlockState blockstate : blocks) { ++ int x = blockstate.getX(); ++ int y = blockstate.getY(); ++ int z = blockstate.getZ(); ++ int updateFlag = ((CraftBlockState) blockstate).getFlag(); ++ org.bukkit.Material mat = blockstate.getType(); ++ Block oldBlock = CraftMagicNumbers.getBlock(mat); ++ BlockPosition newblockposition = new BlockPosition(x, y, z); ++ IBlockData block = world.getType(newblockposition); ++ ++ if (!(block instanceof BlockContainer)) { // Containers get placed automatically ++ block.getBlock().onPlace(world, newblockposition, block); ++ } ++ ++ world.notifyAndUpdatePhysics(newblockposition, null, oldBlock, block.getBlock(), updateFlag); // send null chunk as chunk.k() returns false by this point ++ } ++ entityhuman.b(StatisticList.USE_ITEM_COUNT[Item.getId(this.item)]); ++ } + } ++ world.capturedBlockStates.clear(); ++ // CraftBukkit end + + return flag; + } +@@ -111,7 +210,7 @@ + nbttagcompound.setByte("Count", (byte) this.count); + nbttagcompound.setShort("Damage", (short) this.damage); + if (this.tag != null) { +- nbttagcompound.set("tag", this.tag); ++ nbttagcompound.set("tag", this.tag.clone()); // CraftBukkit - make defensive copy, data is going to another thread + } + + return nbttagcompound; +@@ -125,13 +224,18 @@ + } + + this.count = nbttagcompound.getByte("Count"); ++ /* CraftBukkit start - Route through setData for filtering + this.damage = nbttagcompound.getShort("Damage"); + if (this.damage < 0) { + this.damage = 0; + } ++ */ ++ this.setData(nbttagcompound.getShort("Damage")); ++ // CraftBukkit end + + if (nbttagcompound.hasKeyOfType("tag", 10)) { +- this.tag = nbttagcompound.getCompound("tag"); ++ // CraftBukkit - make defensive copy as this data may be coming from the save thread ++ this.tag = (NBTTagCompound) nbttagcompound.getCompound("tag").clone(); + if (this.item != null) { + this.item.a(this.tag); + } +@@ -168,8 +272,29 @@ + } + + public void setData(int i) { ++ // CraftBukkit start - Filter out data for items that shouldn't have it ++ // The crafting system uses this value for a special purpose so we have to allow it ++ if (i == 32767) { ++ this.damage = i; ++ return; ++ } ++ ++ // Is this a block? ++ if (CraftMagicNumbers.getBlock(CraftMagicNumbers.getId(this.getItem())) != Blocks.AIR) { ++ // If vanilla doesn't use data on it don't allow any ++ if (!(this.usesData() || this.getItem().usesDurability())) { ++ i = 0; ++ } ++ } ++ ++ // Filter invalid plant data ++ if (CraftMagicNumbers.getBlock(CraftMagicNumbers.getId(this.getItem())) == Blocks.DOUBLE_PLANT && (i > 5 || i < 0)) { ++ i = 0; ++ } ++ // CraftBukkit end ++ + this.damage = i; +- if (this.damage < 0) { ++ if (this.damage < -1) { // CraftBukkit + this.damage = 0; + } + +@@ -222,6 +347,12 @@ + if (this.count < 0) { + this.count = 0; + } ++ ++ // CraftBukkit start - Check for item breaking ++ if (this.count == 0 && entityliving instanceof EntityHuman) { ++ org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerItemBreakEvent((EntityHuman) entityliving, this); ++ } ++ // CraftBukkit end + + this.damage = 0; + } +@@ -489,6 +620,7 @@ + + public void setItem(Item item) { + this.item = item; ++ this.setData(this.getData()); // CraftBukkit - Set data again to ensure it is filtered properly + } + + public IChatBaseComponent C() { diff --git a/nms-patches/ItemWaterLily.patch b/nms-patches/ItemWaterLily.patch new file mode 100644 index 00000000..2158f642 --- /dev/null +++ b/nms-patches/ItemWaterLily.patch @@ -0,0 +1,18 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ItemWaterLily.java 2014-11-27 08:59:46.793421441 +1100 ++++ src/main/java/net/minecraft/server/ItemWaterLily.java 2014-11-27 08:42:10.152850911 +1100 +@@ -27,7 +27,15 @@ + IBlockData iblockdata = world.getType(blockposition); + + if (iblockdata.getBlock().getMaterial() == Material.WATER && ((Integer) iblockdata.get(BlockFluids.LEVEL)).intValue() == 0 && world.isEmpty(blockposition1)) { ++ // CraftBukkit start - special case for handling block placement with water lilies ++ org.bukkit.block.BlockState blockstate = org.bukkit.craftbukkit.block.CraftBlockState.getBlockState(world, blockposition1.getX(), blockposition1.getY(), blockposition1.getZ()); + world.setTypeUpdate(blockposition1, Blocks.WATERLILY.getBlockData()); ++ org.bukkit.event.block.BlockPlaceEvent placeEvent = org.bukkit.craftbukkit.event.CraftEventFactory.callBlockPlaceEvent(world, entityhuman, blockstate, blockposition.getX(), blockposition.getY(), blockposition.getZ()); ++ if (placeEvent != null && (placeEvent.isCancelled() || !placeEvent.canBuild())) { ++ blockstate.update(true, false); ++ return itemstack; ++ } ++ // CraftBukkit end + if (!entityhuman.abilities.canInstantlyBuild) { + --itemstack.count; + } diff --git a/nms-patches/ItemWorldMap.patch b/nms-patches/ItemWorldMap.patch new file mode 100644 index 00000000..f85596fd --- /dev/null +++ b/nms-patches/ItemWorldMap.patch @@ -0,0 +1,75 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ItemWorldMap.java 2014-11-27 08:59:46.793421441 +1100 ++++ src/main/java/net/minecraft/server/ItemWorldMap.java 2014-11-27 08:42:10.144850927 +1100 +@@ -4,6 +4,11 @@ + import com.google.common.collect.Iterables; + import com.google.common.collect.Multisets; + ++// CraftBukkit start ++import org.bukkit.Bukkit; ++import org.bukkit.event.server.MapInitializeEvent; ++// CraftBukkit end ++ + public class ItemWorldMap extends ItemWorldMapBase { + + protected ItemWorldMap() { +@@ -11,25 +16,32 @@ + } + + 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) world.a(WorldMap.class, s); ++ WorldMap worldmap = (WorldMap) worldMain.a(WorldMap.class, s); // CraftBukkit - use primary world for maps + + if (worldmap == null && !world.isStatic) { +- itemstack.setData(world.b("map")); ++ 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().c(), (double) world.getWorldData().e(), worldmap.scale); +- worldmap.map = (byte) world.worldProvider.getDimension(); ++ worldmap.map = (byte) ((WorldServer) world).dimension; // CraftBukkit - fixes Bukkit multiworld maps + worldmap.c(); +- world.a(s, (PersistentBase) worldmap); ++ 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) { +- if (world.worldProvider.getDimension() == worldmap.map && entity instanceof EntityHuman) { ++ // 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; +@@ -179,6 +191,8 @@ + if (itemstack.hasTag() && itemstack.getTag().getBoolean("map_is_scaling")) { + 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()); + +@@ -190,7 +204,12 @@ + worldmap1.a((double) worldmap.centerX, (double) worldmap.centerZ, worldmap1.scale); + worldmap1.map = worldmap.map; + worldmap1.c(); +- world.a("map_" + itemstack.getData(), (PersistentBase) worldmap1); ++ 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/nms-patches/JsonList.patch b/nms-patches/JsonList.patch new file mode 100644 index 00000000..0dcf0352 --- /dev/null +++ b/nms-patches/JsonList.patch @@ -0,0 +1,33 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/JsonList.java 2014-11-27 08:59:46.797421424 +1100 ++++ src/main/java/net/minecraft/server/JsonList.java 2014-11-27 08:42:10.168850880 +1100 +@@ -79,6 +79,12 @@ + public String[] getEntries() { + return (String[]) this.d.keySet().toArray(new String[this.d.size()]); + } ++ ++ // CraftBukkit start ++ public Collection<JsonListEntry> getValues() { ++ return this.d.values(); ++ } ++ // CraftBukkit end + + public boolean isEmpty() { + return this.d.size() < 1; +@@ -122,7 +128,7 @@ + return this.d; + } + +- public void save() { ++ public void save() throws IOException { // CraftBukkit - Added throws + Collection collection = this.d.values(); + String s = this.b.toJson(collection); + BufferedWriter bufferedwriter = null; +@@ -136,7 +142,7 @@ + + } + +- public void load() { ++ public void load() throws IOException { // CraftBukkit - Added throws + Collection collection = null; + BufferedReader bufferedreader = null; + diff --git a/nms-patches/LoginListener.patch b/nms-patches/LoginListener.patch new file mode 100644 index 00000000..b80cdad0 --- /dev/null +++ b/nms-patches/LoginListener.patch @@ -0,0 +1,35 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/LoginListener.java 2014-11-27 08:59:46.797421424 +1100 ++++ src/main/java/net/minecraft/server/LoginListener.java 2014-11-27 08:42:10.172850872 +1100 +@@ -26,6 +26,7 @@ + private GameProfile i; + private String j; + private SecretKey loginKey; ++ public String hostname = ""; // CraftBukkit - add field + + public LoginListener(MinecraftServer minecraftserver, NetworkManager networkmanager) { + this.g = EnumProtocolState.HELLO; +@@ -64,10 +65,12 @@ + this.i = this.a(this.i); + } + +- String s = this.server.getPlayerList().attemptLogin(this.networkManager.getSocketAddress(), this.i); ++ // CraftBukkit start - fire PlayerLoginEvent ++ EntityPlayer s = this.server.getPlayerList().attemptLogin(this, this.i, hostname); + +- if (s != null) { +- this.disconnect(s); ++ if (s == null) { ++ // this.disconnect(s); ++ // CraftBukkit end + } else { + this.g = EnumProtocolState.ACCEPTED; + if (this.server.aI() >= 0 && !this.networkManager.c()) { +@@ -75,7 +78,7 @@ + } + + this.networkManager.handle(new PacketLoginOutSuccess(this.i)); +- this.server.getPlayerList().a(this.networkManager, this.server.getPlayerList().processLogin(this.i)); ++ this.server.getPlayerList().a(this.networkManager, this.server.getPlayerList().processLogin(this.i, s)); // CraftBukkit - add player reference + } + + } diff --git a/nms-patches/MethodProfiler.patch b/nms-patches/MethodProfiler.patch new file mode 100644 index 00000000..62424983 --- /dev/null +++ b/nms-patches/MethodProfiler.patch @@ -0,0 +1,135 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/MethodProfiler.java 2014-11-27 08:59:46.797421424 +1100 ++++ src/main/java/net/minecraft/server/MethodProfiler.java 2014-11-27 08:42:10.132850949 +1100 +@@ -10,129 +10,29 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// CraftBukkit start - Strip down to empty methods, performance cost + public class MethodProfiler { +- +- private static final Logger b = LogManager.getLogger(); +- private final List c = Lists.newArrayList(); +- private final List d = Lists.newArrayList(); + public boolean a; +- private String e = ""; +- private final Map f = Maps.newHashMap(); + + public MethodProfiler() {} + + public void a() { +- this.f.clear(); +- this.e = ""; +- this.c.clear(); + } + + public void a(String s) { +- if (this.a) { +- if (this.e.length() > 0) { +- this.e = this.e + "."; +- } +- +- this.e = this.e + s; +- this.c.add(this.e); +- this.d.add(Long.valueOf(System.nanoTime())); +- } + } + + public void b() { +- if (this.a) { +- long i = System.nanoTime(); +- long j = ((Long) this.d.remove(this.d.size() - 1)).longValue(); +- +- this.c.remove(this.c.size() - 1); +- long k = i - j; +- +- if (this.f.containsKey(this.e)) { +- this.f.put(this.e, Long.valueOf(((Long) this.f.get(this.e)).longValue() + k)); +- } else { +- this.f.put(this.e, Long.valueOf(k)); +- } +- +- if (k > 100000000L) { +- MethodProfiler.b.warn("Something\'s taking too long! \'" + this.e + "\' took aprox " + (double) k / 1000000.0D + " ms"); +- } +- +- this.e = !this.c.isEmpty() ? (String) this.c.get(this.c.size() - 1) : ""; +- } + } + + public List b(String s) { +- if (!this.a) { +- return null; +- } else { +- long i = this.f.containsKey("root") ? ((Long) this.f.get("root")).longValue() : 0L; +- long j = this.f.containsKey(s) ? ((Long) this.f.get(s)).longValue() : -1L; +- ArrayList arraylist = Lists.newArrayList(); +- +- if (s.length() > 0) { +- s = s + "."; +- } +- +- long k = 0L; +- Iterator iterator = this.f.keySet().iterator(); +- +- while (iterator.hasNext()) { +- String s1 = (String) iterator.next(); +- +- if (s1.length() > s.length() && s1.startsWith(s) && s1.indexOf(".", s.length() + 1) < 0) { +- k += ((Long) this.f.get(s1)).longValue(); +- } +- } +- +- float f = (float) k; +- +- if (k < j) { +- k = j; +- } +- +- if (i < k) { +- i = k; +- } +- +- Iterator iterator1 = this.f.keySet().iterator(); +- +- String s2; +- +- while (iterator1.hasNext()) { +- s2 = (String) iterator1.next(); +- if (s2.length() > s.length() && s2.startsWith(s) && s2.indexOf(".", s.length() + 1) < 0) { +- long l = ((Long) this.f.get(s2)).longValue(); +- double d0 = (double) l * 100.0D / (double) k; +- double d1 = (double) l * 100.0D / (double) i; +- String s3 = s2.substring(s.length()); +- +- arraylist.add(new ProfilerInfo(s3, d0, d1)); +- } +- } +- +- iterator1 = this.f.keySet().iterator(); +- +- while (iterator1.hasNext()) { +- s2 = (String) iterator1.next(); +- this.f.put(s2, Long.valueOf(((Long) this.f.get(s2)).longValue() * 999L / 1000L)); +- } +- +- if ((float) k > f) { +- arraylist.add(new ProfilerInfo("unspecified", (double) ((float) k - f) * 100.0D / (double) k, (double) ((float) k - f) * 100.0D / (double) i)); +- } +- +- Collections.sort(arraylist); +- arraylist.add(0, new ProfilerInfo(s, 100.0D, (double) k * 100.0D / (double) i)); +- return arraylist; +- } ++ return null; + } + + public void c(String s) { +- this.b(); +- this.a(s); + } + + public String c() { +- return this.c.size() == 0 ? "[UNKNOWN]" : (String) this.c.get(this.c.size() - 1); ++ return null; + } + } diff --git a/nms-patches/MinecraftServer.patch b/nms-patches/MinecraftServer.patch new file mode 100644 index 00000000..22d7748f --- /dev/null +++ b/nms-patches/MinecraftServer.patch @@ -0,0 +1,726 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/MinecraftServer.java 2014-11-27 08:59:46.801421406 +1100 ++++ src/main/java/net/minecraft/server/MinecraftServer.java 2014-11-27 08:42:10.092851027 +1100 +@@ -37,6 +37,18 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// CraftBukkit start ++import java.io.IOException; ++ ++import jline.console.ConsoleReader; ++import joptsimple.OptionSet; ++ ++import org.bukkit.craftbukkit.Main; ++import org.bukkit.World.Environment; ++import org.bukkit.craftbukkit.util.Waitable; ++import org.bukkit.event.server.RemoteServerCommandEvent; ++import org.bukkit.event.world.WorldSaveEvent; ++// CraftBukkit end + public abstract class MinecraftServer implements ICommandListener, Runnable, IAsyncTaskHandler, IMojangStatistics { + + private static final Logger LOGGER = LogManager.getLogger(); +@@ -93,24 +105,66 @@ + private Thread serverThread; + private long ab = ax(); + +- public MinecraftServer(File file, Proxy proxy, File file1) { ++ // CraftBukkit start ++ public List<WorldServer> worlds = new ArrayList<WorldServer>(); ++ public org.bukkit.craftbukkit.CraftServer server; ++ public OptionSet options; ++ public org.bukkit.command.ConsoleCommandSender console; ++ public org.bukkit.command.RemoteConsoleCommandSender remoteConsole; ++ public ConsoleReader reader; ++ public static int currentTick = (int) (System.currentTimeMillis() / 50); ++ public final Thread primaryThread; ++ public java.util.Queue<Runnable> processQueue = new java.util.concurrent.ConcurrentLinkedQueue<Runnable>(); ++ public int autosavePeriod; ++ // CraftBukkit end ++ ++ public MinecraftServer(OptionSet options, Proxy proxy, File file1) { + this.d = proxy; + MinecraftServer.k = this; +- this.universe = file; ++ // this.universe = file; // CraftBukkit + this.q = new ServerConnection(this); + this.Z = new UserCache(this, file1); + this.p = this.h(); +- this.convertable = new WorldLoaderServer(file); ++ // this.convertable = new WorldLoaderServer(file); // CraftBukkit - moved to DedicatedServer.init + this.V = new YggdrasilAuthenticationService(proxy, UUID.randomUUID().toString()); + this.W = this.V.createMinecraftSessionService(); + this.Y = this.V.createProfileRepository(); ++ // CraftBukkit start ++ this.options = options; ++ // Try to see if we're actually running in a terminal, disable jline if not ++ if (System.console() == null) { ++ System.setProperty("jline.terminal", "jline.UnsupportedTerminal"); ++ Main.useJline = false; ++ } ++ ++ try { ++ reader = new ConsoleReader(System.in, System.out); ++ reader.setExpandEvents(false); // Avoid parsing exceptions for uncommonly used event designators ++ } catch (Throwable e) { ++ try { ++ // Try again with jline disabled for Windows users without C++ 2008 Redistributable ++ System.setProperty("jline.terminal", "jline.UnsupportedTerminal"); ++ System.setProperty("user.language", "en"); ++ Main.useJline = false; ++ reader = new ConsoleReader(System.in, System.out); ++ reader.setExpandEvents(false); ++ } catch (IOException ex) { ++ LOGGER.warn((String) null, ex); ++ } ++ } ++ Runtime.getRuntime().addShutdownHook(new org.bukkit.craftbukkit.util.ServerShutdownThread(this)); ++ ++ this.serverThread = primaryThread = new Thread(this, "Server thread"); // Moved from main + } + ++ public abstract PropertyManager getPropertyManager(); ++ // CraftBukkit end ++ + protected CommandDispatcher h() { + return new CommandDispatcher(); + } + +- protected abstract boolean init(); ++ protected abstract boolean init() throws java.net.UnknownHostException; // CraftBukkit - throws UnknownHostException + + protected void a(String s) { + if (this.getConvertable().isConvertable(s)) { +@@ -129,6 +183,7 @@ + this.a(s); + this.b("menu.loadingLevel"); + this.worldServer = new WorldServer[3]; ++ /* CraftBukkit start - Remove ticktime arrays and worldsettings + this.h = new long[this.worldServer.length][100]; + IDataManager idatamanager = this.convertable.a(s, true); + +@@ -152,37 +207,110 @@ + worlddata.a(s1); + worldsettings = new WorldSettings(worlddata); + } ++ */ ++ int worldCount = 3; + +- for (int j = 0; j < this.worldServer.length; ++j) { +- byte b0 = 0; ++ for (int j = 0; j < worldCount; ++j) { ++ WorldServer world; ++ byte dimension = 0; + + if (j == 1) { +- b0 = -1; ++ if (getAllowNether()) { ++ dimension = -1; ++ } else { ++ continue; ++ } + } + + if (j == 2) { +- b0 = 1; ++ if (server.getAllowEnd()) { ++ dimension = 1; ++ } else { ++ continue; ++ } + } + ++ String worldType = org.bukkit.World.Environment.getEnvironment(dimension).toString().toLowerCase(); ++ String name = (dimension == 0) ? s : s + "_" + worldType; ++ ++ org.bukkit.generator.ChunkGenerator gen = this.server.getGenerator(name); ++ WorldSettings worldsettings = new WorldSettings(i, this.getGamemode(), this.getGenerateStructures(), this.isHardcore(), worldtype); ++ worldsettings.setGeneratorSettings(s2); ++ + if (j == 0) { ++ IDataManager idatamanager = new ServerNBTManager(server.getWorldContainer(), s1, true); ++ WorldData worlddata = idatamanager.getWorldData(); ++ if (worlddata == null) { ++ worlddata = new WorldData(worldsettings, s1); ++ } + if (this.W()) { +- this.worldServer[j] = (WorldServer) (new DemoWorldServer(this, idatamanager, worlddata, b0, this.methodProfiler)).b(); ++ world = (WorldServer) (new DemoWorldServer(this, idatamanager, worlddata, dimension, this.methodProfiler)).b(); + } else { +- this.worldServer[j] = (WorldServer) (new WorldServer(this, idatamanager, worlddata, b0, this.methodProfiler)).b(); ++ world = (WorldServer) (new WorldServer(this, idatamanager, worlddata, dimension, this.methodProfiler, org.bukkit.World.Environment.getEnvironment(dimension), gen)).b(); + } + +- this.worldServer[j].a(worldsettings); ++ world.a(worldsettings); ++ this.server.scoreboardManager = new org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager(this, world.getScoreboard()); + } else { +- this.worldServer[j] = (WorldServer) (new SecondaryWorldServer(this, idatamanager, b0, this.worldServer[0], this.methodProfiler)).b(); ++ String dim = "DIM" + dimension; ++ ++ File newWorld = new File(new File(name), dim); ++ File oldWorld = new File(new File(s), dim); ++ ++ if ((!newWorld.isDirectory()) && (oldWorld.isDirectory())) { ++ MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder required ----"); ++ MinecraftServer.LOGGER.info("Unfortunately due to the way that Minecraft implemented multiworld support in 1.6, Bukkit requires that you move your " + worldType + " folder to a new location in order to operate correctly."); ++ MinecraftServer.LOGGER.info("We will move this folder for you, but it will mean that you need to move it back should you wish to stop using Bukkit in the future."); ++ MinecraftServer.LOGGER.info("Attempting to move " + oldWorld + " to " + newWorld + "..."); ++ ++ if (newWorld.exists()) { ++ MinecraftServer.LOGGER.warn("A file or folder already exists at " + newWorld + "!"); ++ MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder failed ----"); ++ } else if (newWorld.getParentFile().mkdirs()) { ++ if (oldWorld.renameTo(newWorld)) { ++ MinecraftServer.LOGGER.info("Success! To restore " + worldType + " in the future, simply move " + newWorld + " to " + oldWorld); ++ // Migrate world data too. ++ try { ++ com.google.common.io.Files.copy(new File(new File(s), "level.dat"), new File(new File(name), "level.dat")); ++ } catch (IOException exception) { ++ MinecraftServer.LOGGER.warn("Unable to migrate world data."); ++ } ++ MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder complete ----"); ++ } else { ++ MinecraftServer.LOGGER.warn("Could not move folder " + oldWorld + " to " + newWorld + "!"); ++ MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder failed ----"); ++ } ++ } else { ++ MinecraftServer.LOGGER.warn("Could not create path for " + newWorld + "!"); ++ MinecraftServer.LOGGER.info("---- Migration of old " + worldType + " folder failed ----"); ++ } ++ } ++ ++ IDataManager idatamanager = new ServerNBTManager(server.getWorldContainer(), name, true); ++ // world =, b0 to dimension, s1 to name, added Environment and gen ++ WorldData worlddata = idatamanager.getWorldData(); ++ if (worlddata == null) { ++ worlddata = new WorldData(worldsettings, name); ++ } ++ world = (WorldServer) new SecondaryWorldServer(this, idatamanager, dimension, this.worlds.get(0), this.methodProfiler, worlddata, org.bukkit.World.Environment.getEnvironment(dimension), gen).b(); + } + +- this.worldServer[j].addIWorldAccess(new WorldManager(this, this.worldServer[j])); ++ if (gen != null) { ++ world.getWorld().getPopulators().addAll(gen.getDefaultPopulators(world.getWorld())); ++ } ++ ++ this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldInitEvent(world.getWorld())); ++ ++ world.addIWorldAccess(new WorldManager(this, world)); + if (!this.S()) { +- this.worldServer[j].getWorldData().setGameType(this.getGamemode()); ++ world.getWorldData().setGameType(this.getGamemode()); + } ++ ++ worlds.add(world); ++ getPlayerList().setPlayerFileData(worlds.toArray(new WorldServer[worlds.size()])); + } + +- this.v.setPlayerFileData(this.worldServer); ++ // CraftBukkit end + this.a(this.getDifficulty()); + this.k(); + } +@@ -197,25 +325,38 @@ + this.b("menu.generatingTerrain"); + byte b0 = 0; + +- MinecraftServer.LOGGER.info("Preparing start region for level " + b0); +- WorldServer worldserver = this.worldServer[b0]; +- BlockPosition blockposition = worldserver.getSpawn(); +- long j = ax(); +- +- for (int k = -192; k <= 192 && this.isRunning(); k += 16) { +- for (int l = -192; l <= 192 && this.isRunning(); l += 16) { +- long i1 = ax(); +- +- if (i1 - j > 1000L) { +- this.a_("Preparing spawn area", i * 100 / 625); +- j = i1; +- } ++ // CraftBukkit start - fire WorldLoadEvent and handle whether or not to keep the spawn in memory ++ for (int m = 0; m < worlds.size(); m++) { ++ WorldServer worldserver = this.worlds.get(m); ++ LOGGER.info("Preparing start region for level " + m + " (Seed: " + worldserver.getSeed() + ")"); ++ ++ if (!worldserver.getWorld().getKeepSpawnInMemory()) { ++ continue; ++ } ++ ++ BlockPosition blockposition = worldserver.getSpawn(); ++ long j = ax(); ++ i = 0; ++ ++ for (int k = -192; k <= 192 && this.isRunning(); k += 16) { ++ for (int l = -192; l <= 192 && this.isRunning(); l += 16) { ++ long i1 = ax(); ++ ++ if (i1 - j > 1000L) { ++ this.a_("Preparing spawn area", i * 100 / 625); ++ j = i1; ++ } + +- ++i; +- worldserver.chunkProviderServer.getChunkAt(blockposition.getX() + k >> 4, blockposition.getZ() + l >> 4); ++ ++i; ++ worldserver.chunkProviderServer.getChunkAt(blockposition.getX() + k >> 4, blockposition.getZ() + l >> 4); ++ } + } + } + ++ for (WorldServer world : this.worlds) { ++ this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldLoadEvent(world.getWorld())); ++ } ++ // CraftBukkit end + this.q(); + } + +@@ -247,35 +388,42 @@ + protected void q() { + this.e = null; + this.f = 0; ++ ++ this.server.enablePlugins(org.bukkit.plugin.PluginLoadOrder.POSTWORLD); // CraftBukkit + } + +- protected void saveChunks(boolean flag) { ++ protected void saveChunks(boolean flag) throws ExceptionWorldConflict { // CraftBukkit - added throws + if (!this.N) { +- WorldServer[] aworldserver = this.worldServer; +- int i = aworldserver.length; +- +- for (int j = 0; j < i; ++j) { +- WorldServer worldserver = aworldserver[j]; + ++ // CraftBukkit start ++ for (int j = 0; j < worlds.size(); ++j) { ++ WorldServer worldserver = worlds.get(j); ++ // CraftBukkit end + if (worldserver != null) { + if (!flag) { + MinecraftServer.LOGGER.info("Saving chunks for level \'" + worldserver.getWorldData().getName() + "\'/" + worldserver.worldProvider.getName()); + } + +- try { +- worldserver.save(true, (IProgressUpdate) null); +- } catch (ExceptionWorldConflict exceptionworldconflict) { +- MinecraftServer.LOGGER.warn(exceptionworldconflict.getMessage()); +- } ++ worldserver.save(true, (IProgressUpdate) null); ++ worldserver.saveLevel(); ++ ++ WorldSaveEvent event = new WorldSaveEvent(worldserver.getWorld()); ++ this.server.getPluginManager().callEvent(event); ++ // CraftBukkit end + } + } + + } + } + +- public void stop() { ++ public void stop() throws ExceptionWorldConflict { // CraftBukkit - added throws + if (!this.N) { + MinecraftServer.LOGGER.info("Stopping server"); ++ // CraftBukkit start ++ if (this.server != null) { ++ this.server.disablePlugins(); ++ } ++ // CraftBukkit end + if (this.ao() != null) { + this.ao().b(); + } +@@ -290,11 +438,13 @@ + MinecraftServer.LOGGER.info("Saving worlds"); + this.saveChunks(false); + ++ /* CraftBukkit start - Handled in saveChunks + for (int i = 0; i < this.worldServer.length; ++i) { + WorldServer worldserver = this.worldServer[i]; + + worldserver.saveLevel(); + } ++ // CraftBukkit end */ + } + + if (this.m.d()) { +@@ -335,6 +485,7 @@ + long k = j - this.ab; + + if (k > 2000L && this.ab - this.R >= 15000L) { ++ if (server.getWarnOnOverload()) // CraftBukkit + MinecraftServer.LOGGER.warn("Can\'t keep up! Did the system time change, or is the server overloaded? Running {}ms behind, skipping {} tick(s)", new Object[] { Long.valueOf(k), Long.valueOf(k / 50L)}); + k = 2000L; + this.R = this.ab; +@@ -347,11 +498,12 @@ + + i += k; + this.ab = j; +- if (this.worldServer[0].everyoneDeeplySleeping()) { ++ if (this.worlds.get(0).everyoneDeeplySleeping()) { // CraftBukkit + this.y(); + i = 0L; + } else { + while (i > 50L) { ++ MinecraftServer.currentTick = (int) (System.currentTimeMillis() / 50); // CraftBukkit + i -= 50L; + this.y(); + } +@@ -389,6 +541,12 @@ + } catch (Throwable throwable1) { + MinecraftServer.LOGGER.error("Exception stopping the server", throwable1); + } finally { ++ // CraftBukkit start - Restore terminal to original settings ++ try { ++ reader.getTerminal().restore(); ++ } catch (Exception ignored) { ++ } ++ // CraftBukkit end + this.x(); + } + +@@ -428,7 +586,7 @@ + + protected void x() {} + +- protected void y() { ++ protected void y() throws ExceptionWorldConflict { // CraftBukkit - added throws + long i = System.nanoTime(); + + ++this.ticks; +@@ -454,7 +612,7 @@ + this.r.b().a(agameprofile); + } + +- if (this.ticks % 900 == 0) { ++ if (autosavePeriod > 0 && this.ticks % autosavePeriod == 0) { // CraftBukkit + this.methodProfiler.a("save"); + this.v.savePlayers(); + this.saveChunks(true); +@@ -493,20 +651,40 @@ + + this.methodProfiler.c("levels"); + ++ // CraftBukkit start ++ this.server.getScheduler().mainThreadHeartbeat(this.ticks); ++ ++ // Run tasks that are waiting on processing ++ while (!processQueue.isEmpty()) { ++ processQueue.remove().run(); ++ } ++ ++ org.bukkit.craftbukkit.chunkio.ChunkIOExecutor.tick(); ++ ++ // Send time updates to everyone, it will get the right time from the world the player is in. ++ if (this.ticks % 20 == 0) { ++ for (int i = 0; i < this.getPlayerList().players.size(); ++i) { ++ EntityPlayer entityplayer = (EntityPlayer) this.getPlayerList().players.get(i); ++ entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateTime(entityplayer.world.getTime(), entityplayer.getPlayerTime(), entityplayer.world.getGameRules().getBoolean("doDaylightCycle"))); // Add support for per player time ++ } ++ } ++ + int i; + +- for (i = 0; i < this.worldServer.length; ++i) { ++ for (i = 0; i < this.worlds.size(); ++i) { + long j = System.nanoTime(); + +- if (i == 0 || this.getAllowNether()) { +- WorldServer worldserver = this.worldServer[i]; ++ // if (i == 0 || this.getAllowNether()) { ++ WorldServer worldserver = this.worlds.get(i); + + this.methodProfiler.a(worldserver.getWorldData().getName()); ++ /* Drop global time updates + if (this.ticks % 20 == 0) { + this.methodProfiler.a("timeSync"); + this.v.a(new PacketPlayOutUpdateTime(worldserver.getTime(), worldserver.getDayTime(), worldserver.getGameRules().getBoolean("doDaylightCycle")), worldserver.worldProvider.getDimension()); + this.methodProfiler.b(); + } ++ // CraftBukkit end */ + + this.methodProfiler.a("tick"); + +@@ -533,9 +711,9 @@ + worldserver.getTracker().updatePlayers(); + this.methodProfiler.b(); + this.methodProfiler.b(); +- } ++ // } // CraftBukkit + +- this.h[i][this.ticks % 100] = System.nanoTime() - j; ++ // this.h[i][this.ticks % 100] = System.nanoTime() - j; // CraftBukkit + } + + this.methodProfiler.c("connection"); +@@ -559,10 +737,11 @@ + this.o.add(iupdateplayerlistbox); + } + +- public static void main(String[] astring) { ++ public static void main(final OptionSet options) { // CraftBukkit - replaces main(String[] astring) + DispenserRegistry.c(); + + try { ++ /* CraftBukkit start - Replace everything + boolean flag = true; + String s = null; + String s1 = "."; +@@ -636,6 +815,29 @@ + + dedicatedserver.B(); + Runtime.getRuntime().addShutdownHook(new ThreadShutdown("Server Shutdown Thread", dedicatedserver)); ++ */ ++ ++ DedicatedServer dedicatedserver = new DedicatedServer(options); ++ ++ if (options.has("port")) { ++ int port = (Integer) options.valueOf("port"); ++ if (port > 0) { ++ dedicatedserver.setPort(port); ++ } ++ } ++ ++ if (options.has("universe")) { ++ dedicatedserver.universe = (File) options.valueOf("universe"); ++ } else { ++ dedicatedserver.universe = new File("."); ++ } ++ ++ if (options.has("world")) { ++ dedicatedserver.setWorld((String) options.valueOf("world")); ++ } ++ ++ dedicatedserver.primaryThread.start(); ++ // CraftBukkit end + } catch (Exception exception) { + MinecraftServer.LOGGER.fatal("Failed to start the minecraft server", exception); + } +@@ -643,8 +845,10 @@ + } + + public void B() { ++ /* CraftBukkit start - prevent abuse + this.serverThread = new Thread(this, "Server thread"); + this.serverThread.start(); ++ // CraftBukkit end */ + } + + public File d(String s) { +@@ -660,7 +864,14 @@ + } + + public WorldServer getWorldServer(int i) { +- return i == -1 ? this.worldServer[1] : (i == 1 ? this.worldServer[2] : this.worldServer[0]); ++ // CraftBukkit start ++ for (WorldServer world : worlds) { ++ if (world.dimension == i) { ++ return world; ++ } ++ } ++ return worlds.get(0); ++ // CraftBukkit end + } + + public String C() { +@@ -696,17 +907,62 @@ + } + + public String getPlugins() { +- return ""; +- } ++ // CraftBukkit start - Whole method ++ StringBuilder result = new StringBuilder(); ++ org.bukkit.plugin.Plugin[] plugins = server.getPluginManager().getPlugins(); ++ ++ result.append(server.getName()); ++ result.append(" on Bukkit "); ++ result.append(server.getBukkitVersion()); ++ ++ if (plugins.length > 0 && server.getQueryPlugins()) { ++ result.append(": "); ++ ++ for (int i = 0; i < plugins.length; i++) { ++ if (i > 0) { ++ result.append("; "); ++ } + +- public String executeRemoteCommand(String s) { +- RemoteControlCommandListener.getInstance().i(); +- this.p.a(RemoteControlCommandListener.getInstance(), s); +- return RemoteControlCommandListener.getInstance().j(); ++ result.append(plugins[i].getDescription().getName()); ++ result.append(" "); ++ result.append(plugins[i].getDescription().getVersion().replaceAll(";", ",")); ++ } ++ } ++ ++ return result.toString(); ++ // CraftBukkit end ++ } ++ ++ // CraftBukkit start - fire RemoteServerCommandEvent ++ public String executeRemoteCommand(final String s) { ++ Waitable<String> waitable = new Waitable<String>() { ++ @Override ++ protected String evaluate() { ++ RemoteControlCommandListener.getInstance().i(); ++ // Event changes start ++ RemoteServerCommandEvent event = new RemoteServerCommandEvent(remoteConsole, s); ++ server.getPluginManager().callEvent(event); ++ // Event change end ++ ServerCommand serverCommand = new ServerCommand(event.getCommand(), RemoteControlCommandListener.getInstance()); ++ server.dispatchServerCommand(remoteConsole, serverCommand); ++ // this.p.a(RemoteControlCommandListener.getInstance(), s); ++ return RemoteControlCommandListener.getInstance().j(); ++ } ++ }; ++ processQueue.add(waitable); ++ try { ++ return waitable.get(); ++ } catch (java.util.concurrent.ExecutionException e) { ++ throw new RuntimeException("Exception processing rcon command " + s, e.getCause()); ++ } catch (InterruptedException e) { ++ Thread.currentThread().interrupt(); // Maintain interrupted state ++ throw new RuntimeException("Interrupted processing rcon command " + s, e); ++ } ++ // CraftBukkit end + } + + public boolean isDebugging() { +- return false; ++ return this.getPropertyManager().getBoolean("debug", false); // CraftBukkit - don't hardcode + } + + public void h(String s) { +@@ -721,7 +977,7 @@ + } + + public String getServerModName() { +- return "vanilla"; ++ return server.getName(); // CraftBukkit - cb > vanilla! + } + + public CrashReport b(CrashReport crashreport) { +@@ -734,6 +990,7 @@ + } + + public List tabCompleteCommand(ICommandListener icommandlistener, String s, BlockPosition blockposition) { ++ /* CraftBukkit start - Allow tab-completion of Bukkit commands + ArrayList arraylist = Lists.newArrayList(); + + if (s.startsWith("/")) { +@@ -772,6 +1029,9 @@ + + return arraylist; + } ++ */ ++ return server.tabComplete(icommandlistener, s); ++ // CraftBukkit end + } + + public static MinecraftServer getServer() { +@@ -835,8 +1095,10 @@ + } + + public void a(EnumDifficulty enumdifficulty) { +- for (int i = 0; i < this.worldServer.length; ++i) { +- WorldServer worldserver = this.worldServer[i]; ++ // CraftBukkit start ++ for (int i = 0; i < this.worlds.size(); ++i) { ++ WorldServer worldserver = this.worlds.get(i); ++ // CraftBukkit end + + if (worldserver != null) { + if (worldserver.getWorldData().isHardcore()) { +@@ -878,15 +1140,17 @@ + this.N = true; + this.getConvertable().d(); + +- for (int i = 0; i < this.worldServer.length; ++i) { +- WorldServer worldserver = this.worldServer[i]; ++ // CraftBukkit start ++ for (int i = 0; i < this.worlds.size(); ++i) { ++ WorldServer worldserver = this.worlds.get(i); ++ // CraftBukkit end + + if (worldserver != null) { + worldserver.saveLevel(); + } + } + +- this.getConvertable().e(this.worldServer[0].getDataManager().g()); ++ this.getConvertable().e(this.worlds.get(0).getDataManager().g()); // CraftBukkit + this.safeShutdown(); + } + +@@ -919,9 +1183,11 @@ + int i = 0; + + if (this.worldServer != null) { +- for (int j = 0; j < this.worldServer.length; ++j) { +- if (this.worldServer[j] != null) { +- WorldServer worldserver = this.worldServer[j]; ++ // CraftBukkit start ++ for (int j = 0; j < this.worlds.size(); ++j) { ++ WorldServer worldserver = this.worlds.get(j); ++ if (worldserver != null) { ++ // CraftBukkit end + WorldData worlddata = worldserver.getWorldData(); + + mojangstatisticsgenerator.a("world[" + i + "][dimension]", Integer.valueOf(worldserver.worldProvider.getDimension())); +@@ -954,7 +1220,7 @@ + public abstract boolean ad(); + + public boolean getOnlineMode() { +- return this.onlineMode; ++ return server.getOnlineMode(); // CraftBukkit + } + + public void setOnlineMode(boolean flag) { +@@ -1024,8 +1290,10 @@ + } + + public void setGamemode(EnumGamemode enumgamemode) { +- for (int i = 0; i < this.worldServer.length; ++i) { +- getServer().worldServer[i].getWorldData().setGameType(enumgamemode); ++ // CraftBukkit start ++ for (int i = 0; i < this.worlds.size(); ++i) { ++ getServer().worlds.get(i).getWorldData().setGameType(enumgamemode); ++ // CraftBukkit end + } + + } +@@ -1057,7 +1325,7 @@ + } + + public World getWorld() { +- return this.worldServer[0]; ++ return this.worlds.get(0); // CraftBukkit + } + + public Entity f() { +@@ -1125,11 +1393,10 @@ + } + + public Entity a(UUID uuid) { +- WorldServer[] aworldserver = this.worldServer; +- int i = aworldserver.length; +- +- for (int j = 0; j < i; ++j) { +- WorldServer worldserver = aworldserver[j]; ++ // CraftBukkit start ++ for (int j = 0; j < worlds.size(); ++j) { ++ WorldServer worldserver = worlds.get(j); ++ // CraftBukkit end + + if (worldserver != null) { + Entity entity = worldserver.getEntity(uuid); +@@ -1144,7 +1411,7 @@ + } + + public boolean getSendCommandFeedback() { +- return getServer().worldServer[0].getGameRules().getBoolean("sendCommandFeedback"); ++ return getServer().worlds.get(0).getGameRules().getBoolean("sendCommandFeedback"); // CraftBukkit + } + + public void a(EnumCommandResult enumcommandresult, int i) {} diff --git a/nms-patches/MobEffectList.patch b/nms-patches/MobEffectList.patch new file mode 100644 index 00000000..33ebcae2 --- /dev/null +++ b/nms-patches/MobEffectList.patch @@ -0,0 +1,74 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/MobEffectList.java 2014-11-27 08:59:46.801421406 +1100 ++++ src/main/java/net/minecraft/server/MobEffectList.java 2014-11-27 08:42:10.120850973 +1100 +@@ -6,6 +6,11 @@ + import java.util.UUID; + import java.util.Map.Entry; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.entity.EntityRegainHealthEvent.RegainReason; ++// CraftBukkit end ++ + public class MobEffectList { + + public static final MobEffectList[] byId = new MobEffectList[32]; +@@ -63,6 +68,8 @@ + } + + this.L = j; ++ org.bukkit.potion.PotionEffectType.registerPotionEffectType(new org.bukkit.craftbukkit.potion.CraftPotionEffectType(this)); // CraftBukkit ++ + } + + public static MobEffectList b(String s) { +@@ -94,11 +101,11 @@ + public void tick(EntityLiving entityliving, int i) { + if (this.id == MobEffectList.REGENERATION.id) { + if (entityliving.getHealth() < entityliving.getMaxHealth()) { +- entityliving.heal(1.0F); ++ entityliving.heal(1.0F, RegainReason.MAGIC_REGEN); // CraftBukkit + } + } else if (this.id == MobEffectList.POISON.id) { + if (entityliving.getHealth() > 1.0F) { +- entityliving.damageEntity(DamageSource.MAGIC, 1.0F); ++ entityliving.damageEntity(CraftEventFactory.POISON, 1.0F); // CraftBukkit - DamageSource.MAGIC -> CraftEventFactory.POISON + } + } else if (this.id == MobEffectList.WITHER.id) { + entityliving.damageEntity(DamageSource.WITHER, 1.0F); +@@ -106,14 +113,25 @@ + ((EntityHuman) entityliving).applyExhaustion(0.025F * (float) (i + 1)); + } else if (this.id == MobEffectList.SATURATION.id && entityliving instanceof EntityHuman) { + if (!entityliving.world.isStatic) { +- ((EntityHuman) entityliving).getFoodData().eat(i + 1, 1.0F); ++ // CraftBukkit start ++ EntityHuman entityhuman = (EntityHuman) entityliving; ++ int oldFoodLevel = entityhuman.getFoodData().foodLevel; ++ ++ org.bukkit.event.entity.FoodLevelChangeEvent event = CraftEventFactory.callFoodLevelChangeEvent(entityhuman, i + 1 + oldFoodLevel); ++ ++ if (!event.isCancelled()) { ++ entityhuman.getFoodData().eat(event.getFoodLevel() - oldFoodLevel, 1.0F); ++ } ++ ++ ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutUpdateHealth(((EntityPlayer) entityhuman).getBukkitEntity().getScaledHealth(), entityhuman.getFoodData().foodLevel, entityhuman.getFoodData().saturationLevel)); ++ // CraftBukkit end + } + } else if ((this.id != MobEffectList.HEAL.id || entityliving.bl()) && (this.id != MobEffectList.HARM.id || !entityliving.bl())) { + if (this.id == MobEffectList.HARM.id && !entityliving.bl() || this.id == MobEffectList.HEAL.id && entityliving.bl()) { + entityliving.damageEntity(DamageSource.MAGIC, (float) (6 << i)); + } + } else { +- entityliving.heal((float) Math.max(4 << i, 0)); ++ entityliving.heal((float) Math.max(4 << i, 0), RegainReason.MAGIC); // CraftBukkit + } + + } +@@ -132,7 +150,7 @@ + } + } else { + j = (int) (d0 * (double) (4 << i) + 0.5D); +- entityliving.heal((float) j); ++ entityliving.heal((float) j, RegainReason.MAGIC); // CraftBukkit + } + + } diff --git a/nms-patches/MobSpawnerAbstract.patch b/nms-patches/MobSpawnerAbstract.patch new file mode 100644 index 00000000..1a936183 --- /dev/null +++ b/nms-patches/MobSpawnerAbstract.patch @@ -0,0 +1,38 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/MobSpawnerAbstract.java 2014-11-27 08:59:46.805421389 +1100 ++++ src/main/java/net/minecraft/server/MobSpawnerAbstract.java 2014-11-27 08:42:10.108850996 +1100 +@@ -4,6 +4,8 @@ + import java.util.Iterator; + import java.util.List; + ++import org.bukkit.event.entity.CreatureSpawnEvent; // CraftBukkit ++ + public abstract class MobSpawnerAbstract { + + public int spawnDelay = 20; +@@ -129,7 +131,7 @@ + + entity.f(nbttagcompound); + if (entity.world != null && flag) { +- entity.world.addEntity(entity); ++ entity.world.addEntity(entity, CreatureSpawnEvent.SpawnReason.SPAWNER); // CraftBukkit + } + + NBTTagCompound nbttagcompound1; +@@ -154,7 +156,7 @@ + entity2.f(nbttagcompound2); + entity2.setPositionRotation(entity1.locX, entity1.locY, entity1.locZ, entity1.yaw, entity1.pitch); + if (entity.world != null && flag) { +- entity.world.addEntity(entity2); ++ entity.world.addEntity(entity2, CreatureSpawnEvent.SpawnReason.SPAWNER); // CraftBukkit + } + + entity1.mount(entity2); +@@ -164,7 +166,7 @@ + } + } else if (entity instanceof EntityLiving && entity.world != null && flag) { + ((EntityInsentient) entity).prepare(entity.world.E(new BlockPosition(entity)), (GroupDataEntity) null); +- entity.world.addEntity(entity); ++ entity.world.addEntity(entity, CreatureSpawnEvent.SpawnReason.SPAWNER); // CraftBukkit + } + + return entity; diff --git a/nms-patches/NameReferencingFileConverter.patch b/nms-patches/NameReferencingFileConverter.patch new file mode 100644 index 00000000..621762db --- /dev/null +++ b/nms-patches/NameReferencingFileConverter.patch @@ -0,0 +1,76 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/NameReferencingFileConverter.java 2014-11-27 08:59:46.805421389 +1100 ++++ src/main/java/net/minecraft/server/NameReferencingFileConverter.java 2014-11-27 08:42:10.168850880 +1100 +@@ -32,7 +32,7 @@ + public static final File c = new File("ops.txt"); + public static final File d = new File("white-list.txt"); + +- static List a(File file, Map map) { ++ static List a(File file, Map map) throws IOException { // CraftBukkit - Added throws + List list = Files.readLines(file, Charsets.UTF_8); + Iterator iterator = list.iterator(); + +@@ -77,9 +77,11 @@ + if (gameprofilebanlist.c().exists()) { + try { + gameprofilebanlist.load(); +- } catch (FileNotFoundException filenotfoundexception) { +- NameReferencingFileConverter.e.warn("Could not load existing file " + gameprofilebanlist.c().getName(), filenotfoundexception); ++ // CraftBukkit start - FileNotFoundException -> IOException, don't print stacetrace ++ } catch (IOException filenotfoundexception) { ++ e.warn("Could not load existing file " + gameprofilebanlist.c().getName() + ", " + filenotfoundexception.getMessage()); + } ++ // CraftBukkit end + } + + try { +@@ -111,9 +113,11 @@ + if (ipbanlist.c().exists()) { + try { + ipbanlist.load(); +- } catch (FileNotFoundException filenotfoundexception) { +- NameReferencingFileConverter.e.warn("Could not load existing file " + ipbanlist.c().getName(), filenotfoundexception); ++ // CraftBukkit start - FileNotFoundException -> IOException, don't print stacetrace ++ } catch (IOException filenotfoundexception) { ++ e.warn("Could not load existing file " + ipbanlist.c().getName() + ", " + filenotfoundexception.getMessage()); + } ++ // CraftBukkit end + } + + try { +@@ -152,9 +156,11 @@ + if (oplist.c().exists()) { + try { + oplist.load(); +- } catch (FileNotFoundException filenotfoundexception) { +- NameReferencingFileConverter.e.warn("Could not load existing file " + oplist.c().getName(), filenotfoundexception); ++ // CraftBukkit start - FileNotFoundException -> IOException, don't print stacetrace ++ } catch (IOException filenotfoundexception) { ++ e.warn("Could not load existing file " + oplist.c().getName() + ", " + filenotfoundexception.getMessage()); + } ++ // CraftBukkit end + } + + try { +@@ -184,9 +190,11 @@ + if (whitelist.c().exists()) { + try { + whitelist.load(); +- } catch (FileNotFoundException filenotfoundexception) { +- NameReferencingFileConverter.e.warn("Could not load existing file " + whitelist.c().getName(), filenotfoundexception); ++ // CraftBukkit start - FileNotFoundException -> IOException, don't print stacetrace ++ } catch (IOException filenotfoundexception) { ++ e.warn("Could not load existing file " + whitelist.c().getName() + ", " + filenotfoundexception.getMessage()); + } ++ // CraftBukkit end + } + + try { +@@ -351,7 +359,7 @@ + + private static File d(PropertyManager propertymanager) { + String s = propertymanager.getString("level-name", "world"); +- File file = new File(s); ++ File file = new File(MinecraftServer.getServer().server.getWorldContainer(), s); // CraftBukkit - Respect container setting + + return new File(file, "players"); + } diff --git a/nms-patches/NetworkManager.patch b/nms-patches/NetworkManager.patch new file mode 100644 index 00000000..99a6dc91 --- /dev/null +++ b/nms-patches/NetworkManager.patch @@ -0,0 +1,20 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/NetworkManager.java 2014-11-27 08:59:46.809421371 +1100 ++++ src/main/java/net/minecraft/server/NetworkManager.java 2014-11-27 08:42:10.120850973 +1100 +@@ -41,7 +41,7 @@ + this.g = enumprotocoldirection; + } + +- public void channelActive(ChannelHandlerContext channelhandlercontext) { ++ public void channelActive(ChannelHandlerContext channelhandlercontext) throws Exception { // CraftBukkit - added throws + super.channelActive(channelhandlercontext); + this.i = channelhandlercontext.channel(); + this.j = this.i.remoteAddress(); +@@ -159,7 +159,7 @@ + + public void close(IChatBaseComponent ichatbasecomponent) { + if (this.i.isOpen()) { +- this.i.close().awaitUninterruptibly(); ++ this.i.close(); // We can't wait as this may be called from an event loop. + this.l = ichatbasecomponent; + } + diff --git a/nms-patches/Packet.patch b/nms-patches/Packet.patch new file mode 100644 index 00000000..29864db4 --- /dev/null +++ b/nms-patches/Packet.patch @@ -0,0 +1,14 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/Packet.java 2014-11-27 08:59:46.813421353 +1100 ++++ src/main/java/net/minecraft/server/Packet.java 2014-11-27 08:42:10.152850911 +1100 +@@ -2,9 +2,9 @@ + + public interface Packet { + +- void a(PacketDataSerializer packetdataserializer); ++ void a(PacketDataSerializer packetdataserializer) throws java.io.IOException; // CraftBukkit - added throws + +- void b(PacketDataSerializer packetdataserializer); ++ void b(PacketDataSerializer packetdataserializer) throws java.io.IOException; // CraftBukkit - added throws + + void a(PacketListener packetlistener); + } diff --git a/nms-patches/PacketDataSerializer.patch b/nms-patches/PacketDataSerializer.patch new file mode 100644 index 00000000..20a1268c --- /dev/null +++ b/nms-patches/PacketDataSerializer.patch @@ -0,0 +1,122 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PacketDataSerializer.java 2014-11-27 08:59:46.809421371 +1100 ++++ src/main/java/net/minecraft/server/PacketDataSerializer.java 2014-11-27 08:42:10.108850996 +1100 +@@ -8,7 +8,6 @@ + import io.netty.buffer.ByteBufProcessor; + import io.netty.handler.codec.DecoderException; + import io.netty.handler.codec.EncoderException; +-import io.netty.util.ReferenceCounted; + import java.io.DataInput; + import java.io.DataOutput; + import java.io.IOException; +@@ -21,6 +20,8 @@ + import java.nio.charset.Charset; + import java.util.UUID; + ++import org.bukkit.craftbukkit.inventory.CraftItemStack; // CraftBukkit ++ + public class PacketDataSerializer extends ByteBuf { + + private final ByteBuf a; +@@ -142,7 +143,7 @@ + } else { + try { + NBTCompressedStreamTools.a(nbttagcompound, (DataOutput) (new ByteBufOutputStream(this))); +- } catch (IOException ioexception) { ++ } catch (Exception ioexception) { // CraftBukkit - IOException -> Exception + throw new EncoderException(ioexception); + } + } +@@ -162,7 +163,7 @@ + } + + public void a(ItemStack itemstack) { +- if (itemstack == null) { ++ if (itemstack == null || itemstack.getItem() == null) { // CraftBukkit - NPE fix itemstack.getItem() + this.writeShort(-1); + } else { + this.writeShort(Item.getId(itemstack.getItem())); +@@ -189,6 +190,11 @@ + + itemstack = new ItemStack(Item.getById(short0), b0, short1); + itemstack.setTag(this.h()); ++ // CraftBukkit start ++ if (itemstack.getTag() != null) { ++ CraftItemStack.setItemMeta(itemstack, CraftItemStack.getItemMeta(itemstack)); ++ } ++ // CraftBukkit end + } + + return itemstack; +@@ -416,11 +422,11 @@ + return this.a.getBytes(i, bytebuffer); + } + +- public ByteBuf getBytes(int i, OutputStream outputstream, int j) { ++ public ByteBuf getBytes(int i, OutputStream outputstream, int j) throws IOException { // CraftBukkit - throws IOException + return this.a.getBytes(i, outputstream, j); + } + +- public int getBytes(int i, GatheringByteChannel gatheringbytechannel, int j) { ++ public int getBytes(int i, GatheringByteChannel gatheringbytechannel, int j) throws IOException { // CraftBukkit - throws IOException + return this.a.getBytes(i, gatheringbytechannel, j); + } + +@@ -484,11 +490,11 @@ + return this.a.setBytes(i, bytebuffer); + } + +- public int setBytes(int i, InputStream inputstream, int j) { ++ public int setBytes(int i, InputStream inputstream, int j) throws IOException { // CraftBukkit - throws IOException + return this.a.setBytes(i, inputstream, j); + } + +- public int setBytes(int i, ScatteringByteChannel scatteringbytechannel, int j) { ++ public int setBytes(int i, ScatteringByteChannel scatteringbytechannel, int j) throws IOException { // CraftBukkit - throws IOException + return this.a.setBytes(i, scatteringbytechannel, j); + } + +@@ -580,11 +586,11 @@ + return this.a.readBytes(bytebuffer); + } + +- public ByteBuf readBytes(OutputStream outputstream, int i) { ++ public ByteBuf readBytes(OutputStream outputstream, int i) throws IOException { // CraftBukkit - throws IOException + return this.a.readBytes(outputstream, i); + } + +- public int readBytes(GatheringByteChannel gatheringbytechannel, int i) { ++ public int readBytes(GatheringByteChannel gatheringbytechannel, int i) throws IOException { // CraftBukkit - throws IOException + return this.a.readBytes(gatheringbytechannel, i); + } + +@@ -652,11 +658,11 @@ + return this.a.writeBytes(bytebuffer); + } + +- public int writeBytes(InputStream inputstream, int i) { ++ public int writeBytes(InputStream inputstream, int i) throws IOException { // CraftBukkit - throws IOException + return this.a.writeBytes(inputstream, i); + } + +- public int writeBytes(ScatteringByteChannel scatteringbytechannel, int i) { ++ public int writeBytes(ScatteringByteChannel scatteringbytechannel, int i) throws IOException { // CraftBukkit - throws IOException + return this.a.writeBytes(scatteringbytechannel, i); + } + +@@ -803,16 +809,4 @@ + public boolean release(int i) { + return this.a.release(i); + } +- +- public ReferenceCounted retain(int i) { +- return this.retain(i); +- } +- +- public ReferenceCounted retain() { +- return this.retain(); +- } +- +- public int compareTo(Object object) { +- return this.compareTo((ByteBuf) object); +- } + } diff --git a/nms-patches/PacketPlayInBlockPlace.patch b/nms-patches/PacketPlayInBlockPlace.patch new file mode 100644 index 00000000..d986bc09 --- /dev/null +++ b/nms-patches/PacketPlayInBlockPlace.patch @@ -0,0 +1,32 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PacketPlayInBlockPlace.java 2014-11-27 08:59:46.813421353 +1100 ++++ src/main/java/net/minecraft/server/PacketPlayInBlockPlace.java 2014-11-27 08:42:10.152850911 +1100 +@@ -9,6 +9,8 @@ + private float e; + private float f; + private float g; ++ ++ public long timestamp; // CraftBukkit + + public PacketPlayInBlockPlace() {} + +@@ -26,6 +28,7 @@ + } + + public void a(PacketDataSerializer packetdataserializer) { ++ timestamp = System.currentTimeMillis(); // CraftBukkit + this.b = packetdataserializer.c(); + this.c = packetdataserializer.readUnsignedByte(); + this.d = packetdataserializer.i(); +@@ -71,7 +74,10 @@ + return this.g; + } + +- public void a(PacketListener packetlistener) { +- this.a((PacketListenerPlayIn) packetlistener); ++ // CraftBukkit start - fix decompile error ++ @Override ++ public void a(PacketListener pl) { ++ a((PacketListenerPlayIn)pl); + } ++ // CraftBukkit end + } diff --git a/nms-patches/PacketPlayInCloseWindow.patch b/nms-patches/PacketPlayInCloseWindow.patch new file mode 100644 index 00000000..14efaf9c --- /dev/null +++ b/nms-patches/PacketPlayInCloseWindow.patch @@ -0,0 +1,29 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PacketPlayInCloseWindow.java 2014-11-27 08:59:46.817421336 +1100 ++++ src/main/java/net/minecraft/server/PacketPlayInCloseWindow.java 2014-11-27 08:42:10.168850880 +1100 +@@ -6,6 +6,17 @@ + + public PacketPlayInCloseWindow() {} + ++ // CraftBukkit start ++ @Override ++ public void a(PacketListener pl) { ++ a((PacketListenerPlayIn) pl); ++ } ++ ++ public PacketPlayInCloseWindow(int id) { ++ this.id = id; ++ } ++ // CraftBukkit end ++ + public void a(PacketListenerPlayIn packetlistenerplayin) { + packetlistenerplayin.a(this); + } +@@ -17,8 +28,4 @@ + public void b(PacketDataSerializer packetdataserializer) { + packetdataserializer.writeByte(this.id); + } +- +- public void a(PacketListener packetlistener) { +- this.a((PacketListenerPlayIn) packetlistener); +- } + } diff --git a/nms-patches/PacketStatusListener.patch b/nms-patches/PacketStatusListener.patch new file mode 100644 index 00000000..b7aad224 --- /dev/null +++ b/nms-patches/PacketStatusListener.patch @@ -0,0 +1,113 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PacketStatusListener.java 2014-11-27 08:59:46.817421336 +1100 ++++ src/main/java/net/minecraft/server/PacketStatusListener.java 2014-11-27 08:42:10.168850880 +1100 +@@ -1,5 +1,15 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import com.mojang.authlib.GameProfile; ++import java.net.InetSocketAddress; ++import java.util.Iterator; ++ ++import org.bukkit.craftbukkit.util.CraftIconCache; ++import org.bukkit.entity.Player; ++ ++// CraftBukkit end ++ + public class PacketStatusListener implements PacketStatusInListener { + + private final MinecraftServer minecraftServer; +@@ -13,7 +23,93 @@ + public void a(IChatBaseComponent ichatbasecomponent) {} + + public void a(PacketStatusInStart packetstatusinstart) { +- this.networkManager.handle(new PacketStatusOutServerInfo(this.minecraftServer.aE())); ++ // this.networkManager.handle(new PacketStatusOutServerInfo(this.minecraftServer.aE())); ++ // CraftBukkit start - fire ping event ++ final Object[] players = minecraftServer.getPlayerList().players.toArray(); ++ class ServerListPingEvent extends org.bukkit.event.server.ServerListPingEvent { ++ CraftIconCache icon = minecraftServer.server.getServerIcon(); ++ ++ ServerListPingEvent() { ++ super(((InetSocketAddress) networkManager.getSocketAddress()).getAddress(), minecraftServer.getMotd(), minecraftServer.getPlayerList().getMaxPlayers()); ++ } ++ ++ @Override ++ public void setServerIcon(org.bukkit.util.CachedServerIcon icon) { ++ if (!(icon instanceof CraftIconCache)) { ++ throw new IllegalArgumentException(icon + " was not created by " + org.bukkit.craftbukkit.CraftServer.class); ++ } ++ this.icon = (CraftIconCache) icon; ++ } ++ ++ @Override ++ public Iterator<Player> iterator() throws UnsupportedOperationException { ++ return new Iterator<Player>() { ++ int i; ++ int ret = Integer.MIN_VALUE; ++ EntityPlayer player; ++ ++ @Override ++ public boolean hasNext() { ++ if (player != null) { ++ return true; ++ } ++ final Object[] currentPlayers = players; ++ for (int length = currentPlayers.length, i = this.i; i < length; i++) { ++ final EntityPlayer player = (EntityPlayer) currentPlayers[i]; ++ if (player != null) { ++ this.i = i + 1; ++ this.player = player; ++ return true; ++ } ++ } ++ return false; ++ } ++ ++ @Override ++ public Player next() { ++ if (!hasNext()) { ++ throw new java.util.NoSuchElementException(); ++ } ++ final EntityPlayer player = this.player; ++ this.player = null; ++ this.ret = this.i - 1; ++ return player.getBukkitEntity(); ++ } ++ ++ @Override ++ public void remove() { ++ final Object[] currentPlayers = players; ++ final int i = this.ret; ++ if (i < 0 || currentPlayers[i] == null) { ++ throw new IllegalStateException(); ++ } ++ currentPlayers[i] = null; ++ } ++ }; ++ } ++ } ++ ++ ServerListPingEvent event = new ServerListPingEvent(); ++ this.minecraftServer.server.getPluginManager().callEvent(event); ++ ++ java.util.List<GameProfile> profiles = new java.util.ArrayList<GameProfile>(players.length); ++ for (Object player : players) { ++ if (player != null) { ++ profiles.add(((EntityPlayer) player).getProfile()); ++ } ++ } ++ ++ ServerPingPlayerSample playerSample = new ServerPingPlayerSample(event.getMaxPlayers(), profiles.size()); ++ playerSample.a(profiles.toArray(new GameProfile[profiles.size()])); ++ ++ ServerPing ping = new ServerPing(); ++ ping.setFavicon(event.icon.value); ++ ping.setMOTD(new ChatComponentText(event.getMotd())); ++ ping.setPlayerSample(playerSample); ++ ping.setServerInfo(new ServerPingServerData(minecraftServer.getServerModName() + " " + minecraftServer.getVersion(), 47)); // TODO: Update when protocol changes ++ ++ this.networkManager.handle(new PacketStatusOutServerInfo(ping)); ++ // CraftBukkit end + } + + public void a(PacketStatusInPing packetstatusinping) { diff --git a/nms-patches/Path.patch b/nms-patches/Path.patch new file mode 100644 index 00000000..39db7a87 --- /dev/null +++ b/nms-patches/Path.patch @@ -0,0 +1,11 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/Path.java 2014-11-27 08:59:46.849421195 +1100 ++++ src/main/java/net/minecraft/server/Path.java 2014-11-27 08:42:10.088851036 +1100 +@@ -2,7 +2,7 @@ + + public class Path { + +- private PathPoint[] a = new PathPoint[1024]; ++ private PathPoint[] a = new PathPoint[128]; // CraftBukkit - reduce default size + private int b; + + public Path() {} diff --git a/nms-patches/PathfinderGoalBreakDoor.patch b/nms-patches/PathfinderGoalBreakDoor.patch new file mode 100644 index 00000000..c690fcf5 --- /dev/null +++ b/nms-patches/PathfinderGoalBreakDoor.patch @@ -0,0 +1,15 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalBreakDoor.java 2014-11-27 08:59:46.817421336 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalBreakDoor.java 2014-11-27 08:42:10.152850911 +1100 +@@ -63,6 +63,12 @@ + } + + if (this.g == 240 && this.a.world.getDifficulty() == EnumDifficulty.HARD) { ++ // CraftBukkit start ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityBreakDoorEvent(this.a, this.b.getX(), this.b.getY(), this.b.getZ()).isCancelled()) { ++ this.c(); ++ return; ++ } ++ // CraftBukkit end + this.a.world.setAir(this.b); + this.a.world.triggerEffect(1012, this.b, 0); + this.a.world.triggerEffect(2001, this.b, Block.getId(this.c)); diff --git a/nms-patches/PathfinderGoalBreed.patch b/nms-patches/PathfinderGoalBreed.patch new file mode 100644 index 00000000..50c07973 --- /dev/null +++ b/nms-patches/PathfinderGoalBreed.patch @@ -0,0 +1,23 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalBreed.java 2014-11-27 08:59:46.821421318 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalBreed.java 2014-11-27 08:42:10.164850887 +1100 +@@ -70,6 +70,11 @@ + EntityAgeable entityageable = this.d.createChild(this.e); + + if (entityageable != null) { ++ // CraftBukkit start - set persistence for tame animals ++ if (entityageable instanceof EntityTameableAnimal && ((EntityTameableAnimal) entityageable).isTamed()) { ++ entityageable.persistent = true; ++ } ++ // CraftBukkit end + EntityHuman entityhuman = this.d.co(); + + if (entityhuman == null && this.e.co() != null) { +@@ -89,7 +94,7 @@ + this.e.cq(); + entityageable.setAgeRaw(-24000); + entityageable.setPositionRotation(this.d.locX, this.d.locY, this.d.locZ, 0.0F, 0.0F); +- this.a.addEntity(entityageable); ++ this.a.addEntity(entityageable, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason + Random random = this.d.bb(); + + for (int i = 0; i < 7; ++i) { diff --git a/nms-patches/PathfinderGoalDefendVillage.patch b/nms-patches/PathfinderGoalDefendVillage.patch new file mode 100644 index 00000000..2f2260c3 --- /dev/null +++ b/nms-patches/PathfinderGoalDefendVillage.patch @@ -0,0 +1,11 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalDefendVillage.java 2014-11-27 08:59:46.821421318 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalDefendVillage.java 2014-11-27 08:42:10.172850872 +1100 +@@ -32,7 +32,7 @@ + } + + public void c() { +- this.a.setGoalTarget(this.b); ++ this.a.setGoalTarget(this.b, org.bukkit.event.entity.EntityTargetEvent.TargetReason.DEFEND_VILLAGE, true); // CraftBukkit - reason + super.c(); + } + } diff --git a/nms-patches/PathfinderGoalEatTile.patch b/nms-patches/PathfinderGoalEatTile.patch new file mode 100644 index 00000000..88f1cd79 --- /dev/null +++ b/nms-patches/PathfinderGoalEatTile.patch @@ -0,0 +1,34 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalEatTile.java 2014-11-27 08:59:46.821421318 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalEatTile.java 2014-11-27 08:42:10.108850996 +1100 +@@ -3,6 +3,11 @@ + import com.google.common.base.Predicate; + import com.google.common.base.Predicates; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.Material; ++// CraftBukkit end ++ + public class PathfinderGoalEatTile extends PathfinderGoal { + + private static final Predicate b = BlockStatePredicate.a((Block) Blocks.TALLGRASS).a(BlockLongGrass.TYPE, Predicates.equalTo(EnumTallGrassType.GRASS)); +@@ -50,7 +55,8 @@ + BlockPosition blockposition = new BlockPosition(this.c.locX, this.c.locY, this.c.locZ); + + if (PathfinderGoalEatTile.b.apply(this.d.getType(blockposition))) { +- if (this.d.getGameRules().getBoolean("mobGriefing")) { ++ // CraftBukkit ++ if (!CraftEventFactory.callEntityChangeBlockEvent(this.c, this.c.world.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), Material.AIR, !this.d.getGameRules().getBoolean("mobGriefing")).isCancelled()) { + this.d.setAir(blockposition, false); + } + +@@ -59,7 +65,8 @@ + BlockPosition blockposition1 = blockposition.down(); + + if (this.d.getType(blockposition1).getBlock() == Blocks.GRASS) { +- if (this.d.getGameRules().getBoolean("mobGriefing")) { ++ // CraftBukkit ++ if (!CraftEventFactory.callEntityChangeBlockEvent(this.c, this.c.world.getWorld().getBlockAt(blockposition1.getX(), blockposition1.getY(), blockposition1.getZ()), Material.DIRT, !this.d.getGameRules().getBoolean("mobGriefing")).isCancelled()) { + this.d.triggerEffect(2001, blockposition1, Block.getId(Blocks.GRASS)); + this.d.setTypeAndData(blockposition1, Blocks.DIRT.getBlockData(), 2); + } diff --git a/nms-patches/PathfinderGoalEndermanPickupBlock.patch b/nms-patches/PathfinderGoalEndermanPickupBlock.patch new file mode 100644 index 00000000..31b450c1 --- /dev/null +++ b/nms-patches/PathfinderGoalEndermanPickupBlock.patch @@ -0,0 +1,17 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalEndermanPickupBlock.java 2014-11-27 08:59:46.825421301 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalEndermanPickupBlock.java 2014-11-27 08:42:10.164850887 +1100 +@@ -25,8 +25,12 @@ + Block block = iblockdata.getBlock(); + + if (EntityEnderman.co().contains(block)) { +- this.enderman.setCarried(iblockdata); +- world.setTypeUpdate(blockposition, Blocks.AIR.getBlockData()); ++ // 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.setTypeUpdate(blockposition, Blocks.AIR.getBlockData()); ++ } ++ // CraftBukkit end + } + + } diff --git a/nms-patches/PathfinderGoalEndermanPlaceBlock.patch b/nms-patches/PathfinderGoalEndermanPlaceBlock.patch new file mode 100644 index 00000000..e858f052 --- /dev/null +++ b/nms-patches/PathfinderGoalEndermanPlaceBlock.patch @@ -0,0 +1,15 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalEndermanPlaceBlock.java 2014-11-27 08:59:46.825421301 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalEndermanPlaceBlock.java 2014-11-27 08:42:10.172850872 +1100 +@@ -25,8 +25,12 @@ + Block block1 = world.getType(blockposition.down()).getBlock(); + + if (this.a(world, blockposition, this.a.getCarried().getBlock(), block, block1)) { ++ // CraftBukkit start - Place event ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.a, blockposition.getX(), blockposition.getY(), blockposition.getZ(), this.a.getCarried().getBlock(), this.a.getCarried().getBlock().toLegacyData(this.a.getCarried())).isCancelled()) { + world.setTypeAndData(blockposition, this.a.getCarried(), 3); + this.a.setCarried(Blocks.AIR.getBlockData()); ++ } ++ // CraftBukkit end + } + + } diff --git a/nms-patches/PathfinderGoalGhastAttackTarget.patch b/nms-patches/PathfinderGoalGhastAttackTarget.patch new file mode 100644 index 00000000..adc48e2b --- /dev/null +++ b/nms-patches/PathfinderGoalGhastAttackTarget.patch @@ -0,0 +1,12 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalGhastAttackTarget.java 2014-11-27 08:59:46.829421283 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalGhastAttackTarget.java 2014-11-27 08:42:10.152850911 +1100 +@@ -43,7 +43,8 @@ + world.a((EntityHuman) null, 1008, new BlockPosition(this.b), 0); + EntityLargeFireball entitylargefireball = new EntityLargeFireball(world, this.b, d2, d3, d4); + +- entitylargefireball.yield = this.b.cd(); ++ // CraftBukkit - set bukkitYield when setting explosionpower ++ entitylargefireball.bukkitYield = entitylargefireball.yield = this.b.cd(); + entitylargefireball.locX = this.b.locX + vec3d.a * d1; + entitylargefireball.locY = this.b.locY + (double) (this.b.length / 2.0F) + 0.5D; + entitylargefireball.locZ = this.b.locZ + vec3d.c * d1; diff --git a/nms-patches/PathfinderGoalHurtByTarget.patch b/nms-patches/PathfinderGoalHurtByTarget.patch new file mode 100644 index 00000000..c0763af1 --- /dev/null +++ b/nms-patches/PathfinderGoalHurtByTarget.patch @@ -0,0 +1,19 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalHurtByTarget.java 2014-11-27 08:59:46.829421283 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalHurtByTarget.java 2014-11-27 08:42:10.156850903 +1100 +@@ -23,7 +23,7 @@ + } + + public void c() { +- this.e.setGoalTarget(this.e.getLastDamager()); ++ this.e.setGoalTarget(this.e.getLastDamager(), org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_NEARBY_ENTITY, true); // CraftBukkit - reason + this.b = this.e.bd(); + if (this.a) { + double d0 = this.f(); +@@ -58,6 +58,6 @@ + } + + protected void a(EntityCreature entitycreature, EntityLiving entityliving) { +- entitycreature.setGoalTarget(entityliving); ++ entitycreature.setGoalTarget(entityliving, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_NEARBY_ENTITY, true); // CraftBukkit - reason + } + } diff --git a/nms-patches/PathfinderGoalMakeLove.patch b/nms-patches/PathfinderGoalMakeLove.patch new file mode 100644 index 00000000..ce58bcfe --- /dev/null +++ b/nms-patches/PathfinderGoalMakeLove.patch @@ -0,0 +1,11 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalMakeLove.java 2014-11-27 08:59:46.829421283 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalMakeLove.java 2014-11-27 08:42:10.140850934 +1100 +@@ -87,7 +87,7 @@ + this.b.o(false); + entityvillager.setAgeRaw(-24000); + entityvillager.setPositionRotation(this.b.locX, this.b.locY, this.b.locZ, 0.0F, 0.0F); +- this.d.addEntity(entityvillager); ++ this.d.addEntity(entityvillager, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.BREEDING); // CraftBukkit - added SpawnReason + this.d.broadcastEntityEffect(entityvillager, (byte) 12); + } + } diff --git a/nms-patches/PathfinderGoalNearestAttackableTarget.patch b/nms-patches/PathfinderGoalNearestAttackableTarget.patch new file mode 100644 index 00000000..438278b0 --- /dev/null +++ b/nms-patches/PathfinderGoalNearestAttackableTarget.patch @@ -0,0 +1,11 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalNearestAttackableTarget.java 2014-11-27 08:59:46.833421266 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalNearestAttackableTarget.java 2014-11-27 08:42:10.168850880 +1100 +@@ -48,7 +48,7 @@ + } + + public void c() { +- this.e.setGoalTarget(this.d); ++ this.e.setGoalTarget(this.d, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER, true); // Craftbukkit - reason + super.c(); + } + } diff --git a/nms-patches/PathfinderGoalNearestAttackableTargetInsentient.patch b/nms-patches/PathfinderGoalNearestAttackableTargetInsentient.patch new file mode 100644 index 00000000..fefd3ad8 --- /dev/null +++ b/nms-patches/PathfinderGoalNearestAttackableTargetInsentient.patch @@ -0,0 +1,11 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalNearestAttackableTargetInsentient.java 2014-11-27 08:59:46.833421266 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalNearestAttackableTargetInsentient.java 2014-11-27 08:42:10.084851043 +1100 +@@ -54,7 +54,7 @@ + } + + public void c() { +- this.b.setGoalTarget(this.e); ++ this.b.setGoalTarget(this.e, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_ENTITY, true); // CraftBukkit - reason + super.c(); + } + diff --git a/nms-patches/PathfinderGoalOwnerHurtByTarget.patch b/nms-patches/PathfinderGoalOwnerHurtByTarget.patch new file mode 100644 index 00000000..b8b709f2 --- /dev/null +++ b/nms-patches/PathfinderGoalOwnerHurtByTarget.patch @@ -0,0 +1,11 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalOwnerHurtByTarget.java 2014-11-27 08:59:46.833421266 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalOwnerHurtByTarget.java 2014-11-27 08:42:10.096851020 +1100 +@@ -30,7 +30,7 @@ + } + + public void c() { +- this.e.setGoalTarget(this.b); ++ this.e.setGoalTarget(this.b, org.bukkit.event.entity.EntityTargetEvent.TargetReason.TARGET_ATTACKED_OWNER, true); // CraftBukkit - reason + EntityLiving entityliving = this.a.getOwner(); + + if (entityliving != null) { diff --git a/nms-patches/PathfinderGoalOwnerHurtTarget.patch b/nms-patches/PathfinderGoalOwnerHurtTarget.patch new file mode 100644 index 00000000..6e0843f1 --- /dev/null +++ b/nms-patches/PathfinderGoalOwnerHurtTarget.patch @@ -0,0 +1,11 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalOwnerHurtTarget.java 2014-11-27 08:59:46.837421248 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalOwnerHurtTarget.java 2014-11-27 08:42:10.144850927 +1100 +@@ -30,7 +30,7 @@ + } + + public void c() { +- this.e.setGoalTarget(this.b); ++ this.e.setGoalTarget(this.b, org.bukkit.event.entity.EntityTargetEvent.TargetReason.OWNER_ATTACKED_TARGET, true); // CraftBukkit - reason + EntityLiving entityliving = this.a.getOwner(); + + if (entityliving != null) { diff --git a/nms-patches/PathfinderGoalPanic.patch b/nms-patches/PathfinderGoalPanic.patch new file mode 100644 index 00000000..e6b5dc80 --- /dev/null +++ b/nms-patches/PathfinderGoalPanic.patch @@ -0,0 +1,15 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalPanic.java 2014-11-27 08:59:46.837421248 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalPanic.java 2014-11-27 08:42:10.084851043 +1100 +@@ -36,6 +36,12 @@ + } + + public boolean b() { ++ // CraftBukkit start - introduce a temporary timeout hack until this is fixed properly ++ if ((this.b.ticksLived - this.b.bd/*getHurtTimestamp*/()) > 100) { ++ this.b.b((EntityLiving) null); ++ return false; ++ } ++ // CraftBukkit end + return !this.b.getNavigation().m(); + } + } diff --git a/nms-patches/PathfinderGoalSelector.patch b/nms-patches/PathfinderGoalSelector.patch new file mode 100644 index 00000000..e4b9428f --- /dev/null +++ b/nms-patches/PathfinderGoalSelector.patch @@ -0,0 +1,32 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalSelector.java 2014-11-27 08:59:46.837421248 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalSelector.java 2014-11-27 08:42:10.164850887 +1100 +@@ -6,11 +6,15 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++import org.bukkit.craftbukkit.util.UnsafeList; // CraftBukkit ++ + public class PathfinderGoalSelector { + + private static final Logger a = LogManager.getLogger(); +- private List b = Lists.newArrayList(); +- private List c = Lists.newArrayList(); ++ // CraftBukkit start - ArrayList -> UnsafeList ++ private List b = new UnsafeList(); ++ private List c = new UnsafeList(); ++ // CraftBukkit end + private final MethodProfiler d; + private int e; + private int f = 3; +@@ -107,9 +111,11 @@ + if (pathfindergoalselectoritem1 != pathfindergoalselectoritem) { + if (pathfindergoalselectoritem.b >= pathfindergoalselectoritem1.b) { + if (!this.a(pathfindergoalselectoritem, pathfindergoalselectoritem1) && this.c.contains(pathfindergoalselectoritem1)) { ++ ((UnsafeList.Itr) iterator).valid = false; // CraftBukkit - mark iterator for reuse + return false; + } + } else if (!pathfindergoalselectoritem1.a.i() && this.c.contains(pathfindergoalselectoritem1)) { ++ ((UnsafeList.Itr) iterator).valid = false; // CraftBukkit - mark iterator for reuse + return false; + } + } diff --git a/nms-patches/PathfinderGoalSilverfishHideInBlock.patch b/nms-patches/PathfinderGoalSilverfishHideInBlock.patch new file mode 100644 index 00000000..31d6582b --- /dev/null +++ b/nms-patches/PathfinderGoalSilverfishHideInBlock.patch @@ -0,0 +1,14 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalSilverfishHideInBlock.java 2014-11-27 08:59:46.841421230 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalSilverfishHideInBlock.java 2014-11-27 08:42:10.176850864 +1100 +@@ -51,6 +51,11 @@ + IBlockData iblockdata = world.getType(blockposition); + + if (BlockMonsterEggs.d(iblockdata)) { ++ // CraftBukkit start ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.silverfish, blockposition.getX(), blockposition.getY(), blockposition.getZ(), Blocks.MONSTER_EGG, Block.getId(BlockMonsterEggs.getById(iblockdata.getBlock().toLegacyData(iblockdata)))).isCancelled()) { ++ return; ++ } ++ // CraftBukkit end + world.setTypeAndData(blockposition, Blocks.MONSTER_EGG.getBlockData().set(BlockMonsterEggs.VARIANT, EnumMonsterEggVarient.a(iblockdata)), 3); + this.silverfish.y(); + this.silverfish.die(); diff --git a/nms-patches/PathfinderGoalSilverfishWakeOthers.patch b/nms-patches/PathfinderGoalSilverfishWakeOthers.patch new file mode 100644 index 00000000..f8fb1845 --- /dev/null +++ b/nms-patches/PathfinderGoalSilverfishWakeOthers.patch @@ -0,0 +1,14 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalSilverfishWakeOthers.java 2014-11-27 08:59:46.841421230 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalSilverfishWakeOthers.java 2014-11-27 08:42:10.144850927 +1100 +@@ -36,6 +36,11 @@ + IBlockData iblockdata = world.getType(blockposition1); + + if (iblockdata.getBlock() == Blocks.MONSTER_EGG) { ++ // CraftBukkit start ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callEntityChangeBlockEvent(this.silverfish, blockposition1.getX(), blockposition1.getY(), blockposition1.getZ(), Blocks.AIR, 0).isCancelled()) { ++ continue; ++ } ++ // CraftBukkit end + if (world.getGameRules().getBoolean("mobGriefing")) { + world.setAir(blockposition1, true); + } else { diff --git a/nms-patches/PathfinderGoalSit.patch b/nms-patches/PathfinderGoalSit.patch new file mode 100644 index 00000000..f7333321 --- /dev/null +++ b/nms-patches/PathfinderGoalSit.patch @@ -0,0 +1,11 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalSit.java 2014-11-27 08:59:46.845421213 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalSit.java 2014-11-27 08:42:10.096851020 +1100 +@@ -12,7 +12,7 @@ + + public boolean a() { + if (!this.entity.isTamed()) { +- return false; ++ return this.willSit && this.entity.getGoalTarget() == null; // CraftBukkit - Allow sitting for wild animals + } else if (this.entity.V()) { + return false; + } else if (!this.entity.onGround) { diff --git a/nms-patches/PathfinderGoalTame.patch b/nms-patches/PathfinderGoalTame.patch new file mode 100644 index 00000000..94aa0e1b --- /dev/null +++ b/nms-patches/PathfinderGoalTame.patch @@ -0,0 +1,31 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalTame.java 2014-11-27 08:59:46.845421213 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalTame.java 2014-11-27 08:42:10.152850911 +1100 +@@ -45,7 +45,8 @@ + int i = this.entity.getTemper(); + int j = this.entity.getMaxDomestication(); + +- if (j > 0 && this.entity.bb().nextInt(j) < i) { ++ // CraftBukkit - fire EntityTameEvent ++ if (j > 0 && this.entity.bb().nextInt(j) < i && !org.bukkit.craftbukkit.event.CraftEventFactory.callEntityTameEvent(this.entity, (EntityHuman) this.entity.passenger).isCancelled() && this.entity.passenger instanceof EntityHuman) { + this.entity.h((EntityHuman) this.entity.passenger); + this.entity.world.broadcastEntityEffect(this.entity, (byte) 7); + return; +@@ -54,8 +55,16 @@ + this.entity.u(5); + } + +- this.entity.passenger.mount((Entity) null); +- this.entity.passenger = null; ++ // CraftBukkit start - Handle dismounting to account for VehicleExitEvent being fired. ++ if (this.entity.passenger != null) { ++ this.entity.passenger.mount((Entity) null); ++ // If the entity still has a passenger, then a plugin cancelled the event. ++ if (this.entity.passenger != null) { ++ return; ++ } ++ } ++ // this.entity.passenger = null; ++ // CraftBukkit end + this.entity.cU(); + this.entity.world.broadcastEntityEffect(this.entity, (byte) 6); + } diff --git a/nms-patches/PathfinderGoalTargetNearestPlayer.patch b/nms-patches/PathfinderGoalTargetNearestPlayer.patch new file mode 100644 index 00000000..054d1b3c --- /dev/null +++ b/nms-patches/PathfinderGoalTargetNearestPlayer.patch @@ -0,0 +1,11 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PathfinderGoalTargetNearestPlayer.java 2014-11-27 08:59:46.845421213 +1100 ++++ src/main/java/net/minecraft/server/PathfinderGoalTargetNearestPlayer.java 2014-11-27 08:42:10.120850973 +1100 +@@ -59,7 +59,7 @@ + } + + public void c() { +- this.b.setGoalTarget(this.e); ++ this.b.setGoalTarget(this.e, org.bukkit.event.entity.EntityTargetEvent.TargetReason.CLOSEST_PLAYER, true); // CraftBukkit - added reason + super.c(); + } + diff --git a/nms-patches/PlayerChunk.patch b/nms-patches/PlayerChunk.patch new file mode 100644 index 00000000..6170f804 --- /dev/null +++ b/nms-patches/PlayerChunk.patch @@ -0,0 +1,104 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PlayerChunk.java 2014-11-27 08:59:46.849421195 +1100 ++++ src/main/java/net/minecraft/server/PlayerChunk.java 2014-11-27 08:42:10.136850942 +1100 +@@ -3,6 +3,11 @@ + import com.google.common.collect.Lists; + import java.util.List; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor; ++import java.util.HashMap; ++// CraftBukkit end ++ + class PlayerChunk { + + private final List b; +@@ -12,16 +17,26 @@ + private int f; + private long g; + final PlayerChunkMap playerChunkMap; ++ ++ // CraftBukkit start - add fields ++ private final HashMap<EntityPlayer, Runnable> players = new HashMap<EntityPlayer, Runnable>(); ++ private boolean loaded = false; ++ private Runnable loadedRunnable = new Runnable() { ++ public void run() { ++ PlayerChunk.this.loaded = true; ++ } ++ }; ++ // CraftBukkit end + + public PlayerChunk(PlayerChunkMap playerchunkmap, int i, int j) { + this.playerChunkMap = playerchunkmap; + this.b = Lists.newArrayList(); + this.dirtyBlocks = new short[64]; + this.location = new ChunkCoordIntPair(i, j); +- playerchunkmap.a().chunkProviderServer.getChunkAt(i, j); ++ playerchunkmap.a().chunkProviderServer.getChunkAt(i, j, loadedRunnable); // CraftBukkit + } + +- public void a(EntityPlayer entityplayer) { ++ public void a(final EntityPlayer entityplayer) { // CraftBukkit - added final to argument + if (this.b.contains(entityplayer)) { + PlayerChunkMap.c().debug("Failed to add player. {} already is in chunk {}, {}", new Object[] { entityplayer, Integer.valueOf(this.location.x), Integer.valueOf(this.location.z)}); + } else { +@@ -30,18 +45,50 @@ + } + + this.b.add(entityplayer); +- entityplayer.chunkCoordIntPairQueue.add(this.location); ++ // CraftBukkit start - use async chunk io ++ Runnable playerRunnable; ++ if (this.loaded) { ++ playerRunnable = null; ++ entityplayer.chunkCoordIntPairQueue.add(this.location); ++ } else { ++ playerRunnable = new Runnable() { ++ public void run() { ++ entityplayer.chunkCoordIntPairQueue.add(PlayerChunk.this.location); ++ } ++ }; ++ this.playerChunkMap.a().chunkProviderServer.getChunkAt(this.location.x, this.location.z, playerRunnable); ++ } ++ ++ this.players.put(entityplayer, playerRunnable); ++ // CraftBukkit end + } + } + + public void b(EntityPlayer entityplayer) { + if (this.b.contains(entityplayer)) { ++ // CraftBukkit start - If we haven't loaded yet don't load the chunk just so we can clean it up ++ if (!this.loaded) { ++ ChunkIOExecutor.dropQueuedChunkLoad(this.playerChunkMap.a(), this.location.x, this.location.z, this.players.get(entityplayer)); ++ this.b.remove(entityplayer); ++ this.players.remove(entityplayer); ++ ++ if (this.b.isEmpty()) { ++ ChunkIOExecutor.dropQueuedChunkLoad(this.playerChunkMap.a(), this.location.x, this.location.z, this.loadedRunnable); ++ long i = (long) this.location.x + 2147483647L | (long) this.location.z + 2147483647L << 32; ++ PlayerChunkMap.b(this.playerChunkMap).remove(i); ++ PlayerChunkMap.c(this.playerChunkMap).remove(this); ++ } ++ ++ return; ++ } ++ // CraftBukkit end + Chunk chunk = PlayerChunkMap.a(this.playerChunkMap).getChunkAt(this.location.x, this.location.z); + + if (chunk.isReady()) { + entityplayer.playerConnection.sendPacket(new PacketPlayOutMapChunk(chunk, true, 0)); + } + ++ this.players.remove(entityplayer); // CraftBukkit + this.b.remove(entityplayer); + entityplayer.chunkCoordIntPairQueue.remove(this.location); + if (this.b.isEmpty()) { +@@ -122,7 +169,7 @@ + if (this.dirtyCount == 64) { + i = this.location.x * 16; + j = this.location.z * 16; +- this.a((Packet) (new PacketPlayOutMapChunk(PlayerChunkMap.a(this.playerChunkMap).getChunkAt(this.location.x, this.location.z), false, this.f))); ++ this.a((Packet) (new PacketPlayOutMapChunk(PlayerChunkMap.a(this.playerChunkMap).getChunkAt(this.location.x, this.location.z), (this.f == 0xFFFF), this.f))); // CraftBukkit - send everything (including biome) if all sections flagged + + for (k = 0; k < 16; ++k) { + if ((this.f & 1 << k) != 0) { diff --git a/nms-patches/PlayerChunkMap.patch b/nms-patches/PlayerChunkMap.patch new file mode 100644 index 00000000..48b86df8 --- /dev/null +++ b/nms-patches/PlayerChunkMap.patch @@ -0,0 +1,208 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PlayerChunkMap.java 2014-11-27 08:59:46.849421195 +1100 ++++ src/main/java/net/minecraft/server/PlayerChunkMap.java 2014-11-27 08:42:10.152850911 +1100 +@@ -7,17 +7,24 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// CraftBukkit start ++import java.util.Collections; ++import java.util.Queue; ++import java.util.LinkedList; ++// CraftBukkit end ++ + public class PlayerChunkMap { + + private static final Logger a = LogManager.getLogger(); + private final WorldServer world; + private final List managedPlayers = Lists.newArrayList(); + private final LongHashMap d = new LongHashMap(); +- private final List e = Lists.newArrayList(); +- private final List f = Lists.newArrayList(); ++ private final Queue e = new java.util.concurrent.ConcurrentLinkedQueue(); // CraftBukkit ArrayList -> ConcurrentLinkedQueue ++ private final Queue f = new java.util.concurrent.ConcurrentLinkedQueue(); // CraftBukkit ArrayList -> ConcurrentLinkedQueue + private int g; + private long h; + private final int[][] i = new int[][] { { 1, 0}, { 0, 1}, { -1, 0}, { 0, -1}}; ++ private boolean wasNotEmpty; // CraftBukkit - add field + + public PlayerChunkMap(WorldServer worldserver) { + this.world = worldserver; +@@ -35,28 +42,39 @@ + + if (i - this.h > 8000L) { + this.h = i; +- +- for (j = 0; j < this.f.size(); ++j) { +- playerchunk = (PlayerChunk) this.f.get(j); ++ ++ // CraftBukkit start - Use iterator ++ java.util.Iterator iterator = this.f.iterator(); ++ while (iterator.hasNext()) { ++ playerchunk = (PlayerChunk) iterator.next(); + playerchunk.b(); + playerchunk.a(); + } + } else { +- for (j = 0; j < this.e.size(); ++j) { +- playerchunk = (PlayerChunk) this.e.get(j); ++ java.util.Iterator iterator = this.e.iterator(); ++ ++ while (iterator.hasNext()) { ++ playerchunk = (PlayerChunk) iterator.next(); + playerchunk.b(); ++ iterator.remove(); ++ // CraftBukkit end + } + } + +- this.e.clear(); ++ // this.e.clear(); // CraftBukkit - Removals are already covered + if (this.managedPlayers.isEmpty()) { ++ if (!wasNotEmpty) return; // CraftBukkit - Only do unload when we go from non-empty to empty + WorldProvider worldprovider = this.world.worldProvider; + + if (!worldprovider.e()) { + this.world.chunkProviderServer.b(); + } ++ // CraftBukkit start ++ wasNotEmpty = false; ++ } else { ++ wasNotEmpty = true; + } +- ++ // CraftBukkit end + } + + public boolean a(int i, int j) { +@@ -77,6 +95,16 @@ + + return playerchunk; + } ++ ++ // CraftBukkit start - add method ++ public final boolean isChunkInUse(int x, int z) { ++ PlayerChunk pi = a(x, z, false); ++ if (pi != null) { ++ return (PlayerChunk.b(pi).size() > 0); ++ } ++ return false; ++ } ++ // CraftBukkit end + + public void flagDirty(BlockPosition blockposition) { + int i = blockposition.getX() >> 4; +@@ -95,13 +123,22 @@ + + entityplayer.d = entityplayer.locX; + entityplayer.e = entityplayer.locZ; ++ ++ // CraftBukkit start - Load nearby chunks first ++ List<ChunkCoordIntPair> chunkList = new LinkedList<ChunkCoordIntPair>(); + + for (int k = i - this.g; k <= i + this.g; ++k) { + for (int l = j - this.g; l <= j + this.g; ++l) { +- this.a(k, l, true).a(entityplayer); ++ chunkList.add(new ChunkCoordIntPair(k, l)); + } + } +- ++ ++ Collections.sort(chunkList, new ChunkCoordComparator(entityplayer)); ++ for (ChunkCoordIntPair pair : chunkList) { ++ this.a(pair.x, pair.z, true).a(entityplayer); ++ } ++ // CraftBukkit end ++ + this.managedPlayers.add(entityplayer); + this.b(entityplayer); + } +@@ -188,12 +225,13 @@ + int i1 = this.g; + int j1 = i - k; + int k1 = j - l; ++ List<ChunkCoordIntPair> chunksToLoad = new LinkedList<ChunkCoordIntPair>(); // CraftBukkit + + if (j1 != 0 || k1 != 0) { + for (int l1 = i - i1; l1 <= i + i1; ++l1) { + for (int i2 = j - i1; i2 <= j + i1; ++i2) { + if (!this.a(l1, i2, k, l, i1)) { +- this.a(l1, i2, true).a(entityplayer); ++ chunksToLoad.add(new ChunkCoordIntPair(l1, i2)); // CraftBukkit + } + + if (!this.a(l1 - j1, i2 - k1, i, j, i1)) { +@@ -209,6 +247,17 @@ + this.b(entityplayer); + entityplayer.d = entityplayer.locX; + entityplayer.e = entityplayer.locZ; ++ ++ // CraftBukkit start - send nearest chunks first ++ Collections.sort(chunksToLoad, new ChunkCoordComparator(entityplayer)); ++ for (ChunkCoordIntPair pair : chunksToLoad) { ++ this.a(pair.x, pair.z, true).a(entityplayer); ++ } ++ ++ if (j1 > 1 || j1 < -1 || k1 > 1 || k1 < -1) { ++ Collections.sort(entityplayer.chunkCoordIntPairQueue, new ChunkCoordComparator(entityplayer)); ++ } ++ // CraftBukkit end + } + } + } +@@ -274,11 +323,54 @@ + return playerchunkmap.d; + } + +- static List c(PlayerChunkMap playerchunkmap) { ++ static Queue c(PlayerChunkMap playerchunkmap) { // CraftBukkit List -> Queue + return playerchunkmap.f; + } + +- static List d(PlayerChunkMap playerchunkmap) { ++ static Queue d(PlayerChunkMap playerchunkmap) { // CraftBukkit List -> Queue + return playerchunkmap.e; + } ++ ++ // CraftBukkit start - Sorter to load nearby chunks first ++ private static class ChunkCoordComparator implements java.util.Comparator<ChunkCoordIntPair> { ++ private int x; ++ private int z; ++ ++ public ChunkCoordComparator (EntityPlayer entityplayer) { ++ x = (int) entityplayer.locX >> 4; ++ z = (int) entityplayer.locZ >> 4; ++ } ++ ++ public int compare(ChunkCoordIntPair a, ChunkCoordIntPair b) { ++ if (a.equals(b)) { ++ return 0; ++ } ++ ++ // Subtract current position to set center point ++ int ax = a.x - this.x; ++ int az = a.z - this.z; ++ int bx = b.x - this.x; ++ int bz = b.z - this.z; ++ ++ int result = ((ax - bx) * (ax + bx)) + ((az - bz) * (az + bz)); ++ if (result != 0) { ++ return result; ++ } ++ ++ if (ax < 0) { ++ if (bx < 0) { ++ return bz - az; ++ } else { ++ return -1; ++ } ++ } else { ++ if (bx < 0) { ++ return 1; ++ } else { ++ return az - bz; ++ } ++ } ++ } ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/PlayerConnection.patch b/nms-patches/PlayerConnection.patch new file mode 100644 index 00000000..577557e6 --- /dev/null +++ b/nms-patches/PlayerConnection.patch @@ -0,0 +1,1458 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PlayerConnection.java 2014-11-27 08:59:46.853421177 +1100 ++++ src/main/java/net/minecraft/server/PlayerConnection.java 2014-11-27 08:42:10.148850918 +1100 +@@ -16,6 +16,48 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// CraftBukkit start ++import java.util.concurrent.ExecutionException; ++import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; ++import java.util.HashSet; ++ ++import org.bukkit.craftbukkit.entity.CraftPlayer; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.craftbukkit.inventory.CraftInventoryView; ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.craftbukkit.util.CraftChatMessage; ++import org.bukkit.craftbukkit.util.LazyPlayerSet; ++import org.bukkit.craftbukkit.util.Waitable; ++ ++import org.bukkit.Location; ++import org.bukkit.entity.Player; ++import org.bukkit.event.Event; ++import org.bukkit.event.block.Action; ++import org.bukkit.event.block.SignChangeEvent; ++import org.bukkit.event.inventory.ClickType; ++import org.bukkit.event.inventory.CraftItemEvent; ++import org.bukkit.event.inventory.InventoryAction; ++import org.bukkit.event.inventory.InventoryClickEvent; ++import org.bukkit.event.inventory.InventoryCreativeEvent; ++import org.bukkit.event.inventory.InventoryType.SlotType; ++import org.bukkit.event.player.AsyncPlayerChatEvent; ++import org.bukkit.event.player.PlayerAnimationEvent; ++import org.bukkit.event.player.PlayerChatEvent; ++import org.bukkit.event.player.PlayerCommandPreprocessEvent; ++import org.bukkit.event.player.PlayerInteractEntityEvent; ++import org.bukkit.event.player.PlayerInteractAtEntityEvent; ++import org.bukkit.event.player.PlayerItemHeldEvent; ++import org.bukkit.event.player.PlayerKickEvent; ++import org.bukkit.event.player.PlayerMoveEvent; ++import org.bukkit.event.player.PlayerTeleportEvent; ++import org.bukkit.event.player.PlayerToggleFlightEvent; ++import org.bukkit.event.player.PlayerToggleSneakEvent; ++import org.bukkit.event.player.PlayerToggleSprintEvent; ++import org.bukkit.inventory.CraftingInventory; ++import org.bukkit.inventory.InventoryView; ++import org.bukkit.util.NumberConversions; ++// CraftBukkit end ++ + public class PlayerConnection implements PacketListenerPlayIn, IUpdatePlayerListBox { + + private static final Logger c = LogManager.getLogger(); +@@ -29,13 +71,17 @@ + private int i; + private long j; + private long k; +- private int chatThrottle; ++ // CraftBukkit start - multithreaded fields ++ private volatile int chatThrottle; ++ private static final AtomicIntegerFieldUpdater chatSpamField = AtomicIntegerFieldUpdater.newUpdater(PlayerConnection.class, "chatThrottle"); ++ // CraftBukkit end + private int m; + private IntHashMap n = new IntHashMap(); + private double o; + private double p; + private double q; + public boolean checkMovement = true; ++ private boolean processedDisconnect; // CraftBukkit - added + + public PlayerConnection(MinecraftServer minecraftserver, NetworkManager networkmanager, EntityPlayer entityplayer) { + this.minecraftServer = minecraftserver; +@@ -43,7 +89,37 @@ + networkmanager.a((PacketListener) this); + this.player = entityplayer; + entityplayer.playerConnection = this; ++ ++ // CraftBukkit start - add fields and methods ++ this.server = minecraftserver.server; ++ } ++ ++ private final org.bukkit.craftbukkit.CraftServer server; ++ private int lastTick = MinecraftServer.currentTick; ++ private int lastDropTick = MinecraftServer.currentTick; ++ private int dropCount = 0; ++ private static final int SURVIVAL_PLACE_DISTANCE_SQUARED = 6 * 6; ++ private static final int CREATIVE_PLACE_DISTANCE_SQUARED = 7 * 7; ++ ++ // Get position of last block hit for BlockDamageLevel.STOPPED ++ private double lastPosX = Double.MAX_VALUE; ++ private double lastPosY = Double.MAX_VALUE; ++ private double lastPosZ = Double.MAX_VALUE; ++ private float lastPitch = Float.MAX_VALUE; ++ private float lastYaw = Float.MAX_VALUE; ++ private boolean justTeleported = false; ++ ++ // For the PacketPlayOutBlockPlace hack :( ++ Long lastPacket; ++ ++ // Store the last block right clicked and what type it was ++ private Item lastMaterial; ++ ++ public CraftPlayer getPlayer() { ++ return (this.player == null) ? null : (CraftPlayer) this.player.getBukkitEntity(); + } ++ private final static HashSet<Integer> invalidItems = new HashSet<Integer>(java.util.Arrays.asList(8, 9, 10, 11, 26, 34, 36, 43, 51, 52, 55, 59, 60, 62, 63, 64, 68, 71, 74, 75, 83, 90, 92, 93, 94, 104, 105, 115, 117, 118, 119, 125, 127, 132, 140, 141, 142, 144)); // TODO: Check after every update. ++ // CraftBukkit end + + public void c() { + this.h = false; +@@ -57,9 +133,14 @@ + } + + this.minecraftServer.methodProfiler.b(); ++ // CraftBukkit start ++ for (int spam; (spam = this.chatThrottle) > 0 && !chatSpamField.compareAndSet(this, spam, spam - 1); ) ; ++ /* Use thread-safe field access instead + if (this.chatThrottle > 0) { + --this.chatThrottle; + } ++ */ ++ // CraftBukkit end + + if (this.m > 0) { + --this.m; +@@ -76,11 +157,27 @@ + } + + public void disconnect(String s) { ++ // CraftBukkit start - fire PlayerKickEvent ++ String leaveMessage = EnumChatFormat.YELLOW + this.player.getName() + " left the game."; ++ ++ PlayerKickEvent event = new PlayerKickEvent(this.server.getPlayer(this.player), s, leaveMessage); ++ ++ if (this.server.getServer().isRunning()) { ++ this.server.getPluginManager().callEvent(event); ++ } ++ ++ if (event.isCancelled()) { ++ // Do not kick the player ++ return; ++ } ++ // Send the possibly modified leave message ++ s = event.getReason(); ++ // CraftBukkit end + ChatComponentText chatcomponenttext = new ChatComponentText(s); + + this.networkManager.a(new PacketPlayOutKickDisconnect(chatcomponenttext), new PlayerConnectionFuture(this, chatcomponenttext), new GenericFutureListener[0]); + this.networkManager.k(); +- Futures.getUnchecked(this.minecraftServer.postToMainThread(new PlayerConnectionDisconnector(this))); ++ this.minecraftServer.postToMainThread(new PlayerConnectionDisconnector(this)); // CraftBukkit - Don't wait + } + + public void a(PacketPlayInSteerVehicle packetplayinsteervehicle) { +@@ -90,6 +187,13 @@ + + public void a(PacketPlayInFlying packetplayinflying) { + PlayerConnectionUtils.ensureMainThread(packetplayinflying, this, this.player.u()); ++ // CraftBukkit start - Check for NaN ++ if (Double.isNaN(packetplayinflying.x) || Double.isNaN(packetplayinflying.y) || Double.isNaN(packetplayinflying.z)) { ++ c.warn(player.getName() + " was caught trying to crash the server with an invalid position."); ++ getPlayer().kickPlayer("Nope!"); ++ return; ++ } ++ // CraftBukkit end + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); + + this.h = true; +@@ -108,8 +212,65 @@ + this.checkMovement = true; + } + } ++ // CraftBukkit start - fire PlayerMoveEvent ++ Player player = this.getPlayer(); ++ Location from = new Location(player.getWorld(), lastPosX, lastPosY, lastPosZ, lastYaw, lastPitch); // Get the Players previous Event location. ++ Location to = player.getLocation().clone(); // Start off the To location as the Players current location. ++ ++ // If the packet contains movement information then we update the To location with the correct XYZ. ++ if (packetplayinflying.hasPos && !(packetplayinflying.hasPos && packetplayinflying.y == -999.0D)) { ++ to.setX(packetplayinflying.x); ++ to.setY(packetplayinflying.y); ++ to.setZ(packetplayinflying.z); ++ } ++ ++ // If the packet contains look information then we update the To location with the correct Yaw & Pitch. ++ if (packetplayinflying.hasLook) { ++ to.setYaw(packetplayinflying.yaw); ++ to.setPitch(packetplayinflying.pitch); ++ } ++ ++ // Prevent 40 event-calls for less than a single pixel of movement >.> ++ double delta = Math.pow(this.lastPosX - to.getX(), 2) + Math.pow(this.lastPosY - to.getY(), 2) + Math.pow(this.lastPosZ - to.getZ(), 2); ++ float deltaAngle = Math.abs(this.lastYaw - to.getYaw()) + Math.abs(this.lastPitch - to.getPitch()); ++ ++ if ((delta > 1f / 256 || deltaAngle > 10f) && (this.checkMovement && !this.player.dead)) { ++ this.lastPosX = to.getX(); ++ this.lastPosY = to.getY(); ++ this.lastPosZ = to.getZ(); ++ this.lastYaw = to.getYaw(); ++ this.lastPitch = to.getPitch(); ++ ++ // Skip the first time we do this ++ if (from.getX() != Double.MAX_VALUE) { ++ PlayerMoveEvent event = new PlayerMoveEvent(player, from, to); ++ this.server.getPluginManager().callEvent(event); ++ ++ // If the event is cancelled we move the player back to their old location. ++ if (event.isCancelled()) { ++ this.player.playerConnection.sendPacket(new PacketPlayOutPosition(from.getX(), from.getY() + 1.6200000047683716D, from.getZ(), from.getYaw(), from.getPitch(), Collections.emptySet())); ++ return; ++ } ++ ++ /* If a Plugin has changed the To destination then we teleport the Player ++ there to avoid any 'Moved wrongly' or 'Moved too quickly' errors. ++ We only do this if the Event was not cancelled. */ ++ if (!to.equals(event.getTo()) && !event.isCancelled()) { ++ this.player.getBukkitEntity().teleport(event.getTo(), PlayerTeleportEvent.TeleportCause.UNKNOWN); ++ return; ++ } ++ ++ /* Check to see if the Players Location has some how changed during the call of the event. ++ This can happen due to a plugin teleporting the player instead of using .setTo() */ ++ if (!from.equals(this.getPlayer().getLocation()) && this.justTeleported) { ++ this.justTeleported = false; ++ return; ++ } ++ } ++ } + +- if (this.checkMovement) { ++ if (this.checkMovement && !this.player.dead) { ++ // CraftBukkit end + this.f = this.e; + double d7; + double d8; +@@ -203,12 +364,14 @@ + double d11 = d7 - this.player.locX; + double d12 = d8 - this.player.locY; + double d13 = d9 - this.player.locZ; +- double d14 = Math.min(Math.abs(d11), Math.abs(this.player.motX)); +- double d15 = Math.min(Math.abs(d12), Math.abs(this.player.motY)); +- double d16 = Math.min(Math.abs(d13), Math.abs(this.player.motZ)); ++ // CraftBukkit start - min to max ++ double d14 = Math.max(Math.abs(d11), Math.abs(this.player.motX)); ++ double d15 = Math.max(Math.abs(d12), Math.abs(this.player.motY)); ++ double d16 = Math.max(Math.abs(d13), Math.abs(this.player.motZ)); ++ // CraftBukkit end + double d17 = d14 * d14 + d15 * d15 + d16 * d16; + +- if (d17 > 100.0D && (!this.minecraftServer.S() || !this.minecraftServer.R().equals(this.player.getName()))) { ++ if (d17 > 100.0D && this.checkMovement && (!this.minecraftServer.S() || !this.minecraftServer.R().equals(this.player.getName()))) { // CraftBukkit - Added this.checkMovement condition to solve this check being triggered by teleports + PlayerConnection.c.warn(this.player.getName() + " moved too quickly! " + d11 + "," + d12 + "," + d13 + " (" + d14 + ", " + d15 + ", " + d16 + ")"); + this.a(this.o, this.p, this.q, this.player.yaw, this.player.pitch); + return; +@@ -281,6 +444,49 @@ + } + + public void a(double d0, double d1, double d2, float f, float f1, Set set) { ++ // CraftBukkit start - Delegate to teleport(Location) ++ Player player = this.getPlayer(); ++ Location from = player.getLocation(); ++ Location to = new Location(this.getPlayer().getWorld(), d0, d1, d2, f, f1); ++ PlayerTeleportEvent event = new PlayerTeleportEvent(player, from, to, PlayerTeleportEvent.TeleportCause.UNKNOWN); ++ this.server.getPluginManager().callEvent(event); ++ ++ from = event.getFrom(); ++ to = event.isCancelled() ? from : event.getTo(); ++ ++ this.teleport(to, set); ++ } ++ ++ public void teleport(Location dest) { ++ teleport(dest, Collections.emptySet()); ++ } ++ ++ public void teleport(Location dest, Set set) { ++ double d0, d1, d2; ++ float f, f1; ++ ++ d0 = dest.getX(); ++ d1 = dest.getY(); ++ d2 = dest.getZ(); ++ f = dest.getYaw(); ++ f1 = dest.getPitch(); ++ ++ // TODO: make sure this is the best way to address this. ++ if (Float.isNaN(f)) { ++ f = 0; ++ } ++ ++ if (Float.isNaN(f1)) { ++ f1 = 0; ++ } ++ ++ this.lastPosX = d0; ++ this.lastPosY = d1; ++ this.lastPosZ = d2; ++ this.lastYaw = f; ++ this.lastPitch = f1; ++ this.justTeleported = true; ++ // CraftBukkit end + this.checkMovement = false; + this.o = d0; + this.p = d1; +@@ -314,32 +520,49 @@ + + public void a(PacketPlayInBlockDig packetplayinblockdig) { + PlayerConnectionUtils.ensureMainThread(packetplayinblockdig, this, this.player.u()); ++ if (this.player.dead) return; // CraftBukkit + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); + BlockPosition blockposition = packetplayinblockdig.a(); + + this.player.z(); ++ // CraftBukkit start + switch (SwitchHelperCommandActionType.a[packetplayinblockdig.c().ordinal()]) { +- case 1: ++ case 1: // DROP_ITEM + if (!this.player.v()) { ++ // limit how quickly items can be dropped ++ // If the ticks aren't the same then the count starts from 0 and we update the lastDropTick. ++ if (this.lastDropTick != MinecraftServer.currentTick) { ++ this.dropCount = 0; ++ this.lastDropTick = MinecraftServer.currentTick; ++ } else { ++ // Else we increment the drop count and check the amount. ++ this.dropCount++; ++ if (this.dropCount >= 20) { ++ this.c.warn(this.player.getName() + " dropped their items too quickly!"); ++ this.disconnect("You dropped your items too quickly (Hacking?)"); ++ return; ++ } ++ } ++ // CraftBukkit end + this.player.a(false); + } + + return; + +- case 2: ++ case 2: // DROP_ALL_ITEMS + if (!this.player.v()) { + this.player.a(true); + } + + return; + +- case 3: ++ case 3: // RELEASE_USE_ITEM + this.player.bT(); + return; + +- case 4: +- case 5: +- case 6: ++ case 4: // START_DESTROY_BLOCK ++ case 5: // ABORT_DESTROY_BLOCK ++ case 6: // STOP_DESTROY_BLOCK + 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); +@@ -354,7 +577,15 @@ + if (!this.minecraftServer.a(worldserver, blockposition, this.player) && worldserver.af().a(blockposition)) { + this.player.playerInteractManager.a(blockposition, packetplayinblockdig.b()); + } else { ++ // CraftBukkit start - fire PlayerInteractEvent ++ CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_BLOCK, blockposition, packetplayinblockdig.b(), this.player.inventory.getItemInHand()); + this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(worldserver, blockposition)); ++ // Update any tile entity data for this block ++ TileEntity tileentity = worldserver.getTileEntity(blockposition); ++ if (tileentity != null) { ++ this.player.playerConnection.sendPacket(tileentity.getUpdatePacket()); ++ } ++ // CraftBukkit end + } + } else { + if (packetplayinblockdig.c() == EnumPlayerDigType.STOP_DESTROY_BLOCK) { +@@ -374,11 +605,38 @@ + default: + throw new IllegalArgumentException("Invalid player action"); + } ++ // CraftBukkit end + } + + public void a(PacketPlayInBlockPlace packetplayinblockplace) { + PlayerConnectionUtils.ensureMainThread(packetplayinblockplace, this, this.player.u()); + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); ++ ++ // CraftBukkit start ++ if (this.player.dead) return; ++ ++ // This is a horrible hack needed because the client sends 2 packets on 'right mouse click' ++ // aimed at a block. We shouldn't need to get the second packet if the data is handled ++ // but we cannot know what the client will do, so we might still get it ++ // ++ // If the time between packets is small enough, and the 'signature' similar, we discard the ++ // second one. This sadly has to remain until Mojang makes their packets saner. :( ++ // -- Grum ++ if (packetplayinblockplace.getFace() == 255) { ++ if (packetplayinblockplace.getItemStack() != null && packetplayinblockplace.getItemStack().getItem() == this.lastMaterial && this.lastPacket != null && packetplayinblockplace.timestamp - this.lastPacket < 100) { ++ this.lastPacket = null; ++ return; ++ } ++ } else { ++ this.lastMaterial = packetplayinblockplace.getItemStack() == null ? null : packetplayinblockplace.getItemStack().getItem(); ++ this.lastPacket = packetplayinblockplace.timestamp; ++ } ++ // CraftBukkit - if rightclick decremented the item, always send the update packet. */ ++ // this is not here for CraftBukkit's own functionality; rather it is to fix ++ // a notch bug where the item doesn't update correctly. ++ boolean always = false; ++ // CraftBukkit end ++ + ItemStack itemstack = this.player.inventory.getItemInHand(); + boolean flag = false; + BlockPosition blockposition = packetplayinblockplace.a(); +@@ -390,7 +648,18 @@ + return; + } + +- this.player.playerInteractManager.useItem(this.player, worldserver, itemstack); ++ // CraftBukkit start ++ int itemstackAmount = itemstack.count; ++ org.bukkit.event.player.PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(this.player, Action.RIGHT_CLICK_AIR, itemstack); ++ if (event.useItemInHand() != Event.Result.DENY) { ++ this.player.playerInteractManager.useItem(this.player, this.player.world, itemstack); ++ } ++ ++ // CraftBukkit - notch decrements the counter by 1 in the above method with food, ++ // snowballs and so forth, but he does it in a place that doesn't cause the ++ // inventory update packet to get sent ++ always = (itemstack.count != itemstackAmount) || itemstack.getItem() == Item.getItemOf(Blocks.WATERLILY); ++ // CraftBukkit end + } else if (blockposition.getY() >= this.minecraftServer.getMaxBuildHeight() - 1 && (enumdirection == EnumDirection.UP || blockposition.getY() >= this.minecraftServer.getMaxBuildHeight())) { + ChatMessage chatmessage = new ChatMessage("build.tooHigh", new Object[] { Integer.valueOf(this.minecraftServer.getMaxBuildHeight())}); + +@@ -398,9 +667,21 @@ + this.player.playerConnection.sendPacket(new PacketPlayOutChat(chatmessage)); + flag = true; + } else { ++ // CraftBukkit start - Check if we can actually do something over this large a distance ++ Location eyeLoc = this.getPlayer().getEyeLocation(); ++ double reachDistance = NumberConversions.square(eyeLoc.getX() - blockposition.getX()) + NumberConversions.square(eyeLoc.getY() - blockposition.getY()) + NumberConversions.square(eyeLoc.getZ() - blockposition.getZ()); ++ if (reachDistance > (this.getPlayer().getGameMode() == org.bukkit.GameMode.CREATIVE ? CREATIVE_PLACE_DISTANCE_SQUARED : SURVIVAL_PLACE_DISTANCE_SQUARED)) { ++ return; ++ } ++ ++ if (!worldserver.af().a(blockposition)) { ++ return; ++ } ++ + if (this.checkMovement && this.player.e((double) blockposition.getX() + 0.5D, (double) blockposition.getY() + 0.5D, (double) blockposition.getZ() + 0.5D) < 64.0D && !this.minecraftServer.a(worldserver, blockposition, this.player) && worldserver.af().a(blockposition)) { +- this.player.playerInteractManager.interact(this.player, worldserver, itemstack, blockposition, enumdirection, packetplayinblockplace.d(), packetplayinblockplace.e(), packetplayinblockplace.f()); ++ always = !this.player.playerInteractManager.interact(this.player, worldserver, itemstack, blockposition, enumdirection, packetplayinblockplace.d(), packetplayinblockplace.e(), packetplayinblockplace.f()); + } ++ // CraftBukkit end + + flag = true; + } +@@ -423,7 +704,8 @@ + + this.player.activeContainer.b(); + this.player.g = false; +- if (!ItemStack.matches(this.player.inventory.getItemInHand(), packetplayinblockplace.getItemStack())) { ++ // CraftBukkit - TODO CHECK IF NEEDED -- new if structure might not need 'always'. Kept it in for now, but may be able to remove in future ++ if (!ItemStack.matches(this.player.inventory.getItemInHand(), packetplayinblockplace.getItemStack()) || always) { + this.sendPacket(new PacketPlayOutSetSlot(this.player.activeContainer.windowId, slot.rawSlotIndex, this.player.inventory.getItemInHand())); + } + } +@@ -437,8 +719,8 @@ + WorldServer[] aworldserver = this.minecraftServer.worldServer; + int i = aworldserver.length; + +- for (int j = 0; j < i; ++j) { +- WorldServer worldserver = aworldserver[j]; ++ // CraftBukkit - use the worlds array list ++ for (WorldServer worldserver : minecraftServer.worlds) { + + if (worldserver != null) { + entity = packetplayinspectate.a(worldserver); +@@ -455,6 +737,7 @@ + WorldServer worldserver1 = this.player.u(); + WorldServer worldserver2 = (WorldServer) entity.world; + ++ /* CraftBukkit start - replace with bukkit handling for multi-world + this.player.dimension = entity.dimension; + this.sendPacket(new PacketPlayOutRespawn(this.player.dimension, worldserver1.getDifficulty(), worldserver1.getWorldData().getType(), this.player.playerInteractManager.getGameMode())); + worldserver1.removeEntity(this.player); +@@ -472,6 +755,9 @@ + this.player.playerInteractManager.a(worldserver2); + this.minecraftServer.getPlayerList().b(this.player, worldserver2); + this.minecraftServer.getPlayerList().updateClient(this.player); ++ */ ++ this.player.getBukkitEntity().teleport(entity.getBukkitEntity()); ++ // CraftBukkit end + } else { + this.player.enderTeleportTo(entity.locX, entity.locY, entity.locZ); + } +@@ -483,14 +769,29 @@ + public void a(PacketPlayInResourcePackStatus packetplayinresourcepackstatus) {} + + public void a(IChatBaseComponent ichatbasecomponent) { +- PlayerConnection.c.info(this.player.getName() + " lost connection: " + ichatbasecomponent); ++ // CraftBukkit start - Rarely it would send a disconnect line twice ++ if (this.processedDisconnect) { ++ return; ++ } else { ++ this.processedDisconnect = true; ++ } ++ // CraftBukkit end ++ c.info(this.player.getName() + " lost connection: " + ichatbasecomponent.c()); // CraftBukkit - Don't toString the component + this.minecraftServer.aF(); ++ // CraftBukkit start - Replace vanilla quit message handling with our own. ++ /* + ChatMessage chatmessage = new ChatMessage("multiplayer.player.left", new Object[] { this.player.getScoreboardDisplayName()}); + + chatmessage.getChatModifier().setColor(EnumChatFormat.YELLOW); + this.minecraftServer.getPlayerList().sendMessage(chatmessage); ++ */ ++ + this.player.q(); +- this.minecraftServer.getPlayerList().disconnect(this.player); ++ String quitMessage = this.minecraftServer.getPlayerList().disconnect(this.player); ++ if ((quitMessage != null) && (quitMessage.length() > 0)) { ++ this.minecraftServer.getPlayerList().sendMessage(CraftChatMessage.fromString(quitMessage)); ++ } ++ // CraftBukkit end + if (this.minecraftServer.S() && this.player.getName().equals(this.minecraftServer.R())) { + PlayerConnection.c.info("Stopping singleplayer server as player logged out"); + this.minecraftServer.safeShutdown(); +@@ -511,6 +812,15 @@ + return; + } + } ++ ++ // CraftBukkit start ++ if (packet == null) { ++ return; ++ } else if (packet instanceof PacketPlayOutSpawnPosition) { ++ PacketPlayOutSpawnPosition packet6 = (PacketPlayOutSpawnPosition) packet; ++ this.player.compassTarget = new Location(this.getPlayer().getWorld(), packet6.position.getX(), packet6.position.getY(), packet6.position.getZ()); ++ } ++ // CraftBukkit end + + try { + this.networkManager.handle(packet); +@@ -524,18 +834,34 @@ + } + + public void a(PacketPlayInHeldItemSlot packetplayinhelditemslot) { ++ // CraftBukkit start ++ if (this.player.dead) return; + PlayerConnectionUtils.ensureMainThread(packetplayinhelditemslot, this, this.player.u()); + if (packetplayinhelditemslot.a() >= 0 && packetplayinhelditemslot.a() < PlayerInventory.getHotbarSize()) { ++ PlayerItemHeldEvent event = new PlayerItemHeldEvent(this.getPlayer(), this.player.inventory.itemInHandIndex, packetplayinhelditemslot.a()); ++ this.server.getPluginManager().callEvent(event); ++ if (event.isCancelled()) { ++ this.sendPacket(new PacketPlayOutHeldItemSlot(this.player.inventory.itemInHandIndex)); ++ this.player.z(); // RENAME ++ return; ++ } ++ // CraftBukkit end + this.player.inventory.itemInHandIndex = packetplayinhelditemslot.a(); + this.player.z(); + } else { + PlayerConnection.c.warn(this.player.getName() + " tried to set an invalid carried item"); ++ this.disconnect("Nope!"); // CraftBukkit + } + } + + public void a(PacketPlayInChat packetplayinchat) { +- PlayerConnectionUtils.ensureMainThread(packetplayinchat, this, this.player.u()); +- if (this.player.getChatFlags() == EnumChatVisibility.HIDDEN) { ++ // CraftBukkit start - async chat ++ boolean isSync = packetplayinchat.a().startsWith("/"); ++ if (packetplayinchat.a().startsWith("/")) { ++ PlayerConnectionUtils.ensureMainThread(packetplayinchat, this, this.player.u()); ++ } ++ // CraftBukkit end ++ if (this.player.dead || this.player.getChatFlags() == EnumChatVisibility.HIDDEN) { // CraftBukkit - dead men tell no tales + ChatMessage chatmessage = new ChatMessage("chat.cannotSend", new Object[0]); + + chatmessage.getChatModifier().setColor(EnumChatFormat.RED); +@@ -548,39 +874,248 @@ + + for (int i = 0; i < s.length(); ++i) { + if (!SharedConstants.isAllowedChatCharacter(s.charAt(i))) { +- this.disconnect("Illegal characters in chat"); ++ // CraftBukkit start - threadsafety ++ if (!isSync) { ++ Waitable waitable = new Waitable() { ++ @Override ++ protected Object evaluate() { ++ PlayerConnection.this.disconnect("Illegal characters in chat"); ++ return null; ++ } ++ }; ++ ++ this.minecraftServer.processQueue.add(waitable); ++ ++ try { ++ waitable.get(); ++ } catch (InterruptedException e) { ++ Thread.currentThread().interrupt(); ++ } catch (ExecutionException e) { ++ throw new RuntimeException(e); ++ } ++ } else { ++ this.disconnect("Illegal characters in chat"); ++ } ++ // CraftBukkit end + return; + } + } + +- if (s.startsWith("/")) { +- this.handleCommand(s); ++ // CraftBukkit start ++ if (isSync) { ++ try { ++ this.minecraftServer.server.playerCommandState = true; ++ this.handleCommand(s); ++ } finally { ++ this.minecraftServer.server.playerCommandState = false; ++ } ++ } else if (s.isEmpty()) { ++ c.warn(this.player.getName() + " tried to send an empty message"); ++ } else if (getPlayer().isConversing()) { ++ getPlayer().acceptConversationInput(s); ++ } else if (this.player.getChatFlags() == EnumChatVisibility.SYSTEM) { // Re-add "Command Only" flag check ++ ChatMessage chatmessage = new ChatMessage("chat.cannotSend", new Object[0]); ++ ++ chatmessage.getChatModifier().setColor(EnumChatFormat.RED); ++ this.sendPacket(new PacketPlayOutChat(chatmessage)); ++ } else if (true) { ++ this.chat(s, true); ++ // CraftBukkit end - the below is for reference. :) + } else { + ChatMessage chatmessage1 = new ChatMessage("chat.type.text", new Object[] { this.player.getScoreboardDisplayName(), s}); + + this.minecraftServer.getPlayerList().sendMessage(chatmessage1, false); + } + +- this.chatThrottle += 20; +- if (this.chatThrottle > 200 && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) { +- this.disconnect("disconnect.spam"); +- } ++ // CraftBukkit start - replaced with thread safe throttle ++ // this.chatThrottle += 20; ++ if (chatSpamField.addAndGet(this, 20) > 200 && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) { ++ if (!isSync) { ++ Waitable waitable = new Waitable() { ++ @Override ++ protected Object evaluate() { ++ PlayerConnection.this.disconnect("disconnect.spam"); ++ return null; ++ } ++ }; ++ ++ this.minecraftServer.processQueue.add(waitable); + ++ try { ++ waitable.get(); ++ } catch (InterruptedException e) { ++ Thread.currentThread().interrupt(); ++ } catch (ExecutionException e) { ++ throw new RuntimeException(e); ++ } ++ } else { ++ this.disconnect("disconnect.spam"); ++ } ++ // CraftBukkit end ++ } + } + } ++ ++ // CraftBukkit start - add method ++ public void chat(String s, boolean async) { ++ if (s.isEmpty() || this.player.getChatFlags() == EnumChatVisibility.HIDDEN) { ++ return; ++ } ++ ++ if (!async && s.startsWith("/")) { ++ this.handleCommand(s); ++ } else if (this.player.getChatFlags() == EnumChatVisibility.SYSTEM) { ++ // Do nothing, this is coming from a plugin ++ } else { ++ Player player = this.getPlayer(); ++ AsyncPlayerChatEvent event = new AsyncPlayerChatEvent(async, player, s, new LazyPlayerSet()); ++ this.server.getPluginManager().callEvent(event); ++ ++ if (PlayerChatEvent.getHandlerList().getRegisteredListeners().length != 0) { ++ // Evil plugins still listening to deprecated event ++ final PlayerChatEvent queueEvent = new PlayerChatEvent(player, event.getMessage(), event.getFormat(), event.getRecipients()); ++ queueEvent.setCancelled(event.isCancelled()); ++ Waitable waitable = new Waitable() { ++ @Override ++ protected Object evaluate() { ++ org.bukkit.Bukkit.getPluginManager().callEvent(queueEvent); ++ ++ if (queueEvent.isCancelled()) { ++ return null; ++ } ++ ++ String message = String.format(queueEvent.getFormat(), queueEvent.getPlayer().getDisplayName(), queueEvent.getMessage()); ++ PlayerConnection.this.minecraftServer.console.sendMessage(message); ++ if (((LazyPlayerSet) queueEvent.getRecipients()).isLazy()) { ++ for (Object player : PlayerConnection.this.minecraftServer.getPlayerList().players) { ++ ((EntityPlayer) player).sendMessage(CraftChatMessage.fromString(message)); ++ } ++ } else { ++ for (Player player : queueEvent.getRecipients()) { ++ player.sendMessage(message); ++ } ++ } ++ return null; ++ }}; ++ if (async) { ++ minecraftServer.processQueue.add(waitable); ++ } else { ++ waitable.run(); ++ } ++ try { ++ waitable.get(); ++ } catch (InterruptedException e) { ++ Thread.currentThread().interrupt(); // This is proper habit for java. If we aren't handling it, pass it on! ++ } catch (ExecutionException e) { ++ throw new RuntimeException("Exception processing chat event", e.getCause()); ++ } ++ } else { ++ if (event.isCancelled()) { ++ return; ++ } ++ ++ s = String.format(event.getFormat(), event.getPlayer().getDisplayName(), event.getMessage()); ++ minecraftServer.console.sendMessage(s); ++ if (((LazyPlayerSet) event.getRecipients()).isLazy()) { ++ for (Object recipient : minecraftServer.getPlayerList().players) { ++ ((EntityPlayer) recipient).sendMessage(CraftChatMessage.fromString(s)); ++ } ++ } else { ++ for (Player recipient : event.getRecipients()) { ++ recipient.sendMessage(s); ++ } ++ } ++ } ++ } ++ } ++ // CraftBukkit end + + private void handleCommand(String s) { +- this.minecraftServer.getCommandHandler().a(this.player, s); ++ // CraftBukkit start - whole method ++ this.c.info(this.player.getName() + " issued server command: " + s); ++ ++ CraftPlayer player = this.getPlayer(); ++ ++ PlayerCommandPreprocessEvent event = new PlayerCommandPreprocessEvent(player, s, new LazyPlayerSet()); ++ this.server.getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ return; ++ } ++ ++ try { ++ if (this.server.dispatchCommand(event.getPlayer(), event.getMessage().substring(1))) { ++ return; ++ } ++ } catch (org.bukkit.command.CommandException ex) { ++ player.sendMessage(org.bukkit.ChatColor.RED + "An internal error occurred while attempting to perform this command"); ++ java.util.logging.Logger.getLogger(PlayerConnection.class.getName()).log(java.util.logging.Level.SEVERE, null, ex); ++ return; ++ } ++ // this.minecraftServer.getCommandHandler().a(this.player, s); ++ // CraftBukkit end + } + + public void a(PacketPlayInArmAnimation packetplayinarmanimation) { ++ if (this.player.dead) return; // CraftBukkit + PlayerConnectionUtils.ensureMainThread(packetplayinarmanimation, this, this.player.u()); + this.player.z(); ++ // CraftBukkit start - Raytrace to look for 'rogue armswings' ++ float f = 1.0F; ++ float f1 = this.player.lastPitch + (this.player.pitch - this.player.lastPitch) * f; ++ float f2 = this.player.lastYaw + (this.player.yaw - this.player.lastYaw) * f; ++ double d0 = this.player.lastX + (this.player.locX - this.player.lastX) * (double) f; ++ double d1 = this.player.lastY + (this.player.locY - this.player.lastY) * (double) f + 1.62D - (double) this.player.getHeadHeight(); ++ double d2 = this.player.lastZ + (this.player.locZ - this.player.lastZ) * (double) f; ++ Vec3D vec3d = new Vec3D(d0, d1, d2); ++ ++ float f3 = MathHelper.cos(-f2 * 0.017453292F - 3.1415927F); ++ float f4 = MathHelper.sin(-f2 * 0.017453292F - 3.1415927F); ++ float f5 = -MathHelper.cos(-f1 * 0.017453292F); ++ float f6 = MathHelper.sin(-f1 * 0.017453292F); ++ float f7 = f4 * f5; ++ float f8 = f3 * f5; ++ double d3 = 5.0D; ++ Vec3D vec3d1 = vec3d.add((double) f7 * d3, (double) f6 * d3, (double) f8 * d3); ++ MovingObjectPosition movingobjectposition = this.player.world.rayTrace(vec3d, vec3d1, false); ++ ++ if (movingobjectposition == null || movingobjectposition.type != EnumMovingObjectType.BLOCK) { ++ CraftEventFactory.callPlayerInteractEvent(this.player, Action.LEFT_CLICK_AIR, this.player.inventory.getItemInHand()); ++ } ++ ++ // Arm swing animation ++ PlayerAnimationEvent event = new PlayerAnimationEvent(this.getPlayer()); ++ this.server.getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) return; ++ // CraftBukkit end + this.player.bv(); + } + + public void a(PacketPlayInEntityAction packetplayinentityaction) { + PlayerConnectionUtils.ensureMainThread(packetplayinentityaction, this, this.player.u()); ++ // CraftBukkit start ++ if (this.player.dead) return; ++ switch (packetplayinentityaction.b()) { ++ case START_SNEAKING: ++ case STOP_SNEAKING: ++ PlayerToggleSneakEvent event = new PlayerToggleSneakEvent(this.getPlayer(), packetplayinentityaction.b() == EnumPlayerAction.START_SNEAKING); ++ this.server.getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ return; ++ } ++ break; ++ case START_SPRINTING: ++ case STOP_SPRINTING: ++ PlayerToggleSprintEvent e2 = new PlayerToggleSprintEvent(this.getPlayer(), packetplayinentityaction.b() == EnumPlayerAction.START_SPRINTING); ++ this.server.getPluginManager().callEvent(e2); ++ ++ if (e2.isCancelled()) { ++ return; ++ } ++ break; ++ } + this.player.z(); + switch (SwitchHelperCommandActionType.b[packetplayinentityaction.b().ordinal()]) { + case 1: +@@ -601,7 +1136,7 @@ + + case 5: + this.player.a(false, true, true); +- this.checkMovement = false; ++ // this.checkMovement = false; // CraftBukkit - this is handled in teleport + break; + + case 6: +@@ -623,6 +1158,7 @@ + } + + public void a(PacketPlayInUseEntity packetplayinuseentity) { ++ if (this.player.dead) return; // CraftBukkit + PlayerConnectionUtils.ensureMainThread(packetplayinuseentity, this, this.player.u()); + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); + Entity entity = packetplayinuseentity.a((World) worldserver); +@@ -637,18 +1173,72 @@ + } + + if (this.player.h(entity) < d0) { ++ ItemStack itemInHand = this.player.inventory.getItemInHand(); // CraftBukkit ++ ++ if (packetplayinuseentity.a() == EnumEntityUseAction.INTERACT ++ || packetplayinuseentity.a() == EnumEntityUseAction.INTERACT_AT) { ++ // CraftBukkit start ++ boolean triggerTagUpdate = itemInHand != null && itemInHand.getItem() == Items.NAME_TAG && entity instanceof EntityInsentient; ++ boolean triggerChestUpdate = itemInHand != null && itemInHand.getItem() == Item.getItemOf(Blocks.CHEST) && entity instanceof EntityHorse; ++ boolean triggerLeashUpdate = itemInHand != null && itemInHand.getItem() == Items.LEAD && entity instanceof EntityInsentient; ++ PlayerInteractEntityEvent event; ++ if (packetplayinuseentity.a() == EnumEntityUseAction.INTERACT) { ++ event = new PlayerInteractEntityEvent((Player) this.getPlayer(), entity.getBukkitEntity()); ++ } else { ++ Vec3D target = packetplayinuseentity.b(); ++ event = new PlayerInteractAtEntityEvent((Player) this.getPlayer(), entity.getBukkitEntity(), new org.bukkit.util.Vector(target.a, target.b, target.c)); ++ } ++ this.server.getPluginManager().callEvent(event); ++ ++ if (triggerLeashUpdate && (event.isCancelled() || this.player.inventory.getItemInHand() == null || this.player.inventory.getItemInHand().getItem() != Items.LEAD)) { ++ // Refresh the current leash state ++ this.sendPacket(new PacketPlayOutAttachEntity(1, entity, ((EntityInsentient) entity).getLeashHolder())); ++ } ++ ++ if (triggerTagUpdate && (event.isCancelled() || this.player.inventory.getItemInHand() == null || this.player.inventory.getItemInHand().getItem() != Items.NAME_TAG)) { ++ // Refresh the current entity metadata ++ this.sendPacket(new PacketPlayOutEntityMetadata(entity.getId(), entity.datawatcher, true)); ++ } ++ if (triggerChestUpdate && (event.isCancelled() || this.player.inventory.getItemInHand() == null || this.player.inventory.getItemInHand().getItem() != Item.getItemOf(Blocks.CHEST))) { ++ this.sendPacket(new PacketPlayOutEntityMetadata(entity.getId(), entity.datawatcher, true)); ++ } ++ ++ if (event.isCancelled()) { ++ return; ++ } ++ // CraftBukkit end ++ } ++ + if (packetplayinuseentity.a() == EnumEntityUseAction.INTERACT) { + this.player.u(entity); ++ ++ // CraftBukkit start ++ if (itemInHand != null && itemInHand.count <= -1) { ++ this.player.updateInventory(this.player.activeContainer); ++ } ++ // CraftBukkit end + } else if (packetplayinuseentity.a() == EnumEntityUseAction.INTERACT_AT) { + entity.a((EntityHuman) this.player, packetplayinuseentity.b()); ++ ++ // CraftBukkit start ++ if (itemInHand != null && itemInHand.count <= -1) { ++ this.player.updateInventory(this.player.activeContainer); ++ } ++ // CraftBukkit end + } else if (packetplayinuseentity.a() == EnumEntityUseAction.ATTACK) { +- if (entity instanceof EntityItem || entity instanceof EntityExperienceOrb || entity instanceof EntityArrow || entity == this.player) { ++ if (entity instanceof EntityItem || entity instanceof EntityExperienceOrb || entity instanceof EntityArrow || (entity == this.player && !player.v())) { // CraftBukkit, RENAME + this.disconnect("Attempting to attack an invalid entity"); + this.minecraftServer.warning("Player " + this.player.getName() + " tried to attack an invalid entity"); + return; + } + + this.player.attack(entity); ++ ++ // CraftBukkit start ++ if (itemInHand != null && itemInHand.count <= -1) { ++ this.player.updateInventory(this.player.activeContainer); ++ } ++ // CraftBukkit end + } + } + } +@@ -663,7 +1253,8 @@ + switch (SwitchHelperCommandActionType.c[enumclientcommand.ordinal()]) { + case 1: + if (this.player.viewingCredits) { +- this.player = this.minecraftServer.getPlayerList().moveToWorld(this.player, 0, true); ++ // this.player = this.minecraftServer.getPlayerList().moveToWorld(this.player, 0, true); ++ this.minecraftServer.getPlayerList().changeDimension(this.player, 0, PlayerTeleportEvent.TeleportCause.END_PORTAL); // CraftBukkit - reroute logic through custom portal management + } else if (this.player.u().getWorldData().isHardcore()) { + if (this.minecraftServer.S() && this.player.getName().equals(this.minecraftServer.R())) { + this.player.playerConnection.disconnect("You have died. Game over, man, it\'s game over!"); +@@ -694,11 +1285,17 @@ + } + + public void a(PacketPlayInCloseWindow packetplayinclosewindow) { ++ if (this.player.dead) return; // CraftBukkit + PlayerConnectionUtils.ensureMainThread(packetplayinclosewindow, this, this.player.u()); ++ ++ CraftEventFactory.handleInventoryCloseEvent(this.player); // CraftBukkit ++ + this.player.p(); + } + + public void a(PacketPlayInWindowClick packetplayinwindowclick) { ++ if (this.player.dead) return; // CraftBukkit ++ + PlayerConnectionUtils.ensureMainThread(packetplayinwindowclick, this, this.player.u()); + this.player.z(); + if (this.player.activeContainer.windowId == packetplayinwindowclick.a() && this.player.activeContainer.c(this.player)) { +@@ -711,7 +1308,263 @@ + + this.player.a(this.player.activeContainer, (List) arraylist); + } else { +- ItemStack itemstack = this.player.activeContainer.clickItem(packetplayinwindowclick.b(), packetplayinwindowclick.c(), packetplayinwindowclick.f(), this.player); ++ // ItemStack itemstack = this.player.activeContainer.clickItem(packetplayinwindowclick.b(), packetplayinwindowclick.c(), packetplayinwindowclick.f(), this.player); ++ // CraftBukkit start - Call InventoryClickEvent ++ if (packetplayinwindowclick.b() < -1 && packetplayinwindowclick.b() != -999) { ++ return; ++ } ++ ++ InventoryView inventory = this.player.activeContainer.getBukkitView(); ++ SlotType type = CraftInventoryView.getSlotType(inventory, packetplayinwindowclick.b()); ++ ++ InventoryClickEvent event = null; ++ ClickType click = ClickType.UNKNOWN; ++ InventoryAction action = InventoryAction.UNKNOWN; ++ ++ ItemStack itemstack = null; ++ ++ if (packetplayinwindowclick.b() == -1) { ++ type = SlotType.OUTSIDE; // override ++ click = packetplayinwindowclick.c() == 0 ? ClickType.WINDOW_BORDER_LEFT : ClickType.WINDOW_BORDER_RIGHT; ++ action = InventoryAction.NOTHING; ++ } else if (packetplayinwindowclick.f() == 0) { ++ if (packetplayinwindowclick.c() == 0) { ++ click = ClickType.LEFT; ++ } else if (packetplayinwindowclick.c() == 1) { ++ click = ClickType.RIGHT; ++ } ++ if (packetplayinwindowclick.c() == 0 || packetplayinwindowclick.c() == 1) { ++ action = InventoryAction.NOTHING; // Don't want to repeat ourselves ++ if (packetplayinwindowclick.b() == -999) { ++ if (player.inventory.getCarried() != null) { ++ action = packetplayinwindowclick.c() == 0 ? InventoryAction.DROP_ALL_CURSOR : InventoryAction.DROP_ONE_CURSOR; ++ } ++ } else { ++ Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.b()); ++ if (slot != null) { ++ ItemStack clickedItem = slot.getItem(); ++ ItemStack cursor = player.inventory.getCarried(); ++ if (clickedItem == null) { ++ if (cursor != null) { ++ action = packetplayinwindowclick.c() == 0 ? InventoryAction.PLACE_ALL : InventoryAction.PLACE_ONE; ++ } ++ } else if (slot.isAllowed(player)) { ++ if (cursor == null) { ++ action = packetplayinwindowclick.c() == 0 ? InventoryAction.PICKUP_ALL : InventoryAction.PICKUP_HALF; ++ } else if (slot.isAllowed(cursor)) { ++ if (clickedItem.doMaterialsMatch(cursor) && ItemStack.equals(clickedItem, cursor)) { ++ int toPlace = packetplayinwindowclick.c() == 0 ? cursor.count : 1; ++ toPlace = Math.min(toPlace, clickedItem.getMaxStackSize() - clickedItem.count); ++ toPlace = Math.min(toPlace, slot.inventory.getMaxStackSize() - clickedItem.count); ++ if (toPlace == 1) { ++ action = InventoryAction.PLACE_ONE; ++ } else if (toPlace == cursor.count) { ++ action = InventoryAction.PLACE_ALL; ++ } else if (toPlace < 0) { ++ action = toPlace != -1 ? InventoryAction.PICKUP_SOME : InventoryAction.PICKUP_ONE; // this happens with oversized stacks ++ } else if (toPlace != 0) { ++ action = InventoryAction.PLACE_SOME; ++ } ++ } else if (cursor.count <= slot.getMaxStackSize()) { ++ action = InventoryAction.SWAP_WITH_CURSOR; ++ } ++ } else if (cursor.getItem() == clickedItem.getItem() && (!cursor.usesData() || cursor.getData() == clickedItem.getData()) && ItemStack.equals(cursor, clickedItem)) { ++ if (clickedItem.count >= 0) { ++ if (clickedItem.count + cursor.count <= cursor.getMaxStackSize()) { ++ // As of 1.5, this is result slots only ++ action = InventoryAction.PICKUP_ALL; ++ } ++ } ++ } ++ } ++ } ++ } ++ } ++ } else if (packetplayinwindowclick.f() == 1) { ++ if (packetplayinwindowclick.c() == 0) { ++ click = ClickType.SHIFT_LEFT; ++ } else if (packetplayinwindowclick.c() == 1) { ++ click = ClickType.SHIFT_RIGHT; ++ } ++ if (packetplayinwindowclick.c() == 0 || packetplayinwindowclick.c() == 1) { ++ if (packetplayinwindowclick.b() < 0) { ++ action = InventoryAction.NOTHING; ++ } else { ++ Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.b()); ++ if (slot != null && slot.isAllowed(this.player) && slot.hasItem()) { ++ action = InventoryAction.MOVE_TO_OTHER_INVENTORY; ++ } else { ++ action = InventoryAction.NOTHING; ++ } ++ } ++ } ++ } else if (packetplayinwindowclick.f() == 2) { ++ if (packetplayinwindowclick.c() >= 0 && packetplayinwindowclick.c() < 9) { ++ click = ClickType.NUMBER_KEY; ++ Slot clickedSlot = this.player.activeContainer.getSlot(packetplayinwindowclick.b()); ++ if (clickedSlot.isAllowed(player)) { ++ ItemStack hotbar = this.player.inventory.getItem(packetplayinwindowclick.c()); ++ boolean canCleanSwap = hotbar == null || (clickedSlot.inventory == player.inventory && clickedSlot.isAllowed(hotbar)); // the slot will accept the hotbar item ++ if (clickedSlot.hasItem()) { ++ if (canCleanSwap) { ++ action = InventoryAction.HOTBAR_SWAP; ++ } else { ++ int firstEmptySlot = player.inventory.getFirstEmptySlotIndex(); ++ if (firstEmptySlot > -1) { ++ action = InventoryAction.HOTBAR_MOVE_AND_READD; ++ } else { ++ action = InventoryAction.NOTHING; // This is not sane! Mojang: You should test for other slots of same type ++ } ++ } ++ } else if (!clickedSlot.hasItem() && hotbar != null && clickedSlot.isAllowed(hotbar)) { ++ action = InventoryAction.HOTBAR_SWAP; ++ } else { ++ action = InventoryAction.NOTHING; ++ } ++ } else { ++ action = InventoryAction.NOTHING; ++ } ++ // Special constructor for number key ++ event = new InventoryClickEvent(inventory, type, packetplayinwindowclick.b(), click, action, packetplayinwindowclick.c()); ++ } ++ } else if (packetplayinwindowclick.f() == 3) { ++ if (packetplayinwindowclick.c() == 2) { ++ click = ClickType.MIDDLE; ++ if (packetplayinwindowclick.b() == -999) { ++ action = InventoryAction.NOTHING; ++ } else { ++ Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.b()); ++ if (slot != null && slot.hasItem() && player.abilities.canInstantlyBuild && player.inventory.getCarried() == null) { ++ action = InventoryAction.CLONE_STACK; ++ } else { ++ action = InventoryAction.NOTHING; ++ } ++ } ++ } else { ++ click = ClickType.UNKNOWN; ++ action = InventoryAction.UNKNOWN; ++ } ++ } else if (packetplayinwindowclick.f() == 4) { ++ if (packetplayinwindowclick.b() >= 0) { ++ if (packetplayinwindowclick.c() == 0) { ++ click = ClickType.DROP; ++ Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.b()); ++ if (slot != null && slot.hasItem() && slot.isAllowed(player) && slot.getItem() != null && slot.getItem().getItem() != Item.getItemOf(Blocks.AIR)) { ++ action = InventoryAction.DROP_ONE_SLOT; ++ } else { ++ action = InventoryAction.NOTHING; ++ } ++ } else if (packetplayinwindowclick.c() == 1) { ++ click = ClickType.CONTROL_DROP; ++ Slot slot = this.player.activeContainer.getSlot(packetplayinwindowclick.b()); ++ if (slot != null && slot.hasItem() && slot.isAllowed(player) && slot.getItem() != null && slot.getItem().getItem() != Item.getItemOf(Blocks.AIR)) { ++ action = InventoryAction.DROP_ALL_SLOT; ++ } else { ++ action = InventoryAction.NOTHING; ++ } ++ } ++ } else { ++ // Sane default (because this happens when they are holding nothing. Don't ask why.) ++ click = ClickType.LEFT; ++ if (packetplayinwindowclick.c() == 1) { ++ click = ClickType.RIGHT; ++ } ++ action = InventoryAction.NOTHING; ++ } ++ } else if (packetplayinwindowclick.f() == 5) { ++ itemstack = this.player.activeContainer.clickItem(packetplayinwindowclick.b(), packetplayinwindowclick.c(), 5, this.player); ++ } else if (packetplayinwindowclick.f() == 6) { ++ click = ClickType.DOUBLE_CLICK; ++ action = InventoryAction.NOTHING; ++ if (packetplayinwindowclick.b() >= 0 && this.player.inventory.getCarried() != null) { ++ ItemStack cursor = this.player.inventory.getCarried(); ++ action = InventoryAction.NOTHING; ++ // Quick check for if we have any of the item ++ if (inventory.getTopInventory().contains(org.bukkit.Material.getMaterial(Item.getId(cursor.getItem()))) || inventory.getBottomInventory().contains(org.bukkit.Material.getMaterial(Item.getId(cursor.getItem())))) { ++ action = InventoryAction.COLLECT_TO_CURSOR; ++ } ++ } ++ } ++ // TODO check on updates ++ ++ if (packetplayinwindowclick.f() != 5) { ++ if (click == ClickType.NUMBER_KEY) { ++ event = new InventoryClickEvent(inventory, type, packetplayinwindowclick.b(), click, action, packetplayinwindowclick.c()); ++ } else { ++ event = new InventoryClickEvent(inventory, type, packetplayinwindowclick.b(), click, action); ++ } ++ ++ org.bukkit.inventory.Inventory top = inventory.getTopInventory(); ++ if (packetplayinwindowclick.b() == 0 && top instanceof CraftingInventory) { ++ org.bukkit.inventory.Recipe recipe = ((CraftingInventory) top).getRecipe(); ++ if (recipe != null) { ++ if (click == ClickType.NUMBER_KEY) { ++ event = new CraftItemEvent(recipe, inventory, type, packetplayinwindowclick.b(), click, action, packetplayinwindowclick.c()); ++ } else { ++ event = new CraftItemEvent(recipe, inventory, type, packetplayinwindowclick.b(), click, action); ++ } ++ } ++ } ++ ++ server.getPluginManager().callEvent(event); ++ ++ switch (event.getResult()) { ++ case ALLOW: ++ case DEFAULT: ++ itemstack = this.player.activeContainer.clickItem(packetplayinwindowclick.b(), packetplayinwindowclick.c(), packetplayinwindowclick.f(), this.player); ++ break; ++ case DENY: ++ /* Needs enum constructor in InventoryAction ++ if (action.modifiesOtherSlots()) { ++ ++ } else { ++ if (action.modifiesCursor()) { ++ this.player.playerConnection.sendPacket(new Packet103SetSlot(-1, -1, this.player.inventory.getCarried())); ++ } ++ if (action.modifiesClicked()) { ++ this.player.playerConnection.sendPacket(new Packet103SetSlot(this.player.activeContainer.windowId, packet102windowclick.slot, this.player.activeContainer.getSlot(packet102windowclick.slot).getItem())); ++ } ++ }*/ ++ switch (action) { ++ // Modified other slots ++ case PICKUP_ALL: ++ case MOVE_TO_OTHER_INVENTORY: ++ case HOTBAR_MOVE_AND_READD: ++ case HOTBAR_SWAP: ++ case COLLECT_TO_CURSOR: ++ case UNKNOWN: ++ this.player.updateInventory(this.player.activeContainer); ++ break; ++ // Modified cursor and clicked ++ case PICKUP_SOME: ++ case PICKUP_HALF: ++ case PICKUP_ONE: ++ case PLACE_ALL: ++ case PLACE_SOME: ++ case PLACE_ONE: ++ case SWAP_WITH_CURSOR: ++ this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.player.inventory.getCarried())); ++ this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(this.player.activeContainer.windowId, packetplayinwindowclick.b(), this.player.activeContainer.getSlot(packetplayinwindowclick.b()).getItem())); ++ break; ++ // Modified clicked only ++ case DROP_ALL_SLOT: ++ case DROP_ONE_SLOT: ++ this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(this.player.activeContainer.windowId, packetplayinwindowclick.b(), this.player.activeContainer.getSlot(packetplayinwindowclick.b()).getItem())); ++ break; ++ // Modified cursor only ++ case DROP_ALL_CURSOR: ++ case DROP_ONE_CURSOR: ++ case CLONE_STACK: ++ this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, this.player.inventory.getCarried())); ++ break; ++ // Nothing ++ case NOTHING: ++ break; ++ } ++ return; ++ } ++ } ++ // CraftBukkit end + + if (ItemStack.matches(packetplayinwindowclick.e(), itemstack)) { + this.player.playerConnection.sendPacket(new PacketPlayOutTransaction(packetplayinwindowclick.a(), packetplayinwindowclick.d(), true)); +@@ -772,8 +1625,50 @@ + } + + boolean flag1 = packetplayinsetcreativeslot.a() >= 1 && packetplayinsetcreativeslot.a() < 36 + PlayerInventory.getHotbarSize(); +- boolean flag2 = itemstack == null || itemstack.getItem() != null; ++ // CraftBukkit - Add invalidItems check ++ boolean flag2 = itemstack == null || itemstack.getItem() != null && !invalidItems.contains(Item.getId(itemstack.getItem())); + boolean flag3 = itemstack == null || itemstack.getData() >= 0 && itemstack.count <= 64 && itemstack.count > 0; ++ ++ ++ // CraftBukkit start - Call click event ++ if (flag || (flag1 && !ItemStack.matches(this.player.defaultContainer.getSlot(packetplayinsetcreativeslot.a()).getItem(), packetplayinsetcreativeslot.getItemStack()))) { // Insist on valid slot ++ ++ org.bukkit.entity.HumanEntity player = this.player.getBukkitEntity(); ++ InventoryView inventory = new CraftInventoryView(player, player.getInventory(), this.player.defaultContainer); ++ org.bukkit.inventory.ItemStack item = CraftItemStack.asBukkitCopy(packetplayinsetcreativeslot.getItemStack()); ++ ++ SlotType type = SlotType.QUICKBAR; ++ if (flag) { ++ type = SlotType.OUTSIDE; ++ } else if (packetplayinsetcreativeslot.a() < 36) { ++ if (packetplayinsetcreativeslot.a() >= 5 && packetplayinsetcreativeslot.a() < 9) { ++ type = SlotType.ARMOR; ++ } else { ++ type = SlotType.CONTAINER; ++ } ++ } ++ InventoryCreativeEvent event = new InventoryCreativeEvent(inventory, type, flag ? -999 : packetplayinsetcreativeslot.a(), item); ++ server.getPluginManager().callEvent(event); ++ ++ itemstack = CraftItemStack.asNMSCopy(event.getCursor()); ++ ++ switch (event.getResult()) { ++ case ALLOW: ++ // Plugin cleared the id / stacksize checks ++ flag2 = flag3 = true; ++ break; ++ case DEFAULT: ++ break; ++ case DENY: ++ // Reset the slot ++ if (packetplayinsetcreativeslot.a() >= 0) { ++ this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(this.player.defaultContainer.windowId, packetplayinsetcreativeslot.a(), this.player.defaultContainer.getSlot(packetplayinsetcreativeslot.a()).getItem())); ++ this.player.playerConnection.sendPacket(new PacketPlayOutSetSlot(-1, -1, null)); ++ } ++ return; ++ } ++ } ++ // CraftBukkit end + + if (flag1 && flag2 && flag3) { + if (itemstack == null) { +@@ -796,6 +1691,7 @@ + } + + public void a(PacketPlayInTransaction packetplayintransaction) { ++ if (this.player.dead) return; // CraftBukkit + PlayerConnectionUtils.ensureMainThread(packetplayintransaction, this, this.player.u()); + Short oshort = (Short) this.n.get(this.player.activeContainer.windowId); + +@@ -806,6 +1702,7 @@ + } + + public void a(PacketPlayInUpdateSign packetplayinupdatesign) { ++ if (this.player.dead) return; // CraftBukkit + PlayerConnectionUtils.ensureMainThread(packetplayinupdatesign, this, this.player.u()); + this.player.z(); + WorldServer worldserver = this.minecraftServer.getWorldServer(this.player.dimension); +@@ -822,10 +1719,24 @@ + + if (!tileentitysign.b() || tileentitysign.c() != this.player) { + this.minecraftServer.warning("Player " + this.player.getName() + " just tried to change non-editable sign"); ++ this.sendPacket(new PacketPlayOutUpdateSign(tileentity.world, packetplayinupdatesign.a(), tileentitysign.lines)); // CraftBukkit + return; + } + +- System.arraycopy(packetplayinupdatesign.b(), 0, tileentitysign.lines, 0, 4); ++ // CraftBukkit start ++ Player player = this.server.getPlayer(this.player); ++ int x = packetplayinupdatesign.a().getX(); ++ int y = packetplayinupdatesign.a().getY(); ++ int z = packetplayinupdatesign.a().getZ(); ++ SignChangeEvent event = new SignChangeEvent((org.bukkit.craftbukkit.block.CraftBlock) player.getWorld().getBlockAt(x, y, z), this.server.getPlayer(this.player), org.bukkit.craftbukkit.block.CraftSign.revertComponents(packetplayinupdatesign.b())); ++ this.server.getPluginManager().callEvent(event); ++ ++ if (!event.isCancelled()) { ++ System.arraycopy(org.bukkit.craftbukkit.block.CraftSign.sanitizeLines(event.getLines()), 0, tileentitysign.lines, 0, 4); ++ tileentitysign.isEditable = false; ++ } ++ // System.arraycopy(packetplayinupdatesign.b(), 0, tileentitysign.lines, 0, 4); ++ // CraftBukkit end + tileentitysign.update(); + worldserver.notify(blockposition); + } +@@ -847,11 +1758,28 @@ + + public void a(PacketPlayInAbilities packetplayinabilities) { + PlayerConnectionUtils.ensureMainThread(packetplayinabilities, this, this.player.u()); +- this.player.abilities.isFlying = packetplayinabilities.isFlying() && this.player.abilities.canFly; ++ // CraftBukkit start ++ if (this.player.abilities.canFly && this.player.abilities.isFlying != packetplayinabilities.isFlying()) { ++ PlayerToggleFlightEvent event = new PlayerToggleFlightEvent(this.server.getPlayer(this.player), packetplayinabilities.isFlying()); ++ this.server.getPluginManager().callEvent(event); ++ if (!event.isCancelled()) { ++ this.player.abilities.isFlying = packetplayinabilities.isFlying(); // Actually set the player's flying status ++ } else { ++ this.player.updateAbilities(); // Tell the player their ability was reverted ++ } ++ } ++ // CraftBukkit end + } + + public void a(PacketPlayInTabComplete packetplayintabcomplete) { + PlayerConnectionUtils.ensureMainThread(packetplayintabcomplete, this, this.player.u()); ++ // CraftBukkit start ++ if (chatSpamField.addAndGet(this, 20) > 200 && !this.minecraftServer.getPlayerList().isOp(this.player.getProfile())) { ++ this.disconnect("disconnect.spam"); ++ return; ++ } ++ // CraftBukkit end ++ + ArrayList arraylist = Lists.newArrayList(); + Iterator iterator = this.minecraftServer.tabCompleteCommand(this.player, packetplayintabcomplete.a(), packetplayintabcomplete.b()).iterator(); + +@@ -891,13 +1819,15 @@ + itemstack1 = this.player.inventory.getItemInHand(); + if (itemstack1 != null) { + if (itemstack.getItem() == Items.WRITABLE_BOOK && itemstack.getItem() == itemstack1.getItem()) { +- itemstack1.a("pages", (NBTBase) itemstack.getTag().getList("pages", 8)); ++ // itemstack1.a("pages", (NBTBase) itemstack.getTag().getList("pages", 8)); ++ CraftEventFactory.handleEditBookEvent(player, itemstack); // CraftBukkit + } + + return; + } + } catch (Exception exception) { + PlayerConnection.c.error("Couldn\'t handle book info", exception); ++ this.disconnect("Invalid book data!"); // CraftBukkit + return; + } finally { + packetdataserializer.release(); +@@ -909,27 +1839,31 @@ + + try { + itemstack = packetdataserializer.i(); +- if (itemstack == null) { +- return; +- } ++ if (itemstack != null) { ++ if (!ItemWrittenBook.b(itemstack.getTag())) { ++ throw new IOException("Invalid book tag!"); ++ } + +- if (!ItemWrittenBook.b(itemstack.getTag())) { +- throw new IOException("Invalid book tag!"); +- } ++ itemstack1 = this.player.inventory.getItemInHand(); ++ if (itemstack1 == null) { ++ return; ++ } + +- itemstack1 = this.player.inventory.getItemInHand(); +- if (itemstack1 != null) { + if (itemstack.getItem() == Items.WRITTEN_BOOK && itemstack1.getItem() == Items.WRITABLE_BOOK) { +- itemstack1.a("author", (NBTBase) (new NBTTagString(this.player.getName()))); +- itemstack1.a("title", (NBTBase) (new NBTTagString(itemstack.getTag().getString("title")))); +- itemstack1.a("pages", (NBTBase) itemstack.getTag().getList("pages", 8)); +- itemstack1.setItem(Items.WRITTEN_BOOK); ++ // CraftBukkit start ++ // itemstack1.a("author", (NBTBase) (new NBTTagString(this.player.getName()))); ++ // itemstack1.a("title", (NBTBase) (new NBTTagString(itemstack.getTag().getString("title")))); ++ // itemstack1.a("pages", (NBTBase) itemstack.getTag().getList("pages", 8)); ++ // itemstack1.setItem(Items.WRITTEN_BOOK); ++ CraftEventFactory.handleEditBookEvent(player, itemstack); ++ // CraftBukkit end + } + + return; + } + } catch (Exception exception1) { + PlayerConnection.c.error("Couldn\'t sign book", exception1); ++ this.disconnect("Invalid book data!"); // CraftBukkit + return; + } finally { + packetdataserializer.release(); +@@ -946,6 +1880,7 @@ + } + } catch (Exception exception2) { + PlayerConnection.c.error("Couldn\'t select trade", exception2); ++ this.disconnect("Invalid trade data!"); // CraftBukkit + } + } else if ("MC|AdvCdm".equals(packetplayincustompayload.a())) { + if (!this.minecraftServer.getEnableCommandBlock()) { +@@ -986,6 +1921,7 @@ + } + } catch (Exception exception3) { + PlayerConnection.c.error("Couldn\'t set command block", exception3); ++ this.disconnect("Invalid CommandBlock data!"); // CraftBukkit + } finally { + packetdataserializer.release(); + } +@@ -1011,6 +1947,7 @@ + } + } catch (Exception exception4) { + PlayerConnection.c.error("Couldn\'t set beacon", exception4); ++ this.disconnect("Invalid beacon data!"); // CraftBukkit + } + } + } else if ("MC|ItemName".equals(packetplayincustompayload.a()) && this.player.activeContainer instanceof ContainerAnvil) { +@@ -1026,6 +1963,27 @@ + containeranvil.a(""); + } + } ++ // CraftBukkit start ++ else if (packetplayincustompayload.a().equals("REGISTER")) { ++ String channels = packetplayincustompayload.b().toString(com.google.common.base.Charsets.UTF_8); ++ for (String channel : channels.split("\0")) { ++ getPlayer().addChannel(channel); ++ } ++ } else if (packetplayincustompayload.a().equals("UNREGISTER")) { ++ String channels = packetplayincustompayload.b().toString(com.google.common.base.Charsets.UTF_8); ++ for (String channel : channels.split("\0")) { ++ getPlayer().removeChannel(channel); ++ } ++ } else { ++ byte[] data = new byte[packetplayincustompayload.b().readableBytes()]; ++ packetplayincustompayload.b().readBytes(data); ++ server.getMessenger().dispatchIncomingMessage(player.getBukkitEntity(), packetplayincustompayload.a(), data); ++ } ++ // CraftBukkit end ++ } + ++ // CraftBukkit start - Add "isDisconnected" method ++ public final boolean isDisconnected() { ++ return !this.player.joining && !NetworkManager.a(this.networkManager).config().isAutoRead(); + } + } diff --git a/nms-patches/PlayerDatFileConverter.patch b/nms-patches/PlayerDatFileConverter.patch new file mode 100644 index 00000000..a1286509 --- /dev/null +++ b/nms-patches/PlayerDatFileConverter.patch @@ -0,0 +1,33 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PlayerDatFileConverter.java 2014-11-27 08:59:46.857421159 +1100 ++++ src/main/java/net/minecraft/server/PlayerDatFileConverter.java 2014-11-27 08:42:10.168850880 +1100 +@@ -47,6 +47,30 @@ + private void a(File file, String s, String s1) { + File file1 = new File(this.d, s + ".dat"); + File file2 = new File(file, s1 + ".dat"); ++ ++ // CraftBukkit start - Use old file name to seed lastKnownName ++ NBTTagCompound root = null; ++ ++ try { ++ root = NBTCompressedStreamTools.a(new java.io.FileInputStream(file1)); ++ } catch (Exception exception) { ++ exception.printStackTrace(); ++ } ++ ++ if (root != null) { ++ if (!root.hasKey("bukkit")) { ++ root.set("bukkit", new NBTTagCompound()); ++ } ++ NBTTagCompound data = root.getCompound("bukkit"); ++ data.setString("lastKnownName", s); ++ ++ try { ++ NBTCompressedStreamTools.a(root, new java.io.FileOutputStream(file2)); ++ } catch (Exception exception) { ++ exception.printStackTrace(); ++ } ++ } ++ // CraftBukkit end + + NameReferencingFileConverter.a(file); + if (!file1.renameTo(file2)) { diff --git a/nms-patches/PlayerInteractManager.patch b/nms-patches/PlayerInteractManager.patch new file mode 100644 index 00000000..ac0ab569 --- /dev/null +++ b/nms-patches/PlayerInteractManager.patch @@ -0,0 +1,289 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PlayerInteractManager.java 2014-11-27 08:59:46.857421159 +1100 ++++ src/main/java/net/minecraft/server/PlayerInteractManager.java 2014-11-27 08:42:10.108850996 +1100 +@@ -1,5 +1,13 @@ + package net.minecraft.server; + ++// CraftBukkit start ++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; +@@ -50,7 +58,7 @@ + } + + public void a() { +- ++this.currentTick; ++ this.currentTick = MinecraftServer.currentTick; // CraftBukkit; + float f; + int i; + +@@ -95,6 +103,19 @@ + } + + 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()); ++ if (event.isCancelled()) { ++ // Let the client know the block still exists ++ ((EntityPlayer) this.player).playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); ++ // 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); +@@ -121,15 +142,49 @@ + } + } + +- this.world.douseFire((EntityHuman) null, blockposition, enumdirection); ++ // this.world.douseFire((EntityHuman) null, blockposition, enumdirection); // CraftBukkit - Moved down + this.lastDigTick = this.currentTick; + float f = 1.0F; + +- if (block.getMaterial() != Material.AIR) { ++ // 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) == 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 (block.getMaterial() != Material.AIR) { + block.attack(this.world, blockposition, this.player); + f = block.getDamage(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 (block.getMaterial() != Material.AIR && f >= 1.0F) { + this.breakBlock(blockposition); + } else { +@@ -146,6 +201,7 @@ + + public void a(BlockPosition blockposition) { + if (blockposition.equals(this.f)) { ++ this.currentTick = MinecraftServer.currentTick; // CraftBukkit + int i = this.currentTick - this.lastDigTick; + Block block = this.world.getType(blockposition).getBlock(); + +@@ -163,6 +219,10 @@ + this.j = this.lastDigTick; + } + } ++ // CraftBukkit start - Force block reset to client ++ } else { ++ this.player.playerConnection.sendPacket(new PacketPlayOutBlockChange(this.world, blockposition)); ++ // CraftBukkit end + } + + } +@@ -186,12 +246,72 @@ + } + + public boolean breakBlock(BlockPosition blockposition) { +- if (this.gamemode.d() && this.player.bz() != null && this.player.bz().getItem() instanceof ItemSword) { ++ // 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.d() && this.player.bz() != null && this.player.bz().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(); ++ ++ if (nmsBlock != null && !event.isCancelled() && !this.isCreative() && this.player.b(nmsBlock)) { ++ // Copied from block.a(World world, EntityHuman entityhuman, BlockPosition blockposition, IBlockData iblockdata, TileEntity tileentity) ++ if (!(nmsBlock.G() && EnchantmentManager.hasSilkTouchEnchantment(this.player))) { ++ int data = block.getData(); ++ int bonusLevel = EnchantmentManager.getBonusBlockLootEnchantmentLevel(this.player); ++ ++ 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)); ++ // 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.d() && this.player.bz() != null && this.player.bz().getItem() instanceof ItemSword) { + 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); +- ++ ++ // CraftBukkit start - Special case skulls, their item data comes from a tile entity ++ if (iblockdata.getBlock() == Blocks.SKULL && !this.isCreative()) { ++ iblockdata.getBlock().dropNaturally(world, blockposition, iblockdata, 1.0F, 0); ++ return this.c(blockposition); ++ } ++ // CraftBukkit end ++ + if (this.gamemode.c()) { + if (this.gamemode == EnumGamemode.SPECTATOR) { + return false; +@@ -229,7 +349,13 @@ + if (flag && flag1) { + iblockdata.getBlock().a(this.world, this.player, blockposition, iblockdata, tileentity); + } ++ } ++ ++ // CraftBukkit start - Drop event experience ++ if (flag && event != null) { ++ iblockdata.getBlock().dropExperience(this.world, blockposition, event.getExpToDrop()); + } ++ // CraftBukkit end + + return flag; + } +@@ -268,6 +394,7 @@ + } + + public boolean interact(EntityHuman entityhuman, World world, ItemStack itemstack, BlockPosition blockposition, EnumDirection enumdirection, float f, float f1, float f2) { ++ /* CraftBukkit start - whole method + if (this.gamemode == EnumGamemode.SPECTATOR) { + TileEntity tileentity = world.getTileEntity(blockposition); + +@@ -312,6 +439,75 @@ + return itemstack.placeItem(entityhuman, world, blockposition, enumdirection, f, f1, f2); + } + } ++ // Interract event */ ++ IBlockData blockdata = world.getType(blockposition); ++ boolean result = false; ++ 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); ++ } ++ ++ PlayerInteractEvent event = CraftEventFactory.callPlayerInteractEvent(entityhuman, Action.RIGHT_CLICK_BLOCK, blockposition, enumdirection, itemstack, cancelledBlock); ++ ++ 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) == EnumDoorHalf.LOWER; ++ ((EntityPlayer) entityhuman).playerConnection.sendPacket(new PacketPlayOutBlockChange(world, bottom ? blockposition.up() : blockposition.down())); ++ } ++ result = (event.useItemInHand() != Event.Result.ALLOW); ++ } 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).d(world, blockposition); ++ } ++ ++ if (itileinventory != null) { ++ entityhuman.openContainer(itileinventory); ++ return true; ++ } ++ } else if (tileentity instanceof IInventory) { ++ entityhuman.openContainer((IInventory) tileentity); ++ return true; ++ } ++ ++ return false; ++ } else if (!entityhuman.isSneaking() || itemstack == null) { ++ result = blockdata.getBlock().interact(world, blockposition, blockdata, entityhuman, enumdirection, f, f1, f2); ++ } ++ ++ if (itemstack != null && !result) { ++ int j1 = itemstack.getData(); ++ int k1 = itemstack.count; ++ ++ result = itemstack.placeItem(entityhuman, world, blockposition, enumdirection, f, f1, f2); ++ ++ // The item count should not decrement in Creative mode. ++ if (this.isCreative()) { ++ itemstack.setData(j1); ++ itemstack.count = k1; ++ } ++ } ++ ++ // If we have 'true' and no explicit deny *or* an explicit allow -- run the item part of the hook ++ if (itemstack != null && ((!result && event.useItemInHand() != Event.Result.DENY) || event.useItemInHand() == Event.Result.ALLOW)) { ++ if (itemstack.getItem() instanceof ItemBucket) { ++ this.useItem(entityhuman, world, itemstack); ++ } else { ++ itemstack.placeItem(entityhuman, world, blockposition, enumdirection, f, f1, f2); ++ } ++ } ++ } ++ return result; ++ // CraftBukkit end + } + + public void a(WorldServer worldserver) { diff --git a/nms-patches/PlayerInventory.patch b/nms-patches/PlayerInventory.patch new file mode 100644 index 00000000..e4b36670 --- /dev/null +++ b/nms-patches/PlayerInventory.patch @@ -0,0 +1,100 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PlayerInventory.java 2014-11-27 08:59:46.861421142 +1100 ++++ src/main/java/net/minecraft/server/PlayerInventory.java 2014-11-27 08:42:10.172850872 +1100 +@@ -2,6 +2,13 @@ + + import java.util.concurrent.Callable; + ++// CraftBukkit start ++import java.util.List; ++ ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ + public class PlayerInventory implements IInventory { + + public ItemStack[] items = new ItemStack[36]; +@@ -10,6 +17,39 @@ + public EntityHuman player; + private ItemStack f; + public boolean e; ++ ++ // CraftBukkit start - add fields and methods ++ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>(); ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() { ++ return this.items; ++ } ++ ++ public ItemStack[] getArmorContents() { ++ return this.armor; ++ } ++ ++ public void onOpen(CraftHumanEntity who) { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) { ++ transaction.remove(who); ++ } ++ ++ public List<HumanEntity> getViewers() { ++ return transaction; ++ } ++ ++ public org.bukkit.inventory.InventoryHolder getOwner() { ++ return this.player.getBukkitEntity(); ++ } ++ ++ public void setMaxStackSize(int size) { ++ maxStack = size; ++ } ++ // CraftBukkit end + + public PlayerInventory(EntityHuman entityhuman) { + this.player = entityhuman; +@@ -42,6 +82,22 @@ + + return -1; + } ++ ++ // CraftBukkit start - Watch method above! :D ++ public int canHold(ItemStack itemstack) { ++ int remains = itemstack.count; ++ for (int i = 0; i < this.items.length; ++i) { ++ if (this.items[i] == null) return itemstack.count; ++ ++ // Taken from firstPartial(ItemStack) ++ if (this.items[i] != null && this.items[i].getItem() == itemstack.getItem() && this.items[i].isStackable() && this.items[i].count < this.items[i].getMaxStackSize() && this.items[i].count < this.getMaxStackSize() && (!this.items[i].usesData() || this.items[i].getData() == itemstack.getData()) && ItemStack.equals(this.items[i], itemstack)) { ++ remains -= (this.items[i].getMaxStackSize() < this.getMaxStackSize() ? this.items[i].getMaxStackSize() : this.getMaxStackSize()) - this.items[i].count; ++ } ++ if (remains <= 0) return itemstack.count; ++ } ++ return itemstack.count - remains; ++ } ++ // CraftBukkit end + + public int getFirstEmptySlotIndex() { + for (int i = 0; i < this.items.length; ++i) { +@@ -382,7 +438,7 @@ + } + + public int getMaxStackSize() { +- return 64; ++ return maxStack; // CraftBukkit + } + + public boolean b(Block block) { +@@ -458,6 +514,11 @@ + } + + public ItemStack getCarried() { ++ // CraftBukkit start ++ if (this.f != null && this.f.count == 0) { ++ this.setCarried(null); ++ } ++ // CraftBukkit end + return this.f; + } + diff --git a/nms-patches/PlayerList.patch b/nms-patches/PlayerList.patch new file mode 100644 index 00000000..f38a5b9d --- /dev/null +++ b/nms-patches/PlayerList.patch @@ -0,0 +1,788 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PlayerList.java 2014-11-27 08:59:46.861421142 +1100 ++++ src/main/java/net/minecraft/server/PlayerList.java 2014-11-27 08:42:10.160850895 +1100 +@@ -18,6 +18,25 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.CraftServer; ++import org.bukkit.craftbukkit.CraftWorld; ++import org.bukkit.craftbukkit.chunkio.ChunkIOExecutor; ++ ++import org.bukkit.Bukkit; ++import org.bukkit.Location; ++import org.bukkit.TravelAgent; ++import org.bukkit.entity.Player; ++import org.bukkit.event.player.PlayerChangedWorldEvent; ++import org.bukkit.event.player.PlayerPortalEvent; ++import org.bukkit.event.player.PlayerJoinEvent; ++import org.bukkit.event.player.PlayerLoginEvent; ++import org.bukkit.event.player.PlayerQuitEvent; ++import org.bukkit.event.player.PlayerRespawnEvent; ++import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause; ++import org.bukkit.util.Vector; ++// CraftBukkit end ++ + public abstract class PlayerList { + + public static final File a = new File("banned-players.json"); +@@ -27,7 +46,7 @@ + private static final Logger h = LogManager.getLogger(); + private static final SimpleDateFormat i = new SimpleDateFormat("yyyy-MM-dd \'at\' HH:mm:ss z"); + private final MinecraftServer server; +- public final List players = Lists.newArrayList(); ++ public final List players = new java.util.concurrent.CopyOnWriteArrayList(); // CraftBukkit - ArrayList -> CopyOnWriteArrayList: Iterator safety + public final Map f = Maps.newHashMap(); + private final GameProfileBanList k; + private final IpBanList l; +@@ -42,7 +61,15 @@ + private boolean t; + private int u; + ++ // CraftBukkit start ++ private CraftServer cserver; ++ + public PlayerList(MinecraftServer minecraftserver) { ++ this.cserver = minecraftserver.server = new CraftServer(minecraftserver, this); ++ minecraftserver.console = org.bukkit.craftbukkit.command.ColouredConsoleSender.getInstance(); ++ minecraftserver.reader.addCompleter(new org.bukkit.craftbukkit.command.ConsoleCommandCompleter(minecraftserver.server)); ++ // CraftBukkit end ++ + this.k = new GameProfileBanList(PlayerList.a); + this.l = new IpBanList(PlayerList.b); + this.operators = new OpList(PlayerList.c); +@@ -71,7 +98,8 @@ + s1 = networkmanager.getSocketAddress().toString(); + } + +- PlayerList.h.info(entityplayer.getName() + "[" + s1 + "] logged in with entity id " + entityplayer.getId() + " at (" + entityplayer.locX + ", " + entityplayer.locY + ", " + entityplayer.locZ + ")"); ++ // CraftBukkit - Moved message to after join ++ // PlayerList.h.info(entityplayer.getName() + "[" + s1 + "] logged in with entity id " + entityplayer.getId() + " at (" + entityplayer.locX + ", " + entityplayer.locY + ", " + entityplayer.locZ + ")"); + WorldServer worldserver = this.server.getWorldServer(entityplayer.dimension); + WorldData worlddata = worldserver.getWorldData(); + BlockPosition blockposition = worldserver.getSpawn(); +@@ -80,6 +108,7 @@ + PlayerConnection playerconnection = new PlayerConnection(this.server, networkmanager, entityplayer); + + playerconnection.sendPacket(new PacketPlayOutLogin(entityplayer.getId(), entityplayer.playerInteractManager.getGameMode(), worlddata.isHardcore(), worldserver.worldProvider.getDimension(), worldserver.getDifficulty(), this.getMaxPlayers(), worlddata.getType(), worldserver.getGameRules().getBoolean("reducedDebugInfo"))); ++ entityplayer.getBukkitEntity().sendSupportedChannels(); // CraftBukkit + playerconnection.sendPacket(new PacketPlayOutCustomPayload("MC|Brand", (new PacketDataSerializer(Unpooled.buffer())).a(this.getServer().getServerModName()))); + playerconnection.sendPacket(new PacketPlayOutServerDifficulty(worlddata.y(), worlddata.z())); + playerconnection.sendPacket(new PacketPlayOutSpawnPosition(blockposition)); +@@ -89,6 +118,7 @@ + entityplayer.getStatisticManager().updateStatistics(entityplayer); + this.sendScoreboard((ScoreboardServer) worldserver.getScoreboard(), entityplayer); + this.server.aF(); ++ /* CraftBukkit start - login message is handled in the event + ChatMessage chatmessage; + + if (!entityplayer.getName().equalsIgnoreCase(s)) { +@@ -99,7 +129,9 @@ + + chatmessage.getChatModifier().setColor(EnumChatFormat.YELLOW); + this.sendMessage(chatmessage); ++ // CraftBukkit end */ + this.onPlayerJoin(entityplayer); ++ worldserver = server.getWorldServer(entityplayer.dimension); // CraftBukkit - Update in case join event changed it + playerconnection.a(entityplayer.locX, entityplayer.locY, entityplayer.locZ, entityplayer.yaw, entityplayer.pitch); + this.b(entityplayer, worldserver); + if (this.server.getResourcePack().length() > 0) { +@@ -126,6 +158,8 @@ + } + } + ++ // CraftBukkit - Moved from above, added world ++ PlayerList.h.info(entityplayer.getName() + "[" + s1 + "] logged in with entity id " + entityplayer.getId() + " at ([" + entityplayer.world.worldData.getName() + "] " + entityplayer.locX + ", " + entityplayer.locY + ", " + entityplayer.locZ + ")"); + } + + public void sendScoreboard(ScoreboardServer scoreboardserver, EntityPlayer entityplayer) { +@@ -158,6 +192,7 @@ + } + + public void setPlayerFileData(WorldServer[] aworldserver) { ++ if (playerFileData != null) return; // CraftBukkit + this.playerFileData = aworldserver[0].getDataManager().getPlayerFileData(); + aworldserver[0].af().a((IWorldBorderListener) (new WorldBorderListener(this))); + } +@@ -178,7 +213,7 @@ + } + + public NBTTagCompound a(EntityPlayer entityplayer) { +- NBTTagCompound nbttagcompound = this.server.worldServer[0].getWorldData().i(); ++ NBTTagCompound nbttagcompound = this.server.worlds.get(0).getWorldData().i(); // CraftBukkit + NBTTagCompound nbttagcompound1; + + if (entityplayer.getName().equals(this.server.R()) && nbttagcompound != null) { +@@ -205,30 +240,69 @@ + public void onPlayerJoin(EntityPlayer entityplayer) { + this.players.add(entityplayer); + this.f.put(entityplayer.getUniqueID(), entityplayer); +- this.sendAll(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, new EntityPlayer[] { entityplayer})); ++ // this.sendAll(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, new EntityPlayer[] { entityplayer})); // CraftBukkit - replaced with loop below + WorldServer worldserver = this.server.getWorldServer(entityplayer.dimension); + +- worldserver.addEntity(entityplayer); +- this.a(entityplayer, (WorldServer) null); ++ // CraftBukkit start ++ PlayerJoinEvent playerJoinEvent = new PlayerJoinEvent(cserver.getPlayer(entityplayer), "\u00A7e" + entityplayer.getName() + " joined the game."); ++ cserver.getPluginManager().callEvent(playerJoinEvent); ++ ++ String joinMessage = playerJoinEvent.getJoinMessage(); ++ ++ if (joinMessage != null && joinMessage.length() > 0) { ++ for (IChatBaseComponent line : org.bukkit.craftbukkit.util.CraftChatMessage.fromString(joinMessage)) { ++ server.getPlayerList().sendAll(new PacketPlayOutChat(line)); ++ } ++ } ++ ++ ChunkIOExecutor.adjustPoolSize(getPlayerCount()); ++ // CraftBukkit end ++ ++ // CraftBukkit start - sendAll above replaced with this loop ++ PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, entityplayer); + + for (int i = 0; i < this.players.size(); ++i) { + EntityPlayer entityplayer1 = (EntityPlayer) this.players.get(i); + ++ if (entityplayer1.getBukkitEntity().canSee(entityplayer.getBukkitEntity())) { ++ entityplayer1.playerConnection.sendPacket(packet); ++ } ++ ++ if (!entityplayer.getBukkitEntity().canSee(entityplayer1.getBukkitEntity())) { ++ continue; ++ } ++ + entityplayer.playerConnection.sendPacket(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.ADD_PLAYER, new EntityPlayer[] { entityplayer1})); + } ++ // CraftBukkit end + ++ // CraftBukkit start - Only add if the player wasn't moved in the event ++ if (entityplayer.world == worldserver && !worldserver.players.contains(entityplayer)) { ++ worldserver.addEntity(entityplayer); ++ this.a(entityplayer, (WorldServer) null); ++ } ++ // CraftBukkit end + } + + public void d(EntityPlayer entityplayer) { + entityplayer.u().getPlayerChunkMap().movePlayer(entityplayer); + } + +- public void disconnect(EntityPlayer entityplayer) { ++ public String disconnect(EntityPlayer entityplayer) { // CraftBukkit - return string + entityplayer.b(StatisticList.f); ++ ++ // CraftBukkit start - Quitting must be before we do final save of data, in case plugins need to modify it ++ org.bukkit.craftbukkit.event.CraftEventFactory.handleInventoryCloseEvent(entityplayer); ++ ++ PlayerQuitEvent playerQuitEvent = new PlayerQuitEvent(cserver.getPlayer(entityplayer), "\u00A7e" + entityplayer.getName() + " left the game."); ++ cserver.getPluginManager().callEvent(playerQuitEvent); ++ entityplayer.getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage()); ++ // CraftBukkit end ++ + this.savePlayerFile(entityplayer); + WorldServer worldserver = entityplayer.u(); + +- if (entityplayer.vehicle != null) { ++ if (entityplayer.vehicle != null && !(entityplayer.vehicle instanceof EntityPlayer)) { // CraftBukkit - Don't remove players + worldserver.removeEntity(entityplayer.vehicle); + PlayerList.h.debug("removing player mount"); + } +@@ -238,13 +312,40 @@ + this.players.remove(entityplayer); + this.f.remove(entityplayer.getUniqueID()); + this.o.remove(entityplayer.getUniqueID()); +- this.sendAll(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER, new EntityPlayer[] { entityplayer})); ++ // CraftBukkit start ++ // this.sendAll(new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER, new EntityPlayer[] { entityplayer})); ++ PacketPlayOutPlayerInfo packet = new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.REMOVE_PLAYER, entityplayer); ++ for (int i = 0; i < players.size(); i++) { ++ EntityPlayer entityplayer1 = (EntityPlayer) this.players.get(i); ++ ++ if (entityplayer1.getBukkitEntity().canSee(entityplayer.getBukkitEntity())) { ++ entityplayer1.playerConnection.sendPacket(packet); ++ } else { ++ entityplayer1.getBukkitEntity().removeDisconnectingPlayer(entityplayer.getBukkitEntity()); ++ } ++ } ++ // This removes the scoreboard (and player reference) for the specific player in the manager ++ cserver.getScoreboardManager().removePlayer(entityplayer.getBukkitEntity()); ++ // CraftBukkit end ++ ++ ChunkIOExecutor.adjustPoolSize(this.getPlayerCount()); // CraftBukkit ++ ++ return playerQuitEvent.getQuitMessage(); // CraftBukkit + } + +- public String attemptLogin(SocketAddress socketaddress, GameProfile gameprofile) { ++ // CraftBukkit start - Whole method, SocketAddress to LoginListener, added hostname to signature, return EntityPlayer ++ public EntityPlayer attemptLogin(LoginListener loginlistener, GameProfile gameprofile, String hostname) { ++ // Instead of kicking then returning, we need to store the kick reason ++ // in the event, check with plugins to see if it's ok, and THEN kick ++ // depending on the outcome. ++ SocketAddress socketaddress = loginlistener.networkManager.getSocketAddress(); ++ ++ EntityPlayer entity = new EntityPlayer(server, server.getWorldServer(0), gameprofile, new PlayerInteractManager(server.getWorldServer(0))); ++ Player player = entity.getBukkitEntity(); ++ PlayerLoginEvent event = new PlayerLoginEvent(player, hostname, ((java.net.InetSocketAddress) socketaddress).getAddress()); + String s; + +- if (this.k.isBanned(gameprofile)) { ++ if (getProfileBans().isBanned(gameprofile) && !getProfileBans().get(gameprofile).hasExpired()) { + GameProfileBanEntry gameprofilebanentry = (GameProfileBanEntry) this.k.get(gameprofile); + + s = "You are banned from this server!\nReason: " + gameprofilebanentry.getReason(); +@@ -252,10 +353,12 @@ + s = s + "\nYour ban will be removed on " + PlayerList.i.format(gameprofilebanentry.getExpires()); + } + +- return s; ++ // return s; ++ event.disallow(PlayerLoginEvent.Result.KICK_BANNED, s); + } else if (!this.isWhitelisted(gameprofile)) { +- return "You are not white-listed on this server!"; +- } else if (this.l.isBanned(socketaddress)) { ++ // return "You are not white-listed on this server!"; ++ event.disallow(PlayerLoginEvent.Result.KICK_WHITELIST, "You are not white-listed on this server!"); ++ } else if (getIPBans().isBanned(socketaddress) && !getIPBans().get(socketaddress).hasExpired()) { + IpBanEntry ipbanentry = this.l.get(socketaddress); + + s = "Your IP address is banned from this server!\nReason: " + ipbanentry.getReason(); +@@ -263,13 +366,24 @@ + s = s + "\nYour ban will be removed on " + PlayerList.i.format(ipbanentry.getExpires()); + } + +- return s; ++ // return s; ++ event.disallow(PlayerLoginEvent.Result.KICK_BANNED, s); + } else { +- return this.players.size() >= this.maxPlayers ? "The server is full!" : null; ++ // return this.players.size() >= this.maxPlayers ? "The server is full!" : null; ++ if (this.players.size() >= this.maxPlayers) { ++ event.disallow(PlayerLoginEvent.Result.KICK_FULL, "The server is full"); ++ } ++ } ++ ++ cserver.getPluginManager().callEvent(event); ++ if (event.getResult() != PlayerLoginEvent.Result.ALLOWED) { ++ loginlistener.disconnect(event.getKickMessage()); ++ return null; + } ++ return entity; + } + +- public EntityPlayer processLogin(GameProfile gameprofile) { ++ public EntityPlayer processLogin(GameProfile gameprofile, EntityPlayer player) { // CraftBukkit - added EntityPlayer + UUID uuid = EntityHuman.a(gameprofile); + ArrayList arraylist = Lists.newArrayList(); + +@@ -289,6 +403,7 @@ + entityplayer.playerConnection.disconnect("You logged in from another location"); + } + ++ /* CraftBukkit start + Object object; + + if (this.server.W()) { +@@ -298,17 +413,25 @@ + } + + return new EntityPlayer(this.server, this.server.getWorldServer(0), gameprofile, (PlayerInteractManager) object); ++ // */ ++ return player; ++ // CraftBukkit end + } + ++ // CraftBukkit start + public EntityPlayer moveToWorld(EntityPlayer entityplayer, int i, boolean flag) { ++ return this.moveToWorld(entityplayer, i, flag, null, true); ++ } ++ public EntityPlayer moveToWorld(EntityPlayer entityplayer, int i, boolean flag, Location location, boolean avoidSuffocation) { + entityplayer.u().getTracker().untrackPlayer(entityplayer); +- entityplayer.u().getTracker().untrackEntity(entityplayer); ++ // entityplayer.u().getTracker().untrackEntity(entityplayer); // CraftBukkit + entityplayer.u().getPlayerChunkMap().removePlayer(entityplayer); + this.players.remove(entityplayer); + this.server.getWorldServer(entityplayer.dimension).removeEntity(entityplayer); + BlockPosition blockposition = entityplayer.getBed(); + boolean flag1 = entityplayer.isRespawnForced(); + ++ /* CraftBukkit start + entityplayer.dimension = i; + Object object; + +@@ -319,80 +442,270 @@ + } + + EntityPlayer entityplayer1 = new EntityPlayer(this.server, this.server.getWorldServer(entityplayer.dimension), entityplayer.getProfile(), (PlayerInteractManager) object); ++ // */ ++ EntityPlayer entityplayer1 = entityplayer; ++ org.bukkit.World fromWorld = entityplayer.getBukkitEntity().getWorld(); ++ entityplayer.viewingCredits = false; ++ // CraftBukkit end + + entityplayer1.playerConnection = entityplayer.playerConnection; + entityplayer1.copyTo(entityplayer, flag); + entityplayer1.d(entityplayer.getId()); + entityplayer1.o(entityplayer); +- WorldServer worldserver = this.server.getWorldServer(entityplayer.dimension); ++ // WorldServer worldserver = this.server.getWorldServer(entityplayer.dimension); // CraftBukkit - handled later + +- this.a(entityplayer1, entityplayer, worldserver); ++ // this.a(entityplayer1, entityplayer, worldserver); // CraftBukkit - removed + BlockPosition blockposition1; + +- if (blockposition != null) { +- blockposition1 = EntityHuman.getBed(this.server.getWorldServer(entityplayer.dimension), blockposition, flag1); +- if (blockposition1 != null) { +- entityplayer1.setPositionRotation((double) ((float) blockposition1.getX() + 0.5F), (double) ((float) blockposition1.getY() + 0.1F), (double) ((float) blockposition1.getZ() + 0.5F), 0.0F, 0.0F); +- entityplayer1.setRespawnPosition(blockposition, flag1); +- } else { +- entityplayer1.playerConnection.sendPacket(new PacketPlayOutGameStateChange(0, 0.0F)); ++ // CraftBukkit start - fire PlayerRespawnEvent ++ if (location == null) { ++ boolean isBedSpawn = false; ++ CraftWorld cworld = (CraftWorld) this.server.server.getWorld(entityplayer.spawnWorld); ++ if (cworld != null && blockposition != null) { ++ blockposition1 = EntityHuman.getBed(cworld.getHandle(), blockposition, flag1); ++ if (blockposition1 != null) { ++ isBedSpawn = true; ++ location = new Location(cworld, blockposition1.getX() + 0.5, blockposition1.getY(), blockposition1.getZ() + 0.5); ++ } else { ++ entityplayer1.setRespawnPosition(null, true); ++ entityplayer1.playerConnection.sendPacket(new PacketPlayOutGameStateChange(0, 0.0F)); ++ } + } ++ ++ if (location == null) { ++ cworld = (CraftWorld) this.server.server.getWorlds().get(0); ++ blockposition = cworld.getHandle().getSpawn(); ++ location = new Location(cworld, blockposition.getX() + 0.5, blockposition.getY(), blockposition.getZ() + 0.5); ++ } ++ ++ Player respawnPlayer = cserver.getPlayer(entityplayer1); ++ PlayerRespawnEvent respawnEvent = new PlayerRespawnEvent(respawnPlayer, location, isBedSpawn); ++ cserver.getPluginManager().callEvent(respawnEvent); ++ ++ location = respawnEvent.getRespawnLocation(); ++ entityplayer.reset(); ++ } else { ++ location.setWorld(server.getWorldServer(i).getWorld()); + } ++ WorldServer worldserver = ((CraftWorld) location.getWorld()).getHandle(); ++ entityplayer1.setLocation(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch()); ++ // CraftBukkit end + + worldserver.chunkProviderServer.getChunkAt((int) entityplayer1.locX >> 4, (int) entityplayer1.locZ >> 4); + +- while (!worldserver.getCubes(entityplayer1, entityplayer1.getBoundingBox()).isEmpty() && entityplayer1.locY < 256.0D) { ++ while (avoidSuffocation && !worldserver.getCubes(entityplayer1, entityplayer1.getBoundingBox()).isEmpty() && entityplayer1.locY < 256.0D) { // CraftBukkit + entityplayer1.setPosition(entityplayer1.locX, entityplayer1.locY + 1.0D, entityplayer1.locZ); + } + +- entityplayer1.playerConnection.sendPacket(new PacketPlayOutRespawn(entityplayer1.dimension, entityplayer1.world.getDifficulty(), entityplayer1.world.getWorldData().getType(), entityplayer1.playerInteractManager.getGameMode())); ++ // CraftBukkit start ++ byte actualDimension = (byte) (worldserver.getWorld().getEnvironment().getId()); ++ // Force the client to refresh their chunk cache ++ entityplayer1.playerConnection.sendPacket(new PacketPlayOutRespawn((byte) (actualDimension >= 0 ? -1 : 0), worldserver.getDifficulty(), worldserver.getWorldData().getType(), entityplayer.playerInteractManager.getGameMode())); ++ entityplayer1.playerConnection.sendPacket(new PacketPlayOutRespawn(actualDimension, worldserver.getDifficulty(), worldserver.getWorldData().getType(), entityplayer1.playerInteractManager.getGameMode())); ++ entityplayer1.spawnIn(worldserver); ++ entityplayer1.dead = false; ++ entityplayer1.playerConnection.teleport(new Location(worldserver.getWorld(), entityplayer1.locX, entityplayer1.locY, entityplayer1.locZ, entityplayer1.yaw, entityplayer1.pitch)); ++ entityplayer1.setSneaking(false); + blockposition1 = worldserver.getSpawn(); +- entityplayer1.playerConnection.a(entityplayer1.locX, entityplayer1.locY, entityplayer1.locZ, entityplayer1.yaw, entityplayer1.pitch); ++ // entityplayer1.playerConnection.a(entityplayer1.locX, entityplayer1.locY, entityplayer1.locZ, entityplayer1.yaw, entityplayer1.pitch); ++ // CraftBukkit end + entityplayer1.playerConnection.sendPacket(new PacketPlayOutSpawnPosition(blockposition1)); + entityplayer1.playerConnection.sendPacket(new PacketPlayOutExperience(entityplayer1.exp, entityplayer1.expTotal, entityplayer1.expLevel)); + this.b(entityplayer1, worldserver); +- worldserver.getPlayerChunkMap().addPlayer(entityplayer1); +- worldserver.addEntity(entityplayer1); +- this.players.add(entityplayer1); +- this.f.put(entityplayer1.getUniqueID(), entityplayer1); +- entityplayer1.syncInventory(); ++ if (!entityplayer.playerConnection.isDisconnected()) { ++ worldserver.getPlayerChunkMap().addPlayer(entityplayer1); ++ worldserver.addEntity(entityplayer1); ++ this.players.add(entityplayer1); ++ this.f.put(entityplayer1.getUniqueID(), entityplayer1); ++ } ++ // Added from changeDimension ++ updateClient(entityplayer); // Update health, etc... ++ entityplayer.updateAbilities(); ++ for (Object o1 : entityplayer.getEffects()) { ++ MobEffect mobEffect = (MobEffect) o1; ++ entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEffect(entityplayer.getId(), mobEffect)); ++ } ++ // entityplayer1.syncInventory(); ++ // CraftBukkit end + entityplayer1.setHealth(entityplayer1.getHealth()); ++ ++ // CraftBukkit start ++ // Don't fire on respawn ++ if (fromWorld != location.getWorld()) { ++ PlayerChangedWorldEvent event = new PlayerChangedWorldEvent(entityplayer.getBukkitEntity(), fromWorld); ++ server.server.getPluginManager().callEvent(event); ++ } ++ ++ // Save player file again if they were disconnected ++ if (entityplayer.playerConnection.isDisconnected()) { ++ this.savePlayerFile(entityplayer); ++ } ++ // CraftBukkit end ++ + return entityplayer1; + } + +- public void changeDimension(EntityPlayer entityplayer, int i) { +- int j = entityplayer.dimension; +- WorldServer worldserver = this.server.getWorldServer(entityplayer.dimension); ++ // CraftBukkit start - Replaced the standard handling of portals with a more customised method. ++ public void changeDimension(EntityPlayer entityplayer, int i, TeleportCause cause) { ++ WorldServer exitWorld = null; ++ if (entityplayer.dimension < CraftWorld.CUSTOM_DIMENSION_OFFSET) { // plugins must specify exit from custom Bukkit worlds ++ // only target existing worlds (compensate for allow-nether/allow-end as false) ++ for (WorldServer world : this.server.worlds) { ++ if (world.dimension == i) { ++ exitWorld = world; ++ } ++ } ++ } + +- entityplayer.dimension = i; +- WorldServer worldserver1 = this.server.getWorldServer(entityplayer.dimension); ++ Location enter = entityplayer.getBukkitEntity().getLocation(); ++ Location exit = null; ++ boolean useTravelAgent = false; // don't use agent for custom worlds or return from THE_END ++ if (exitWorld != null) { ++ if ((cause == TeleportCause.END_PORTAL) && (i == 0)) { ++ // THE_END -> NORMAL; use bed if available, otherwise default spawn ++ exit = ((org.bukkit.craftbukkit.entity.CraftPlayer) entityplayer.getBukkitEntity()).getBedSpawnLocation(); ++ if (exit == null || ((CraftWorld) exit.getWorld()).getHandle().dimension != 0) { ++ exit = exitWorld.getWorld().getSpawnLocation(); ++ } ++ } else { ++ // NORMAL <-> NETHER or NORMAL -> THE_END ++ exit = this.calculateTarget(enter, exitWorld); ++ useTravelAgent = true; ++ } ++ } + +- entityplayer.playerConnection.sendPacket(new PacketPlayOutRespawn(entityplayer.dimension, entityplayer.world.getDifficulty(), entityplayer.world.getWorldData().getType(), entityplayer.playerInteractManager.getGameMode())); +- worldserver.removeEntity(entityplayer); +- entityplayer.dead = false; +- this.changeWorld(entityplayer, j, worldserver, worldserver1); +- this.a(entityplayer, worldserver); +- entityplayer.playerConnection.a(entityplayer.locX, entityplayer.locY, entityplayer.locZ, entityplayer.yaw, entityplayer.pitch); +- entityplayer.playerInteractManager.a(worldserver1); +- this.b(entityplayer, worldserver1); +- this.updateClient(entityplayer); +- Iterator iterator = entityplayer.getEffects().iterator(); ++ TravelAgent agent = exit != null ? (TravelAgent) ((CraftWorld) exit.getWorld()).getHandle().getTravelAgent() : org.bukkit.craftbukkit.CraftTravelAgent.DEFAULT; // return arbitrary TA to compensate for implementation dependent plugins ++ PlayerPortalEvent event = new PlayerPortalEvent(entityplayer.getBukkitEntity(), enter, exit, agent, cause); ++ event.useTravelAgent(useTravelAgent); ++ Bukkit.getServer().getPluginManager().callEvent(event); ++ if (event.isCancelled() || event.getTo() == null) { ++ return; ++ } ++ ++ exit = event.useTravelAgent() ? event.getPortalTravelAgent().findOrCreate(event.getTo()) : event.getTo(); ++ if (exit == null) { ++ return; ++ } ++ exitWorld = ((CraftWorld) exit.getWorld()).getHandle(); ++ ++ Vector velocity = entityplayer.getBukkitEntity().getVelocity(); ++ boolean before = exitWorld.chunkProviderServer.forceChunkLoad; ++ exitWorld.chunkProviderServer.forceChunkLoad = true; ++ exitWorld.getTravelAgent().adjustExit(entityplayer, exit, velocity); ++ exitWorld.chunkProviderServer.forceChunkLoad = before; ++ ++ this.moveToWorld(entityplayer, exitWorld.dimension, true, exit, false); // Vanilla doesn't check for suffocation when handling portals, so neither should we ++ if (entityplayer.motX != velocity.getX() || entityplayer.motY != velocity.getY() || entityplayer.motZ != velocity.getZ()) { ++ entityplayer.getBukkitEntity().setVelocity(velocity); ++ } ++ } + +- while (iterator.hasNext()) { +- MobEffect mobeffect = (MobEffect) iterator.next(); ++ public void changeWorld(Entity entity, int i, WorldServer worldserver, WorldServer worldserver1) { ++ // CraftBukkit start - Split into modular functions ++ Location exit = calculateTarget(entity.getBukkitEntity().getLocation(), worldserver1); ++ repositionEntity(entity, exit, true); ++ } ++ ++ // Copy of original changeWorld(Entity, int, WorldServer, WorldServer) method with only location calculation logic ++ public Location calculateTarget(Location enter, World target) { ++ WorldServer worldserver = ((CraftWorld) enter.getWorld()).getHandle(); ++ WorldServer worldserver1 = ((CraftWorld) target.getWorld()).getHandle(); ++ int i = worldserver.dimension; ++ ++ double y = enter.getY(); ++ float yaw = enter.getYaw(); ++ float pitch = enter.getPitch(); ++ double d0 = enter.getX(); ++ double d1 = enter.getZ(); ++ double d2 = 8.0D; ++ /* ++ double d0 = entity.locX; ++ double d1 = entity.locZ; ++ double d2 = 8.0D; ++ float f = entity.yaw; ++ ++ worldserver.methodProfiler.a("moving"); ++ */ ++ if (worldserver1.dimension == -1) { ++ d0 = MathHelper.a(d0 / d2, worldserver1.af().b() + 16.0D, worldserver1.af().d() - 16.0D); ++ d1 = MathHelper.a(d1 / d2, worldserver1.af().c() + 16.0D, worldserver1.af().e() - 16.0D); ++ /* ++ entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); ++ if (entity.isAlive()) { ++ worldserver.entityJoinedWorld(entity, false); ++ } ++ */ ++ } else if (worldserver1.dimension == 0) { ++ d0 = MathHelper.a(d0 * d2, worldserver1.af().b() + 16.0D, worldserver1.af().d() - 16.0D); ++ d1 = MathHelper.a(d1 * d2, worldserver1.af().c() + 16.0D, worldserver1.af().e() - 16.0D); ++ /* ++ entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); ++ if (entity.isAlive()) { ++ worldserver.entityJoinedWorld(entity, false); ++ } ++ */ ++ } else { ++ BlockPosition blockposition; ++ ++ if (i == 1) { ++ // use default NORMAL world spawn instead of target ++ worldserver1 = this.server.worlds.get(0); ++ blockposition = worldserver1.getSpawn(); ++ } else { ++ blockposition = worldserver1.getDimensionSpawn(); ++ } ++ ++ d0 = (double) blockposition.getX(); ++ y = (double) blockposition.getY(); ++ d1 = (double) blockposition.getZ(); ++ /* ++ entity.setPositionRotation(d0, entity.locY, d1, 90.0F, 0.0F); ++ if (entity.isAlive()) { ++ worldserver.entityJoinedWorld(entity, false); ++ } ++ */ ++ } + +- entityplayer.playerConnection.sendPacket(new PacketPlayOutEntityEffect(entityplayer.getId(), mobeffect)); ++ // worldserver.methodProfiler.b(); ++ if (i != 1) { ++ worldserver.methodProfiler.a("placing"); ++ d0 = (double) MathHelper.clamp((int) d0, -29999872, 29999872); ++ d1 = (double) MathHelper.clamp((int) d1, -29999872, 29999872); ++ /* ++ if (entity.isAlive()) { ++ entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); ++ worldserver1.getTravelAgent().a(entity, f); ++ worldserver1.addEntity(entity); ++ worldserver1.entityJoinedWorld(entity, false); ++ } ++ ++ worldserver.methodProfiler.b(); ++ */ + } + ++ // entity.spawnIn(worldserver1); ++ return new Location(worldserver1.getWorld(), d0, y, d1, yaw, pitch); + } + +- public void changeWorld(Entity entity, int i, WorldServer worldserver, WorldServer worldserver1) { ++ // copy of original a(Entity, int, WorldServer, WorldServer) method with only entity repositioning logic ++ public void repositionEntity(Entity entity, Location exit, boolean portal) { ++ WorldServer worldserver = (WorldServer) entity.world; ++ WorldServer worldserver1 = ((CraftWorld) exit.getWorld()).getHandle(); ++ int i = worldserver.dimension; ++ ++ /* + double d0 = entity.locX; + double d1 = entity.locZ; + double d2 = 8.0D; + float f = entity.yaw; + + worldserver.methodProfiler.a("moving"); ++ */ ++ entity.setPositionRotation(exit.getX(), exit.getY(), exit.getZ(), exit.getYaw(), exit.getPitch()); ++ if (entity.isAlive()) { ++ worldserver.entityJoinedWorld(entity, false); ++ } ++ /* + if (entity.dimension == -1) { + d0 = MathHelper.a(d0 / d2, worldserver1.af().b() + 16.0D, worldserver1.af().d() - 16.0D); + d1 = MathHelper.a(d1 / d2, worldserver1.af().c() + 16.0D, worldserver1.af().e() - 16.0D); +@@ -411,6 +724,8 @@ + BlockPosition blockposition; + + if (i == 1) { ++ // use default NORMAL world spawn instead of target ++ worldserver1 = this.server.worlds.get(0); + blockposition = worldserver1.getSpawn(); + } else { + blockposition = worldserver1.getDimensionSpawn(); +@@ -424,15 +739,26 @@ + worldserver.entityJoinedWorld(entity, false); + } + } ++ */ + + worldserver.methodProfiler.b(); + if (i != 1) { + worldserver.methodProfiler.a("placing"); ++ /* + d0 = (double) MathHelper.clamp((int) d0, -29999872, 29999872); + d1 = (double) MathHelper.clamp((int) d1, -29999872, 29999872); ++ */ + if (entity.isAlive()) { +- entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); +- worldserver1.getTravelAgent().a(entity, f); ++ // entity.setPositionRotation(d0, entity.locY, d1, entity.yaw, entity.pitch); ++ // worldserver1.getTravelAgent().a(entity, f); ++ if (portal) { ++ Vector velocity = entity.getBukkitEntity().getVelocity(); ++ worldserver1.getTravelAgent().adjustExit(entity, exit, velocity); ++ entity.setPositionRotation(exit.getX(), exit.getY(), exit.getZ(), exit.getYaw(), exit.getPitch()); ++ if (entity.motX != velocity.getX() || entity.motY != velocity.getY() || entity.motZ != velocity.getZ()) { ++ entity.getBukkitEntity().setVelocity(velocity); ++ } ++ } + worldserver1.addEntity(entity); + worldserver1.entityJoinedWorld(entity, false); + } +@@ -441,6 +767,7 @@ + } + + entity.spawnIn(worldserver1); ++ // CraftBukkit end + } + + public void tick() { +@@ -549,10 +876,24 @@ + + public void addOp(GameProfile gameprofile) { + this.operators.add(new OpListEntry(gameprofile, this.server.p())); ++ ++ // CraftBukkit start ++ Player player = server.server.getPlayer(gameprofile.getId()); ++ if (player != null) { ++ player.recalculatePermissions(); ++ } ++ // CraftBukkit end + } + + public void removeOp(GameProfile gameprofile) { + this.operators.remove(gameprofile); ++ ++ // CraftBukkit start ++ Player player = server.server.getPlayer(gameprofile.getId()); ++ if (player != null) { ++ player.recalculatePermissions(); ++ } ++ // CraftBukkit end + } + + public boolean isWhitelisted(GameProfile gameprofile) { +@@ -560,7 +901,7 @@ + } + + public boolean isOp(GameProfile gameprofile) { +- return this.operators.d(gameprofile) || this.server.S() && this.server.worldServer[0].getWorldData().v() && this.server.R().equalsIgnoreCase(gameprofile.getName()) || this.t; ++ return this.operators.d(gameprofile) || this.server.S() && this.server.worlds.get(0).getWorldData().v() && this.server.R().equalsIgnoreCase(gameprofile.getName()) || this.t; // CraftBukkit + } + + public EntityPlayer getPlayer(String s) { +@@ -587,6 +928,12 @@ + for (int j = 0; j < this.players.size(); ++j) { + EntityPlayer entityplayer = (EntityPlayer) this.players.get(j); + ++ // CraftBukkit start - Test if player receiving packet can see the source of the packet ++ if (entityhuman != null && entityhuman instanceof EntityPlayer && !entityplayer.getBukkitEntity().canSee(((EntityPlayer) entityhuman).getBukkitEntity())) { ++ continue; ++ } ++ // CraftBukkit end ++ + if (entityplayer != entityhuman && entityplayer.dimension == i) { + double d4 = d0 - entityplayer.locX; + double d5 = d1 - entityplayer.locY; +@@ -634,21 +981,25 @@ + public void reloadWhitelist() {} + + public void b(EntityPlayer entityplayer, WorldServer worldserver) { +- WorldBorder worldborder = this.server.worldServer[0].af(); ++ WorldBorder worldborder = this.server.worlds.get(0).af(); // CraftBukkit + + entityplayer.playerConnection.sendPacket(new PacketPlayOutWorldBorder(worldborder, EnumWorldBorderAction.INITIALIZE)); + entityplayer.playerConnection.sendPacket(new PacketPlayOutUpdateTime(worldserver.getTime(), worldserver.getDayTime(), worldserver.getGameRules().getBoolean("doDaylightCycle"))); + if (worldserver.S()) { +- entityplayer.playerConnection.sendPacket(new PacketPlayOutGameStateChange(1, 0.0F)); +- entityplayer.playerConnection.sendPacket(new PacketPlayOutGameStateChange(7, worldserver.j(1.0F))); +- entityplayer.playerConnection.sendPacket(new PacketPlayOutGameStateChange(8, worldserver.h(1.0F))); ++ // CraftBukkit start - handle player weather ++ // entityplayer.playerConnection.sendPacket(new PacketPlayOutGameStateChange(1, 0.0F)); ++ // entityplayer.playerConnection.sendPacket(new PacketPlayOutGameStateChange(7, worldserver.j(1.0F))); ++ // entityplayer.playerConnection.sendPacket(new PacketPlayOutGameStateChange(8, worldserver.h(1.0F))); ++ entityplayer.setPlayerWeather(org.bukkit.WeatherType.DOWNFALL, false); ++ // CraftBukkit end + } + + } + + public void updateClient(EntityPlayer entityplayer) { + entityplayer.updateInventory(entityplayer.defaultContainer); +- entityplayer.triggerHealthUpdate(); ++ // entityplayer.triggerHealthUpdate(); ++ entityplayer.getBukkitEntity().updateScaledHealth(); // CraftBukkit - Update scaled health on respawn and worldchange + entityplayer.playerConnection.sendPacket(new PacketPlayOutHeldItemSlot(entityplayer.inventory.itemInHandIndex)); + } + +@@ -661,7 +1012,7 @@ + } + + public String[] getSeenPlayers() { +- return this.server.worldServer[0].getDataManager().getPlayerFileData().getSeenPlayers(); ++ return this.server.worlds.get(0).getDataManager().getPlayerFileData().getSeenPlayers(); // CraftBukkit + } + + public boolean getHasWhitelist() { +@@ -711,10 +1062,17 @@ + + public void v() { + for (int i = 0; i < this.players.size(); ++i) { +- ((EntityPlayer) this.players.get(i)).playerConnection.disconnect("Server closed"); ++ ((EntityPlayer) this.players.get(i)).playerConnection.disconnect(this.server.server.getShutdownMessage()); // CraftBukkit - add custom shutdown message + } ++ } + ++ // CraftBukkit start ++ public void sendMessage(IChatBaseComponent[] iChatBaseComponents) { ++ for (IChatBaseComponent component : iChatBaseComponents) { ++ sendMessage(component, true); ++ } + } ++ // CraftBukkit end + + public void sendMessage(IChatBaseComponent ichatbasecomponent, boolean flag) { + this.server.sendMessage(ichatbasecomponent); +@@ -754,11 +1112,10 @@ + public void a(int i) { + this.r = i; + if (this.server.worldServer != null) { +- WorldServer[] aworldserver = this.server.worldServer; +- int j = aworldserver.length; +- +- for (int k = 0; k < j; ++k) { +- WorldServer worldserver = aworldserver[k]; ++ // CraftBukkit start ++ for (int k = 0; k < server.worlds.size(); ++k) { ++ WorldServer worldserver = server.worlds.get(0); ++ // CraftBukkit end + + if (worldserver != null) { + worldserver.getPlayerChunkMap().a(i); diff --git a/nms-patches/PlayerSelector.patch b/nms-patches/PlayerSelector.patch new file mode 100644 index 00000000..e5583a10 --- /dev/null +++ b/nms-patches/PlayerSelector.patch @@ -0,0 +1,14 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PlayerSelector.java 2014-11-27 08:59:46.865421124 +1100 ++++ src/main/java/net/minecraft/server/PlayerSelector.java 2014-11-27 08:42:10.140850934 +1100 +@@ -52,6 +52,11 @@ + } + + public static List getPlayers(ICommandListener icommandlistener, String s, Class oclass) { ++ // CraftBukkit start - disable playerselections for ICommandListeners other than command blocks ++ if (!(icommandlistener instanceof CommandBlockListenerAbstract)) { ++ return com.google.common.collect.ImmutableList.of(); ++ } ++ // CraftBukkit end + Matcher matcher = PlayerSelector.a.matcher(s); + + if (matcher.matches() && icommandlistener.a(1, "@")) { diff --git a/nms-patches/PortalCreator.patch b/nms-patches/PortalCreator.patch new file mode 100644 index 00000000..5bb4eae9 --- /dev/null +++ b/nms-patches/PortalCreator.patch @@ -0,0 +1,102 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PortalCreator.java 2014-11-27 08:59:46.865421124 +1100 ++++ src/main/java/net/minecraft/server/PortalCreator.java 2014-11-27 08:42:10.132850949 +1100 +@@ -1,5 +1,7 @@ + package net.minecraft.server; + ++import org.bukkit.event.world.PortalCreateEvent; // CraftBukkit ++ + public class PortalCreator { + + private final World a; +@@ -10,6 +12,7 @@ + private BlockPosition f; + private int g; + private int h; ++ java.util.Collection<org.bukkit.block.Block> blocks = new java.util.HashSet<org.bukkit.block.Block>(); // CraftBukkit - add field + + public PortalCreator(World world, BlockPosition blockposition, EnumAxis enumaxis) { + this.a = world; +@@ -60,6 +63,10 @@ + } + + protected int a() { ++ // CraftBukkit start ++ this.blocks.clear(); ++ org.bukkit.World bworld = this.a.getWorld(); ++ // CraftBukkit end + int i; + + label56: +@@ -80,11 +87,21 @@ + block = this.a.getType(blockposition.shift(this.d)).getBlock(); + if (block != Blocks.OBSIDIAN) { + break label56; ++ // CraftBukkit start - add the block to our list ++ } else { ++ BlockPosition pos = blockposition.shift(this.d); ++ blocks.add(bworld.getBlockAt(pos.getX(), pos.getY(), pos.getZ())); ++ // CraftBukkit end + } + } else if (i == this.h - 1) { + block = this.a.getType(blockposition.shift(this.c)).getBlock(); + if (block != Blocks.OBSIDIAN) { + break label56; ++ // CraftBukkit start - add the block to our list ++ } else { ++ BlockPosition pos = blockposition.shift(this.c); ++ blocks.add(bworld.getBlockAt(pos.getX(), pos.getY(), pos.getZ())); ++ // CraftBukkit end + } + } + } +@@ -94,6 +111,11 @@ + if (this.a.getType(this.f.shift(this.c, i).up(this.g)).getBlock() != Blocks.OBSIDIAN) { + this.g = 0; + break; ++ // CraftBukkit start - add the block to our list ++ } else { ++ BlockPosition pos = this.f.shift(this.c, i).up(this.g); ++ blocks.add(bworld.getBlockAt(pos.getX(), pos.getY(), pos.getZ())); ++ // CraftBukkit end + } + } + +@@ -115,15 +137,36 @@ + return this.f != null && this.h >= 2 && this.h <= 21 && this.g >= 3 && this.g <= 21; + } + +- public void c() { ++ // CraftBukkit start - return boolean ++ public boolean c() { ++ org.bukkit.World bworld = this.a.getWorld(); ++ ++ // Copy below for loop + for (int i = 0; i < this.h; ++i) { + BlockPosition blockposition = this.f.shift(this.c, i); + + for (int j = 0; j < this.g; ++j) { +- this.a.setTypeAndData(blockposition.up(j), Blocks.PORTAL.getBlockData().set(BlockPortal.AXIS, this.b), 2); ++ BlockPosition pos = blockposition.up(j); ++ blocks.add(bworld.getBlockAt(pos.getX(), pos.getY(), pos.getZ())); + } + } + ++ PortalCreateEvent event = new PortalCreateEvent(blocks, bworld, PortalCreateEvent.CreateReason.FIRE); ++ this.a.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ return false; ++ } ++ // CraftBukkit end ++ for (int i = 0; i < this.h; ++i) { ++ BlockPosition blockposition = this.f.shift(this.c, i); ++ ++ for (int j = 0; j < this.g; ++j) { ++ this.a.setTypeAndData(blockposition.up(j), Blocks.PORTAL.getBlockData().set(BlockPortal.AXIS, this.b), 2); ++ } ++ } ++ ++ return true; // Craft Bukkit + } + + public static int a(PortalCreator portalcreator) { diff --git a/nms-patches/PortalTravelAgent.patch b/nms-patches/PortalTravelAgent.patch new file mode 100644 index 00000000..1a6d02d9 --- /dev/null +++ b/nms-patches/PortalTravelAgent.patch @@ -0,0 +1,273 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PortalTravelAgent.java 2014-11-27 08:59:46.869421107 +1100 ++++ src/main/java/net/minecraft/server/PortalTravelAgent.java 2014-11-27 08:42:10.096851020 +1100 +@@ -5,6 +5,12 @@ + import java.util.List; + import java.util.Random; + ++// CraftBukkit start ++import org.bukkit.Location; ++import org.bukkit.event.entity.EntityPortalExitEvent; ++import org.bukkit.util.Vector; ++// CraftBukkit end ++ + public class PortalTravelAgent { + + private final WorldServer a; +@@ -27,8 +33,21 @@ + int i = MathHelper.floor(entity.locX); + int j = MathHelper.floor(entity.locY) - 1; + int k = MathHelper.floor(entity.locZ); ++ // CraftBukkit start - Modularize end portal creation ++ BlockPosition created = this.createEndPortal(entity.locX, entity.locY, entity.locZ); ++ entity.setPositionRotation((double) created.getX(), (double) created.getY(), (double) created.getZ(), entity.yaw, 0.0F); ++ entity.motX = entity.motY = entity.motZ = 0.0D; ++ } ++ } ++ ++ // Split out from original a(Entity, double, double, double, float) method in order to enable being called from createPortal ++ private BlockPosition createEndPortal(double x, double y, double z) { ++ int i = MathHelper.floor(x); ++ int j = MathHelper.floor(y) - 1; ++ int k = MathHelper.floor(z); + byte b0 = 1; + byte b1 = 0; ++ // CraftBukkit end + + for (int l = -2; l <= 2; ++l) { + for (int i1 = -2; i1 <= 2; ++i1) { +@@ -43,16 +62,63 @@ + } + } + +- entity.setPositionRotation((double) i, (double) j, (double) k, entity.yaw, 0.0F); +- entity.motX = entity.motY = entity.motZ = 0.0D; ++ // CraftBukkit start ++ return new BlockPosition(i, k, k); ++ } ++ ++ // use logic based on creation to verify end portal ++ private BlockPosition findEndPortal(BlockPosition portal) { ++ int i = portal.getX(); ++ int j = portal.getY() - 1; ++ int k = portal.getZ(); ++ byte b0 = 1; ++ byte b1 = 0; ++ ++ for (int l = -2; l <= 2; ++l) { ++ for (int i1 = -2; i1 <= 2; ++i1) { ++ for (int j1 = -1; j1 < 3; ++j1) { ++ int k1 = i + i1 * b0 + l * b1; ++ int l1 = j + j1; ++ int i2 = k + i1 * b1 - l * b0; ++ boolean flag = j1 < 0; ++ ++ if (this.a.getType(new BlockPosition(k1, l1, i2)).getBlock() != (flag ? Blocks.OBSIDIAN : Blocks.AIR)) { ++ return null; ++ } ++ } ++ } + } ++ return new BlockPosition(i, j, k); + } ++ // CraftBukkit end + + public boolean b(Entity entity, float f) { +- boolean flag = true; ++ // CraftBukkit start - Modularize portal search process and entity teleportation ++ BlockPosition found = this.findPortal(entity.locX, entity.locY, entity.locZ, 128); ++ if (found == null) { ++ return false; ++ } ++ ++ Location exit = new Location(this.a.getWorld(), found.getX(), found.getY(), found.getZ(), f, entity.pitch); ++ Vector velocity = entity.getBukkitEntity().getVelocity(); ++ this.adjustExit(entity, exit, velocity); ++ entity.setPositionRotation(exit.getX(), exit.getY(), exit.getZ(), exit.getYaw(), exit.getPitch()); ++ if (entity.motX != velocity.getX() || entity.motY != velocity.getY() || entity.motZ != velocity.getZ()) { ++ entity.getBukkitEntity().setVelocity(velocity); ++ } ++ return true; ++ } ++ ++ public BlockPosition findPortal(double x, double y, double z, int short1) { ++ if (this.a.getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END) { ++ return this.findEndPortal(this.a.worldProvider.h()); ++ } ++ // CraftBukkit end + double d0 = -1.0D; +- int i = MathHelper.floor(entity.locX); +- int j = MathHelper.floor(entity.locZ); ++ // CraftBukkit start ++ int i = MathHelper.floor(x); ++ int j = MathHelper.floor(z); ++ // CraftBukkit end + boolean flag1 = true; + Object object = BlockPosition.ZERO; + long k = ChunkCoordIntPair.a(i, j); +@@ -65,7 +131,7 @@ + chunkcoordinatesportal.b = this.a.getTime(); + flag1 = false; + } else { +- BlockPosition blockposition = new BlockPosition(entity); ++ BlockPosition blockposition = new BlockPosition(x, y, z); + + for (int l = -128; l <= 128; ++l) { + BlockPosition blockposition1; +@@ -95,7 +161,29 @@ + this.c.put(k, new ChunkCoordinatesPortal(this, (BlockPosition) object, this.a.getTime())); + this.d.add(Long.valueOf(k)); + } ++ // CraftBukkit start - Move entity teleportation logic into exit ++ return (BlockPosition) object; ++ } else { ++ return null; ++ } ++ } + ++ // Entity repositioning logic split out from original b method and combined with repositioning logic for The End from original a method ++ public void adjustExit(Entity entity, Location position, Vector velocity) { ++ Location from = position.clone(); ++ Vector before = velocity.clone(); ++ BlockPosition object = new BlockPosition(position.getBlockX(), position.getBlockY(), position.getBlockZ()); ++ float f = position.getYaw(); ++ ++ if (this.a.getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END) { ++ // entity.setPositionRotation((double) i, (double) j, (double) k, entity.yaw, 0.0F); ++ // entity.motX = entity.motY = entity.motZ = 0.0D; ++ position.setPitch(0.0F); ++ velocity.setX(0); ++ velocity.setY(0); ++ velocity.setZ(0); ++ } else { ++ // CraftBukkit end + double d2 = (double) ((BlockPosition) object).getX() + 0.5D; + double d3 = (double) ((BlockPosition) object).getY() + 0.5D; + double d4 = (double) ((BlockPosition) object).getZ() + 0.5D; +@@ -170,21 +258,46 @@ + f6 = 1.0F; + } + +- double d5 = entity.motX; +- double d6 = entity.motZ; +- +- entity.motX = d5 * (double) f3 + d6 * (double) f6; +- entity.motZ = d5 * (double) f5 + d6 * (double) f4; +- entity.yaw = f - (float) (enumdirection1.b() * 90) + (float) (enumdirection.b() * 90); ++ // CraftBukkit start ++ double d5 = velocity.getX(); ++ double d6 = velocity.getZ(); ++ // CraftBukkit end ++ ++ // CraftBukkit start - Adjust position and velocity instances instead of entity ++ velocity.setX(d5 * (double) f3 + d6 * (double) f6); ++ velocity.setZ(d5 * (double) f5 + d6 * (double) f4); ++ f = f - (float) (enumdirection1.b() * 90) + (float) (enumdirection.b() * 90); + } else { +- entity.motX = entity.motY = entity.motZ = 0.0D; ++ velocity.setX(0); ++ velocity.setY(0); ++ velocity.setZ(0); + } + +- entity.setPositionRotation(d2, d3, d4, entity.yaw, entity.pitch); +- return true; ++ // entity.setPositionRotation(d2, d3, d4, entity.yaw, entity.pitch); ++ position.setX(d2); ++ position.setY(d3); ++ position.setZ(d4); ++ position.setYaw(f); ++ } ++ EntityPortalExitEvent event = new EntityPortalExitEvent(entity.getBukkitEntity(), from, position, before, velocity); ++ this.a.getServer().getPluginManager().callEvent(event); ++ Location to = event.getTo(); ++ if (event.isCancelled() || to == null || !entity.isAlive()) { ++ position.setX(from.getX()); ++ position.setY(from.getY()); ++ position.setZ(from.getZ()); ++ position.setYaw(from.getYaw()); ++ position.setPitch(from.getPitch()); ++ velocity.copy(before); + } else { +- return false; ++ position.setX(to.getX()); ++ position.setY(to.getY()); ++ position.setZ(to.getZ()); ++ position.setYaw(to.getYaw()); ++ position.setPitch(to.getPitch()); ++ velocity.copy(event.getAfter()); // event.getAfter() will never be null, as setAfter() will cause an NPE if null is passed in + } ++ // CraftBukkit end + } + + private boolean a(BlockPosition blockposition) { +@@ -192,11 +305,22 @@ + } + + public boolean a(Entity entity) { +- byte b0 = 16; ++ // CraftBukkit start - Allow for portal creation to be based on coordinates instead of entity ++ return this.createPortal(entity.locX, entity.locY, entity.locZ, 16); ++ } ++ ++ public boolean createPortal(double x, double y, double z, int b0) { ++ if (this.a.getWorld().getEnvironment() == org.bukkit.World.Environment.THE_END) { ++ createEndPortal(x, y, z); ++ return true; ++ } ++ // CraftBukkit end + double d0 = -1.0D; +- int i = MathHelper.floor(entity.locX); +- int j = MathHelper.floor(entity.locY); +- int k = MathHelper.floor(entity.locZ); ++ // CraftBukkit start ++ int i = MathHelper.floor(x); ++ int j = MathHelper.floor(y); ++ int k = MathHelper.floor(z); ++ // CraftBukkit end + int l = i; + int i1 = j; + int j1 = k; +@@ -220,10 +344,10 @@ + double d4; + + for (i2 = i - b0; i2 <= i + b0; ++i2) { +- d1 = (double) i2 + 0.5D - entity.locX; ++ d1 = (double) i2 + 0.5D - x; // CraftBukkit + + for (j2 = k - b0; j2 <= k + b0; ++j2) { +- d2 = (double) j2 + 0.5D - entity.locZ; ++ d2 = (double) j2 + 0.5D - z; // CraftBukkit + + label271: + for (k2 = this.a.V() - 1; k2 >= 0; --k2) { +@@ -254,7 +378,7 @@ + } + } + +- d3 = (double) k2 + 0.5D - entity.locY; ++ d3 = (double) k2 + 0.5D - y; // CraftBukkit + d4 = d1 * d1 + d3 * d3 + d2 * d2; + if (d0 < 0.0D || d4 < d0) { + d0 = d4; +@@ -271,10 +395,10 @@ + + if (d0 < 0.0D) { + for (i2 = i - b0; i2 <= i + b0; ++i2) { +- d1 = (double) i2 + 0.5D - entity.locX; ++ d1 = (double) i2 + 0.5D - x; // CraftBukkit + + for (j2 = k - b0; j2 <= k + b0; ++j2) { +- d2 = (double) j2 + 0.5D - entity.locZ; ++ d2 = (double) j2 + 0.5D - z; // CraftBukkit + + label219: + for (k2 = this.a.V() - 1; k2 >= 0; --k2) { +@@ -298,7 +422,7 @@ + } + } + +- d3 = (double) k2 + 0.5D - entity.locY; ++ d3 = (double) k2 + 0.5D - y; // CraftBukkit + d4 = d1 * d1 + d3 * d3 + d2 * d2; + if (d0 < 0.0D || d4 < d0) { + d0 = d4; diff --git a/nms-patches/PropertyManager.patch b/nms-patches/PropertyManager.patch new file mode 100644 index 00000000..9812b439 --- /dev/null +++ b/nms-patches/PropertyManager.patch @@ -0,0 +1,94 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/PropertyManager.java 2014-11-27 08:59:46.869421107 +1100 ++++ src/main/java/net/minecraft/server/PropertyManager.java 2014-11-27 08:42:10.100851012 +1100 +@@ -8,6 +8,8 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++import joptsimple.OptionSet; // CraftBukkit ++ + public class PropertyManager { + + private static final Logger a = LogManager.getLogger(); +@@ -39,8 +41,25 @@ + PropertyManager.a.warn(file + " does not exist"); + this.a(); + } ++ } ++ ++ // CraftBukkit start ++ private OptionSet options = null; ++ ++ public PropertyManager(final OptionSet options) { ++ this((File) options.valueOf("config")); ++ ++ this.options = options; ++ } ++ ++ private <T> T getOverride(String name, T value) { ++ if ((this.options != null) && (this.options.has(name))) { ++ return (T) this.options.valueOf(name); ++ } + ++ return value; + } ++ // CraftBukkit end + + public void a() { + PropertyManager.a.info("Generating new properties file"); +@@ -51,6 +70,12 @@ + FileOutputStream fileoutputstream = null; + + try { ++ // CraftBukkit start - Don't attempt writing to file if it's read only ++ if (this.file.exists() && !this.file.canWrite()) { ++ return; ++ } ++ // CraftBukkit end ++ + fileoutputstream = new FileOutputStream(this.file); + this.properties.store(fileoutputstream, "Minecraft server properties"); + } catch (Exception exception) { +@@ -80,36 +105,36 @@ + this.savePropertiesFile(); + } + +- return this.properties.getProperty(s, s1); ++ return getOverride(s, this.properties.getProperty(s, s1)); // CraftBukkit + } + + public int getInt(String s, int i) { + try { +- return Integer.parseInt(this.getString(s, "" + i)); ++ return getOverride(s, Integer.parseInt(this.getString(s, "" + i))); // CraftBukkit + } catch (Exception exception) { + this.properties.setProperty(s, "" + i); + this.savePropertiesFile(); +- return i; ++ return getOverride(s, i); // CraftBukkit + } + } + + public long getLong(String s, long i) { + try { +- return Long.parseLong(this.getString(s, "" + i)); ++ return getOverride(s, Long.parseLong(this.getString(s, "" + i))); // CraftBukkit + } catch (Exception exception) { + this.properties.setProperty(s, "" + i); + this.savePropertiesFile(); +- return i; ++ return getOverride(s ,i); // CraftBukkit + } + } + + public boolean getBoolean(String s, boolean flag) { + try { +- return Boolean.parseBoolean(this.getString(s, "" + flag)); ++ return getOverride(s, Boolean.parseBoolean(this.getString(s, "" + flag))); // CraftBukkit + } catch (Exception exception) { + this.properties.setProperty(s, "" + flag); + this.savePropertiesFile(); +- return flag; ++ return getOverride(s, flag); // CraftBukkit + } + } + diff --git a/nms-patches/RecipeArmorDye.patch b/nms-patches/RecipeArmorDye.patch new file mode 100644 index 00000000..80002455 --- /dev/null +++ b/nms-patches/RecipeArmorDye.patch @@ -0,0 +1,18 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/RecipeArmorDye.java 2014-11-27 08:59:46.869421107 +1100 ++++ src/main/java/net/minecraft/server/RecipeArmorDye.java 2014-11-27 08:42:10.116850981 +1100 +@@ -3,9 +3,13 @@ + import com.google.common.collect.Lists; + import java.util.ArrayList; + +-public class RecipeArmorDye implements IRecipe { ++public class RecipeArmorDye extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + +- public RecipeArmorDye() {} ++ // CraftBukkit start - Delegate to new parent class with bogus info ++ public RecipeArmorDye() { ++ super(new ItemStack(Items.LEATHER_HELMET, 0, 0), java.util.Arrays.asList(new ItemStack(Items.DYE, 0, 5))); ++ } ++ // CraftBukkit end + + public boolean a(InventoryCrafting inventorycrafting, World world) { + ItemStack itemstack = null; diff --git a/nms-patches/RecipeBookClone.patch b/nms-patches/RecipeBookClone.patch new file mode 100644 index 00000000..5d14c2db --- /dev/null +++ b/nms-patches/RecipeBookClone.patch @@ -0,0 +1,17 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/RecipeBookClone.java 2014-11-27 08:59:46.873421089 +1100 ++++ src/main/java/net/minecraft/server/RecipeBookClone.java 2014-11-27 08:42:10.144850927 +1100 +@@ -1,8 +1,12 @@ + package net.minecraft.server; + +-public class RecipeBookClone implements IRecipe { ++public class RecipeBookClone extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + +- public RecipeBookClone() {} ++ // CraftBukkit start - Delegate to new parent class ++ public RecipeBookClone() { ++ super(new ItemStack(Items.WRITTEN_BOOK, 0, -1), java.util.Arrays.asList(new ItemStack(Items.WRITABLE_BOOK, 0, 0))); ++ } ++ // CraftBukkit end + + public boolean a(InventoryCrafting inventorycrafting, World world) { + int i = 0; diff --git a/nms-patches/RecipeFireworks.patch b/nms-patches/RecipeFireworks.patch new file mode 100644 index 00000000..b7b388d0 --- /dev/null +++ b/nms-patches/RecipeFireworks.patch @@ -0,0 +1,21 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/RecipeFireworks.java 2014-11-27 08:59:46.873421089 +1100 ++++ src/main/java/net/minecraft/server/RecipeFireworks.java 2014-11-27 08:42:10.088851036 +1100 +@@ -3,11 +3,15 @@ + import com.google.common.collect.Lists; + import java.util.ArrayList; + +-public class RecipeFireworks implements IRecipe { ++public class RecipeFireworks extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + + private ItemStack a; +- +- public RecipeFireworks() {} ++ ++ // CraftBukkit start - Delegate to new parent class with bogus info ++ public RecipeFireworks() { ++ super(new ItemStack(Items.FIREWORKS, 0, 0), java.util.Arrays.asList(new ItemStack(Items.GUNPOWDER, 0, 5))); ++ } ++ // CraftBukkit end + + public boolean a(InventoryCrafting inventorycrafting, World world) { + this.a = null; diff --git a/nms-patches/RecipeMapClone.patch b/nms-patches/RecipeMapClone.patch new file mode 100644 index 00000000..a33ee9e9 --- /dev/null +++ b/nms-patches/RecipeMapClone.patch @@ -0,0 +1,17 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/RecipeMapClone.java 2014-11-27 08:59:46.873421089 +1100 ++++ src/main/java/net/minecraft/server/RecipeMapClone.java 2014-11-27 08:42:10.152850911 +1100 +@@ -1,8 +1,12 @@ + package net.minecraft.server; + +-public class RecipeMapClone implements IRecipe { ++public class RecipeMapClone extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + +- public RecipeMapClone() {} ++ // CraftBukkit start - Delegate to new parent class ++ public RecipeMapClone() { ++ super(new ItemStack(Items.MAP, 0, -1), java.util.Arrays.asList(new ItemStack(Items.MAP, 0, 0))); ++ } ++ // CraftBukkit end + + public boolean a(InventoryCrafting inventorycrafting, World world) { + int i = 0; diff --git a/nms-patches/RecipeRepair.patch b/nms-patches/RecipeRepair.patch new file mode 100644 index 00000000..51349bb2 --- /dev/null +++ b/nms-patches/RecipeRepair.patch @@ -0,0 +1,39 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/RecipeRepair.java 2014-11-27 08:59:46.877421071 +1100 ++++ src/main/java/net/minecraft/server/RecipeRepair.java 2014-11-27 08:42:10.148850918 +1100 +@@ -3,9 +3,13 @@ + import com.google.common.collect.Lists; + import java.util.ArrayList; + +-public class RecipeRepair implements IRecipe { ++public class RecipeRepair extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + +- public RecipeRepair() {} ++ // CraftBukkit start - Delegate to new parent class ++ public RecipeRepair() { ++ super(new ItemStack(Items.LEATHER_HELMET), java.util.Arrays.asList(new ItemStack(Items.LEATHER_HELMET))); ++ } ++ // CraftBukkit end + + public boolean a(InventoryCrafting inventorycrafting, World world) { + ArrayList arraylist = Lists.newArrayList(); +@@ -61,8 +65,18 @@ + if (i1 < 0) { + i1 = 0; + } +- +- return new ItemStack(itemstack2.getItem(), 1, i1); ++ ++ // CraftBukkit start - Construct a dummy repair recipe ++ ItemStack result = new ItemStack(itemstack.getItem(), 1, i1); ++ java.util.List<ItemStack> ingredients = new ArrayList<ItemStack>(); ++ ingredients.add(itemstack2.cloneItemStack()); ++ ingredients.add(itemstack.cloneItemStack()); ++ ShapelessRecipes recipe = new ShapelessRecipes(result.cloneItemStack(), ingredients); ++ inventorycrafting.currentRecipe = recipe; ++ result = org.bukkit.craftbukkit.event.CraftEventFactory.callPreCraftEvent(inventorycrafting, result, CraftingManager.getInstance().lastCraftView, true); ++ return result; ++ // return new ItemStack(itemstack2.getItem(), 1, i1); ++ // CraftBukkit end + } + } + diff --git a/nms-patches/RecipesBannerInnerClass1.patch b/nms-patches/RecipesBannerInnerClass1.patch new file mode 100644 index 00000000..78471632 --- /dev/null +++ b/nms-patches/RecipesBannerInnerClass1.patch @@ -0,0 +1,17 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/RecipesBannerInnerClass1.java 2014-11-27 08:59:46.877421071 +1100 ++++ src/main/java/net/minecraft/server/RecipesBannerInnerClass1.java 2014-11-27 08:42:10.084851043 +1100 +@@ -1,8 +1,12 @@ + package net.minecraft.server; + +-class RecipesBannerInnerClass1 implements IRecipe { ++class RecipesBannerInnerClass1 extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + +- private RecipesBannerInnerClass1() {} ++ // CraftBukkit start - Delegate to new parent class with bogus info ++ private RecipesBannerInnerClass1() { ++ super(new ItemStack(Items.BANNER, 0, 0), java.util.Arrays.asList(new ItemStack(Items.BANNER))); ++ } ++ // CraftBukkit end + + public boolean a(InventoryCrafting inventorycrafting, World world) { + ItemStack itemstack = null; diff --git a/nms-patches/RecipesBannerInnerClass2.patch b/nms-patches/RecipesBannerInnerClass2.patch new file mode 100644 index 00000000..3fa03250 --- /dev/null +++ b/nms-patches/RecipesBannerInnerClass2.patch @@ -0,0 +1,17 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/RecipesBannerInnerClass2.java 2014-11-27 08:59:46.881421054 +1100 ++++ src/main/java/net/minecraft/server/RecipesBannerInnerClass2.java 2014-11-27 08:42:10.164850887 +1100 +@@ -1,8 +1,12 @@ + package net.minecraft.server; + +-class RecipesBannerInnerClass2 implements IRecipe { ++class RecipesBannerInnerClass2 extends ShapelessRecipes implements IRecipe { // CraftBukkit - added extends + +- private RecipesBannerInnerClass2() {} ++ // CraftBukkit start - Delegate to new parent class with bogus info ++ private RecipesBannerInnerClass2() { ++ super(new ItemStack(Items.BANNER, 0, 0), java.util.Arrays.asList(new ItemStack(Items.DYE, 0, 5))); ++ } ++ // CraftBukkit end + + public boolean a(InventoryCrafting inventorycrafting, World world) { + boolean flag = false; diff --git a/nms-patches/RecipesFurnace.patch b/nms-patches/RecipesFurnace.patch new file mode 100644 index 00000000..be4d28fb --- /dev/null +++ b/nms-patches/RecipesFurnace.patch @@ -0,0 +1,49 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/RecipesFurnace.java 2014-11-27 08:59:46.881421054 +1100 ++++ src/main/java/net/minecraft/server/RecipesFurnace.java 2014-11-27 08:42:10.132850949 +1100 +@@ -10,6 +10,7 @@ + private static final RecipesFurnace a = new RecipesFurnace(); + public Map recipes = Maps.newHashMap(); + private Map c = Maps.newHashMap(); ++ public Map customRecipes = Maps.newHashMap(); // CraftBukkit - add field + + public static RecipesFurnace getInstance() { + return RecipesFurnace.a; +@@ -52,6 +53,12 @@ + this.registerRecipe(Blocks.LAPIS_ORE, new ItemStack(Items.DYE, 1, EnumColor.BLUE.getInvColorIndex()), 0.2F); + this.registerRecipe(Blocks.QUARTZ_ORE, new ItemStack(Items.QUARTZ), 0.2F); + } ++ ++ // CraftBukkit start - add method ++ public void registerRecipe(ItemStack itemstack, ItemStack itemstack1) { ++ this.customRecipes.put(itemstack, itemstack1); ++ } ++ // CraftBukkit end + + public void registerRecipe(Block block, ItemStack itemstack, float f) { + this.a(Item.getItemOf(block), itemstack, f); +@@ -67,13 +74,23 @@ + } + + public ItemStack getResult(ItemStack itemstack) { +- Iterator iterator = this.recipes.entrySet().iterator(); ++ // CraftBukkit start - initialize to customRecipes ++ boolean vanilla = false; ++ Iterator iterator = this.customRecipes.entrySet().iterator(); ++ // CraftBukkit end + + Entry entry; + + do { + if (!iterator.hasNext()) { +- return null; ++ // CraftBukkit start - fall back to vanilla recipes ++ if (!vanilla && !recipes.isEmpty()) { ++ iterator = this.recipes.entrySet().iterator(); ++ vanilla = true; ++ } else { ++ return null; ++ } ++ // CraftBukkit end + } + + entry = (Entry) iterator.next(); diff --git a/nms-patches/RegionFile.patch b/nms-patches/RegionFile.patch new file mode 100644 index 00000000..80999d9c --- /dev/null +++ b/nms-patches/RegionFile.patch @@ -0,0 +1,81 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/RegionFile.java 2014-11-27 08:59:46.881421054 +1100 ++++ src/main/java/net/minecraft/server/RegionFile.java 2014-11-27 08:42:10.160850895 +1100 +@@ -86,8 +86,46 @@ + } catch (IOException ioexception) { + ioexception.printStackTrace(); + } ++ } ++ ++ // CraftBukkit start - This is a copy (sort of) of the method below it, make sure they stay in sync ++ public synchronized boolean chunkExists(int i, int j) { ++ if (this.d(i, j)) { ++ return false; ++ } else { ++ try { ++ int k = this.e(i, j); ++ ++ if (k == 0) { ++ return false; ++ } else { ++ int l = k >> 8; ++ int i1 = k & 255; ++ ++ if (l + i1 > this.f.size()) { ++ return false; ++ } ++ ++ this.c.seek((long) (l * 4096)); ++ int j1 = this.c.readInt(); ++ ++ if (j1 > 4096 * i1 || j1 <= 0) { ++ return false; ++ } ++ ++ byte b0 = this.c.readByte(); ++ if (b0 == 1 || b0 == 2) { ++ return true; ++ } ++ } ++ } catch (IOException ioexception) { ++ return false; ++ } ++ } + ++ return false; + } ++ // CraftBukkit end + + public synchronized DataInputStream a(int i, int j) { + if (this.d(i, j)) { +@@ -214,7 +252,7 @@ + + } + +- private void a(int i, byte[] abyte, int j) { ++ private void a(int i, byte[] abyte, int j) throws IOException { // CraftBukkit - added throws + this.c.seek((long) (i * 4096)); + this.c.writeInt(j + 1); + this.c.writeByte(2); +@@ -233,19 +271,19 @@ + return this.e(i, j) != 0; + } + +- private void a(int i, int j, int k) { ++ private void a(int i, int j, int k) throws IOException { // CraftBukkit - added throws + this.d[i + j * 32] = k; + this.c.seek((long) ((i + j * 32) * 4)); + this.c.writeInt(k); + } + +- private void b(int i, int j, int k) { ++ private void b(int i, int j, int k) throws IOException { // CraftBukkit - added throws + this.e[i + j * 32] = k; + this.c.seek((long) (4096 + (i + j * 32) * 4)); + this.c.writeInt(k); + } + +- public void c() { ++ public void c() throws IOException { // CraftBukkit - added throws + if (this.c != null) { + this.c.close(); + } diff --git a/nms-patches/RemoteControlCommandListener.patch b/nms-patches/RemoteControlCommandListener.patch new file mode 100644 index 00000000..9cf48bf6 --- /dev/null +++ b/nms-patches/RemoteControlCommandListener.patch @@ -0,0 +1,15 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/RemoteControlCommandListener.java 2014-11-27 08:59:46.885421036 +1100 ++++ src/main/java/net/minecraft/server/RemoteControlCommandListener.java 2014-11-27 08:42:10.152850911 +1100 +@@ -26,6 +26,12 @@ + public IChatBaseComponent getScoreboardDisplayName() { + return new ChatComponentText(this.getName()); + } ++ ++ // CraftBukkit start - Send a String ++ public void sendMessage(String message) { ++ this.b.append(message); ++ } ++ // CraftBukkit end + + public void sendMessage(IChatBaseComponent ichatbasecomponent) { + this.b.append(ichatbasecomponent.c()); diff --git a/nms-patches/ScoreboardServer.patch b/nms-patches/ScoreboardServer.patch new file mode 100644 index 00000000..4b480d13 --- /dev/null +++ b/nms-patches/ScoreboardServer.patch @@ -0,0 +1,126 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ScoreboardServer.java 2014-11-27 08:59:46.885421036 +1100 ++++ src/main/java/net/minecraft/server/ScoreboardServer.java 2014-11-27 08:42:10.164850887 +1100 +@@ -21,7 +21,7 @@ + public void handleScoreChanged(ScoreboardScore scoreboardscore) { + super.handleScoreChanged(scoreboardscore); + if (this.b.contains(scoreboardscore.getObjective())) { +- this.a.getPlayerList().sendAll(new PacketPlayOutScoreboardScore(scoreboardscore)); ++ this.sendAll(new PacketPlayOutScoreboardScore(scoreboardscore)); // CraftBukkit - Internal packet method + } + + this.b(); +@@ -29,13 +29,13 @@ + + public void handlePlayerRemoved(String s) { + super.handlePlayerRemoved(s); +- this.a.getPlayerList().sendAll(new PacketPlayOutScoreboardScore(s)); ++ this.sendAll(new PacketPlayOutScoreboardScore(s)); // CraftBukkit - Internal packet method + this.b(); + } + + public void a(String s, ScoreboardObjective scoreboardobjective) { + super.a(s, scoreboardobjective); +- this.a.getPlayerList().sendAll(new PacketPlayOutScoreboardScore(s, scoreboardobjective)); ++ this.sendAll(new PacketPlayOutScoreboardScore(s, scoreboardobjective)); // CraftBukkit - Internal packet method + this.b(); + } + +@@ -45,7 +45,7 @@ + super.setDisplaySlot(i, scoreboardobjective); + if (scoreboardobjective1 != scoreboardobjective && scoreboardobjective1 != null) { + if (this.h(scoreboardobjective1) > 0) { +- this.a.getPlayerList().sendAll(new PacketPlayOutScoreboardDisplayObjective(i, scoreboardobjective)); ++ this.sendAll(new PacketPlayOutScoreboardDisplayObjective(i, scoreboardobjective)); // CraftBukkit - Internal packet method + } else { + this.g(scoreboardobjective1); + } +@@ -53,7 +53,7 @@ + + if (scoreboardobjective != null) { + if (this.b.contains(scoreboardobjective)) { +- this.a.getPlayerList().sendAll(new PacketPlayOutScoreboardDisplayObjective(i, scoreboardobjective)); ++ this.sendAll(new PacketPlayOutScoreboardDisplayObjective(i, scoreboardobjective)); // CraftBukkit - Internal packet method + } else { + this.e(scoreboardobjective); + } +@@ -66,7 +66,7 @@ + if (super.addPlayerToTeam(s, s1)) { + ScoreboardTeam scoreboardteam = this.getTeam(s1); + +- this.a.getPlayerList().sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, Arrays.asList(new String[] { s}), 3)); ++ this.sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, Arrays.asList(new String[] { s}), 3)); // CraftBukkit - Internal packet method + this.b(); + return true; + } else { +@@ -76,7 +76,7 @@ + + public void removePlayerFromTeam(String s, ScoreboardTeam scoreboardteam) { + super.removePlayerFromTeam(s, scoreboardteam); +- this.a.getPlayerList().sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, Arrays.asList(new String[] { s}), 4)); ++ this.sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, Arrays.asList(new String[] { s}), 4)); // CraftBukkit - Internal packet method + this.b(); + } + +@@ -88,7 +88,7 @@ + public void handleObjectiveChanged(ScoreboardObjective scoreboardobjective) { + super.handleObjectiveChanged(scoreboardobjective); + if (this.b.contains(scoreboardobjective)) { +- this.a.getPlayerList().sendAll(new PacketPlayOutScoreboardObjective(scoreboardobjective, 2)); ++ this.sendAll(new PacketPlayOutScoreboardObjective(scoreboardobjective, 2)); // CraftBukkit - Internal packet method + } + + this.b(); +@@ -105,19 +105,19 @@ + + public void handleTeamAdded(ScoreboardTeam scoreboardteam) { + super.handleTeamAdded(scoreboardteam); +- this.a.getPlayerList().sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, 0)); ++ this.sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, 0)); // CraftBukkit - Internal packet method + this.b(); + } + + public void handleTeamChanged(ScoreboardTeam scoreboardteam) { + super.handleTeamChanged(scoreboardteam); +- this.a.getPlayerList().sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, 2)); ++ this.sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, 2)); // CraftBukkit - Internal packet method + this.b(); + } + + public void handleTeamRemoved(ScoreboardTeam scoreboardteam) { + super.handleTeamRemoved(scoreboardteam); +- this.a.getPlayerList().sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, 1)); ++ this.sendAll(new PacketPlayOutScoreboardTeam(scoreboardteam, 1)); // CraftBukkit - Internal packet method + this.b(); + } + +@@ -160,6 +160,7 @@ + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); ++ if (entityplayer.getBukkitEntity().getScoreboard().getHandle() != this) continue; // CraftBukkit - Only players on this board + Iterator iterator1 = list.iterator(); + + while (iterator1.hasNext()) { +@@ -192,6 +193,7 @@ + + while (iterator.hasNext()) { + EntityPlayer entityplayer = (EntityPlayer) iterator.next(); ++ if (entityplayer.getBukkitEntity().getScoreboard().getHandle() != this) continue; // CraftBukkit - Only players on this board + Iterator iterator1 = list.iterator(); + + while (iterator1.hasNext()) { +@@ -215,4 +217,14 @@ + + return i; + } ++ ++ // CraftBukkit start - Send to players ++ private void sendAll(Packet packet) { ++ for (EntityPlayer entityplayer : (List<EntityPlayer>) this.a.getPlayerList().players) { ++ if (entityplayer.getBukkitEntity().getScoreboard().getHandle() == this) { ++ entityplayer.playerConnection.sendPacket(packet); ++ } ++ } ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/SecondaryWorldServer.patch b/nms-patches/SecondaryWorldServer.patch new file mode 100644 index 00000000..935246b6 --- /dev/null +++ b/nms-patches/SecondaryWorldServer.patch @@ -0,0 +1,15 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/SecondaryWorldServer.java 2014-11-27 08:59:46.889421019 +1100 ++++ src/main/java/net/minecraft/server/SecondaryWorldServer.java 2014-11-27 08:42:10.088851036 +1100 +@@ -4,8 +4,10 @@ + + private WorldServer a; + +- public SecondaryWorldServer(MinecraftServer minecraftserver, IDataManager idatamanager, int i, WorldServer worldserver, MethodProfiler methodprofiler) { +- super(minecraftserver, idatamanager, new SecondaryWorldData(worldserver.getWorldData()), i, methodprofiler); ++ // CraftBukkit start - Add WorldData, Environment and ChunkGenerator arguments ++ public SecondaryWorldServer(MinecraftServer minecraftserver, IDataManager idatamanager, int i, WorldServer worldserver, MethodProfiler methodprofiler, WorldData worldData, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) { ++ super(minecraftserver, idatamanager, worldData, i, methodprofiler, env, gen); ++ // CraftBukkit end + this.a = worldserver; + worldserver.af().a((IWorldBorderListener) (new SecondaryWorldServerInnerClass1(this))); + } diff --git a/nms-patches/ShapedRecipes.patch b/nms-patches/ShapedRecipes.patch new file mode 100644 index 00000000..5b3908ce --- /dev/null +++ b/nms-patches/ShapedRecipes.patch @@ -0,0 +1,77 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ShapedRecipes.java 2014-11-27 08:59:46.889421019 +1100 ++++ src/main/java/net/minecraft/server/ShapedRecipes.java 2014-11-27 08:42:10.164850887 +1100 +@@ -1,5 +1,11 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.craftbukkit.inventory.CraftShapedRecipe; ++// CraftBukkit end ++ ++ + public class ShapedRecipes implements IRecipe { + + private final int width; +@@ -14,6 +20,62 @@ + this.items = aitemstack; + this.result = itemstack; + } ++ ++ // CraftBukkit start ++ public org.bukkit.inventory.ShapedRecipe toBukkitRecipe() { ++ CraftItemStack result = CraftItemStack.asCraftMirror(this.result); ++ CraftShapedRecipe recipe = new CraftShapedRecipe(result, this); ++ switch (this.height) { ++ case 1: ++ switch (this.width) { ++ case 1: ++ recipe.shape("a"); ++ break; ++ case 2: ++ recipe.shape("ab"); ++ break; ++ case 3: ++ recipe.shape("abc"); ++ break; ++ } ++ break; ++ case 2: ++ switch (this.width) { ++ case 1: ++ recipe.shape("a","b"); ++ break; ++ case 2: ++ recipe.shape("ab","cd"); ++ break; ++ case 3: ++ recipe.shape("abc","def"); ++ break; ++ } ++ break; ++ case 3: ++ switch (this.width) { ++ case 1: ++ recipe.shape("a","b","c"); ++ break; ++ case 2: ++ recipe.shape("ab","cd","ef"); ++ break; ++ case 3: ++ recipe.shape("abc","def","ghi"); ++ break; ++ } ++ break; ++ } ++ char c = 'a'; ++ for (ItemStack stack : this.items) { ++ if (stack != null) { ++ recipe.setIngredient(c, org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(stack.getItem()), stack.getData()); ++ } ++ c++; ++ } ++ return recipe; ++ } ++ // CraftBukkit end + + public ItemStack b() { + return this.result; diff --git a/nms-patches/ShapelessRecipes.patch b/nms-patches/ShapelessRecipes.patch new file mode 100644 index 00000000..7e4230c9 --- /dev/null +++ b/nms-patches/ShapelessRecipes.patch @@ -0,0 +1,35 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ShapelessRecipes.java 2014-11-27 08:59:46.889421019 +1100 ++++ src/main/java/net/minecraft/server/ShapelessRecipes.java 2014-11-27 08:42:10.128850958 +1100 +@@ -5,6 +5,11 @@ + import java.util.Iterator; + import java.util.List; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.inventory.CraftItemStack; ++import org.bukkit.craftbukkit.inventory.CraftShapelessRecipe; ++// CraftBukkit end ++ + public class ShapelessRecipes implements IRecipe { + + private final ItemStack result; +@@ -14,6 +19,20 @@ + this.result = itemstack; + this.ingredients = list; + } ++ ++ // CraftBukkit start ++ @SuppressWarnings("unchecked") ++ public org.bukkit.inventory.ShapelessRecipe toBukkitRecipe() { ++ CraftItemStack result = CraftItemStack.asCraftMirror(this.result); ++ CraftShapelessRecipe recipe = new CraftShapelessRecipe(result, this); ++ for (ItemStack stack : (List<ItemStack>) this.ingredients) { ++ if (stack != null) { ++ recipe.addIngredient(org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(stack.getItem()), stack.getData()); ++ } ++ } ++ return recipe; ++ } ++ // CraftBukkit end + + public ItemStack b() { + return this.result; diff --git a/nms-patches/SlotFurnaceResult.patch b/nms-patches/SlotFurnaceResult.patch new file mode 100644 index 00000000..626529de --- /dev/null +++ b/nms-patches/SlotFurnaceResult.patch @@ -0,0 +1,31 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/SlotFurnaceResult.java 2014-11-27 08:59:46.893421001 +1100 ++++ src/main/java/net/minecraft/server/SlotFurnaceResult.java 2014-11-27 08:42:10.128850958 +1100 +@@ -1,5 +1,10 @@ + package net.minecraft.server; + ++// CraftBukkit start ++import org.bukkit.entity.Player; ++import org.bukkit.event.inventory.FurnaceExtractEvent; ++// CraftBukkit end ++ + public class SlotFurnaceResult extends Slot { + + private EntityHuman a; +@@ -49,6 +54,17 @@ + + i = j; + } ++ ++ // CraftBukkit start - fire FurnaceExtractEvent ++ Player player = (Player) a.getBukkitEntity(); ++ TileEntityFurnace furnace = ((TileEntityFurnace) this.inventory); ++ org.bukkit.block.Block block = a.world.getWorld().getBlockAt(furnace.position.getX(), furnace.position.getY(), furnace.position.getZ()); ++ ++ FurnaceExtractEvent event = new FurnaceExtractEvent(player, block, org.bukkit.craftbukkit.util.CraftMagicNumbers.getMaterial(itemstack.getItem()), itemstack.count, i); ++ a.world.getServer().getPluginManager().callEvent(event); ++ ++ i = event.getExpToDrop(); ++ // CraftBukkit end + + while (i > 0) { + j = EntityExperienceOrb.getOrbValue(i); diff --git a/nms-patches/SpawnerCreature.patch b/nms-patches/SpawnerCreature.patch new file mode 100644 index 00000000..96828173 --- /dev/null +++ b/nms-patches/SpawnerCreature.patch @@ -0,0 +1,109 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/SpawnerCreature.java 2014-11-27 08:59:46.893421001 +1100 ++++ src/main/java/net/minecraft/server/SpawnerCreature.java 2014-11-27 08:42:10.164850887 +1100 +@@ -6,10 +6,16 @@ + import java.util.Random; + import java.util.Set; + ++// CraftBukkit start ++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 Set b = Sets.newHashSet(); ++ private final LongHashSet b = new LongHashSet(); + + public SpawnerCreature() {} + +@@ -36,14 +42,18 @@ + for (int i1 = -b0; i1 <= b0; ++i1) { + for (k = -b0; k <= b0; ++k) { + boolean flag3 = i1 == -b0 || i1 == b0 || k == -b0 || k == b0; +- ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i1 + l, k + j); ++ // ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(i1 + l, k + j); + +- if (!this.b.contains(chunkcoordintpair)) { ++ // CraftBukkit start - use LongHash and LongHashSet ++ long chunkCoords = LongHash.toLong(i1 + l, k + j); ++ ++ if (!this.b.contains(chunkCoords)) { + ++i; +- if (!flag3 && worldserver.af().isInBounds(chunkcoordintpair)) { +- this.b.add(chunkcoordintpair); ++ if (!flag3 && worldserver.af().isInBounds(chunkCoords)) { ++ this.b.add(chunkCoords); + } + } ++ // CraftBukkit end + } + } + } +@@ -57,18 +67,41 @@ + + 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; ++ } ++ // CraftBukkit end + + if ((!enumcreaturetype.d() || flag1) && (enumcreaturetype.d() || flag) && (!enumcreaturetype.e() || flag2)) { + k = worldserver.a(enumcreaturetype.a()); +- int l1 = enumcreaturetype.b() * i / SpawnerCreature.a; ++ int l1 = limit * i / a; // CraftBukkit - use per-world limits + + if (k <= l1) { + Iterator iterator1 = this.b.iterator(); + + label115: + while (iterator1.hasNext()) { +- ChunkCoordIntPair chunkcoordintpair1 = (ChunkCoordIntPair) iterator1.next(); +- BlockPosition blockposition1 = getRandomPosition(worldserver, chunkcoordintpair1.x, chunkcoordintpair1.z); ++ // CraftBukkit start = use LongHash and LongObjectHashMap ++ long key = ((Long) iterator1.next()).longValue(); ++ BlockPosition blockposition1 = getRandomPosition(worldserver, LongHash.msw(key), LongHash.lsw(key)); + int i2 = blockposition1.getX(); + int j2 = blockposition1.getY(); + int k2 = blockposition1.getZ(); +@@ -120,7 +153,7 @@ + groupdataentity = entityinsentient.prepare(worldserver.E(new BlockPosition(entityinsentient)), groupdataentity); + if (entityinsentient.canSpawn()) { + ++l2; +- worldserver.addEntity(entityinsentient); ++ worldserver.addEntity(entityinsentient, SpawnReason.NATURAL); // CraftBukkit - Added a reason for spawning this creature + } + + if (l2 >= entityinsentient.bU()) { +@@ -214,8 +247,10 @@ + } + + entityinsentient.setPositionRotation((double) ((float) j1 + 0.5F), (double) blockposition.getY(), (double) ((float) k1 + 0.5F), random.nextFloat() * 360.0F, 0.0F); +- world.addEntity(entityinsentient); ++ // CraftBukkit start - Added a reason for spawning this creature, moved entityinsentient.prepare(groupdataentity) up + groupdataentity = entityinsentient.prepare(world.E(new BlockPosition(entityinsentient)), groupdataentity); ++ world.addEntity(entityinsentient, SpawnReason.CHUNK_GEN); ++ // CraftBukkit end + flag = true; + } + diff --git a/nms-patches/StatisticManager.patch b/nms-patches/StatisticManager.patch new file mode 100644 index 00000000..5b9486b3 --- /dev/null +++ b/nms-patches/StatisticManager.patch @@ -0,0 +1,15 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/StatisticManager.java 2014-11-27 08:59:46.893421001 +1100 ++++ src/main/java/net/minecraft/server/StatisticManager.java 2014-11-27 08:42:10.172850872 +1100 +@@ -19,6 +19,12 @@ + + public void b(EntityHuman entityhuman, Statistic statistic, int i) { + if (!statistic.d() || this.b((Achievement) statistic)) { ++ // CraftBukkit start - fire Statistic events ++ org.bukkit.event.Cancellable cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.handleStatisticsIncrease(entityhuman, statistic, this.getStatisticValue(statistic), i); ++ if (cancellable != null && cancellable.isCancelled()) { ++ return; ++ } ++ // CraftBukkit end + this.setStatistic(entityhuman, statistic, this.getStatisticValue(statistic) + i); + } + } diff --git a/nms-patches/SwitchHelperLogVariant.patch b/nms-patches/SwitchHelperLogVariant.patch new file mode 100644 index 00000000..e6b38cc5 --- /dev/null +++ b/nms-patches/SwitchHelperLogVariant.patch @@ -0,0 +1,9 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/SwitchHelperLogVariant.java 2014-11-27 08:59:46.897420984 +1100 ++++ src/main/java/net/minecraft/server/SwitchHelperLogVariant.java 2014-11-27 08:42:10.084851043 +1100 +@@ -1,5 +1,6 @@ + package net.minecraft.server; + ++// CraftBukkit - imported for visibility + class SwitchHelperLogVariant { + + static final int[] a = new int[EnumLogVariant.values().length]; diff --git a/nms-patches/ThreadCommandReader.patch b/nms-patches/ThreadCommandReader.patch new file mode 100644 index 00000000..87bf341a --- /dev/null +++ b/nms-patches/ThreadCommandReader.patch @@ -0,0 +1,43 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ThreadCommandReader.java 2014-11-27 08:59:46.897420984 +1100 ++++ src/main/java/net/minecraft/server/ThreadCommandReader.java 2014-11-27 08:42:10.084851043 +1100 +@@ -4,6 +4,8 @@ + import java.io.IOException; + import java.io.InputStreamReader; + ++import static org.bukkit.craftbukkit.Main.*; // CraftBukkit ++ + class ThreadCommandReader extends Thread { + + final DedicatedServer server; +@@ -14,13 +16,28 @@ + } + + public void run() { +- BufferedReader bufferedreader = new BufferedReader(new InputStreamReader(System.in)); ++ // CraftBukkit start ++ if (!useConsole) { ++ return; ++ } ++ // CraftBukkit end ++ ++ jline.console.ConsoleReader bufferedreader = this.server.reader; // CraftBukkit + + String s; + + try { +- while (!this.server.isStopped() && this.server.isRunning() && (s = bufferedreader.readLine()) != null) { +- this.server.issueCommand(s, this.server); ++ // CraftBukkit start - JLine disabling compatibility ++ while (!this.server.isStopped() && this.server.isRunning()) { ++ if (useJline) { ++ s = bufferedreader.readLine(">", null); ++ } else { ++ s = bufferedreader.readLine(); ++ } ++ if (s != null && s.trim().length() > 0) { // Trim to filter lines which are just spaces ++ this.server.issueCommand(s, this.server); ++ } ++ // CraftBukkit end + } + } catch (IOException ioexception) { + DedicatedServer.aR().error("Exception handling console input", ioexception); diff --git a/nms-patches/ThreadPlayerLookupUUID.patch b/nms-patches/ThreadPlayerLookupUUID.patch new file mode 100644 index 00000000..d0732690 --- /dev/null +++ b/nms-patches/ThreadPlayerLookupUUID.patch @@ -0,0 +1,72 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/ThreadPlayerLookupUUID.java 2014-11-27 08:59:46.901420966 +1100 ++++ src/main/java/net/minecraft/server/ThreadPlayerLookupUUID.java 2014-11-27 08:42:10.164850887 +1100 +@@ -5,6 +5,12 @@ + import java.math.BigInteger; + import java.util.UUID; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.util.Waitable; ++import org.bukkit.event.player.AsyncPlayerPreLoginEvent; ++import org.bukkit.event.player.PlayerPreLoginEvent; ++// CraftBukkit end ++ + class ThreadPlayerLookupUUID extends Thread { + + final LoginListener a; +@@ -22,6 +28,44 @@ + + LoginListener.a(this.a, LoginListener.a(this.a).aB().hasJoinedServer(new GameProfile((UUID) null, gameprofile.getName()), s)); + if (LoginListener.b(this.a) != null) { ++ // CraftBukkit start - fire PlayerPreLoginEvent ++ if (!this.a.networkManager.g()) { ++ return; ++ } ++ ++ String playerName = LoginListener.a(this.a).getName(); ++ java.net.InetAddress address = ((java.net.InetSocketAddress) a.networkManager.getSocketAddress()).getAddress(); ++ java.util.UUID uniqueId = LoginListener.b(this.a).getId(); ++ final org.bukkit.craftbukkit.CraftServer server = LoginListener.a(this.a).server; ++ ++ AsyncPlayerPreLoginEvent asyncEvent = new AsyncPlayerPreLoginEvent(playerName, address, uniqueId); ++ server.getPluginManager().callEvent(asyncEvent); ++ ++ if (PlayerPreLoginEvent.getHandlerList().getRegisteredListeners().length != 0) { ++ final PlayerPreLoginEvent event = new PlayerPreLoginEvent(playerName, address, uniqueId); ++ if (asyncEvent.getResult() != PlayerPreLoginEvent.Result.ALLOWED) { ++ event.disallow(asyncEvent.getResult(), asyncEvent.getKickMessage()); ++ } ++ Waitable<PlayerPreLoginEvent.Result> waitable = new Waitable<PlayerPreLoginEvent.Result>() { ++ @Override ++ protected PlayerPreLoginEvent.Result evaluate() { ++ server.getPluginManager().callEvent(event); ++ return event.getResult(); ++ }}; ++ ++ LoginListener.a(this.a).processQueue.add(waitable); ++ if (waitable.get() != PlayerPreLoginEvent.Result.ALLOWED) { ++ this.a.disconnect(event.getKickMessage()); ++ return; ++ } ++ } else { ++ if (asyncEvent.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) { ++ this.a.disconnect(asyncEvent.getKickMessage()); ++ return; ++ } ++ } ++ // CraftBukkit end ++ + LoginListener.e().info("UUID of player " + LoginListener.b(this.a).getName() + " is " + LoginListener.b(this.a).getId()); + LoginListener.a(this.a, EnumProtocolState.READY_TO_ACCEPT); + } else if (LoginListener.a(this.a).S()) { +@@ -41,6 +85,11 @@ + this.a.disconnect("Authentication servers are down. Please try again later, sorry!"); + LoginListener.e().error("Couldn\'t verify username because servers are unavailable"); + } ++ // CraftBukkit start - catch all exceptions ++ } catch (Exception exception) { ++ this.a.disconnect("Failed to verify username!"); ++ LoginListener.a(this.a).server.getLogger().log(java.util.logging.Level.WARNING, "Exception verifying " + LoginListener.a(this.a).getName(), exception); ++ // CraftBukkit end + } + + } diff --git a/nms-patches/TileEntity.patch b/nms-patches/TileEntity.patch new file mode 100644 index 00000000..dd76aea1 --- /dev/null +++ b/nms-patches/TileEntity.patch @@ -0,0 +1,24 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/TileEntity.java 2014-11-27 08:59:46.913420913 +1100 ++++ src/main/java/net/minecraft/server/TileEntity.java 2014-11-27 08:42:10.148850918 +1100 +@@ -6,6 +6,8 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++import org.bukkit.inventory.InventoryHolder; // CraftBukkit ++ + public abstract class TileEntity { + + private static final Logger a = LogManager.getLogger(); +@@ -182,4 +184,12 @@ + a(TileEntityFlowerPot.class, "FlowerPot"); + a(TileEntityBanner.class, "Banner"); + } ++ ++ // CraftBukkit start - add method ++ public InventoryHolder getOwner() { ++ org.bukkit.block.BlockState state = world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()).getState(); ++ if (state instanceof InventoryHolder) return (InventoryHolder) state; ++ return null; ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/TileEntityBeacon.patch b/nms-patches/TileEntityBeacon.patch new file mode 100644 index 00000000..a5179536 --- /dev/null +++ b/nms-patches/TileEntityBeacon.patch @@ -0,0 +1,54 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/TileEntityBeacon.java 2014-11-27 08:59:46.901420966 +1100 ++++ src/main/java/net/minecraft/server/TileEntityBeacon.java 2014-11-27 08:42:10.152850911 +1100 +@@ -5,6 +5,11 @@ + import java.util.Iterator; + import java.util.List; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ + public class TileEntityBeacon extends TileEntityContainer implements IUpdatePlayerListBox, IInventory { + + public static final MobEffectList[][] a = new MobEffectList[][] { { MobEffectList.FASTER_MOVEMENT, MobEffectList.FASTER_DIG}, { MobEffectList.RESISTANCE, MobEffectList.JUMP}, { MobEffectList.INCREASE_DAMAGE}, { MobEffectList.REGENERATION}}; +@@ -15,6 +20,30 @@ + private int l; + private ItemStack inventorySlot; + private String n; ++ // CraftBukkit start - add fields and methods ++ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>(); ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() { ++ return new ItemStack[] { this.inventorySlot }; ++ } ++ ++ public void onOpen(CraftHumanEntity who) { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) { ++ transaction.remove(who); ++ } ++ ++ public List<HumanEntity> getViewers() { ++ return transaction; ++ } ++ ++ public void setMaxStackSize(int size) { ++ maxStack = size; ++ } ++ // CraftBukkit end + + public TileEntityBeacon() {} + +@@ -182,7 +211,7 @@ + } + + public int getSize() { +- return 1; ++ return maxStack; // CraftBukkit + } + + public ItemStack getItem(int i) { diff --git a/nms-patches/TileEntityBrewingStand.patch b/nms-patches/TileEntityBrewingStand.patch new file mode 100644 index 00000000..80e791ad --- /dev/null +++ b/nms-patches/TileEntityBrewingStand.patch @@ -0,0 +1,94 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/TileEntityBrewingStand.java 2014-11-27 08:59:46.901420966 +1100 ++++ src/main/java/net/minecraft/server/TileEntityBrewingStand.java 2014-11-27 08:42:10.132850949 +1100 +@@ -3,6 +3,12 @@ + import java.util.Arrays; + import java.util.List; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++import org.bukkit.event.inventory.BrewEvent; ++// CraftBukkit end ++ + public class TileEntityBrewingStand extends TileEntityContainer implements IUpdatePlayerListBox, IWorldInventory { + + private static final int[] a = new int[] { 3}; +@@ -12,8 +18,35 @@ + private boolean[] i; + private Item j; + private String k; ++ private int lastTick = MinecraftServer.currentTick; // CraftBukkit - add field + + public TileEntityBrewingStand() {} ++ ++ ++ // CraftBukkit start - add fields and methods ++ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>(); ++ private int maxStack = 64; ++ ++ public void onOpen(CraftHumanEntity who) { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) { ++ transaction.remove(who); ++ } ++ ++ public List<HumanEntity> getViewers() { ++ return transaction; ++ } ++ ++ public ItemStack[] getContents() { ++ return this.items; ++ } ++ ++ public void setMaxStackSize(int size) { ++ maxStack = size; ++ } ++ // CraftBukkit end + + public String getName() { + return this.hasCustomName() ? this.k : "container.brewing"; +@@ -32,9 +65,14 @@ + } + + public void c() { ++ // CraftBukkit start - Use wall time instead of ticks for brewing ++ int elapsedTicks = MinecraftServer.currentTick - this.lastTick; ++ this.lastTick = MinecraftServer.currentTick; ++ + if (this.brewTime > 0) { +- --this.brewTime; +- if (this.brewTime == 0) { ++ this.brewTime -= elapsedTicks; ++ if (this.brewTime <= 0) { // == -> <= ++ // CraftBukkit end + this.o(); + this.update(); + } else if (!this.n()) { +@@ -109,6 +147,16 @@ + private void o() { + if (this.n()) { + ItemStack itemstack = this.items[3]; ++ ++ // CraftBukkit start ++ if (getOwner() != null) { ++ BrewEvent event = new BrewEvent(world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()), (org.bukkit.inventory.BrewerInventory) this.getOwner().getInventory()); ++ org.bukkit.Bukkit.getPluginManager().callEvent(event); ++ if (event.isCancelled()) { ++ return; ++ } ++ } ++ // CraftBukkit end + + for (int i = 0; i < 3; ++i) { + if (this.items[i] != null && this.items[i].getItem() == Items.POTION) { +@@ -221,7 +269,7 @@ + } + + public int getMaxStackSize() { +- return 64; ++ return this.maxStack; // CraftBukkit + } + + public boolean a(EntityHuman entityhuman) { diff --git a/nms-patches/TileEntityChest.patch b/nms-patches/TileEntityChest.patch new file mode 100644 index 00000000..52d98548 --- /dev/null +++ b/nms-patches/TileEntityChest.patch @@ -0,0 +1,104 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/TileEntityChest.java 2014-11-27 08:59:46.905420949 +1100 ++++ src/main/java/net/minecraft/server/TileEntityChest.java 2014-11-27 08:42:10.168850880 +1100 +@@ -3,6 +3,11 @@ + import java.util.Iterator; + import java.util.List; + ++// CraftBukkit start ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ + public class TileEntityChest extends TileEntityContainer implements IUpdatePlayerListBox, IInventory { + + private ItemStack[] items = new ItemStack[27]; +@@ -19,6 +24,31 @@ + private String p; + + public TileEntityChest() {} ++ ++ // CraftBukkit start - add fields and methods ++ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>(); ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() { ++ return this.items; ++ } ++ ++ public void onOpen(CraftHumanEntity who) { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) { ++ transaction.remove(who); ++ } ++ ++ public List<HumanEntity> getViewers() { ++ return transaction; ++ } ++ ++ public void setMaxStackSize(int size) { ++ maxStack = size; ++ } ++ // CraftBukkit end + + public int getSize() { + return 27; +@@ -125,10 +155,11 @@ + } + + public int getMaxStackSize() { +- return 64; ++ return maxStack; // CraftBukkit + } + + public boolean a(EntityHuman entityhuman) { ++ if (this.world == null) return true; // CraftBukkit + return this.world.getTileEntity(this.position) != this ? false : entityhuman.e((double) this.position.getX() + 0.5D, (double) this.position.getY() + 0.5D, (double) this.position.getZ() + 0.5D) <= 64.0D; + } + +@@ -304,9 +335,22 @@ + if (this.l < 0) { + this.l = 0; + } ++ ++ int oldPower = Math.max(0, Math.min(15, this.l)); // CraftBukkit - Get power before new viewer is added + + ++this.l; ++ if (this.world == null) return; // CraftBukkit + this.world.playBlockAction(this.position, this.w(), 1, this.l); ++ ++ // CraftBukkit start - Call redstone event ++ if (this.w() == Blocks.TRAPPED_CHEST) { ++ int newPower = Math.max(0, Math.min(15, this.l)); ++ ++ if (oldPower != newPower) { ++ org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(world, position.getX(), position.getY(), position.getZ(), oldPower, newPower); ++ } ++ } ++ // CraftBukkit end + this.world.applyPhysics(this.position, this.w()); + this.world.applyPhysics(this.position.down(), this.w()); + } +@@ -315,8 +359,21 @@ + + public void closeContainer(EntityHuman entityhuman) { + if (!entityhuman.v() && this.w() instanceof BlockChest) { ++ int oldPower = Math.max(0, Math.min(15, this.l)); // CraftBukkit - Get power before new viewer is added ++ + --this.l; ++ if (this.world == null) return; // CraftBukkit + this.world.playBlockAction(this.position, this.w(), 1, this.l); ++ ++ // CraftBukkit start - Call redstone event ++ if (this.w() == Blocks.TRAPPED_CHEST) { ++ int newPower = Math.max(0, Math.min(15, this.l)); ++ ++ if (oldPower != newPower) { ++ org.bukkit.craftbukkit.event.CraftEventFactory.callRedstoneChange(world, position.getX(), position.getY(), position.getZ(), oldPower, newPower); ++ } ++ } ++ // CraftBukkit end + this.world.applyPhysics(this.position, this.w()); + this.world.applyPhysics(this.position.down(), this.w()); + } diff --git a/nms-patches/TileEntityCommandListener.patch b/nms-patches/TileEntityCommandListener.patch new file mode 100644 index 00000000..92b43a41 --- /dev/null +++ b/nms-patches/TileEntityCommandListener.patch @@ -0,0 +1,10 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/TileEntityCommandListener.java 2014-11-27 08:59:46.905420949 +1100 ++++ src/main/java/net/minecraft/server/TileEntityCommandListener.java 2014-11-27 08:42:10.132850949 +1100 +@@ -6,6 +6,7 @@ + + TileEntityCommandListener(TileEntityCommand tileentitycommand) { + this.a = tileentitycommand; ++ sender = new org.bukkit.craftbukkit.command.CraftBlockCommandSender(this); // CraftBukkit - add sender + } + + public BlockPosition getChunkCoordinates() { diff --git a/nms-patches/TileEntityDispenser.patch b/nms-patches/TileEntityDispenser.patch new file mode 100644 index 00000000..918eb349 --- /dev/null +++ b/nms-patches/TileEntityDispenser.patch @@ -0,0 +1,63 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/TileEntityDispenser.java 2014-11-27 08:59:46.905420949 +1100 ++++ src/main/java/net/minecraft/server/TileEntityDispenser.java 2014-11-27 08:42:10.116850981 +1100 +@@ -2,11 +2,43 @@ + + import java.util.Random; + ++// CraftBukkit start ++import java.util.List; ++ ++import org.bukkit.craftbukkit.entity.CraftHumanEntity; ++import org.bukkit.entity.HumanEntity; ++// CraftBukkit end ++ + public class TileEntityDispenser extends TileEntityContainer implements IInventory { + + private static final Random f = new Random(); + private ItemStack[] items = new ItemStack[9]; + protected String a; ++ ++ // CraftBukkit start - add fields and methods ++ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>(); ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() { ++ return this.items; ++ } ++ ++ public void onOpen(CraftHumanEntity who) { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) { ++ transaction.remove(who); ++ } ++ ++ public List<HumanEntity> getViewers() { ++ return transaction; ++ } ++ ++ public void setMaxStackSize(int size) { ++ maxStack = size; ++ } ++ // CraftBukkit end + + public TileEntityDispenser() {} + +@@ -58,6 +90,7 @@ + + for (int k = 0; k < this.items.length; ++k) { + if (this.items[k] != null && TileEntityDispenser.f.nextInt(j++) == 0) { ++ if (this.items[k].count == 0) continue; // CraftBukkit + i = k; + } + } +@@ -140,7 +173,7 @@ + } + + public int getMaxStackSize() { +- return 64; ++ return maxStack; // CraftBukkit + } + + public boolean a(EntityHuman entityhuman) { diff --git a/nms-patches/TileEntityFurnace.patch b/nms-patches/TileEntityFurnace.patch new file mode 100644 index 00000000..911664c5 --- /dev/null +++ b/nms-patches/TileEntityFurnace.patch @@ -0,0 +1,183 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/TileEntityFurnace.java 2014-11-27 08:59:46.909420931 +1100 ++++ src/main/java/net/minecraft/server/TileEntityFurnace.java 2014-11-27 08:42:10.156850903 +1100 +@@ -1,5 +1,15 @@ + package net.minecraft.server; + ++// 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 IUpdatePlayerListBox, IWorldInventory { + + private static final int[] a = new int[] { 0}; +@@ -11,6 +21,32 @@ + public 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<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>(); ++ ++ public ItemStack[] getContents() { ++ return this.items; ++ } ++ ++ public void onOpen(CraftHumanEntity who) { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) { ++ transaction.remove(who); ++ } ++ ++ public List<HumanEntity> getViewers() { ++ return transaction; ++ } ++ ++ public void setMaxStackSize(int size) { ++ maxStack = size; ++ } ++ // CraftBukkit end + + public TileEntityFurnace() {} + +@@ -132,7 +168,7 @@ + } + + public int getMaxStackSize() { +- return 64; ++ return maxStack; // CraftBukkit + } + + public boolean isBurning() { +@@ -142,9 +178,27 @@ + public void c() { + boolean flag = this.isBurning(); + 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 ++ if (this.isBurning() && this.canBurn()) { ++ this.cookTime += elapsedTicks; ++ if (this.cookTime >= this.cookTimeTotal) { ++ this.cookTime = 0; ++ this.cookTimeTotal = this.a(this.items[0]); ++ this.burn(); ++ flag1 = true; ++ } ++ } else { ++ this.cookTime = 0; ++ } ++ // CraftBukkit end + + if (this.isBurning()) { +- --this.burnTime; ++ this.burnTime -= elapsedTicks; // CraftBukkit - use elapsedTicks in place of constant + } + + if (!this.world.isStatic) { +@@ -153,9 +207,21 @@ + this.cookTime = MathHelper.clamp(this.cookTime - 2, 0, this.cookTimeTotal); + } + } else { +- if (!this.isBurning() && this.canBurn()) { +- this.ticksForCurrentFuel = this.burnTime = fuelTime(this.items[1]); +- if (this.isBurning()) { ++ // CraftBukkit start - Handle multiple elapsed ticks ++ if (this.burnTime <= 0 && this.canBurn()) { // CraftBukkit - == to <= ++ CraftItemStack fuel = CraftItemStack.asCraftMirror(this.items[1]); ++ ++ FurnaceBurnEvent furnaceBurnEvent = new FurnaceBurnEvent(this.world.getWorld().getBlockAt(position.getX(), position.getY(), position.getZ()), fuel, fuelTime(this.items[1])); ++ 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 (this.items[1] != null) { + --this.items[1].count; +@@ -167,7 +233,8 @@ + } + } + } +- ++ ++ /* CraftBukkit start - Moved up + if (this.isBurning() && this.canBurn()) { + ++this.cookTime; + if (this.cookTime == this.cookTimeTotal) { +@@ -179,6 +246,7 @@ + } else { + this.cookTime = 0; + } ++ */ + } + + if (flag != this.isBurning()) { +@@ -202,20 +270,48 @@ + return false; + } else { + ItemStack itemstack = RecipesFurnace.getInstance().getResult(this.items[0]); +- +- return itemstack == null ? false : (this.items[2] == null ? true : (!this.items[2].doMaterialsMatch(itemstack) ? false : (this.items[2].count < this.getMaxStackSize() && this.items[2].count < this.items[2].getMaxStackSize() ? true : this.items[2].count < itemstack.getMaxStackSize()))); ++ // CraftBukkit - consider resultant count instead of current count ++ return itemstack == null ? false : (this.items[2] == null ? true : (!this.items[2].doMaterialsMatch(itemstack) ? false : (this.items[2].count + itemstack.count <= this.getMaxStackSize() && this.items[2].count < this.items[2].getMaxStackSize() ? true : this.items[2].count + itemstack.count <= itemstack.getMaxStackSize()))); ++ + } + } + + public void burn() { + if (this.canBurn()) { + ItemStack itemstack = RecipesFurnace.getInstance().getResult(this.items[0]); ++ ++ // CraftBukkit start - fire FurnaceSmeltEvent ++ CraftItemStack source = CraftItemStack.asCraftMirror(this.items[0]); ++ org.bukkit.inventory.ItemStack result = CraftItemStack.asBukkitCopy(itemstack); + ++ 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(); ++ itemstack = CraftItemStack.asNMSCopy(result); ++ ++ if (itemstack != null) { ++ if (this.items[2] == null) { ++ this.items[2] = itemstack; ++ } else if (CraftItemStack.asCraftMirror(this.items[2]).isSimilar(result)) { ++ this.items[2].count += itemstack.count; ++ } else { ++ return; ++ } ++ } ++ ++ /* + if (this.items[2] == null) { + this.items[2] = itemstack.cloneItemStack(); + } else if (this.items[2].getItem() == itemstack.getItem()) { + ++this.items[2].count; + } ++ */ ++ // CraftBukkit end + + if (this.items[0].getItem() == Item.getItemOf(Blocks.SPONGE) && this.items[0].getData() == 1 && this.items[1] != null && this.items[1].getItem() == Items.BUCKET) { + this.items[1] = new ItemStack(Items.WATER_BUCKET); diff --git a/nms-patches/TileEntityHopper.patch b/nms-patches/TileEntityHopper.patch new file mode 100644 index 00000000..a8f1dc57 --- /dev/null +++ b/nms-patches/TileEntityHopper.patch @@ -0,0 +1,154 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/TileEntityHopper.java 2014-11-27 08:59:46.909420931 +1100 ++++ src/main/java/net/minecraft/server/TileEntityHopper.java 2014-11-27 08:42:10.132850949 +1100 +@@ -2,11 +2,45 @@ + + import java.util.List; + ++// 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 TileEntityContainer implements IHopper, IUpdatePlayerListBox { + + private ItemStack[] items = new ItemStack[5]; + private String f; + private int g = -1; ++ ++ // CraftBukkit start - add fields and methods ++ public List<HumanEntity> transaction = new java.util.ArrayList<HumanEntity>(); ++ private int maxStack = MAX_STACK; ++ ++ public ItemStack[] getContents() { ++ return this.items; ++ } ++ ++ public void onOpen(CraftHumanEntity who) { ++ transaction.add(who); ++ } ++ ++ public void onClose(CraftHumanEntity who) { ++ transaction.remove(who); ++ } ++ ++ public List<HumanEntity> getViewers() { ++ return transaction; ++ } ++ ++ public void setMaxStackSize(int size) { ++ maxStack = size; ++ } ++ // CraftBukkit end + + public TileEntityHopper() {} + +@@ -119,7 +153,7 @@ + } + + public int getMaxStackSize() { +- return 64; ++ return maxStack; // CraftBukkit + } + + public boolean a(EntityHuman entityhuman) { +@@ -215,10 +249,35 @@ + for (int i = 0; i < this.getSize(); ++i) { + if (this.getItem(i) != null) { + ItemStack itemstack = this.getItem(i).cloneItemStack(); +- ItemStack itemstack1 = addItem(iinventory, this.splitStack(i, 1), enumdirection); ++ // ItemStack itemstack1 = addItem(iinventory, this.splitStack(i, 1), enumdirection); ++ ++ // CraftBukkit start - Call event when pushing items into other inventories ++ CraftItemStack oitemstack = CraftItemStack.asCraftMirror(this.splitStack(i, 1)); ++ ++ 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.d(8); // Delay hopper checks ++ return false; ++ } ++ ItemStack itemstack1 = addItem(iinventory, CraftItemStack.asNMSCopy(event.getItem()), enumdirection); + + if (itemstack1 == null || itemstack1.count == 0) { +- iinventory.update(); ++ if (event.getItem().equals(oitemstack)) { ++ iinventory.update(); ++ } else { ++ this.setItem(i, itemstack); ++ } ++ // CraftBukkit end + return true; + } + +@@ -325,10 +384,41 @@ + + if (itemstack != null && b(iinventory, itemstack, i, enumdirection)) { + ItemStack itemstack1 = itemstack.cloneItemStack(); +- ItemStack itemstack2 = addItem(ihopper, iinventory.splitStack(i, 1), (EnumDirection) null); ++ // ItemStack itemstack2 = addItem(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, 1)); ++ ++ 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).d(8); // Delay hopper checks ++ } else if (ihopper instanceof EntityMinecartHopper) { ++ ((EntityMinecartHopper) ihopper).l(4); // Delay hopper minecart checks ++ } ++ ++ return false; ++ } ++ ItemStack itemstack2 = addItem(ihopper, CraftItemStack.asNMSCopy(event.getItem()), null); + + if (itemstack2 == null || itemstack2.count == 0) { +- iinventory.update(); ++ if (event.getItem().equals(oitemstack)) { ++ iinventory.update(); ++ } else { ++ iinventory.setItem(i, itemstack1); ++ } ++ // CraftBukkit end + return true; + } + +@@ -344,6 +434,14 @@ + if (entityitem == null) { + return false; + } else { ++ // CraftBukkit start ++ InventoryPickupItemEvent event = new InventoryPickupItemEvent(iinventory.getOwner().getInventory(), (org.bukkit.entity.Item) entityitem.getBukkitEntity()); ++ entityitem.world.getServer().getPluginManager().callEvent(event); ++ if (event.isCancelled()) { ++ return false; ++ } ++ // CraftBukkit end ++ + ItemStack itemstack = entityitem.getItemStack().cloneItemStack(); + ItemStack itemstack1 = addItem(iinventory, itemstack, (EnumDirection) null); + diff --git a/nms-patches/TileEntityNote.patch b/nms-patches/TileEntityNote.patch new file mode 100644 index 00000000..fd9a329b --- /dev/null +++ b/nms-patches/TileEntityNote.patch @@ -0,0 +1,16 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/TileEntityNote.java 2014-11-27 08:59:46.913420913 +1100 ++++ src/main/java/net/minecraft/server/TileEntityNote.java 2014-11-27 08:42:10.172850872 +1100 +@@ -44,7 +44,12 @@ + b0 = 4; + } + +- world.playBlockAction(blockposition, Blocks.NOTEBLOCK, b0, this.note); ++ // CraftBukkit start ++ org.bukkit.event.block.NotePlayEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callNotePlayEvent(this.world, blockposition.getX(), blockposition.getY(), blockposition.getZ(), b0, this.note); ++ if (!event.isCancelled()) { ++ world.playBlockAction(blockposition, Blocks.NOTEBLOCK, event.getInstrument().getType(), event.getNote().getId()); ++ } ++ // CraftBukkit end + } + } + } diff --git a/nms-patches/TileEntityPiston.patch b/nms-patches/TileEntityPiston.patch new file mode 100644 index 00000000..0c1fa1d0 --- /dev/null +++ b/nms-patches/TileEntityPiston.patch @@ -0,0 +1,10 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/TileEntityPiston.java 2014-11-27 08:59:46.917420896 +1100 ++++ src/main/java/net/minecraft/server/TileEntityPiston.java 2014-11-27 08:42:10.152850911 +1100 +@@ -104,6 +104,7 @@ + } + + public void c() { ++ if (this.world == null) return; // CraftBukkit + this.j = this.i; + if (this.j >= 1.0F) { + this.a(1.0F, 0.25F); diff --git a/nms-patches/TileEntityRecordPlayer.patch b/nms-patches/TileEntityRecordPlayer.patch new file mode 100644 index 00000000..068fc0b2 --- /dev/null +++ b/nms-patches/TileEntityRecordPlayer.patch @@ -0,0 +1,14 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/TileEntityRecordPlayer.java 2014-11-27 08:59:46.917420896 +1100 ++++ src/main/java/net/minecraft/server/TileEntityRecordPlayer.java 2014-11-27 08:42:10.164850887 +1100 +@@ -29,6 +29,11 @@ + } + + public void setRecord(ItemStack itemstack) { ++ // CraftBukkit start - There can only be one ++ if (itemstack != null) { ++ itemstack.count = 1; ++ } ++ // CraftBukkit end + this.record = itemstack; + this.update(); + } diff --git a/nms-patches/TileEntitySkull.patch b/nms-patches/TileEntitySkull.patch new file mode 100644 index 00000000..1504054f --- /dev/null +++ b/nms-patches/TileEntitySkull.patch @@ -0,0 +1,13 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/TileEntitySkull.java 2014-11-27 08:59:46.917420896 +1100 ++++ src/main/java/net/minecraft/server/TileEntitySkull.java 2014-11-27 08:42:10.168850880 +1100 +@@ -105,4 +105,10 @@ + public void setRotation(int i) { + this.rotation = i; + } ++ ++ // CraftBukkit start - add method ++ public int getRotation() { ++ return this.rotation; ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/Village.patch b/nms-patches/Village.patch new file mode 100644 index 00000000..9b58e724 --- /dev/null +++ b/nms-patches/Village.patch @@ -0,0 +1,11 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/Village.java 2014-11-27 08:59:46.921420877 +1100 ++++ src/main/java/net/minecraft/server/Village.java 2014-11-27 08:42:10.104851005 +1100 +@@ -60,7 +60,7 @@ + EntityIronGolem entityirongolem = new EntityIronGolem(this.a); + + entityirongolem.setPosition(vec3d.a, vec3d.b, vec3d.c); +- this.a.addEntity(entityirongolem); ++ this.a.addEntity(entityirongolem, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.VILLAGE_DEFENSE); // CraftBukkit + ++this.l; + } + } diff --git a/nms-patches/VillageSiege.patch b/nms-patches/VillageSiege.patch new file mode 100644 index 00000000..f6da3f10 --- /dev/null +++ b/nms-patches/VillageSiege.patch @@ -0,0 +1,11 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/VillageSiege.java 2014-11-27 08:59:46.921420877 +1100 ++++ src/main/java/net/minecraft/server/VillageSiege.java 2014-11-27 08:42:10.100851012 +1100 +@@ -140,7 +140,7 @@ + } + + entityzombie.setPositionRotation(vec3d.a, vec3d.b, vec3d.c, this.a.random.nextFloat() * 360.0F, 0.0F); +- this.a.addEntity(entityzombie); ++ this.a.addEntity(entityzombie, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.VILLAGE_INVASION); // CraftBukkit + BlockPosition blockposition = this.f.a(); + + entityzombie.a(blockposition, this.f.b()); diff --git a/nms-patches/World.patch b/nms-patches/World.patch new file mode 100644 index 00000000..03912ea0 --- /dev/null +++ b/nms-patches/World.patch @@ -0,0 +1,560 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/World.java 2014-11-27 08:59:46.933420825 +1100 ++++ src/main/java/net/minecraft/server/World.java 2014-11-27 08:42:10.132850949 +1100 +@@ -13,6 +13,22 @@ + import java.util.UUID; + import java.util.concurrent.Callable; + ++// CraftBukkit start ++import org.bukkit.Bukkit; ++import org.bukkit.block.BlockState; ++import org.bukkit.craftbukkit.util.CraftMagicNumbers; ++import org.bukkit.craftbukkit.util.LongHashSet; ++import org.bukkit.generator.ChunkGenerator; ++import org.bukkit.craftbukkit.CraftServer; ++import org.bukkit.craftbukkit.CraftWorld; ++import org.bukkit.craftbukkit.event.CraftEventFactory; ++import org.bukkit.event.block.BlockCanBuildEvent; ++import org.bukkit.event.block.BlockPhysicsEvent; ++import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; ++import org.bukkit.event.weather.WeatherChangeEvent; ++import org.bukkit.event.weather.ThunderChangeEvent; ++// CraftBukkit end ++ + public abstract class World implements IBlockAccess { + + protected boolean e; +@@ -47,7 +63,8 @@ + private final Calendar J = Calendar.getInstance(); + public Scoreboard scoreboard = new Scoreboard(); + public final boolean isStatic; +- protected Set chunkTickList = Sets.newHashSet(); ++ // CraftBukkit - longhashset ++ protected LongHashSet chunkTickList = new LongHashSet(); + private int K; + public boolean allowMonsters; + public boolean allowAnimals; +@@ -55,7 +72,39 @@ + private final WorldBorder M; + int[] H; + +- protected World(IDataManager idatamanager, WorldData worlddata, WorldProvider worldprovider, MethodProfiler methodprofiler, boolean flag) { ++ // CraftBukkit start Added the following ++ private final CraftWorld world; ++ public boolean pvpMode; ++ public boolean keepSpawnInMemory = true; ++ public ChunkGenerator generator; ++ ++ public boolean captureBlockStates = false; ++ public boolean captureTreeGeneration = false; ++ public ArrayList<BlockState> capturedBlockStates= new ArrayList<BlockState>(); ++ public long ticksPerAnimalSpawns; ++ public long ticksPerMonsterSpawns; ++ public boolean populating; ++ private int tickPosition; ++ ++ public CraftWorld getWorld() { ++ return this.world; ++ } ++ ++ public CraftServer getServer() { ++ return (CraftServer) Bukkit.getServer(); ++ } ++ ++ public Chunk getChunkIfLoaded(int x, int z) { ++ return ((ChunkProviderServer) this.chunkProvider).getChunkIfLoaded(x, z); ++ } ++ ++ protected World(IDataManager idatamanager, WorldData worlddata, WorldProvider worldprovider, MethodProfiler methodprofiler, boolean flag, ChunkGenerator gen, org.bukkit.World.Environment env) { ++ this.generator = gen; ++ this.world = new CraftWorld((WorldServer) this, gen, env); ++ this.ticksPerAnimalSpawns = this.getServer().getTicksPerAnimalSpawns(); // CraftBukkit ++ this.ticksPerMonsterSpawns = this.getServer().getTicksPerMonsterSpawns(); // CraftBukkit ++ // CraftBukkit end ++ + this.K = this.random.nextInt(12000); + this.allowMonsters = true; + this.allowAnimals = true; +@@ -66,6 +115,8 @@ + this.worldProvider = worldprovider; + this.isStatic = flag; + this.M = worldprovider.getWorldBorder(); ++ ++ this.getServer().addWorld(this.world); // CraftBukkit + } + + public World b() { +@@ -184,6 +235,27 @@ + } + + public boolean setTypeAndData(BlockPosition blockposition, IBlockData iblockdata, int i) { ++ // CraftBukkit start - tree generation ++ if (this.captureTreeGeneration) { ++ BlockState blockstate = null; ++ Iterator<BlockState> it = capturedBlockStates.iterator(); ++ while (it.hasNext()) { ++ BlockState previous = it.next(); ++ if (previous.getX() == blockposition.getX() && previous.getY() == blockposition.getY() && previous.getZ() == blockposition.getZ()) { ++ blockstate = previous; ++ it.remove(); ++ break; ++ } ++ } ++ if (blockstate == null) { ++ blockstate = org.bukkit.craftbukkit.block.CraftBlockState.getBlockState(this, blockposition.getX(), blockposition.getY(), blockposition.getZ(), i); ++ } ++ blockstate.setTypeId(CraftMagicNumbers.getId(iblockdata.getBlock())); ++ blockstate.setRawData((byte) iblockdata.getBlock().toLegacyData(iblockdata)); ++ this.capturedBlockStates.add(blockstate); ++ return true; ++ } ++ // CraftBukkit end + if (!this.isValidLocation(blockposition)) { + return false; + } else if (!this.isStatic && this.worldData.getType() == WorldType.DEBUG_ALL_BLOCK_STATES) { +@@ -191,9 +263,23 @@ + } else { + Chunk chunk = this.getChunkAtWorldCoords(blockposition); + Block block = iblockdata.getBlock(); ++ ++ // CraftBukkit start - capture blockstates ++ BlockState blockstate = null; ++ if (this.captureBlockStates) { ++ blockstate = org.bukkit.craftbukkit.block.CraftBlockState.getBlockState(this, blockposition.getX(), blockposition.getY(), blockposition.getZ(), i); ++ this.capturedBlockStates.add(blockstate); ++ } ++ // CraftBukkit end ++ + IBlockData iblockdata1 = chunk.a(blockposition, iblockdata); + + if (iblockdata1 == null) { ++ // CraftBukkit start - remove blockstate if failed ++ if (!this.captureBlockStates) { ++ this.capturedBlockStates.remove(blockstate); ++ } ++ // CraftBukkit end + return false; + } else { + Block block1 = iblockdata1.getBlock(); +@@ -204,6 +290,7 @@ + this.methodProfiler.b(); + } + ++ /* + if ((i & 2) != 0 && (!this.isStatic || (i & 4) == 0) && chunk.isReady()) { + this.notify(blockposition); + } +@@ -214,12 +301,35 @@ + this.updateAdjacentComparators(blockposition, block); + } + } ++ */ ++ ++ // CraftBukkit start ++ if (!this.captureBlockStates) { // Don't notify clients or update physics while capturing blockstates ++ // Modularize client and physic updates ++ notifyAndUpdatePhysics(blockposition, chunk, block1, block, i); ++ } ++ // CraftBukkit end + + return true; + } + } + } + ++ // CraftBukkit start - Split off from original setTypeAndData(int i, int j, int k, Block block, int l, int i1) method in order to directly send client and physic updates ++ public void notifyAndUpdatePhysics(BlockPosition blockposition, Chunk chunk, Block oldBlock, Block newBLock, int flag) { ++ if ((flag & 2) != 0 && (chunk == null || chunk.isReady())) { // allow chunk to be null here as chunk.isReady() is false when we send our notification during block placement ++ this.notify(blockposition); ++ } ++ ++ if (!this.isStatic && (flag & 1) != 0) { ++ this.update(blockposition, oldBlock); ++ if (newBLock.isComplexRedstone()) { ++ this.updateAdjacentComparators(blockposition, newBLock); ++ } ++ } ++ } ++ // CraftBukkit end ++ + public boolean setAir(BlockPosition blockposition) { + return this.setTypeAndData(blockposition, Blocks.AIR.getBlockData(), 3); + } +@@ -253,6 +363,11 @@ + + public void update(BlockPosition blockposition, Block block) { + if (this.worldData.getType() != WorldType.DEBUG_ALL_BLOCK_STATES) { ++ // CraftBukkit start ++ if (populating) { ++ return; ++ } ++ // CraftBukkit end + this.applyPhysics(blockposition, block); + } + +@@ -328,6 +443,17 @@ + IBlockData iblockdata = this.getType(blockposition); + + try { ++ // CraftBukkit start ++ CraftWorld world = ((WorldServer) this).getWorld(); ++ if (world != null) { ++ BlockPhysicsEvent event = new BlockPhysicsEvent(world.getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), CraftMagicNumbers.getId(block)); ++ this.getServer().getPluginManager().callEvent(event); ++ ++ if (event.isCancelled()) { ++ return; ++ } ++ } ++ // CraftBukkit end + iblockdata.getBlock().doPhysics(this, blockposition, iblockdata, block); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Exception while updating neighbours"); +@@ -497,6 +623,17 @@ + } + + public IBlockData getType(BlockPosition blockposition) { ++ // CraftBukkit start - tree generation ++ if (captureTreeGeneration) { ++ Iterator<BlockState> it = capturedBlockStates.iterator(); ++ while (it.hasNext()) { ++ BlockState previous = it.next(); ++ if (previous.getX() == blockposition.getX() && previous.getY() == blockposition.getY() && previous.getZ() == blockposition.getZ()) { ++ return CraftMagicNumbers.getBlock(previous.getTypeId()).fromLegacyData(previous.getRawData()); ++ } ++ } ++ } ++ // CraftBukkit end + if (!this.isValidLocation(blockposition)) { + return Blocks.AIR.getBlockData(); + } else { +@@ -704,6 +841,13 @@ + } + + public boolean addEntity(Entity entity) { ++ // CraftBukkit start - Used for entities other than creatures ++ return addEntity(entity, SpawnReason.DEFAULT); ++ } ++ ++ public boolean addEntity(Entity entity, SpawnReason spawnReason) { // Changed signature, added SpawnReason ++ if (entity == null) return false; ++ // CraftBukkit end + int i = MathHelper.floor(entity.locX / 16.0D); + int j = MathHelper.floor(entity.locZ / 16.0D); + boolean flag = entity.attachedToPlayer; +@@ -712,7 +856,35 @@ + flag = true; + } + ++ // CraftBukkit start ++ org.bukkit.event.Cancellable event = null; ++ if (entity instanceof EntityLiving && !(entity instanceof EntityPlayer)) { ++ boolean isAnimal = entity instanceof EntityAnimal || entity instanceof EntityWaterAnimal || entity instanceof EntityGolem; ++ boolean isMonster = entity instanceof EntityMonster || entity instanceof EntityGhast || entity instanceof EntitySlime; ++ ++ if (spawnReason != SpawnReason.CUSTOM) { ++ if (isAnimal && !allowAnimals || isMonster && !allowMonsters) { ++ entity.dead = true; ++ return false; ++ } ++ } ++ ++ event = CraftEventFactory.callCreatureSpawnEvent((EntityLiving) entity, spawnReason); ++ } else if (entity instanceof EntityItem) { ++ event = CraftEventFactory.callItemSpawnEvent((EntityItem) entity); ++ } else if (entity.getBukkitEntity() instanceof org.bukkit.entity.Projectile) { ++ // Not all projectiles extend EntityProjectile, so check for Bukkit interface instead ++ event = CraftEventFactory.callProjectileLaunchEvent(entity); ++ } ++ ++ if (event != null && (event.isCancelled() || entity.dead)) { ++ entity.dead = true; ++ return false; ++ } ++ // CraftBukkit end ++ + if (!flag && !this.isChunkLoaded(i, j, true)) { ++ entity.dead = true; + return false; + } else { + if (entity instanceof EntityHuman) { +@@ -734,6 +906,7 @@ + ((IWorldAccess) this.u.get(i)).a(entity); + } + ++ entity.valid = true; // CraftBukkit + } + + protected void b(Entity entity) { +@@ -741,6 +914,7 @@ + ((IWorldAccess) this.u.get(i)).b(entity); + } + ++ entity.valid = false; // CraftBukkit + } + + public void kill(Entity entity) { +@@ -775,7 +949,15 @@ + this.getChunkAt(i, j).b(entity); + } + +- this.entityList.remove(entity); ++ // CraftBukkit start - Decrement loop variable field if we've already ticked this entity ++ int index = this.entityList.indexOf(entity); ++ if (index != -1) { ++ if (index <= this.tickPosition) { ++ this.tickPosition--; ++ } ++ this.entityList.remove(index); ++ } ++ // CraftBukkit end + this.b(entity); + } + +@@ -958,6 +1140,11 @@ + + for (i = 0; i < this.k.size(); ++i) { + entity = (Entity) this.k.get(i); ++ // CraftBukkit start - Fixed an NPE ++ if (entity == null) { ++ continue; ++ } ++ // CraftBukkit end + + try { + ++entity.ticksLived; +@@ -1001,8 +1188,10 @@ + this.g.clear(); + this.methodProfiler.c("regular"); + +- for (i = 0; i < this.entityList.size(); ++i) { +- entity = (Entity) this.entityList.get(i); ++ // CraftBukkit start - Use field for loop variable ++ for (this.tickPosition = 0; this.tickPosition < this.entityList.size(); ++this.tickPosition) { ++ entity = (Entity) this.entityList.get(this.tickPosition); ++ // CraftBukkit end + if (entity.vehicle != null) { + if (!entity.vehicle.dead && entity.vehicle.passenger == entity) { + continue; +@@ -1033,7 +1222,7 @@ + this.getChunkAt(j, k).b(entity); + } + +- this.entityList.remove(i--); ++ this.entityList.remove(this.tickPosition--); // CraftBukkit - Use field for loop variable + this.b(entity); + } + +@@ -1042,6 +1231,14 @@ + + this.methodProfiler.c("blockEntities"); + this.L = true; ++ // CraftBukkit start - From below, clean up tile entities before ticking them ++ if (!this.b.isEmpty()) { ++ this.tileEntityList.removeAll(this.b); ++ this.h.removeAll(this.b); ++ this.b.clear(); ++ } ++ // CraftBukkit end ++ + Iterator iterator = this.tileEntityList.iterator(); + + while (iterator.hasNext()) { +@@ -1073,11 +1270,13 @@ + } + + this.L = false; ++ /* CraftBukkit start - Moved up + if (!this.b.isEmpty()) { + this.tileEntityList.removeAll(this.b); + this.h.removeAll(this.b); + this.b.clear(); + } ++ */ // CraftBukkit end + + this.methodProfiler.c("pendingBlockEntities"); + if (!this.a.isEmpty()) { +@@ -1085,9 +1284,11 @@ + TileEntity tileentity1 = (TileEntity) this.a.get(l); + + if (!tileentity1.x()) { ++ /* CraftBukkit start - Order matters, moved down + if (!this.h.contains(tileentity1)) { + this.a(tileentity1); + } ++ // CraftBukkit end */ + + if (this.isLoaded(tileentity1.getPosition())) { + this.getChunkAtWorldCoords(tileentity1.getPosition()).a(tileentity1.getPosition(), tileentity1); +@@ -1141,7 +1342,10 @@ + int j = MathHelper.floor(entity.locZ); + byte b0 = 32; + +- if (!flag || this.isAreaLoaded(i - b0, 0, j - b0, i + b0, 0, j + b0, true)) { ++ // CraftBukkit start - Use neighbor cache instead of looking up ++ Chunk startingChunk = this.getChunkIfLoaded(i >> 4, j >> 4); ++ if (!flag || (startingChunk != null && startingChunk.areNeighborsLoaded(2)) /* this.isAreaLoaded(i - b0, 0, j - b0, i + b0, 0, j + b0) */) { ++ // CraftBukkit end + entity.P = entity.locX; + entity.Q = entity.locY; + entity.R = entity.locZ; +@@ -1615,7 +1819,13 @@ + --j; + this.worldData.setThunderDuration(j); + if (j <= 0) { +- this.worldData.setThundering(!this.worldData.isThundering()); ++ // CraftBukkit start ++ ThunderChangeEvent thunder = new ThunderChangeEvent(this.getWorld(), !this.worldData.isThundering()); ++ this.getServer().getPluginManager().callEvent(thunder); ++ if (!thunder.isCancelled()) { ++ this.worldData.setThundering(!this.worldData.isThundering()); ++ } ++ // CraftBukkit end + } + } + +@@ -1639,7 +1849,14 @@ + --k; + this.worldData.setWeatherDuration(k); + if (k <= 0) { +- this.worldData.setStorm(!this.worldData.hasStorm()); ++ // CraftBukkit start ++ WeatherChangeEvent weather = new WeatherChangeEvent(this.getWorld(), !this.worldData.hasStorm()); ++ this.getServer().getPluginManager().callEvent(weather); ++ ++ if (!weather.isCancelled()) { ++ this.worldData.setStorm(!this.worldData.hasStorm()); ++ } ++ // CraftBukkit end + } + } + +@@ -1656,7 +1873,7 @@ + } + + protected void D() { +- this.chunkTickList.clear(); ++ // this.chunkTickList.clear(); // CraftBukkit - removed + this.methodProfiler.a("buildList"); + + int i; +@@ -1673,7 +1890,7 @@ + + for (int i1 = -l; i1 <= l; ++i1) { + for (int j1 = -l; j1 <= l; ++j1) { +- this.chunkTickList.add(new ChunkCoordIntPair(i1 + j, j1 + k)); ++ this.chunkTickList.add(org.bukkit.craftbukkit.util.LongHash.toLong(i1 + j, j1 + k)); + } + } + } +@@ -1851,7 +2068,10 @@ + } + + public boolean c(EnumSkyBlock enumskyblock, BlockPosition blockposition) { +- if (!this.areChunksLoaded(blockposition, 17, false)) { ++ // CraftBukkit start - Use neighbor cache instead of looking up ++ Chunk chunk = this.getChunkIfLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4); ++ if (chunk == null || !chunk.areNeighborsLoaded(1) /*!this.areChunksLoaded(blockposition, 17, false)*/) { ++ // CraftBukkit end + return false; + } else { + int i = 0; +@@ -2095,8 +2315,17 @@ + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); ++ // CraftBukkit start - Split out persistent check, don't apply it to special persistent mobs ++ if (entity instanceof EntityInsentient) { ++ EntityInsentient entityinsentient = (EntityInsentient) entity; ++ if (entityinsentient.isTypeNotPersistent() && entityinsentient.isPersistent()) { ++ continue; ++ } ++ } + +- if ((!(entity instanceof EntityInsentient) || !((EntityInsentient) entity).isPersistent()) && oclass.isAssignableFrom(entity.getClass())) { ++ if (oclass.isAssignableFrom(entity.getClass())) { ++ // if ((!(entity instanceof EntityInsentient) || !((EntityInsentient) entity).isPersistent()) && oclass.isAssignableFrom(entity.getClass())) { ++ // CraftBukkit end + ++i; + } + } +@@ -2105,12 +2334,17 @@ + } + + public void b(Collection collection) { +- this.entityList.addAll(collection); ++ // CraftBukkit start ++ // this.entityList.addAll(collection); + Iterator iterator = collection.iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); +- ++ if (entity == null) { ++ continue; ++ } ++ this.entityList.add(entity); ++ // CraftBukkit end + this.a(entity); + } + +@@ -2124,7 +2358,13 @@ + Block block1 = this.getType(blockposition).getBlock(); + AxisAlignedBB axisalignedbb = flag ? null : block.a(this, blockposition, block.getBlockData()); + +- return axisalignedbb != null && !this.a(axisalignedbb, entity) ? false : (block1.getMaterial() == Material.ORIENTABLE && block == Blocks.ANVIL ? true : block1.getMaterial().isReplaceable() && block.canPlace(this, blockposition, enumdirection, itemstack)); ++ // CraftBukkit start - store default return ++ boolean defaultReturn = axisalignedbb != null && !this.a(axisalignedbb, entity) ? false : (block1.getMaterial() == Material.ORIENTABLE && block == Blocks.ANVIL ? true : block1.getMaterial().isReplaceable() && block.canPlace(this, blockposition, enumdirection, itemstack)); ++ BlockCanBuildEvent event = new BlockCanBuildEvent(this.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()), CraftMagicNumbers.getId(block), defaultReturn); ++ this.getServer().getPluginManager().callEvent(event); ++ ++ return event.isBuildable(); ++ // CraftBukkit end + } + + public int getBlockPower(BlockPosition blockposition, EnumDirection enumdirection) { +@@ -2215,6 +2455,11 @@ + + for (int i = 0; i < this.players.size(); ++i) { + EntityHuman entityhuman1 = (EntityHuman) this.players.get(i); ++ // CraftBukkit start - Fixed an NPE ++ if (entityhuman1 == null || entityhuman1.dead) { ++ continue; ++ } ++ // CraftBukkit end + + if (IEntitySelector.d.apply(entityhuman1)) { + double d5 = entityhuman1.e(d0, d1, d2); +@@ -2269,7 +2514,7 @@ + return null; + } + +- public void checkSession() { ++ public void checkSession() throws ExceptionWorldConflict { // CraftBukkit - added throws + this.dataManager.checkSession(); + } + +@@ -2331,6 +2576,16 @@ + + public void everyoneSleeping() {} + ++ // CraftBukkit start ++ // Calls the method that checks to see if players are sleeping ++ // Called by CraftPlayer.setPermanentSleeping() ++ public void checkSleepStatus() { ++ if (!this.isStatic) { ++ this.everyoneSleeping(); ++ } ++ } ++ // CraftBukkit end ++ + public float h(float f) { + return (this.q + (this.r - this.q) * f) * this.j(f); + } +@@ -2538,6 +2793,6 @@ + int l = j * 16 + 8 - blockposition.getZ(); + short short0 = 128; + +- return k >= -short0 && k <= short0 && l >= -short0 && l <= short0; ++ return k >= -short0 && k <= short0 && l >= -short0 && l <= short0 || !this.keepSpawnInMemory; // CraftBukkit - Added 'this.world.keepSpawnInMemory' + } + } diff --git a/nms-patches/WorldBorder.patch b/nms-patches/WorldBorder.patch new file mode 100644 index 00000000..37416ef7 --- /dev/null +++ b/nms-patches/WorldBorder.patch @@ -0,0 +1,25 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/WorldBorder.java 2014-11-27 08:59:46.925420860 +1100 ++++ src/main/java/net/minecraft/server/WorldBorder.java 2014-11-27 08:42:10.168850880 +1100 +@@ -32,9 +32,21 @@ + return (double) (blockposition.getX() + 1) > this.b() && (double) blockposition.getX() < this.d() && (double) (blockposition.getZ() + 1) > this.c() && (double) blockposition.getZ() < this.e(); + } + ++ // CraftBukkit start - split method + public boolean isInBounds(ChunkCoordIntPair chunkcoordintpair) { +- return (double) chunkcoordintpair.e() > this.b() && (double) chunkcoordintpair.c() < this.d() && (double) chunkcoordintpair.f() > this.c() && (double) chunkcoordintpair.d() < this.e(); ++ return isInBounds(chunkcoordintpair.x, chunkcoordintpair.z); + } ++ ++ // Inlined the getters from ChunkCoordIntPair ++ public boolean isInBounds(long chunkcoords) { ++ return isInBounds(org.bukkit.craftbukkit.util.LongHash.msw(chunkcoords), org.bukkit.craftbukkit.util.LongHash.lsw(chunkcoords)); ++ } ++ ++ // Inlined the getters from ChunkCoordIntPair ++ public boolean isInBounds(int x, int z) { ++ return (double) ((x << 4) + 15) > this.b() && (double) (x << 4) < this.d() && (double) ((z << 4) + 15) > this.c() && (double) (x << 4) < this.e(); ++ } ++ // CraftBukkit end + + public boolean a(AxisAlignedBB axisalignedbb) { + return axisalignedbb.d > this.b() && axisalignedbb.a < this.d() && axisalignedbb.f > this.c() && axisalignedbb.c < this.e(); diff --git a/nms-patches/WorldGenGroundBush.patch b/nms-patches/WorldGenGroundBush.patch new file mode 100644 index 00000000..9a633c3b --- /dev/null +++ b/nms-patches/WorldGenGroundBush.patch @@ -0,0 +1,14 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/WorldGenGroundBush.java 2014-11-27 08:59:46.925420860 +1100 ++++ src/main/java/net/minecraft/server/WorldGenGroundBush.java 2014-11-27 08:42:10.144850927 +1100 +@@ -46,7 +46,11 @@ + } + } + } ++ // CraftBukkit start - Return false if gen was unsuccessful ++ } else { ++ return false; + } ++ // CraftBukkit end + + return true; + } diff --git a/nms-patches/WorldGenMegaTreeAbstract.patch b/nms-patches/WorldGenMegaTreeAbstract.patch new file mode 100644 index 00000000..4dcc8625 --- /dev/null +++ b/nms-patches/WorldGenMegaTreeAbstract.patch @@ -0,0 +1,11 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/WorldGenMegaTreeAbstract.java 2014-11-27 08:59:46.925420860 +1100 ++++ src/main/java/net/minecraft/server/WorldGenMegaTreeAbstract.java 2014-11-27 08:42:10.140850934 +1100 +@@ -42,7 +42,7 @@ + + for (int k = -b0; k <= b0 && flag; ++k) { + for (int l = -b0; l <= b0 && flag; ++l) { +- if (blockposition.getY() + j < 0 || blockposition.getY() + j >= 256 || !this.a(world.getType(blockposition.a(k, j, l)).getBlock())) { ++ if (blockposition.getY() + j < 0 || blockposition.getY() + j >= 256 || (!this.a(world.getType(blockposition.a(k, j, l)).getBlock()) && world.getType(blockposition.a(k, j, l)).getBlock() != Blocks.SAPLING)) { // CraftBukkit - ignore our own saplings + flag = false; + } + } diff --git a/nms-patches/WorldGenVillagePiece.patch b/nms-patches/WorldGenVillagePiece.patch new file mode 100644 index 00000000..ff11ea6a --- /dev/null +++ b/nms-patches/WorldGenVillagePiece.patch @@ -0,0 +1,11 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/WorldGenVillagePiece.java 2014-11-27 08:59:46.929420842 +1100 ++++ src/main/java/net/minecraft/server/WorldGenVillagePiece.java 2014-11-27 08:42:10.144850927 +1100 +@@ -114,7 +114,7 @@ + entityvillager.setPositionRotation((double) j1 + 0.5D, (double) k1, (double) l1 + 0.5D, 0.0F, 0.0F); + entityvillager.prepare(world.E(new BlockPosition(entityvillager)), (GroupDataEntity) null); + entityvillager.setProfession(this.c(i1, entityvillager.getProfession())); +- world.addEntity(entityvillager); ++ world.addEntity(entityvillager, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN); // CraftBukkit - add SpawnReason + } + + } diff --git a/nms-patches/WorldGenVillagePieces.patch b/nms-patches/WorldGenVillagePieces.patch new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/nms-patches/WorldGenVillagePieces.patch diff --git a/nms-patches/WorldGenWitchHut.patch b/nms-patches/WorldGenWitchHut.patch new file mode 100644 index 00000000..cfd76bed --- /dev/null +++ b/nms-patches/WorldGenWitchHut.patch @@ -0,0 +1,11 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/WorldGenWitchHut.java 2014-11-27 08:59:46.933420825 +1100 ++++ src/main/java/net/minecraft/server/WorldGenWitchHut.java 2014-11-27 08:42:10.152850911 +1100 +@@ -77,7 +77,7 @@ + + entitywitch.setPositionRotation((double) i1 + 0.5D, (double) j1, (double) k1 + 0.5D, 0.0F, 0.0F); + entitywitch.prepare(world.E(new BlockPosition(i1, j1, k1)), (GroupDataEntity) null); +- world.addEntity(entitywitch); ++ world.addEntity(entitywitch, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.CHUNK_GEN); // CraftBukkit - add SpawnReason + } + } + diff --git a/nms-patches/WorldManager.patch b/nms-patches/WorldManager.patch new file mode 100644 index 00000000..a7f97b03 --- /dev/null +++ b/nms-patches/WorldManager.patch @@ -0,0 +1,28 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/WorldManager.java 2014-11-27 08:59:46.937420807 +1100 ++++ src/main/java/net/minecraft/server/WorldManager.java 2014-11-27 08:42:10.132850949 +1100 +@@ -23,11 +23,13 @@ + } + + public void a(String s, double d0, double d1, double d2, float f, float f1) { +- this.a.getPlayerList().sendPacketNearby(d0, d1, d2, f > 1.0F ? (double) (16.0F * f) : 16.0D, this.world.worldProvider.getDimension(), new PacketPlayOutNamedSoundEffect(s, d0, d1, d2, f, f1)); ++ // CraftBukkit - this.world.dimension ++ this.a.getPlayerList().sendPacketNearby(d0, d1, d2, f > 1.0F ? (double) (16.0F * f) : 16.0D, this.world.dimension, new PacketPlayOutNamedSoundEffect(s, d0, d1, d2, f, f1)); + } + + public void a(EntityHuman entityhuman, String s, double d0, double d1, double d2, float f, float f1) { +- this.a.getPlayerList().sendPacketNearby(entityhuman, d0, d1, d2, f > 1.0F ? (double) (16.0F * f) : 16.0D, this.world.worldProvider.getDimension(), new PacketPlayOutNamedSoundEffect(s, d0, d1, d2, f, f1)); ++ // CraftBukkit - this.world.dimension ++ this.a.getPlayerList().sendPacketNearby(entityhuman, d0, d1, d2, f > 1.0F ? (double) (16.0F * f) : 16.0D, this.world.dimension, new PacketPlayOutNamedSoundEffect(s, d0, d1, d2, f, f1)); + } + + public void a(int i, int j, int k, int l, int i1, int j1) {} +@@ -41,7 +43,8 @@ + public void a(String s, BlockPosition blockposition) {} + + public void a(EntityHuman entityhuman, int i, BlockPosition blockposition, int j) { +- this.a.getPlayerList().sendPacketNearby(entityhuman, (double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ(), 64.0D, this.world.worldProvider.getDimension(), new PacketPlayOutWorldEvent(i, blockposition, j, false)); ++ // CraftBukkit - this.world.dimension ++ this.a.getPlayerList().sendPacketNearby(entityhuman, (double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ(), 64.0D, this.world.dimension, new PacketPlayOutWorldEvent(i, blockposition, j, false)); + } + + public void a(int i, BlockPosition blockposition, int j) { diff --git a/nms-patches/WorldMap.patch b/nms-patches/WorldMap.patch new file mode 100644 index 00000000..86ba2aa6 --- /dev/null +++ b/nms-patches/WorldMap.patch @@ -0,0 +1,95 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/WorldMap.java 2014-11-27 08:59:46.941420790 +1100 ++++ src/main/java/net/minecraft/server/WorldMap.java 2014-11-27 08:42:10.152850911 +1100 +@@ -6,6 +6,14 @@ + import java.util.List; + import java.util.Map; + ++// CraftBukkit start ++import java.util.UUID; ++ ++import org.bukkit.craftbukkit.CraftServer; ++import org.bukkit.craftbukkit.CraftWorld; ++import org.bukkit.craftbukkit.map.CraftMapView; ++// CraftBukkit end ++ + public class WorldMap extends PersistentBase { + + public int centerX; +@@ -16,9 +24,19 @@ + public List g = Lists.newArrayList(); + private Map i = Maps.newHashMap(); + public Map decorations = Maps.newLinkedHashMap(); ++ ++ // CraftBukkit start ++ public final CraftMapView mapView; ++ private CraftServer server; ++ private UUID uniqueId = null; ++ // CraftBukkit end + + public WorldMap(String s) { + super(s); ++ // CraftBukkit start ++ mapView = new CraftMapView(this); ++ server = (CraftServer) org.bukkit.Bukkit.getServer(); ++ // CraftBukkit end + } + + public void a(double d0, double d1, int i) { +@@ -31,7 +49,30 @@ + } + + public void a(NBTTagCompound nbttagcompound) { +- this.map = nbttagcompound.getByte("dimension"); ++ // CraftBukkit start ++ byte dimension = nbttagcompound.getByte("dimension"); ++ ++ if (dimension >= 10) { ++ long least = nbttagcompound.getLong("UUIDLeast"); ++ long most = nbttagcompound.getLong("UUIDMost"); ++ ++ if (least != 0L && most != 0L) { ++ this.uniqueId = new UUID(most, least); ++ ++ CraftWorld world = (CraftWorld) server.getWorld(this.uniqueId); ++ // Check if the stored world details are correct. ++ if (world == null) { ++ /* All Maps which do not have their valid world loaded are set to a dimension which hopefully won't be reached. ++ This is to prevent them being corrupted with the wrong map data. */ ++ dimension = 127; ++ } else { ++ dimension = (byte) world.getHandle().dimension; ++ } ++ } ++ } ++ ++ this.map = dimension; ++ // CraftBukkit end + this.centerX = nbttagcompound.getInt("xCenter"); + this.centerZ = nbttagcompound.getInt("zCenter"); + this.scale = nbttagcompound.getByte("scale"); +@@ -66,6 +107,25 @@ + } + + public void b(NBTTagCompound nbttagcompound) { ++ // CraftBukkit start ++ if (this.map >= 10) { ++ if (this.uniqueId == null) { ++ for (org.bukkit.World world : server.getWorlds()) { ++ CraftWorld cWorld = (CraftWorld) world; ++ if (cWorld.getHandle().dimension == this.map) { ++ this.uniqueId = cWorld.getUID(); ++ break; ++ } ++ } ++ } ++ /* Perform a second check to see if a matching world was found, this is a necessary ++ change incase Maps are forcefully unlinked from a World and lack a UID.*/ ++ if (this.uniqueId != null) { ++ nbttagcompound.setLong("UUIDLeast", this.uniqueId.getLeastSignificantBits()); ++ nbttagcompound.setLong("UUIDMost", this.uniqueId.getMostSignificantBits()); ++ } ++ } ++ // CraftBukkit end + nbttagcompound.setByte("dimension", this.map); + nbttagcompound.setInt("xCenter", this.centerX); + nbttagcompound.setInt("zCenter", this.centerZ); diff --git a/nms-patches/WorldMapHumanTracker.patch b/nms-patches/WorldMapHumanTracker.patch new file mode 100644 index 00000000..627bbbfd --- /dev/null +++ b/nms-patches/WorldMapHumanTracker.patch @@ -0,0 +1,31 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/WorldMapHumanTracker.java 2014-11-27 08:59:46.937420807 +1100 ++++ src/main/java/net/minecraft/server/WorldMapHumanTracker.java 2014-11-27 08:42:10.168850880 +1100 +@@ -23,12 +23,26 @@ + } + + public Packet a(ItemStack itemstack) { ++ // CraftBukkit start ++ org.bukkit.craftbukkit.map.RenderData render = this.worldMap.mapView.render((org.bukkit.craftbukkit.entity.CraftPlayer) this.trackee.getBukkitEntity()); // CraftBukkit ++ ++ java.util.Collection<MapIcon> icons = new java.util.ArrayList<MapIcon>(); ++ ++ for ( org.bukkit.map.MapCursor cursor : render.cursors) { ++ ++ if (cursor.isVisible()) { ++ icons.add(new MapIcon(cursor.getRawType(), cursor.getX(), cursor.getY(), cursor.getDirection())); ++ } ++ } ++ ++ + if (this.d) { + this.d = false; +- return new PacketPlayOutMap(itemstack.getData(), this.worldMap.scale, this.worldMap.decorations.values(), this.worldMap.colors, this.e, this.f, this.g + 1 - this.e, this.h + 1 - this.f); ++ return new PacketPlayOutMap(itemstack.getData(), this.worldMap.scale, icons, render.buffer, this.e, this.f, this.g + 1 - this.e, this.h + 1 - this.f); + } else { +- return this.i++ % 5 == 0 ? new PacketPlayOutMap(itemstack.getData(), this.worldMap.scale, this.worldMap.decorations.values(), this.worldMap.colors, 0, 0, 0, 0) : null; ++ return this.i++ % 5 == 0 ? new PacketPlayOutMap(itemstack.getData(), this.worldMap.scale, icons, render.buffer, 0, 0, 0, 0) : null; + } ++ // CraftBukkit end + } + + public void a(int i, int j) { diff --git a/nms-patches/WorldNBTStorage.patch b/nms-patches/WorldNBTStorage.patch new file mode 100644 index 00000000..503af702 --- /dev/null +++ b/nms-patches/WorldNBTStorage.patch @@ -0,0 +1,123 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/WorldNBTStorage.java 2014-11-27 08:59:46.941420790 +1100 ++++ src/main/java/net/minecraft/server/WorldNBTStorage.java 2014-11-27 08:42:10.156850903 +1100 +@@ -11,6 +11,12 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// CraftBukkit start ++import java.util.UUID; ++ ++import org.bukkit.craftbukkit.entity.CraftPlayer; ++// CraftBukkit end ++ + public class WorldNBTStorage implements IDataManager, IPlayerFileData { + + private static final Logger a = LogManager.getLogger(); +@@ -19,6 +25,7 @@ + private final File dataDir; + private final long sessionId = MinecraftServer.ax(); + private final String f; ++ private UUID uuid = null; // CraftBukkit + + public WorldNBTStorage(File file, String s, boolean flag) { + this.baseDir = new File(file, s); +@@ -55,7 +62,7 @@ + return this.baseDir; + } + +- public void checkSession() { ++ public void checkSession() throws ExceptionWorldConflict { // CraftBukkit - throws ExceptionWorldConflict + try { + File file = new File(this.baseDir, "session.lock"); + DataInputStream datainputstream = new DataInputStream(new FileInputStream(file)); +@@ -202,12 +209,39 @@ + } + + if (nbttagcompound != null) { ++ // CraftBukkit start ++ if (entityhuman instanceof EntityPlayer) { ++ CraftPlayer player = (CraftPlayer) entityhuman.getBukkitEntity(); ++ // Only update first played if it is older than the one we have ++ long modified = new File(this.playerDir, entityhuman.getUniqueID().toString() + ".dat").lastModified(); ++ if (modified < player.getFirstPlayed()) { ++ player.setFirstPlayed(modified); ++ } ++ } ++ // CraftBukkit end ++ + entityhuman.f(nbttagcompound); + } + + return nbttagcompound; + } + ++ // CraftBukkit start ++ public NBTTagCompound getPlayerData(String s) { ++ try { ++ File file1 = new File(this.playerDir, s + ".dat"); ++ ++ if (file1.exists()) { ++ return NBTCompressedStreamTools.a((InputStream) (new FileInputStream(file1))); ++ } ++ } catch (Exception exception) { ++ a.warn("Failed to load player data for " + s); ++ } ++ ++ return null; ++ } ++ // CraftBukkit end ++ + public IPlayerFileData getPlayerFileData() { + return this; + } +@@ -237,4 +271,50 @@ + public String g() { + return this.f; + } ++ ++ // CraftBukkit start ++ public UUID getUUID() { ++ if (uuid != null) return uuid; ++ File file1 = new File(this.baseDir, "uid.dat"); ++ if (file1.exists()) { ++ DataInputStream dis = null; ++ try { ++ dis = new DataInputStream(new FileInputStream(file1)); ++ return uuid = new UUID(dis.readLong(), dis.readLong()); ++ } catch (IOException ex) { ++ a.warn("Failed to read " + file1 + ", generating new random UUID", ex); ++ } finally { ++ if (dis != null) { ++ try { ++ dis.close(); ++ } catch (IOException ex) { ++ // NOOP ++ } ++ } ++ } ++ } ++ uuid = UUID.randomUUID(); ++ DataOutputStream dos = null; ++ try { ++ dos = new DataOutputStream(new FileOutputStream(file1)); ++ dos.writeLong(uuid.getMostSignificantBits()); ++ dos.writeLong(uuid.getLeastSignificantBits()); ++ } catch (IOException ex) { ++ a.warn("Failed to write " + file1, ex); ++ } finally { ++ if (dos != null) { ++ try { ++ dos.close(); ++ } catch (IOException ex) { ++ // NOOP ++ } ++ } ++ } ++ return uuid; ++ } ++ ++ public File getPlayerDir() { ++ return playerDir; ++ } ++ // CraftBukkit end + } diff --git a/nms-patches/WorldServer.patch b/nms-patches/WorldServer.patch new file mode 100644 index 00000000..b1ad5607 --- /dev/null +++ b/nms-patches/WorldServer.patch @@ -0,0 +1,550 @@ +--- ../work/decompile-bb26c12b/net/minecraft/server/WorldServer.java 2014-11-27 08:59:46.945420772 +1100 ++++ src/main/java/net/minecraft/server/WorldServer.java 2014-11-27 08:42:10.140850934 +1100 +@@ -16,6 +16,20 @@ + import org.apache.logging.log4j.LogManager; + import org.apache.logging.log4j.Logger; + ++// CraftBukkit start ++import java.util.*; ++import java.util.logging.Level; ++ ++import org.bukkit.WeatherType; ++import org.bukkit.block.BlockState; ++import org.bukkit.craftbukkit.util.LongHash; ++ ++import org.bukkit.event.block.BlockFormEvent; ++import org.bukkit.event.weather.LightningStrikeEvent; ++import org.bukkit.event.weather.ThunderChangeEvent; ++import org.bukkit.event.weather.WeatherChangeEvent; ++// CraftBukkit end ++ + public class WorldServer extends World implements IAsyncTaskHandler { + + private static final Logger a = LogManager.getLogger(); +@@ -37,14 +51,21 @@ + private static final List U = Lists.newArrayList(new StructurePieceTreasure[] { new StructurePieceTreasure(Items.STICK, 0, 1, 3, 10), new StructurePieceTreasure(Item.getItemOf(Blocks.PLANKS), 0, 1, 3, 10), new StructurePieceTreasure(Item.getItemOf(Blocks.LOG), 0, 1, 3, 10), new StructurePieceTreasure(Items.STONE_AXE, 0, 1, 1, 3), new StructurePieceTreasure(Items.WOODEN_AXE, 0, 1, 1, 5), new StructurePieceTreasure(Items.STONE_PICKAXE, 0, 1, 1, 3), new StructurePieceTreasure(Items.WOODEN_PICKAXE, 0, 1, 1, 5), new StructurePieceTreasure(Items.APPLE, 0, 2, 3, 5), new StructurePieceTreasure(Items.BREAD, 0, 2, 3, 3), new StructurePieceTreasure(Item.getItemOf(Blocks.LOG2), 0, 1, 3, 10)}); + private List V = Lists.newArrayList(); + +- public WorldServer(MinecraftServer minecraftserver, IDataManager idatamanager, WorldData worlddata, int i, MethodProfiler methodprofiler) { +- super(idatamanager, worlddata, WorldProvider.byDimension(i), methodprofiler, false); ++ // CraftBukkit start ++ public final int dimension; ++ ++ // Add env and gen to constructor ++ public WorldServer(MinecraftServer minecraftserver, IDataManager idatamanager, WorldData worlddata, int i, MethodProfiler methodprofiler, org.bukkit.World.Environment env, org.bukkit.generator.ChunkGenerator gen) { ++ super(idatamanager, worlddata, WorldProvider.byDimension(env.getId()), methodprofiler, false, gen, env); ++ this.dimension = i; ++ this.pvpMode = minecraftserver.getPVP(); ++ // CraftBukkit end + this.server = minecraftserver; + this.tracker = new EntityTracker(this); + this.manager = new PlayerChunkMap(this); + this.worldProvider.a(this); + this.chunkProvider = this.k(); +- this.Q = new PortalTravelAgent(this); ++ this.Q = new org.bukkit.craftbukkit.CraftTravelAgent(this); // CraftBukkit + this.B(); + this.C(); + this.af().a(minecraftserver.aG()); +@@ -86,6 +107,89 @@ + + return this; + } ++ ++ // CraftBukkit start ++ @Override ++ public TileEntity getTileEntity(BlockPosition pos) { ++ TileEntity result = super.getTileEntity(pos); ++ Block type = getType(pos).getBlock(); ++ ++ if (type == Blocks.CHEST) { ++ if (!(result instanceof TileEntityChest)) { ++ result = fixTileEntity(pos, type, result); ++ } ++ } else if (type == Blocks.FURNACE) { ++ if (!(result instanceof TileEntityFurnace)) { ++ result = fixTileEntity(pos, type, result); ++ } ++ } else if (type == Blocks.DROPPER) { ++ if (!(result instanceof TileEntityDropper)) { ++ result = fixTileEntity(pos, type, result); ++ } ++ } else if (type == Blocks.DISPENSER) { ++ if (!(result instanceof TileEntityDispenser)) { ++ result = fixTileEntity(pos, type, result); ++ } ++ } else if (type == Blocks.JUKEBOX) { ++ if (!(result instanceof TileEntityRecordPlayer)) { ++ result = fixTileEntity(pos, type, result); ++ } ++ } else if (type == Blocks.NOTEBLOCK) { ++ if (!(result instanceof TileEntityNote)) { ++ result = fixTileEntity(pos, type, result); ++ } ++ } else if (type == Blocks.MOB_SPAWNER) { ++ if (!(result instanceof TileEntityMobSpawner)) { ++ result = fixTileEntity(pos, type, result); ++ } ++ } else if ((type == Blocks.STANDING_SIGN) || (type == Blocks.WALL_SIGN)) { ++ if (!(result instanceof TileEntitySign)) { ++ result = fixTileEntity(pos, type, result); ++ } ++ } else if (type == Blocks.ENDER_CHEST) { ++ if (!(result instanceof TileEntityEnderChest)) { ++ result = fixTileEntity(pos, type, result); ++ } ++ } else if (type == Blocks.BREWING_STAND) { ++ if (!(result instanceof TileEntityBrewingStand)) { ++ result = fixTileEntity(pos, type, result); ++ } ++ } else if (type == Blocks.BEACON) { ++ if (!(result instanceof TileEntityBeacon)) { ++ result = fixTileEntity(pos, type, result); ++ } ++ } else if (type == Blocks.HOPPER) { ++ if (!(result instanceof TileEntityHopper)) { ++ result = fixTileEntity(pos, type, result); ++ } ++ } ++ ++ return result; ++ } ++ ++ private TileEntity fixTileEntity(BlockPosition pos, Block type, TileEntity found) { ++ this.getServer().getLogger().log(Level.SEVERE, "Block at {0},{1},{2} is {3} but has {4}" + ". " ++ + "Bukkit will attempt to fix this, but there may be additional damage that we cannot recover.", new Object[]{pos.getX(), pos.getY(), pos.getZ(), org.bukkit.Material.getMaterial(Block.getId(type)).toString(), found}); ++ ++ if (type instanceof IContainer) { ++ TileEntity replacement = ((IContainer) type).a(this, type.toLegacyData(this.getType(pos))); ++ replacement.world = this; ++ this.setTileEntity(pos, replacement); ++ return replacement; ++ } else { ++ this.getServer().getLogger().severe("Don't know how to fix for this type... Can't do anything! :("); ++ return found; ++ } ++ } ++ ++ private boolean canSpawn(int x, int z) { ++ if (this.generator != null) { ++ return this.generator.canSpawn(this.getWorld(), x, z); ++ } else { ++ return this.worldProvider.canSpawn(x, z); ++ } ++ } ++ // CraftBukkit end + + public void doTick() { + super.doTick(); +@@ -105,8 +209,11 @@ + } + + this.methodProfiler.a("mobSpawner"); +- if (this.getGameRules().getBoolean("doMobSpawning") && this.worldData.getType() != WorldType.DEBUG_ALL_BLOCK_STATES) { +- this.R.a(this, this.allowMonsters, this.allowAnimals, this.worldData.getTime() % 400L == 0L); ++ // CraftBukkit start - Only call spawner if we have players online and the world allows for mobs or animals ++ long time = this.worldData.getTime(); ++ if (this.getGameRules().getBoolean("doMobSpawning") && this.worldData.getType() != WorldType.DEBUG_ALL_BLOCK_STATES && (this.allowMonsters || this.allowAnimals) && (this instanceof WorldServer && this.players.size() > 0)) { ++ this.R.a(this, this.allowMonsters && (this.ticksPerMonsterSpawns != 0 && time % this.ticksPerMonsterSpawns == 0L), this.allowAnimals && (this.ticksPerAnimalSpawns != 0 && time % this.ticksPerAnimalSpawns == 0L), this.worldData.getTime() % 400L == 0L); ++ // CraftBukkit end + } + + this.methodProfiler.c("chunkSource"); +@@ -135,6 +242,8 @@ + this.Q.a(this.getTime()); + this.methodProfiler.b(); + this.ak(); ++ ++ this.getWorld().processChunkGC(); // CraftBukkit + } + + public BiomeMeta a(EnumCreatureType enumcreaturetype, BlockPosition blockposition) { +@@ -161,7 +270,7 @@ + + if (entityhuman.v()) { + ++i; +- } else if (entityhuman.isSleeping()) { ++ } else if (entityhuman.isSleeping() || entityhuman.fauxSleeping) { // CraftBukkit + ++j; + } + } +@@ -187,26 +296,45 @@ + } + + private void ag() { +- this.worldData.setWeatherDuration(0); +- this.worldData.setStorm(false); +- this.worldData.setThunderDuration(0); +- this.worldData.setThundering(false); ++ // CraftBukkit start ++ WeatherChangeEvent weather = new WeatherChangeEvent(this.getWorld(), false); ++ this.getServer().getPluginManager().callEvent(weather); ++ ++ ThunderChangeEvent thunder = new ThunderChangeEvent(this.getWorld(), false); ++ this.getServer().getPluginManager().callEvent(thunder); ++ if (!weather.isCancelled()) { ++ this.worldData.setWeatherDuration(0); ++ this.worldData.setStorm(false); ++ } ++ if (!thunder.isCancelled()) { ++ this.worldData.setThunderDuration(0); ++ this.worldData.setThundering(false); ++ } ++ // CraftBukkit end + } + + public boolean everyoneDeeplySleeping() { + if (this.O && !this.isStatic) { + Iterator iterator = this.players.iterator(); + ++ // CraftBukkit - This allows us to assume that some people are in bed but not really, allowing time to pass in spite of AFKers ++ boolean foundActualSleepers = false; ++ + EntityHuman entityhuman; + + do { + if (!iterator.hasNext()) { +- return true; ++ return foundActualSleepers; + } + + entityhuman = (EntityHuman) iterator.next(); +- } while (!entityhuman.v() && entityhuman.isDeeplySleeping()); +- ++ // CraftBukkit start ++ if (entityhuman.isDeeplySleeping()) { ++ foundActualSleepers = true; ++ } ++ } while (!entityhuman.v() && (entityhuman.isDeeplySleeping() || entityhuman.fauxSleeping)); ++ // CraftBukkit end ++ + return false; + } else { + return false; +@@ -227,15 +355,22 @@ + } else { + int i = 0; + int j = 0; +- +- for (Iterator iterator1 = this.chunkTickList.iterator(); iterator1.hasNext(); this.methodProfiler.b()) { +- ChunkCoordIntPair chunkcoordintpair1 = (ChunkCoordIntPair) iterator1.next(); +- int k = chunkcoordintpair1.x * 16; +- int l = chunkcoordintpair1.z * 16; +- ++ ++ // CraftBukkit start ++ //for (Iterator iterator1 = this.chunkTickList.iterator(); iterator1.hasNext(); this.methodProfiler.b()) { ++ // ChunkCoordIntPair chunkcoordintpair1 = (ChunkCoordIntPair) iterator1.next(); ++ // int k = chunkcoordintpair1.x * 16; ++ // int l = chunkcoordintpair1.z * 16; ++ for (long chunkCoord : chunkTickList.popAll()) { ++ int chunkX = LongHash.msw(chunkCoord); ++ int chunkZ = LongHash.lsw(chunkCoord); ++ int k = chunkX * 16; ++ int l = chunkZ * 16; ++ + this.methodProfiler.a("getChunk"); +- Chunk chunk = this.getChunkAt(chunkcoordintpair1.x, chunkcoordintpair1.z); +- ++ Chunk chunk = this.getChunkAt(chunkX, chunkZ); ++ // CraftBukkit end ++ + this.a(k, l, chunk); + this.methodProfiler.c("tickChunk"); + chunk.b(false); +@@ -260,11 +395,29 @@ + BlockPosition blockposition1 = blockposition.down(); + + if (this.w(blockposition1)) { +- this.setTypeUpdate(blockposition1, Blocks.ICE.getBlockData()); ++ // CraftBukkit start ++ BlockState blockState = this.getWorld().getBlockAt(blockposition1.getX(), blockposition1.getY(), blockposition1.getZ()).getState(); ++ blockState.setTypeId(Block.getId(Blocks.ICE)); ++ ++ BlockFormEvent iceBlockForm = new BlockFormEvent(blockState.getBlock(), blockState); ++ this.getServer().getPluginManager().callEvent(iceBlockForm); ++ if (!iceBlockForm.isCancelled()) { ++ blockState.update(true); ++ } ++ // CraftBukkit end + } + + if (this.S() && this.f(blockposition, true)) { +- this.setTypeUpdate(blockposition, Blocks.SNOW_LAYER.getBlockData()); ++ // CraftBukkit start ++ BlockState blockState = this.getWorld().getBlockAt(blockposition.getX(), blockposition.getY(), blockposition.getZ()).getState(); ++ blockState.setTypeId(Block.getId(Blocks.SNOW_LAYER)); ++ ++ BlockFormEvent snow = new BlockFormEvent(blockState.getBlock(), blockState); ++ this.getServer().getPluginManager().callEvent(snow); ++ if (!snow.isCancelled()) { ++ blockState.update(true); ++ } ++ // CraftBukkit end + } + + if (this.S() && this.getBiome(blockposition1).e()) { +@@ -376,7 +529,7 @@ + } + + public void tickEntities() { +- if (this.players.isEmpty()) { ++ if (false && this.players.isEmpty()) { // CraftBukkit - this prevents entity cleanup, other issues on servers with no players + if (this.emptyTime++ >= 1200) { + return; + } +@@ -401,7 +554,13 @@ + throw new IllegalStateException("TickNextTick list out of synch"); + } else { + if (i > 1000) { +- i = 1000; ++ // CraftBukkit start - If the server has too much to process over time, try to alleviate that ++ if (i > 20 * 1000) { ++ i = i / 20; ++ } else { ++ i = 1000; ++ } ++ // CraftBukkit end + } + + this.methodProfiler.a("cleaning"); +@@ -501,6 +660,7 @@ + return arraylist; + } + ++ /* CraftBukkit start - We prevent spawning in general, so this butchering is not needed + public void entityJoinedWorld(Entity entity, boolean flag) { + if (!this.getSpawnAnimals() && (entity instanceof EntityAnimal || entity instanceof EntityWaterAnimal)) { + entity.die(); +@@ -511,7 +671,9 @@ + } + + super.entityJoinedWorld(entity, flag); ++ + } ++ // CraftBukkit end */ + + private boolean getSpawnNPCs() { + return this.server.getSpawnNPCs(); +@@ -523,14 +685,44 @@ + + protected IChunkProvider k() { + IChunkLoader ichunkloader = this.dataManager.createChunkLoader(this.worldProvider); ++ ++ // CraftBukkit start ++ org.bukkit.craftbukkit.generator.InternalChunkGenerator gen; ++ ++ if (this.generator != null) { ++ gen = new org.bukkit.craftbukkit.generator.CustomChunkGenerator(this, this.getSeed(), this.generator); ++ } else if (this.worldProvider instanceof WorldProviderHell) { ++ gen = new org.bukkit.craftbukkit.generator.NetherChunkGenerator(this, this.getSeed()); ++ } else if (this.worldProvider instanceof WorldProviderTheEnd) { ++ gen = new org.bukkit.craftbukkit.generator.SkyLandsChunkGenerator(this, this.getSeed()); ++ } else { ++ gen = new org.bukkit.craftbukkit.generator.NormalChunkGenerator(this, this.getSeed()); ++ } + +- this.chunkProviderServer = new ChunkProviderServer(this, ichunkloader, this.worldProvider.getChunkProvider()); ++ this.chunkProviderServer = new ChunkProviderServer(this, ichunkloader, gen); ++ // CraftBukkit end + return this.chunkProviderServer; + } + + public List getTileEntities(int i, int j, int k, int l, int i1, int j1) { + ArrayList arraylist = Lists.newArrayList(); +- ++ ++ // CraftBukkit start - Get tile entities from chunks instead of world ++ for (int chunkX = (i >> 4); chunkX <= ((l - 1) >> 4); chunkX++) { ++ for (int chunkZ = (k >> 4); chunkZ <= ((j1 - 1) >> 4); chunkZ++) { ++ Chunk chunk = getChunkAt(chunkX, chunkZ); ++ if (chunk == null) { ++ continue; ++ } ++ for (Object te : chunk.tileEntities.values()) { ++ TileEntity tileentity = (TileEntity) te; ++ if ((tileentity.position.getX() >= i) && (tileentity.position.getY() >= j) && (tileentity.position.getZ() >= k) && (tileentity.position.getX() < l) && (tileentity.position.getY() < i1) && (tileentity.position.getZ() < j1)) { ++ arraylist.add(tileentity); ++ } ++ } ++ } ++ } ++ /* + for (int k1 = 0; k1 < this.h.size(); ++k1) { + TileEntity tileentity = (TileEntity) this.h.get(k1); + BlockPosition blockposition = tileentity.getPosition(); +@@ -539,6 +731,8 @@ + arraylist.add(tileentity); + } + } ++ */ ++ // CraftBukkit end + + return arraylist; + } +@@ -601,6 +795,23 @@ + int i = 0; + int j = this.worldProvider.getSeaLevel(); + int k = 0; ++ ++ // CraftBukkit start ++ if (this.generator != null) { ++ Random rand = new Random(this.getSeed()); ++ org.bukkit.Location spawn = this.generator.getFixedSpawnLocation(((WorldServer) this).getWorld(), rand); ++ ++ if (spawn != null) { ++ if (spawn.getWorld() != ((WorldServer) this).getWorld()) { ++ throw new IllegalStateException("Cannot set spawn point for " + this.worldData.getName() + " to be in another world (" + spawn.getWorld().getName() + ")"); ++ } else { ++ this.worldData.setSpawn(new BlockPosition(spawn.getBlockX(), spawn.getBlockY(), spawn.getBlockZ())); ++ this.isLoading = false; ++ return; ++ } ++ } ++ } ++ // CraftBukkit end + + if (blockposition != null) { + i = blockposition.getX(); +@@ -611,7 +822,7 @@ + + int l = 0; + +- while (!this.worldProvider.canSpawn(i, k)) { ++ while (!this.canSpawn(i, k)) { // CraftBukkit - use our own canSpawn + i += random.nextInt(64) - random.nextInt(64); + k += random.nextInt(64) - random.nextInt(64); + ++l; +@@ -648,7 +859,7 @@ + return this.worldProvider.h(); + } + +- public void save(boolean flag, IProgressUpdate iprogressupdate) { ++ public void save(boolean flag, IProgressUpdate iprogressupdate) throws ExceptionWorldConflict { // CraftBukkit - added throws + if (this.chunkProvider.canSave()) { + if (iprogressupdate != null) { + iprogressupdate.a("Saving level"); +@@ -660,7 +871,8 @@ + } + + this.chunkProvider.saveChunks(flag, iprogressupdate); +- List list = this.chunkProviderServer.a(); ++ // CraftBukkit - ArrayList -> Collection ++ Collection list = this.chunkProviderServer.a(); + Iterator iterator = list.iterator(); + + while (iterator.hasNext()) { +@@ -680,7 +892,7 @@ + } + } + +- protected void a() { ++ protected void a() throws ExceptionWorldConflict { // CraftBukkit - added throws + this.checkSession(); + this.worldData.a(this.af().h()); + this.worldData.d(this.af().f()); +@@ -692,7 +904,11 @@ + this.worldData.b(this.af().j()); + this.worldData.e(this.af().i()); + this.dataManager.saveWorldData(this.worldData, this.server.getPlayerList().u()); +- this.worldMaps.a(); ++ // CraftBukkit start - save worldMaps once, rather than once per shared world ++ if (!(this instanceof SecondaryWorldServer)) { ++ this.worldMaps.a(); ++ } ++ // CraftBukkit end + } + + protected void a(Entity entity) { +@@ -724,8 +940,16 @@ + } + + public boolean strikeLightning(Entity entity) { ++ // CraftBukkit start ++ LightningStrikeEvent lightning = new LightningStrikeEvent(this.getWorld(), (org.bukkit.entity.LightningStrike) entity.getBukkitEntity()); ++ this.getServer().getPluginManager().callEvent(lightning); ++ ++ if (lightning.isCancelled()) { ++ return false; ++ } + if (super.strikeLightning(entity)) { +- this.server.getPlayerList().sendPacketNearby(entity.locX, entity.locY, entity.locZ, 512.0D, this.worldProvider.getDimension(), new PacketPlayOutSpawnEntityWeather(entity)); ++ this.server.getPlayerList().sendPacketNearby(entity.locX, entity.locY, entity.locZ, 512.0D, this.dimension, new PacketPlayOutSpawnEntityWeather(entity)); ++ // CraftBukkit end + return true; + } else { + return false; +@@ -737,10 +961,20 @@ + } + + public Explosion createExplosion(Entity entity, double d0, double d1, double d2, float f, boolean flag, boolean flag1) { ++ // CraftBukkit start ++ Explosion explosion = super.createExplosion(entity, d0, d1, d2, f, flag, flag1); ++ ++ if (explosion.wasCanceled) { ++ return explosion; ++ } ++ ++ /* Remove + Explosion explosion = new Explosion(this, entity, d0, d1, d2, f, flag, flag1); + + explosion.a(); + explosion.a(false); ++ */ ++ // CraftBukkit end - TODO: Check if explosions are still properly implemented + if (!flag1) { + explosion.clearBlocks(); + } +@@ -786,7 +1020,8 @@ + BlockActionData blockactiondata = (BlockActionData) iterator.next(); + + if (this.a(blockactiondata)) { +- this.server.getPlayerList().sendPacketNearby((double) blockactiondata.a().getX(), (double) blockactiondata.a().getY(), (double) blockactiondata.a().getZ(), 64.0D, this.worldProvider.getDimension(), new PacketPlayOutBlockAction(blockactiondata.a(), blockactiondata.d(), blockactiondata.b(), blockactiondata.c())); ++ // CraftBukkit - this.worldProvider.dimension -> this.dimension ++ this.server.getPlayerList().sendPacketNearby((double) blockactiondata.a().getX(), (double) blockactiondata.a().getY(), (double) blockactiondata.a().getZ(), 64.0D, this.dimension, new PacketPlayOutBlockAction(blockactiondata.a(), blockactiondata.d(), blockactiondata.b(), blockactiondata.c())); + } + } + +@@ -809,6 +1044,7 @@ + boolean flag = this.S(); + + super.p(); ++ /* CraftBukkit start + if (this.o != this.p) { + this.server.getPlayerList().a(new PacketPlayOutGameStateChange(7, this.p), this.worldProvider.getDimension()); + } +@@ -827,6 +1063,16 @@ + this.server.getPlayerList().sendAll(new PacketPlayOutGameStateChange(7, this.p)); + this.server.getPlayerList().sendAll(new PacketPlayOutGameStateChange(8, this.r)); + } ++ // */ ++ if (flag != this.S()) { ++ // Only send weather packets to those affected ++ for (int i = 0; i < this.players.size(); ++i) { ++ if (((EntityPlayer) this.players.get(i)).world == this) { ++ ((EntityPlayer) this.players.get(i)).setPlayerWeather((!flag ? WeatherType.DOWNFALL : WeatherType.CLEAR), false); ++ } ++ } ++ // CraftBukkit end ++ } + + } + +@@ -855,10 +1101,17 @@ + } + + public void a(EnumParticle enumparticle, boolean flag, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6, int... aint) { ++ // CraftBukkit - visibility api support ++ sendParticles(null, enumparticle, flag, d0, d1, d2, i, d3, d4, d5, d6, aint); ++ } ++ ++ public void sendParticles(EntityPlayer sender, EnumParticle enumparticle, boolean flag, double d0, double d1, double d2, int i, double d3, double d4, double d5, double d6, int... aint) { ++ // CraftBukkit end + PacketPlayOutWorldParticles packetplayoutworldparticles = new PacketPlayOutWorldParticles(enumparticle, flag, (float) d0, (float) d1, (float) d2, (float) d3, (float) d4, (float) d5, (float) d6, i, aint); + + for (int j = 0; j < this.players.size(); ++j) { + EntityPlayer entityplayer = (EntityPlayer) this.players.get(j); ++ if (sender != null && !sender.getBukkitEntity().canSee(entityplayer.getBukkitEntity())) continue; // CraftBukkit + BlockPosition blockposition = entityplayer.getChunkCoordinates(); + double d7 = blockposition.c(d0, d1, d2); + |