--- a/net/minecraft/server/Entity.java +++ b/net/minecraft/server/Entity.java @@ -21,8 +21,56 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +// CraftBukkit start +import org.bukkit.Bukkit; +import org.bukkit.Server; +import org.bukkit.block.BlockFace; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Hanging; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Vehicle; +import org.bukkit.event.entity.EntityCombustByEntityEvent; +import org.bukkit.event.hanging.HangingBreakByEntityEvent; +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.entity.Pose; +import org.bukkit.event.entity.EntityAirChangeEvent; +import org.bukkit.event.entity.EntityCombustEvent; +import org.bukkit.event.entity.EntityDropItemEvent; +import org.bukkit.event.entity.EntityPortalEvent; +import org.bukkit.event.entity.EntityPoseChangeEvent; +import org.bukkit.event.player.PlayerTeleportEvent; +import org.bukkit.plugin.PluginManager; +// CraftBukkit end + public abstract class Entity implements INamableTileEntity, 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; + } + + private CraftEntity bukkitEntity; + + public CraftEntity getBukkitEntity() { + if (bukkitEntity == null) { + bukkitEntity = CraftEntity.getEntity(world.getServer(), this); + } + return bukkitEntity; + } + + @Override + public CommandSender getBukkitSender(CommandListenerWrapper wrapper) { + return getBukkitEntity(); + } + // CraftBukkit end + protected static final Logger LOGGER = LogManager.getLogger(); private static final AtomicInteger entityCount = new AtomicInteger(); private static final List c = Collections.emptyList(); @@ -111,6 +159,20 @@ private long aG; private EntitySize size; private float headHeight; + // CraftBukkit start + public boolean persist = true; + public boolean valid; + public org.bukkit.projectiles.ProjectileSource projectileSource; // For projectiles only + public boolean forceExplosionKnockback; // SPIGOT-949 + + public float getBukkitYaw() { + return this.yaw; + } + + public boolean isChunkLoaded() { + return world.isChunkLoaded((int) Math.floor(this.locX()) >> 4, (int) Math.floor(this.locZ()) >> 4); + } + // CraftBukkit end public Entity(EntityTypes entitytypes, World world) { this.id = Entity.entityCount.incrementAndGet(); @@ -214,6 +276,12 @@ } public void setPose(EntityPose entitypose) { + // CraftBukkit start + if (entitypose == this.getPose()) { + return; + } + this.world.getServer().getPluginManager().callEvent(new EntityPoseChangeEvent(this.getBukkitEntity(), Pose.values()[entitypose.ordinal()])); + // CraftBukkit end this.datawatcher.set(Entity.POSE, entitypose); } @@ -230,6 +298,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("Infinite yaw (Hacking?)"); + } + 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("Infinite pitch (Hacking?)"); + } + f1 = 0; + } + // CraftBukkit end + this.yaw = f % 360.0F; this.pitch = f1 % 360.0F; } @@ -240,6 +335,7 @@ float f1 = this.size.height; this.a(new AxisAlignedBB(d0 - (double) f, d1, d2 - (double) f, d0 + (double) f, d1 + (double) f1, d2 + (double) f)); + if (valid) ((WorldServer) world).chunkCheck(this); // CraftBukkit } protected void ac() { @@ -254,6 +350,15 @@ this.entityBaseTick(); } + // CraftBukkit start + public void postTick() { + // No clean way to break out of ticking once the entity has been copied to a new world, so instead we move the portalling later in the tick cycle + if (!(this instanceof EntityPlayer)) { + this.doPortalTick(); + } + } + // CraftBukkit end + public void entityBaseTick() { this.world.getMethodProfiler().enter("entityBaseTick"); if (this.isPassenger() && this.getVehicle().dead) { @@ -267,7 +372,7 @@ this.z = this.A; this.lastPitch = this.pitch; this.lastYaw = this.yaw; - this.doPortalTick(); + if (this instanceof EntityPlayer) this.doPortalTick(); // CraftBukkit - // Moved up to postTick if (this.aK()) { this.aL(); } @@ -322,12 +427,44 @@ protected void burnFromLava() { if (!this.isFireProof()) { - this.setOnFire(15); + // CraftBukkit start - Fallen in lava TODO: this event spams! + if (this instanceof EntityLiving && 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(), false); + } + } else { + // This will be called every single tick the entity is in lava, so don't throw an event + this.setOnFire(15, false); + } + // CraftBukkit end - we also don't throw an event unless the object in lava is living, to save on some event calls this.damageEntity(DamageSource.LAVA, 4.0F); } } public void setOnFire(int i) { + // CraftBukkit start + this.setOnFire(i, true); + } + + public void setOnFire(int i, boolean callEvent) { + if (callEvent) { + EntityCombustEvent event = new EntityCombustEvent(this.getBukkitEntity(), i); + this.world.getServer().getPluginManager().callEvent(event); + + if (event.isCancelled()) { + return; + } + + i = event.getDuration(); + } + // CraftBukkit end int j = i * 20; if (this instanceof EntityLiving) { @@ -424,6 +561,28 @@ block.a((IBlockAccess) 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()), MathHelper.floor(this.locZ())); + + if (vec3d.x > vec3d1.x) { + bl = bl.getRelative(BlockFace.EAST); + } else if (vec3d.x < vec3d1.x) { + bl = bl.getRelative(BlockFace.WEST); + } else if (vec3d.z > vec3d1.z) { + bl = bl.getRelative(BlockFace.SOUTH); + } else if (vec3d.z < vec3d1.z) { + bl = bl.getRelative(BlockFace.NORTH); + } + + if (!bl.getType().isAir()) { + VehicleBlockCollisionEvent event = new VehicleBlockCollisionEvent(vehicle, bl); + world.getServer().getPluginManager().callEvent(event); + } + } + // CraftBukkit end + if (this.onGround && !this.br()) { block.stepOn(this.world, blockposition, this); } @@ -698,6 +857,7 @@ AxisAlignedBB axisalignedbb = this.getBoundingBox(); this.setPositionRaw((axisalignedbb.minX + axisalignedbb.maxX) / 2.0D, axisalignedbb.minY, (axisalignedbb.minZ + axisalignedbb.maxZ) / 2.0D); + if (valid) ((WorldServer) world).chunkCheck(this); // CraftBukkit } protected SoundEffect getSoundSwim() { @@ -1036,6 +1196,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; } @@ -1051,6 +1218,7 @@ this.pitch = MathHelper.a(f1, -90.0F, 90.0F) % 360.0F; this.lastYaw = this.yaw; this.lastPitch = this.pitch; + world.getChunkAt((int) Math.floor(this.locX()) >> 4, (int) Math.floor(this.locZ()) >> 4); // CraftBukkit } public void c(Vec3D vec3d) { @@ -1225,6 +1393,12 @@ return false; } + // CraftBukkit start - collidable API + public boolean canCollideWith(Entity entity) { + return isCollidable(); + } + // CraftBukkit end + public void a(Entity entity, int i, DamageSource damagesource) { if (entity instanceof EntityPlayer) { CriterionTriggers.c.a((EntityPlayer) entity, this, damagesource); @@ -1235,7 +1409,7 @@ public boolean a_(NBTTagCompound nbttagcompound) { String s = this.getSaveID(); - if (!this.dead && s != null) { + if (this.persist && !this.dead && s != null) { // CraftBukkit - persist flag nbttagcompound.setString("id", s); this.save(nbttagcompound); return true; @@ -1259,6 +1433,18 @@ Vec3D vec3d = this.getMot(); nbttagcompound.set("Motion", this.a(vec3d.x, vec3d.y, vec3d.z)); + + // 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(this.yaw, this.pitch)); nbttagcompound.setFloat("FallDistance", this.fallDistance); nbttagcompound.setShort("Fire", (short) this.fireTicks); @@ -1267,6 +1453,12 @@ nbttagcompound.setBoolean("Invulnerable", this.invulnerable); nbttagcompound.setInt("PortalCooldown", this.portalCooldown); nbttagcompound.a("UUID", this.getUniqueID()); + // CraftBukkit start + // PAIL: Check above UUID reads 1.8 properly, ie: UUIDMost / UUIDLeast + nbttagcompound.setLong("WorldUUIDLeast", ((WorldServer) this.world).getWorld().getUID().getLeastSignificantBits()); + nbttagcompound.setLong("WorldUUIDMost", ((WorldServer) this.world).getWorld().getUID().getMostSignificantBits()); + nbttagcompound.setInt("Bukkit.updateLevel", CURRENT_LEVEL); + // CraftBukkit end IChatBaseComponent ichatbasecomponent = this.getCustomName(); if (ichatbasecomponent != null) { @@ -1324,6 +1516,11 @@ } } + // CraftBukkit start - stores eventually existing bukkit values + if (this.bukkitEntity != null) { + this.bukkitEntity.storeBukkitValues(nbttagcompound); + } + // CraftBukkit end return nbttagcompound; } catch (Throwable throwable) { CrashReport crashreport = CrashReport.a(throwable, "Saving entity NBT"); @@ -1401,6 +1598,43 @@ } else { throw new IllegalStateException("Entity has invalid position"); } + + // 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(0); + } + } + // 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) { + bworld = ((org.bukkit.craftbukkit.CraftServer) server).getServer().getWorldServer(World.OVERWORLD).getWorld(); + } + + spawnIn(bworld == null ? null : ((CraftWorld) bworld).getHandle()); + } + this.getBukkitEntity().readBukkitValues(nbttagcompound); + // CraftBukkit end + } catch (Throwable throwable) { CrashReport crashreport = CrashReport.a(throwable, "Loading entity NBT"); CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being loaded"); @@ -1476,9 +1710,22 @@ } else if (this.world.isClientSide) { return null; } else { + // CraftBukkit start - Capture drops for death event + if (this instanceof EntityLiving && !((EntityLiving) this).forceDrops) { + ((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.defaultPickupDelay(); + // CraftBukkit start + EntityDropItemEvent event = new EntityDropItemEvent(this.getBukkitEntity(), (org.bukkit.entity.Item) entityitem.getBukkitEntity()); + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) { + return null; + } + // CraftBukkit end this.world.addEntity(entityitem); return entityitem; } @@ -1559,7 +1806,7 @@ this.setPose(EntityPose.STANDING); this.vehicle = entity; - this.vehicle.addPassenger(this); + if (!this.vehicle.addPassenger(this)) this.vehicle = null; // CraftBukkit return true; } } @@ -1584,7 +1831,7 @@ Entity entity = this.vehicle; this.vehicle = null; - entity.removePassenger(this); + if (!entity.removePassenger(this)) this.vehicle = entity; // CraftBukkit } } @@ -1593,10 +1840,31 @@ this.bb(); } - protected void addPassenger(Entity entity) { + protected boolean addPassenger(Entity entity) { // CraftBukkit if (entity.getVehicle() != this) { throw new IllegalStateException("Use x.startRiding(y), not y.addPassenger(x)"); } else { + // CraftBukkit start + com.google.common.base.Preconditions.checkState(!entity.passengers.contains(this), "Circular entity riding! %s %s", this, entity); + + CraftEntity craft = (CraftEntity) entity.getBukkitEntity().getVehicle(); + Entity orig = craft == null ? null : craft.getHandle(); + if (getBukkitEntity() instanceof Vehicle && entity.getBukkitEntity() instanceof LivingEntity) { + VehicleEnterEvent event = new VehicleEnterEvent( + (Vehicle) getBukkitEntity(), + entity.getBukkitEntity() + ); + // Suppress during worldgen + if (this.valid) { + Bukkit.getPluginManager().callEvent(event); + } + CraftEntity craftn = (CraftEntity) entity.getBukkitEntity().getVehicle(); + Entity n = craftn == null ? null : craftn.getHandle(); + if (event.isCancelled() || n != orig) { + return false; + } + } + // CraftBukkit end if (!this.world.isClientSide && entity instanceof EntityHuman && !(this.getRidingPassenger() instanceof EntityHuman)) { this.passengers.add(0, entity); } else { @@ -1604,15 +1872,33 @@ } } + return true; // CraftBukkit } - protected void removePassenger(Entity entity) { + protected boolean removePassenger(Entity entity) { // CraftBukkit if (entity.getVehicle() == this) { throw new IllegalStateException("Use x.stopRiding(y), not y.removePassenger(x)"); } else { + // CraftBukkit start + CraftEntity craft = (CraftEntity) entity.getBukkitEntity().getVehicle(); + Entity orig = craft == null ? null : craft.getHandle(); + if (getBukkitEntity() instanceof Vehicle && entity.getBukkitEntity() instanceof LivingEntity) { + VehicleExitEvent event = new VehicleExitEvent( + (Vehicle) getBukkitEntity(), + (LivingEntity) entity.getBukkitEntity() + ); + Bukkit.getPluginManager().callEvent(event); + CraftEntity craftn = (CraftEntity) entity.getBukkitEntity().getVehicle(); + Entity n = craftn == null ? null : craftn.getHandle(); + if (event.isCancelled() || n != orig) { + return false; + } + } + // CraftBukkit end this.passengers.remove(entity); entity.j = 60; } + return true; // CraftBukkit } protected boolean q(Entity entity) { @@ -1661,11 +1947,17 @@ ResourceKey resourcekey = this.world.getDimensionKey() == World.THE_NETHER ? World.OVERWORLD : World.THE_NETHER; WorldServer worldserver1 = minecraftserver.getWorldServer(resourcekey); - if (worldserver1 != null && minecraftserver.getAllowNether() && !this.isPassenger() && this.portalTicks++ >= i) { + if (true && !this.isPassenger() && this.portalTicks++ >= i) { // CraftBukkit this.world.getMethodProfiler().enter("portal"); this.portalTicks = i; this.portalCooldown = this.getDefaultPortalCooldown(); - this.a(worldserver1); + // CraftBukkit start + if (this instanceof EntityPlayer) { + ((EntityPlayer) this).a(worldserver1, PlayerTeleportEvent.TeleportCause.NETHER_PORTAL); + } else { + this.a(worldserver1); + } + // CraftBukkit end this.world.getMethodProfiler().exit(); } @@ -1765,6 +2057,13 @@ } public void setSwimming(boolean flag) { + // CraftBukkit start + if (this.isSwimming() != flag && this instanceof EntityLiving) { + if (CraftEventFactory.callToggleSwimEvent((EntityLiving) this, flag).isCancelled()) { + return; + } + } + // CraftBukkit end this.setFlag(4, flag); } @@ -1825,16 +2124,56 @@ } public void setAirTicks(int i) { - this.datawatcher.set(Entity.AIR_TICKS, i); + // CraftBukkit start + EntityAirChangeEvent event = new EntityAirChangeEvent(this.getBukkitEntity(), i); + // Suppress during worldgen + if (this.valid) { + event.getEntity().getServer().getPluginManager().callEvent(event); + } + if (event.isCancelled()) { + return; + } + this.datawatcher.set(Entity.AIR_TICKS, event.getAmount()); + // CraftBukkit end } public void onLightningStrike(EntityLightning entitylightning) { this.setFireTicks(this.fireTicks + 1); + // CraftBukkit start + final org.bukkit.entity.Entity thisBukkitEntity = this.getBukkitEntity(); + final org.bukkit.entity.Entity stormBukkitEntity = entitylightning.getBukkitEntity(); + final PluginManager pluginManager = Bukkit.getPluginManager(); + // CraftBukkit end + 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(), false); + } + // CraftBukkit end } - this.damageEntity(DamageSource.LIGHTNING, 5.0F); + // CraftBukkit start + if (thisBukkitEntity instanceof Hanging) { + HangingBreakByEntityEvent hangingEvent = new HangingBreakByEntityEvent((Hanging) thisBukkitEntity, stormBukkitEntity); + pluginManager.callEvent(hangingEvent); + + if (hangingEvent.isCancelled()) { + return; + } + } + + if (this.isFireProof()) { + return; + } + CraftEventFactory.entityDamage = entitylightning; + if (!this.damageEntity(DamageSource.LIGHTNING, 5.0F)) { + CraftEventFactory.entityDamage = null; + return; + } + // CraftBukkit end } public void k(boolean flag) { @@ -1986,18 +2325,45 @@ @Nullable public Entity a(WorldServer worldserver) { + // CraftBukkit start + return teleportTo(worldserver, null); + } + + @Nullable + public Entity teleportTo(WorldServer worldserver, BlockPosition location) { + // CraftBukkit end if (this.world instanceof WorldServer && !this.dead) { this.world.getMethodProfiler().enter("changeDimension"); - this.decouple(); + // CraftBukkit start + // this.decouple(); + if (worldserver == null){ + return null; + } + // CraftBukkit end this.world.getMethodProfiler().enter("reposition"); Vec3D vec3d = this.getMot(); float f = 0.0F; - BlockPosition blockposition; + BlockPosition blockposition = location; // CraftBukkit + if (blockposition == null) { // CraftBukkit if (this.world.getDimensionKey() == World.THE_END && worldserver.getDimensionKey() == World.OVERWORLD) { - blockposition = worldserver.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES, worldserver.getSpawn()); + // CraftBukkit start + EntityPortalEvent event = CraftEventFactory.callEntityPortalEvent(this, worldserver, worldserver.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES, worldserver.getSpawn()), 0); + if (event == null) { + return null; + } + worldserver = ((CraftWorld) event.getTo().getWorld()).getHandle(); + blockposition = new BlockPosition(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ()); + // CraftBukkit end } else if (worldserver.getDimensionKey() == World.THE_END) { - blockposition = WorldServer.a; + // CraftBukkit start + EntityPortalEvent event = CraftEventFactory.callEntityPortalEvent(this, worldserver, WorldServer.a, 0); + if (event == null) { + return null; + } + worldserver = ((CraftWorld) event.getTo().getWorld()).getHandle(); + blockposition = new BlockPosition(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ()); + // CraftBukkit end } else { double d0 = this.locX(); double d1 = this.locZ(); @@ -2023,7 +2389,16 @@ Vec3D vec3d1 = this.getPortalOffset(); blockposition = new BlockPosition(d0, this.locY(), d1); - ShapeDetector.Shape shapedetector_shape = worldserver.getTravelAgent().a(blockposition, vec3d, this.getPortalDirection(), vec3d1.x, vec3d1.y, this instanceof EntityHuman); + // CraftBukkit start + EntityPortalEvent event = CraftEventFactory.callEntityPortalEvent(this, worldserver, blockposition, 128); + if (event == null) { + return null; + } + worldserver = ((CraftWorld) event.getTo().getWorld()).getHandle(); + blockposition = new BlockPosition(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ()); + int searchRadius = event.getSearchRadius(); + // CraftBukkit end + ShapeDetector.Shape shapedetector_shape = worldserver.getTravelAgent().findPortal(blockposition, vec3d, this.getPortalDirection(), vec3d1.x, vec3d1.y, this instanceof EntityHuman, searchRadius); // CraftBukkit - search radius if (shapedetector_shape == null) { return null; @@ -2033,6 +2408,11 @@ vec3d = shapedetector_shape.velocity; f = (float) shapedetector_shape.yaw; } + } // CraftBukkit + + // CraftBukkit start + this.decouple(); + // CraftBukkit end this.world.getMethodProfiler().exitEnter("reloading"); Entity entity = this.getEntityType().a((World) worldserver); @@ -2043,8 +2423,16 @@ entity.setMot(vec3d); worldserver.addEntityTeleport(entity); if (worldserver.getDimensionKey() == World.THE_END) { - WorldServer.a(worldserver); + WorldServer.a(worldserver, this); // CraftBukkit + } + // CraftBukkit start - Forward the CraftEntity to the new entity + this.getBukkitEntity().setHandle(entity); + entity.bukkitEntity = this.getBukkitEntity(); + + if (this instanceof EntityInsentient) { + ((EntityInsentient)this).unleash(true, false); // Unleash to prevent duping of leads. } + // CraftBukkit end } this.bJ(); @@ -2253,7 +2641,26 @@ } public void a(AxisAlignedBB axisalignedbb) { - this.boundingBox = axisalignedbb; + // CraftBukkit start - block invalid bounding boxes + double minX = axisalignedbb.minX, + minY = axisalignedbb.minY, + minZ = axisalignedbb.minZ, + maxX = axisalignedbb.maxX, + maxY = axisalignedbb.maxY, + maxZ = axisalignedbb.maxZ; + double len = axisalignedbb.maxX - axisalignedbb.minX; + if (len < 0) maxX = minX; + if (len > 64) maxX = minX + 64.0; + + len = axisalignedbb.maxY - axisalignedbb.minY; + if (len < 0) maxY = minY; + if (len > 64) maxY = minY + 64.0; + + len = axisalignedbb.maxZ - axisalignedbb.minZ; + if (len < 0) maxZ = minZ; + if (len > 64) maxZ = minZ + 64.0; + this.boundingBox = new AxisAlignedBB(minX, minY, minZ, maxX, maxY, maxZ); + // CraftBukkit end } protected float getHeadHeight(EntityPose entitypose, EntitySize entitysize) {