diff --git a/sources/src/main/java/io/akarin/api/internal/utils/random/LightRNG.java b/sources/src/main/java/io/akarin/api/internal/utils/random/LightRNG.java new file mode 100644 index 000000000..296314001 --- /dev/null +++ b/sources/src/main/java/io/akarin/api/internal/utils/random/LightRNG.java @@ -0,0 +1,240 @@ +/* +Written in 2015 by Sebastiano Vigna (vigna@acm.org) + +To the extent possible under law, the author has dedicated all copyright +and related and neighboring rights to this software to the public domain +worldwide. This software is distributed without any warranty. + +See . */ +package io.akarin.api.internal.utils.random; + +/** + * This is a SplittableRandom-style generator, meant to have a tiny state + * that permits storing many different generators with low overhead. + * It should be rather fast, though no guarantees can be made on all hardware. + *
+ * Benchmarking on a Windows laptop with an i7-4700MQ processor running OpenJDK 8 + * reports generation of 64-bit random long output as 17.8x faster than generating + * an equivalent number of random longs with java.util.Random, and generation of + * 32-bit random int output as 9.8x faster. Specifically, generating 1 billion longs + * took about 1.28 nanoseconds per long (1.277 seconds for the whole group) with + * LightRNG, while java.util.Random (which is meant to produce int, to be fair) took + * about 22.8 nanoseconds per long (22.797 seconds for the whole group). XorRNG + * appears to be occasionally faster on int output than LightRNG, but it isn't clear + * why or what causes that (JIT or GC internals, possibly). XorRNG is slightly + * slower at generating 64-bit random data, including long and double, but not by + * a significant degree (a multiplier between 0.9 and 1.2 times). The only deciding + * factor then is state size, where LightRNG is as small as possible for any JVM + * object with even a single field: 16 bytes (on a 64-bit JVM; 8-byte objects with + * 4 bytes or less of non-static members may be possible on 32-bit JVMs but I can't + * find anything confirming that guess). + *
+ * So yes, this should be very fast, and with only a single long used per LightRNG, + * it is about as memory-efficient as these generators get. + *
+ * Written in 2015 by Sebastiano Vigna (vigna@acm.org) + * @author Sebastiano Vigna + * @author Tommy Ettinger + */ +public class LightRNG implements RandomnessSource, StatefulRandomness +{ + /** 2 raised to the 53, - 1. */ + private static final long DOUBLE_MASK = ( 1L << 53 ) - 1; + /** 2 raised to the -53. */ + private static final double NORM_53 = 1. / ( 1L << 53 ); + /** 2 raised to the 24, -1. */ + private static final long FLOAT_MASK = ( 1L << 24 ) - 1; + /** 2 raised to the -24. */ + private static final double NORM_24 = 1. / ( 1L << 24 ); + + private static final long serialVersionUID = -374415589203474497L; + + public long state; /* The state can be seeded with any value. */ + + /** Creates a new generator seeded using Math.random. */ + public LightRNG() { + this((long) Math.floor(Math.random() * Long.MAX_VALUE)); + } + + public LightRNG( final long seed ) { + setSeed(seed); + } + + @Override + public int next( int bits ) { + return (int)( nextLong() & ( 1L << bits ) - 1 ); + } + + /** + * Can return any long, positive or negative, of any size permissible in a 64-bit signed integer. + * @return any long, all 64 bits are random + */ + @Override + public long nextLong() { + long z = ( state += 0x9E3779B97F4A7C15L ); + z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9L; + z = (z ^ (z >>> 27)) * 0x94D049BB133111EBL; + return z ^ (z >>> 31); + } + + /** + * Produces a copy of this RandomnessSource that, if next() and/or nextLong() are called on this object and the + * copy, both will generate the same sequence of random numbers from the point copy() was called. This just need to + * copy the state so it isn't shared, usually, and produce a new value with the same exact state. + * + * @return a copy of this RandomnessSource + */ + @Override + public RandomnessSource copy() { + return new LightRNG(state); + } + + /** + * Can return any int, positive or negative, of any size permissible in a 32-bit signed integer. + * @return any int, all 32 bits are random + */ + public int nextInt() { + return (int)nextLong(); + } + + /** + * Exclusive on the upper bound. The lower bound is 0. + * @param bound the upper bound; should be positive + * @return a random int less than n and at least equal to 0 + */ + public int nextInt( final int bound ) { + if ( bound <= 0 ) return 0; + int threshold = (0x7fffffff - bound + 1) % bound; + for (;;) { + int bits = (int)(nextLong() & 0x7fffffff); + if (bits >= threshold) + return bits % bound; + } + } + /** + * Inclusive lower, exclusive upper. + * @param lower the lower bound, inclusive, can be positive or negative + * @param upper the upper bound, exclusive, should be positive, must be greater than lower + * @return a random int at least equal to lower and less than upper + */ + public int nextInt( final int lower, final int upper ) { + if ( upper - lower <= 0 ) throw new IllegalArgumentException("Upper bound must be greater than lower bound"); + return lower + nextInt(upper - lower); + } + + /** + * Exclusive on the upper bound. The lower bound is 0. + * @param bound the upper bound; should be positive + * @return a random long less than n + */ + public long nextLong( final long bound ) { + if ( bound <= 0 ) return 0; + long threshold = (0x7fffffffffffffffL - bound + 1) % bound; + for (;;) { + long bits = nextLong() & 0x7fffffffffffffffL; + if (bits >= threshold) + return bits % bound; + } + } + + /** + * Inclusive lower, exclusive upper. + * @param lower the lower bound, inclusive, can be positive or negative + * @param upper the upper bound, exclusive, should be positive, must be greater than lower + * @return a random long at least equal to lower and less than upper + */ + public long nextLong( final long lower, final long upper ) { + if ( upper - lower <= 0 ) throw new IllegalArgumentException("Upper bound must be greater than lower bound"); + return lower + nextLong(upper - lower); + } + /** + * Gets a uniform random double in the range [0.0,1.0) + * @return a random double at least equal to 0.0 and less than 1.0 + */ + public double nextDouble() { + return ( nextLong() & DOUBLE_MASK ) * NORM_53; + } + + /** + * Gets a uniform random double in the range [0.0,outer) given a positive parameter outer. If outer + * is negative, it will be the (exclusive) lower bound and 0.0 will be the (inclusive) upper bound. + * @param outer the exclusive outer bound, can be negative + * @return a random double between 0.0 (inclusive) and outer (exclusive) + */ + public double nextDouble(final double outer) { + return nextDouble() * outer; + } + + /** + * Gets a uniform random float in the range [0.0,1.0) + * @return a random float at least equal to 0.0 and less than 1.0 + */ + public float nextFloat() { + return (float)( ( nextLong() & FLOAT_MASK ) * NORM_24 ); + } + + /** + * Gets a random value, true or false. + * Calls nextLong() once. + * @return a random true or false value. + */ + public boolean nextBoolean() { + return ( nextLong() & 1 ) != 0L; + } + + /** + * Given a byte array as a parameter, this will fill the array with random bytes (modifying it + * in-place). Calls nextLong() {@code Math.ceil(bytes.length / 8.0)} times. + * @param bytes a byte array that will have its contents overwritten with random bytes. + */ + public void nextBytes( final byte[] bytes ) { + int i = bytes.length, n = 0; + while( i != 0 ) { + n = Math.min( i, 8 ); + for ( long bits = nextLong(); n-- != 0; bits >>= 8 ) bytes[ --i ] = (byte)bits; + } + } + + + + /** + * Sets the seed of this generator (which is also the current state). + * @param seed the seed to use for this LightRNG, as if it was constructed with this seed. + */ + public void setSeed( final long seed ) { + state = seed; + } + /** + * Sets the seed (also the current state) of this generator. + * @param seed the seed to use for this LightRNG, as if it was constructed with this seed. + */ + @Override + public void setState( final long seed ) { + state = seed; + } + /** + * Gets the current state of this generator. + * @return the current seed of this LightRNG, changed once per call to nextLong() + */ + @Override + public long getState() { + return state; + } + + /** + * Advances or rolls back the LightRNG's state without actually generating numbers. Skip forward + * or backward a number of steps specified by advance, where a step is equal to one call to nextInt(). + * @param advance Number of future generations to skip past. Can be negative to backtrack. + * @return the state after skipping. + */ + public long skip(long advance) + { + return state += 0x9E3779B97F4A7C15L * advance; + } + + @Override + public String toString() { + return "LightRNG (" + state + ")"; // Akarin + } + +} \ No newline at end of file diff --git a/sources/src/main/java/io/akarin/api/internal/utils/random/LightRandom.java b/sources/src/main/java/io/akarin/api/internal/utils/random/LightRandom.java new file mode 100644 index 000000000..b70c4072c --- /dev/null +++ b/sources/src/main/java/io/akarin/api/internal/utils/random/LightRandom.java @@ -0,0 +1,62 @@ +package io.akarin.api.internal.utils.random; + +import java.util.Random; + +/** + * This is a "fake" LightRandom, backed by the LightRNG. + * + * This is useful if you want to quickly replace a Random variable with + * LightRNG without breaking every code. + */ +public class LightRandom extends Random { + public static LightRNG light = new LightRNG(); // LightRNG, static. + + private static final long serialVersionUID = 1L; + + @Override + public int next(int bits) { + return light.next(bits); + } + + @Override + public void nextBytes(byte[] bytes) { + light.nextBytes(bytes); + } + + @Override + public int nextInt() { + return light.nextInt(); + } + + @Override + public int nextInt(int n) { + return light.nextInt(n); + } + + @Override + public long nextLong() { + return light.nextLong(); + } + + @Override + public boolean nextBoolean() { + return light.nextBoolean(); + } + + @Override + public float nextFloat() { + return light.nextFloat(); + } + + @Override + public double nextDouble() { + return light.nextDouble(); + } + + // Akarin start + @Override + public void setSeed(long seed) { + light.setSeed(seed); + } + // Akarin end +} \ No newline at end of file diff --git a/sources/src/main/java/io/akarin/api/internal/utils/random/RandomnessSource.java b/sources/src/main/java/io/akarin/api/internal/utils/random/RandomnessSource.java new file mode 100644 index 000000000..8c86bf176 --- /dev/null +++ b/sources/src/main/java/io/akarin/api/internal/utils/random/RandomnessSource.java @@ -0,0 +1,40 @@ +package io.akarin.api.internal.utils.random; + +import java.io.Serializable; + +/** + * This interface defines the interactions required of a random number + * generator. It is a replacement for Java's built-in Random because for + * improved performance. + * + * @author Eben Howard - http://squidpony.com - howard@squidpony.com + */ +public interface RandomnessSource extends Serializable { + + /** + * Using this method, any algorithm that might use the built-in Java Random + * can interface with this randomness source. + * + * @param bits the number of bits to be returned + * @return the integer containing the appropriate number of bits + */ + int next(int bits); + + /** + * + * Using this method, any algorithm that needs to efficiently generate more + * than 32 bits of random data can interface with this randomness source. + * + * Get a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive). + * @return a random long between Long.MIN_VALUE and Long.MAX_VALUE (both inclusive) + */ + long nextLong(); + + /** + * Produces a copy of this RandomnessSource that, if next() and/or nextLong() are called on this object and the + * copy, both will generate the same sequence of random numbers from the point copy() was called. This just need to + * copy the state so it isn't shared, usually, and produce a new value with the same exact state. + * @return a copy of this RandomnessSource + */ + RandomnessSource copy(); +} \ No newline at end of file diff --git a/sources/src/main/java/io/akarin/api/internal/utils/random/StatefulRandomness.java b/sources/src/main/java/io/akarin/api/internal/utils/random/StatefulRandomness.java new file mode 100644 index 000000000..0a02a0a8a --- /dev/null +++ b/sources/src/main/java/io/akarin/api/internal/utils/random/StatefulRandomness.java @@ -0,0 +1,20 @@ +package io.akarin.api.internal.utils.random; + +/** + * A simple interface for RandomnessSources that have the additional property of a state that can be re-set. + * Created by Tommy Ettinger on 9/15/2015. + */ +public interface StatefulRandomness extends RandomnessSource { + /** + * Get the current internal state of the StatefulRandomness as a long. + * @return the current internal state of this object. + */ + long getState(); + + /** + * Set the current internal state of this StatefulRandomness with a long. + * + * @param state a 64-bit long. You should avoid passing 0, even though some implementations can handle that. + */ + void setState(long state); +} \ No newline at end of file diff --git a/sources/src/main/java/io/akarin/server/mixin/optimization/MixinEntity.java b/sources/src/main/java/io/akarin/server/mixin/optimization/MixinEntity.java index 4f6225cec..450c7f3ff 100644 --- a/sources/src/main/java/io/akarin/server/mixin/optimization/MixinEntity.java +++ b/sources/src/main/java/io/akarin/server/mixin/optimization/MixinEntity.java @@ -21,7 +21,7 @@ public abstract class MixinEntity { @Overwrite // PAIL: isInLava public boolean au() { /* - * This is originally comes from Migot (https://github.com/Poweruser/Migot/commit/cafbf1707107d2a3aa6232879f305975bb1f0285) + * This originally comes from Migot (https://github.com/Poweruser/Migot/commit/cafbf1707107d2a3aa6232879f305975bb1f0285) * Thanks @Poweruser */ int currentTick = MinecraftServer.currentTick; diff --git a/sources/src/main/java/net/minecraft/server/Entity.java b/sources/src/main/java/net/minecraft/server/Entity.java new file mode 100644 index 000000000..139aa5771 --- /dev/null +++ b/sources/src/main/java/net/minecraft/server/Entity.java @@ -0,0 +1,2954 @@ +package net.minecraft.server; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import javax.annotation.Nullable; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +// 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.Vehicle; +import co.aikar.timings.MinecraftTimings; // Paper +import co.aikar.timings.Timing; // Paper +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.event.entity.EntityAirChangeEvent; +import org.bukkit.event.entity.EntityCombustEvent; +import org.bukkit.event.entity.EntityPortalEvent; +import org.bukkit.plugin.PluginManager; +// CraftBukkit end + +/** + * Akarin Changes Note + * 1) Random -> LightRandom (performance) + */ +public abstract class Entity implements ICommandListener, KeyedObject { // Paper + + // CraftBukkit start + private static final int CURRENT_LEVEL = 2; + public static Random SHARED_RANDOM = new io.akarin.api.internal.utils.random.LightRandom(); // Paper // Akarin - LightRNG + static boolean isLevelAtLeast(NBTTagCompound tag, int level) { + return tag.hasKey("Bukkit.updateLevel") && tag.getInt("Bukkit.updateLevel") >= level; + } + + protected CraftEntity bukkitEntity; + + EntityTrackerEntry tracker; // Paper + public CraftEntity getBukkitEntity() { + if (bukkitEntity == null) { + bukkitEntity = CraftEntity.getEntity(world.getServer(), this); + } + return bukkitEntity; + } + // CraftBukikt end + + private static final Logger a = LogManager.getLogger(); + private static final List b = Collections.emptyList(); + private static final AxisAlignedBB c = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D); + private static double f = 1.0D; + private static int entityCount; + private int id; + public boolean i; public boolean blocksEntitySpawning() { return i; } // Paper - OBFHELPER + public final List passengers; + protected int j; + private Entity au;public void setVehicle(Entity entity) { this.au = entity; } // Paper // OBFHELPER + public boolean attachedToPlayer; + public World world; + public double lastX; + public double lastY; + public double lastZ; + public double locX; + public double locY; + public double locZ; + // Paper start - getters to implement HopperPusher + public double getX() { + return locX; + } + + public double getY() { + return locY; + } + + public double getZ() { + return locZ; + } + // Paper end + public double motX; + public double motY; + public double motZ; + public float yaw; + public float pitch; + public float lastYaw; + public float lastPitch; + private AxisAlignedBB boundingBox; + public boolean onGround; + public boolean positionChanged; + public boolean B; + public boolean C; + public boolean velocityChanged; + protected boolean E; + private boolean aw; + public boolean dead; + public float width; + public float length; + public float I; + public float J; + public float K; + public float fallDistance; + private int ax; + private float ay; + public double M; + public double N; + public double O; + public float P; + public boolean noclip; + public float R; + protected Random random; + public int ticksLived; + public int fireTicks; + public boolean inWater; // Spigot - protected -> public // PAIL + public int noDamageTicks; + protected boolean justCreated; + protected boolean fireProof; + protected DataWatcher datawatcher; + protected static final DataWatcherObject Z = DataWatcher.a(Entity.class, DataWatcherRegistry.a); + private static final DataWatcherObject aA = DataWatcher.a(Entity.class, DataWatcherRegistry.b); + private static final DataWatcherObject aB = DataWatcher.a(Entity.class, DataWatcherRegistry.d); + private static final DataWatcherObject aC = DataWatcher.a(Entity.class, DataWatcherRegistry.h); + private static final DataWatcherObject aD = DataWatcher.a(Entity.class, DataWatcherRegistry.h); + private static final DataWatcherObject aE = DataWatcher.a(Entity.class, DataWatcherRegistry.h); + public boolean aa; public boolean isAddedToChunk() { return aa; } // Paper - OBFHELPER + public int ab; public int getChunkX() { return ab; } // Paper - OBFHELPER + public int ac; public int getChunkY() { return ac; } // Paper - OBFHELPER + public int ad; public int getChunkZ() { return ad; } // Paper - OBFHELPER + public boolean ah; + public boolean impulse; + public int portalCooldown; + protected boolean ak; public boolean inPortal() { return ak; } // Paper - OBFHELPER + protected int al; + public int dimension; + protected BlockPosition an; + protected Vec3D ao; + protected EnumDirection ap; + private boolean invulnerable; + protected UUID uniqueID; + protected String ar; + private final CommandObjectiveExecutor aG; + public boolean glowing; + private final Set aH; + private boolean aI; + private final double[] aJ; + private long aK; + // CraftBukkit start + public boolean valid; + public org.bukkit.projectiles.ProjectileSource projectileSource; // For projectiles only + public boolean forceExplosionKnockback; // SPIGOT-949 + public Timing tickTimer = MinecraftTimings.getEntityTimings(this); // Paper + public Location origin; // Paper + // Spigot start + public final byte activationType = org.spigotmc.ActivationRange.initializeEntityActivationType(this); + public final boolean defaultActivationState; + public long activatedTick = Integer.MIN_VALUE; + public boolean fromMobSpawner; + public boolean spawnedViaMobSpawner; // Paper - Yes this name is similar to above, upstream took the better one + protected int numCollisions = 0; // Paper + public void inactiveTick() { } + // Spigot end + + public float getBukkitYaw() { + return this.yaw; + } + // CraftBukkit end + + public Entity(World world) { + this.id = Entity.entityCount++; + this.passengers = Lists.newArrayList(); + this.boundingBox = Entity.c; + this.width = 0.6F; + this.length = 1.8F; + this.ax = 1; + this.ay = 1.0F; + this.random = SHARED_RANDOM; // Paper + this.fireTicks = -this.getMaxFireTicks(); + this.justCreated = true; + this.uniqueID = MathHelper.a(this.random); + this.ar = this.uniqueID.toString(); + this.aG = new CommandObjectiveExecutor(); + this.aH = Sets.newHashSet(); + this.aJ = new double[] { 0.0D, 0.0D, 0.0D}; + this.world = world; + this.setPosition(0.0D, 0.0D, 0.0D); + if (world != null) { + this.dimension = world.worldProvider.getDimensionManager().getDimensionID(); + // Spigot start + this.defaultActivationState = org.spigotmc.ActivationRange.initializeEntityActivationState(this, world.spigotConfig); + } else { + this.defaultActivationState = false; + } + // Spigot end + + this.datawatcher = new DataWatcher(this); + this.datawatcher.register(Entity.Z, Byte.valueOf((byte) 0)); + this.datawatcher.register(Entity.aA, Integer.valueOf(300)); + this.datawatcher.register(Entity.aC, Boolean.valueOf(false)); + this.datawatcher.register(Entity.aB, ""); + this.datawatcher.register(Entity.aD, Boolean.valueOf(false)); + this.datawatcher.register(Entity.aE, Boolean.valueOf(false)); + this.i(); + } + + public int getId() { + return this.id; + } + + public void h(int i) { + this.id = i; + } + + public Set getScoreboardTags() { + return this.aH; + } + + public boolean addScoreboardTag(String s) { + if (this.aH.size() >= 1024) { + return false; + } else { + this.aH.add(s); + return true; + } + } + + public boolean removeScoreboardTag(String s) { + return this.aH.remove(s); + } + + public void killEntity() { + this.die(); + } + + protected abstract void i(); + + public DataWatcher getDataWatcher() { + return this.datawatcher; + } + + public boolean equals(Object object) { + return object instanceof Entity ? ((Entity) object).id == this.id : false; + } + + public int hashCode() { + return this.id; + } + + public void die() { + this.dead = true; + } + + public void b(boolean flag) {} + + public void setSize(float f, float f1) { + if (f != this.width || f1 != this.length) { + float f2 = this.width; + + this.width = f; + this.length = f1; + if (this.width < f2) { + double d0 = (double) f / 2.0D; + + this.a(new AxisAlignedBB(this.locX - d0, this.locY, this.locZ - d0, this.locX + d0, this.locY + (double) this.length, this.locZ + d0)); + return; + } + + AxisAlignedBB axisalignedbb = this.getBoundingBox(); + + this.a(new AxisAlignedBB(axisalignedbb.a, axisalignedbb.b, axisalignedbb.c, axisalignedbb.a + (double) this.width, axisalignedbb.b + (double) this.length, axisalignedbb.c + (double) this.width)); + if (this.width > f2 && !this.justCreated && !this.world.isClientSide) { + this.move(EnumMoveType.SELF, (double) (f2 - this.width), 0.0D, (double) (f2 - this.width)); + } + } + + } + + 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?)"); //Spigot "Nope" -> Descriptive reason + } + 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?)"); //Spigot "Nope" -> Descriptive reason + } + f1 = 0; + } + // CraftBukkit end + + this.yaw = f % 360.0F; + this.pitch = f1 % 360.0F; + } + + public void setPosition(double d0, double d1, double d2) { + this.locX = d0; + this.locY = d1; + this.locZ = d2; + float f = this.width / 2.0F; + float f1 = this.length; + + this.a(new AxisAlignedBB(d0 - (double) f, d1, d2 - (double) f, d0 + (double) f, d1 + (double) f1, d2 + (double) f)); + } + + public void B_() { + if (!this.world.isClientSide) { + this.setFlag(6, this.aW()); + } + + this.Y(); + } + + // 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.world.isClientSide && this.world instanceof WorldServer) { + this.world.methodProfiler.a("portal"); + if (this.ak) { + MinecraftServer minecraftserver = this.world.getMinecraftServer(); + + if (true || minecraftserver.getAllowNether()) { // CraftBukkit + if (!this.isPassenger()) { + int i = this.Z(); + + if (this.al++ >= i) { + this.al = i; + this.portalCooldown = this.aM(); + byte b0; + + if (this.world.worldProvider.getDimensionManager().getDimensionID() == -1) { + b0 = 0; + } else { + b0 = -1; + } + + this.b(b0); + } + } + + this.ak = false; + } + } else { + if (this.al > 0) { + this.al -= 4; + } + + if (this.al < 0) { + this.al = 0; + } + } + + this.I(); + this.world.methodProfiler.b(); + } + } + // CraftBukkit end + + public void Y() { + this.world.methodProfiler.a("entityBaseTick"); + if (this.isPassenger() && this.bJ().dead) { + this.stopRiding(); + } + + if (this.j > 0) { + --this.j; + } + + this.I = this.J; + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + this.lastPitch = this.pitch; + this.lastYaw = this.yaw; + // Moved up to postTick + /* + if (!this.world.isClientSide && this.world instanceof WorldServer) { + this.world.methodProfiler.a("portal"); + if (this.ak) { + MinecraftServer minecraftserver = this.world.getMinecraftServer(); + + if (minecraftserver.getAllowNether()) { + if (!this.isPassenger()) { + int i = this.Z(); + + if (this.al++ >= i) { + this.al = i; + this.portalCooldown = this.aM(); + byte b0; + + if (this.world.worldProvider.getDimensionManager().getDimensionID() == -1) { + b0 = 0; + } else { + b0 = -1; + } + + this.b(b0); + } + } + + this.ak = false; + } + } else { + if (this.al > 0) { + this.al -= 4; + } + + if (this.al < 0) { + this.al = 0; + } + } + + this.I(); + this.world.methodProfiler.b(); + } + */ + + this.as(); + this.aq(); + if (this.world.isClientSide) { + this.extinguish(); + } else if (this.fireTicks > 0) { + if (this.fireProof) { + this.fireTicks -= 4; + if (this.fireTicks < 0) { + this.extinguish(); + } + } else { + if (this.fireTicks % 20 == 0) { + this.damageEntity(DamageSource.BURN, 1.0F); + } + + --this.fireTicks; + } + } + + if (this.au()) { + this.burnFromLava(); + this.fallDistance *= 0.5F; + } + + // Paper start - Configurable nether ceiling damage + // Extracted to own function + /* + if (this.locY < -64.0D) { + this.ac(); + } + */ + this.checkAndDoHeightDamage(); + // Paper end + + if (!this.world.isClientSide) { + this.setFlag(0, this.fireTicks > 0); + } + + this.justCreated = false; + this.world.methodProfiler.b(); + } + + // Paper start - Configurable top of nether void damage + private boolean paperNetherCheck() { + return this.world.paperConfig.netherVoidTopDamage && this.world.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER && this.locY >= 128.0D; + } + + protected void checkAndDoHeightDamage() { + if (this.locY < -64.0D || paperNetherCheck()) { + this.kill(); + } + } + // Paper end + + protected void I() { + if (this.portalCooldown > 0) { + --this.portalCooldown; + } + + } + + public int Z() { + return 1; + } + + 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); + } + } + + public void setOnFire(int i) { + int j = i * 20; + + if (this instanceof EntityLiving) { + j = EnchantmentProtection.a((EntityLiving) this, j); + } + + if (this.fireTicks < j) { + this.fireTicks = j; + } + + } + + public void extinguish() { + this.fireTicks = 0; + } + + protected final void kill() { this.ac(); } // Paper - OBFHELPER + protected void ac() { + this.die(); + } + + public boolean c(double d0, double d1, double d2) { + AxisAlignedBB axisalignedbb = this.getBoundingBox().d(d0, d1, d2); + + return this.b(axisalignedbb); + } + + private boolean b(AxisAlignedBB axisalignedbb) { + return this.world.getCubes(this, axisalignedbb).isEmpty() && !this.world.containsLiquid(axisalignedbb); + } + + public void move(EnumMoveType enummovetype, double d0, double d1, double d2) { + if (this.noclip) { + this.a(this.getBoundingBox().d(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.isVehicle() && this.isPassenger()) { + return; + } + // CraftBukkit end + if (enummovetype == EnumMoveType.PISTON) { + long i = this.world.getTime(); + + if (i != this.aK) { + Arrays.fill(this.aJ, 0.0D); + this.aK = i; + } + + int j; + double d3; + + if (d0 != 0.0D) { + j = EnumDirection.EnumAxis.X.ordinal(); + d3 = MathHelper.a(d0 + this.aJ[j], -0.51D, 0.51D); + d0 = d3 - this.aJ[j]; + this.aJ[j] = d3; + if (Math.abs(d0) <= 9.999999747378752E-6D) { + return; + } + } else if (d1 != 0.0D) { + j = EnumDirection.EnumAxis.Y.ordinal(); + d3 = MathHelper.a(d1 + this.aJ[j], -0.51D, 0.51D); + d1 = d3 - this.aJ[j]; + this.aJ[j] = d3; + if (Math.abs(d1) <= 9.999999747378752E-6D) { + return; + } + } else { + if (d2 == 0.0D) { + return; + } + + j = EnumDirection.EnumAxis.Z.ordinal(); + d3 = MathHelper.a(d2 + this.aJ[j], -0.51D, 0.51D); + d2 = d3 - this.aJ[j]; + this.aJ[j] = d3; + if (Math.abs(d2) <= 9.999999747378752E-6D) { + return; + } + } + } + + this.world.methodProfiler.a("move"); + double d4 = this.locX; + double d5 = this.locY; + double d6 = this.locZ; + + if (this.E) { + this.E = false; + d0 *= 0.25D; + d1 *= 0.05000000074505806D; + d2 *= 0.25D; + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + } + + double d7 = d0; + double d8 = d1; + double d9 = d2; + + if ((enummovetype == EnumMoveType.SELF || enummovetype == EnumMoveType.PLAYER) && this.onGround && this.isSneaking() && this instanceof EntityHuman) { + for (double d10 = 0.05D; d0 != 0.0D && this.world.getCubes(this, this.getBoundingBox().d(d0, (double) (-this.P), 0.0D)).isEmpty(); d7 = d0) { + if (d0 < 0.05D && d0 >= -0.05D) { + d0 = 0.0D; + } else if (d0 > 0.0D) { + d0 -= 0.05D; + } else { + d0 += 0.05D; + } + } + + for (; d2 != 0.0D && this.world.getCubes(this, this.getBoundingBox().d(0.0D, (double) (-this.P), d2)).isEmpty(); d9 = d2) { + if (d2 < 0.05D && d2 >= -0.05D) { + d2 = 0.0D; + } else if (d2 > 0.0D) { + d2 -= 0.05D; + } else { + d2 += 0.05D; + } + } + + for (; d0 != 0.0D && d2 != 0.0D && this.world.getCubes(this, this.getBoundingBox().d(d0, (double) (-this.P), d2)).isEmpty(); d9 = d2) { + if (d0 < 0.05D && d0 >= -0.05D) { + d0 = 0.0D; + } else if (d0 > 0.0D) { + d0 -= 0.05D; + } else { + d0 += 0.05D; + } + + d7 = d0; + if (d2 < 0.05D && d2 >= -0.05D) { + d2 = 0.0D; + } else if (d2 > 0.0D) { + d2 -= 0.05D; + } else { + d2 += 0.05D; + } + } + } + + List list = this.world.getCubes(this, this.getBoundingBox().b(d0, d1, d2)); + AxisAlignedBB axisalignedbb = this.getBoundingBox(); + int k; + int l; + + if (d1 != 0.0D) { + k = 0; + + for (l = list.size(); k < l; ++k) { + d1 = ((AxisAlignedBB) list.get(k)).b(this.getBoundingBox(), d1); + } + + this.a(this.getBoundingBox().d(0.0D, d1, 0.0D)); + } + + if (d0 != 0.0D) { + k = 0; + + for (l = list.size(); k < l; ++k) { + d0 = ((AxisAlignedBB) list.get(k)).a(this.getBoundingBox(), d0); + } + + if (d0 != 0.0D) { + this.a(this.getBoundingBox().d(d0, 0.0D, 0.0D)); + } + } + + if (d2 != 0.0D) { + k = 0; + + for (l = list.size(); k < l; ++k) { + d2 = ((AxisAlignedBB) list.get(k)).c(this.getBoundingBox(), d2); + } + + if (d2 != 0.0D) { + this.a(this.getBoundingBox().d(0.0D, 0.0D, d2)); + } + } + + boolean flag = this.onGround || d1 != d8 && d1 < 0.0D; // CraftBukkit - decompile error + double d11; + + if (this.P > 0.0F && flag && (d7 != d0 || d9 != d2)) { + double d12 = d0; + double d13 = d1; + double d14 = d2; + AxisAlignedBB axisalignedbb1 = this.getBoundingBox(); + + this.a(axisalignedbb); + d1 = (double) this.P; + List list1 = this.world.getCubes(this, this.getBoundingBox().b(d7, d1, d9)); + AxisAlignedBB axisalignedbb2 = this.getBoundingBox(); + AxisAlignedBB axisalignedbb3 = axisalignedbb2.b(d7, 0.0D, d9); + + d11 = d1; + int i1 = 0; + + for (int j1 = list1.size(); i1 < j1; ++i1) { + d11 = ((AxisAlignedBB) list1.get(i1)).b(axisalignedbb3, d11); + } + + axisalignedbb2 = axisalignedbb2.d(0.0D, d11, 0.0D); + double d15 = d7; + int k1 = 0; + + for (int l1 = list1.size(); k1 < l1; ++k1) { + d15 = ((AxisAlignedBB) list1.get(k1)).a(axisalignedbb2, d15); + } + + axisalignedbb2 = axisalignedbb2.d(d15, 0.0D, 0.0D); + double d16 = d9; + int i2 = 0; + + for (int j2 = list1.size(); i2 < j2; ++i2) { + d16 = ((AxisAlignedBB) list1.get(i2)).c(axisalignedbb2, d16); + } + + axisalignedbb2 = axisalignedbb2.d(0.0D, 0.0D, d16); + AxisAlignedBB axisalignedbb4 = this.getBoundingBox(); + double d17 = d1; + int k2 = 0; + + for (int l2 = list1.size(); k2 < l2; ++k2) { + d17 = ((AxisAlignedBB) list1.get(k2)).b(axisalignedbb4, d17); + } + + axisalignedbb4 = axisalignedbb4.d(0.0D, d17, 0.0D); + double d18 = d7; + int i3 = 0; + + for (int j3 = list1.size(); i3 < j3; ++i3) { + d18 = ((AxisAlignedBB) list1.get(i3)).a(axisalignedbb4, d18); + } + + axisalignedbb4 = axisalignedbb4.d(d18, 0.0D, 0.0D); + double d19 = d9; + int k3 = 0; + + for (int l3 = list1.size(); k3 < l3; ++k3) { + d19 = ((AxisAlignedBB) list1.get(k3)).c(axisalignedbb4, d19); + } + + axisalignedbb4 = axisalignedbb4.d(0.0D, 0.0D, d19); + double d20 = d15 * d15 + d16 * d16; + double d21 = d18 * d18 + d19 * d19; + + if (d20 > d21) { + d0 = d15; + d2 = d16; + d1 = -d11; + this.a(axisalignedbb2); + } else { + d0 = d18; + d2 = d19; + d1 = -d17; + this.a(axisalignedbb4); + } + + int i4 = 0; + + for (int j4 = list1.size(); i4 < j4; ++i4) { + d1 = ((AxisAlignedBB) list1.get(i4)).b(this.getBoundingBox(), d1); + } + + this.a(this.getBoundingBox().d(0.0D, d1, 0.0D)); + if (d12 * d12 + d14 * d14 >= d0 * d0 + d2 * d2) { + d0 = d12; + d1 = d13; + d2 = d14; + this.a(axisalignedbb1); + } + } + + this.world.methodProfiler.b(); + this.world.methodProfiler.a("rest"); + this.recalcPosition(); + this.positionChanged = d7 != d0 || d9 != d2; + this.B = d1 != d8; // CraftBukkit - decompile error + this.onGround = this.B && d8 < 0.0D; + this.C = this.positionChanged || this.B; + l = MathHelper.floor(this.locX); + int k4 = MathHelper.floor(this.locY - 0.20000000298023224D); + int l4 = MathHelper.floor(this.locZ); + BlockPosition blockposition = new BlockPosition(l, k4, l4); + IBlockData iblockdata = this.world.getType(blockposition); + + if (iblockdata.getMaterial() == Material.AIR) { + BlockPosition blockposition1 = blockposition.down(); + IBlockData iblockdata1 = this.world.getType(blockposition1); + Block block = iblockdata1.getBlock(); + + if (block instanceof BlockFence || block instanceof BlockCobbleWall || block instanceof BlockFenceGate) { + iblockdata = iblockdata1; + blockposition = blockposition1; + } + } + + this.a(d1, this.onGround, iblockdata, blockposition); + if (d7 != d0) { + this.motX = 0.0D; + } + + if (d9 != d2) { + this.motZ = 0.0D; + } + + Block block1 = iblockdata.getBlock(); + + if (d8 != d1) { + block1.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), MathHelper.floor(this.locZ)); + + if (d7 > d0) { + bl = bl.getRelative(BlockFace.EAST); + } else if (d7 < d0) { + bl = bl.getRelative(BlockFace.WEST); + } else if (d9 > d2) { + bl = bl.getRelative(BlockFace.SOUTH); + } else if (d9 < d2) { + bl = bl.getRelative(BlockFace.NORTH); + } + + if (bl.getType() != org.bukkit.Material.AIR) { + VehicleBlockCollisionEvent event = new VehicleBlockCollisionEvent(vehicle, bl); + world.getServer().getPluginManager().callEvent(event); + } + } + // CraftBukkit end + + if (this.playStepSound() && (!this.onGround || !this.isSneaking() || !(this instanceof EntityHuman)) && !this.isPassenger()) { + double d22 = this.locX - d4; + double d23 = this.locY - d5; + + d11 = this.locZ - d6; + if (block1 != Blocks.LADDER) { + d23 = 0.0D; + } + + if (block1 != null && this.onGround) { + block1.stepOn(this.world, blockposition, this); + } + + this.J = (float) ((double) this.J + (double) MathHelper.sqrt(d22 * d22 + d11 * d11) * 0.6D); + this.K = (float) ((double) this.K + (double) MathHelper.sqrt(d22 * d22 + d23 * d23 + d11 * d11) * 0.6D); + if (this.K > (float) this.ax && iblockdata.getMaterial() != Material.AIR) { + this.ax = (int) this.K + 1; + if (this.isInWater()) { + Entity entity = this.isVehicle() && this.bE() != null ? this.bE() : this; + float f = entity == this ? 0.35F : 0.4F; + float f1 = MathHelper.sqrt(entity.motX * entity.motX * 0.20000000298023224D + entity.motY * entity.motY + entity.motZ * entity.motZ * 0.20000000298023224D) * f; + + if (f1 > 1.0F) { + f1 = 1.0F; + } + + this.a(this.ae(), f1, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F); + } else { + this.a(blockposition, block1); + } + } else if (this.K > this.ay && this.ah() && iblockdata.getMaterial() == Material.AIR) { + this.ay = this.d(this.K); + } + } + + // CraftBukkit start - Move to the top of the method + /* + 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); + } + */ + // CraftBukkit end + + boolean flag1 = this.an(); + + if (this.world.e(this.getBoundingBox().shrink(0.001D))) { + this.burn(1); + if (!flag1) { + ++this.fireTicks; + if (this.fireTicks == 0) { + // CraftBukkit start + EntityCombustEvent event = new org.bukkit.event.entity.EntityCombustByBlockEvent(null, getBukkitEntity(), 8); + world.getServer().getPluginManager().callEvent(event); + + if (!event.isCancelled()) { + this.setOnFire(event.getDuration()); + } + // CraftBukkit end + } + } + } else if (this.fireTicks <= 0) { + this.fireTicks = -this.getMaxFireTicks(); + } + + if (flag1 && this.isBurning()) { + this.a(SoundEffects.bW, 0.7F, 1.6F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F); + this.fireTicks = -this.getMaxFireTicks(); + } + + this.world.methodProfiler.b(); + } + } + + public void recalcPosition() { + AxisAlignedBB axisalignedbb = this.getBoundingBox(); + + this.locX = (axisalignedbb.a + axisalignedbb.d) / 2.0D; + this.locY = axisalignedbb.b; + this.locZ = (axisalignedbb.c + axisalignedbb.f) / 2.0D; + } + + protected SoundEffect ae() { + return SoundEffects.ca; + } + + protected SoundEffect af() { + return SoundEffects.bZ; + } + + protected void checkBlockCollisions() { + AxisAlignedBB axisalignedbb = this.getBoundingBox(); + BlockPosition.PooledBlockPosition blockposition_pooledblockposition = BlockPosition.PooledBlockPosition.d(axisalignedbb.a + 0.001D, axisalignedbb.b + 0.001D, axisalignedbb.c + 0.001D); + BlockPosition.PooledBlockPosition blockposition_pooledblockposition1 = BlockPosition.PooledBlockPosition.d(axisalignedbb.d - 0.001D, axisalignedbb.e - 0.001D, axisalignedbb.f - 0.001D); + BlockPosition.PooledBlockPosition blockposition_pooledblockposition2 = BlockPosition.PooledBlockPosition.s(); + + if (this.world.areChunksLoadedBetween(blockposition_pooledblockposition, blockposition_pooledblockposition1)) { + for (int i = blockposition_pooledblockposition.getX(); i <= blockposition_pooledblockposition1.getX(); ++i) { + for (int j = blockposition_pooledblockposition.getY(); j <= blockposition_pooledblockposition1.getY(); ++j) { + for (int k = blockposition_pooledblockposition.getZ(); k <= blockposition_pooledblockposition1.getZ(); ++k) { + blockposition_pooledblockposition2.f(i, j, k); + IBlockData iblockdata = this.world.getType(blockposition_pooledblockposition2); + + try { + iblockdata.getBlock().a(this.world, (BlockPosition) blockposition_pooledblockposition2, iblockdata, this); + this.a(iblockdata); + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Colliding entity with block"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Block being collided with"); + + CrashReportSystemDetails.a(crashreportsystemdetails, blockposition_pooledblockposition2, iblockdata); + throw new ReportedException(crashreport); + } + } + } + } + } + + blockposition_pooledblockposition.t(); + blockposition_pooledblockposition1.t(); + blockposition_pooledblockposition2.t(); + } + + protected void a(IBlockData iblockdata) {} + + protected void a(BlockPosition blockposition, Block block) { + SoundEffectType soundeffecttype = block.getStepSound(); + + if (this.world.getType(blockposition.up()).getBlock() == Blocks.SNOW_LAYER) { + soundeffecttype = Blocks.SNOW_LAYER.getStepSound(); + this.a(soundeffecttype.d(), soundeffecttype.a() * 0.15F, soundeffecttype.b()); + } else if (!block.getBlockData().getMaterial().isLiquid()) { + this.a(soundeffecttype.d(), soundeffecttype.a() * 0.15F, soundeffecttype.b()); + } + + } + + protected float d(float f) { + return 0.0F; + } + + protected boolean ah() { + return false; + } + + public void a(SoundEffect soundeffect, float f, float f1) { + if (!this.isSilent()) { + this.world.a((EntityHuman) null, this.locX, this.locY, this.locZ, soundeffect, this.bK(), f, f1); + } + + } + + public boolean isSilent() { + return ((Boolean) this.datawatcher.get(Entity.aD)).booleanValue(); + } + + public void setSilent(boolean flag) { + this.datawatcher.set(Entity.aD, Boolean.valueOf(flag)); + } + + public boolean isNoGravity() { + return ((Boolean) this.datawatcher.get(Entity.aE)).booleanValue(); + } + + public void setNoGravity(boolean flag) { + this.datawatcher.set(Entity.aE, Boolean.valueOf(flag)); + } + + protected boolean playStepSound() { + return true; + } + + protected void a(double d0, boolean flag, IBlockData iblockdata, BlockPosition blockposition) { + if (flag) { + if (this.fallDistance > 0.0F) { + iblockdata.getBlock().fallOn(this.world, blockposition, this, this.fallDistance); + } + + this.fallDistance = 0.0F; + } else if (d0 < 0.0D) { + this.fallDistance = (float) ((double) this.fallDistance - d0); + } + + } + + @Nullable + public AxisAlignedBB al() { + return null; + } + + protected void burn(float i) { // CraftBukkit - int -> float + if (!this.fireProof) { + this.damageEntity(DamageSource.FIRE, (float) i); + } + + } + + public final boolean isFireProof() { + return this.fireProof; + } + + public void e(float f, float f1) { + if (this.isVehicle()) { + Iterator iterator = this.bF().iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + + entity.e(f, f1); + } + } + + } + + public boolean an() { + if (this.inWater) { + return true; + } else { + BlockPosition.PooledBlockPosition blockposition_pooledblockposition = BlockPosition.PooledBlockPosition.d(this.locX, this.locY, this.locZ); + + if (!this.world.isRainingAt(blockposition_pooledblockposition) && !this.world.isRainingAt(blockposition_pooledblockposition.e(this.locX, this.locY + (double) this.length, this.locZ))) { + blockposition_pooledblockposition.t(); + return false; + } else { + blockposition_pooledblockposition.t(); + return true; + } + } + } + + public boolean isInWater() { + return this.inWater; + } + + public boolean ap() { + return this.world.a(this.getBoundingBox().grow(0.0D, -20.0D, 0.0D).shrink(0.001D), Material.WATER, this); + } + + public boolean aq() { + return this.doWaterMovement(); + } + + public boolean doWaterMovement() { + // Paper end + if (this.bJ() instanceof EntityBoat) { + this.inWater = false; + } else if (this.world.a(this.getBoundingBox().grow(0.0D, -0.4000000059604645D, 0.0D).shrink(0.001D), Material.WATER, this)) { + if (!this.inWater && !this.justCreated) { + this.ar(); + } + + this.fallDistance = 0.0F; + this.inWater = true; + this.extinguish(); + } else { + this.inWater = false; + } + + return this.inWater; + } + + protected void ar() { + Entity entity = this.isVehicle() && this.bE() != null ? this.bE() : this; + float f = entity == this ? 0.2F : 0.9F; + float f1 = MathHelper.sqrt(entity.motX * entity.motX * 0.20000000298023224D + entity.motY * entity.motY + entity.motZ * entity.motZ * 0.20000000298023224D) * f; + + if (f1 > 1.0F) { + f1 = 1.0F; + } + + this.a(this.af(), f1, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F); + float f2 = (float) MathHelper.floor(this.getBoundingBox().b); + + int i; + float f3; + float f4; + + for (i = 0; (float) i < 1.0F + this.width * 20.0F; ++i) { + f3 = (this.random.nextFloat() * 2.0F - 1.0F) * this.width; + f4 = (this.random.nextFloat() * 2.0F - 1.0F) * this.width; + this.world.addParticle(EnumParticle.WATER_BUBBLE, this.locX + (double) f3, (double) (f2 + 1.0F), this.locZ + (double) f4, this.motX, this.motY - (double) (this.random.nextFloat() * 0.2F), this.motZ, new int[0]); + } + + for (i = 0; (float) i < 1.0F + this.width * 20.0F; ++i) { + f3 = (this.random.nextFloat() * 2.0F - 1.0F) * this.width; + f4 = (this.random.nextFloat() * 2.0F - 1.0F) * this.width; + this.world.addParticle(EnumParticle.WATER_SPLASH, this.locX + (double) f3, (double) (f2 + 1.0F), this.locZ + (double) f4, this.motX, this.motY, this.motZ, new int[0]); + } + + } + + public void as() { + if (this.isSprinting() && !this.isInWater()) { + this.at(); + } + + } + + protected void at() { + int i = MathHelper.floor(this.locX); + int j = MathHelper.floor(this.locY - 0.20000000298023224D); + int k = MathHelper.floor(this.locZ); + BlockPosition blockposition = new BlockPosition(i, j, k); + IBlockData iblockdata = this.world.getType(blockposition); + + if (iblockdata.i() != EnumRenderType.INVISIBLE) { + this.world.addParticle(EnumParticle.BLOCK_CRACK, this.locX + ((double) this.random.nextFloat() - 0.5D) * (double) this.width, this.getBoundingBox().b + 0.1D, this.locZ + ((double) this.random.nextFloat() - 0.5D) * (double) this.width, -this.motX * 4.0D, 1.5D, -this.motZ * 4.0D, new int[] { Block.getCombinedId(iblockdata)}); + } + + } + + public boolean a(Material material) { + if (this.bJ() instanceof EntityBoat) { + return false; + } else { + double d0 = this.locY + (double) this.getHeadHeight(); + BlockPosition blockposition = new BlockPosition(this.locX, d0, this.locZ); + IBlockData iblockdata = this.world.getType(blockposition); + + if (iblockdata.getMaterial() == material) { + float f = BlockFluids.b(iblockdata.getBlock().toLegacyData(iblockdata)) - 0.11111111F; + float f1 = (float) (blockposition.getY() + 1) - f; + boolean flag = d0 < (double) f1; + + return !flag && this instanceof EntityHuman ? false : flag; + } else { + return false; + } + } + } + + public boolean au() { + return this.world.a(this.getBoundingBox().grow(-0.10000000149011612D, -0.4000000059604645D, -0.10000000149011612D), Material.LAVA); + } + + public void b(float f, float f1, float f2, float f3) { + float f4 = f * f + f1 * f1 + f2 * f2; + + if (f4 >= 1.0E-4F) { + f4 = MathHelper.c(f4); + if (f4 < 1.0F) { + f4 = 1.0F; + } + + f4 = f3 / f4; + f *= f4; + f1 *= f4; + f2 *= f4; + float f5 = MathHelper.sin(this.yaw * 0.017453292F); + float f6 = MathHelper.cos(this.yaw * 0.017453292F); + + this.motX += (double) (f * f6 - f2 * f5); + this.motY += (double) f1; + this.motZ += (double) (f2 * f6 + f * f5); + } + } + + public float aw() { + BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(MathHelper.floor(this.locX), 0, MathHelper.floor(this.locZ)); + + if (this.world.isLoaded(blockposition_mutableblockposition)) { + blockposition_mutableblockposition.p(MathHelper.floor(this.locY + (double) this.getHeadHeight())); + return this.world.n(blockposition_mutableblockposition); + } else { + return 0.0F; + } + } + + 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; + } + + public void setLocation(double d0, double d1, double d2, float f, float f1) { + this.locX = MathHelper.a(d0, -3.0E7D, 3.0E7D); + this.locY = d1; + this.locZ = MathHelper.a(d2, -3.0E7D, 3.0E7D); + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + f1 = MathHelper.a(f1, -90.0F, 90.0F); + this.yaw = f; + this.pitch = f1; + this.lastYaw = this.yaw; + this.lastPitch = this.pitch; + double d3 = (double) (this.lastYaw - f); + + if (d3 < -180.0D) { + this.lastYaw += 360.0F; + } + + if (d3 >= 180.0D) { + this.lastYaw -= 360.0F; + } + + this.setPosition(this.locX, this.locY, this.locZ); + this.setYawPitch(f, f1); + } + + public void setPositionRotation(BlockPosition blockposition, float f, float f1) { + this.setPositionRotation((double) blockposition.getX() + 0.5D, (double) blockposition.getY(), (double) blockposition.getZ() + 0.5D, f, f1); + } + + public void setPositionRotation(double d0, double d1, double d2, float f, float f1) { + this.locX = d0; + this.locY = d1; + this.locZ = d2; + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + this.M = this.locX; + this.N = this.locY; + this.O = this.locZ; + this.yaw = f; + this.pitch = f1; + this.setPosition(this.locX, this.locY, this.locZ); + } + + public float g(Entity entity) { + float f = (float) (this.locX - entity.locX); + float f1 = (float) (this.locY - entity.locY); + float f2 = (float) (this.locZ - entity.locZ); + + return MathHelper.c(f * f + f1 * f1 + f2 * f2); + } + + public double d(double d0, double d1, double d2) { + double d3 = this.locX - d0; + double d4 = this.locY - d1; + double d5 = this.locZ - d2; + + return d3 * d3 + d4 * d4 + d5 * d5; + } + + public double c(BlockPosition blockposition) { + return blockposition.distanceSquared(this.locX, this.locY, this.locZ); + } + + public double d(BlockPosition blockposition) { + return blockposition.g(this.locX, this.locY, this.locZ); + } + + public double e(double d0, double d1, double d2) { + double d3 = this.locX - d0; + double d4 = this.locY - d1; + double d5 = this.locZ - d2; + + return (double) MathHelper.sqrt(d3 * d3 + d4 * d4 + d5 * d5); + } + + public double h(Entity entity) { + double d0 = this.locX - entity.locX; + double d1 = this.locY - entity.locY; + double d2 = this.locZ - entity.locZ; + + return d0 * d0 + d1 * d1 + d2 * d2; + } + + public void d(EntityHuman entityhuman) {} + + public void collide(Entity entity) { + if (!this.x(entity)) { + if (!entity.noclip && !this.noclip) { + double d0 = entity.locX - this.locX; + double d1 = entity.locZ - this.locZ; + double d2 = MathHelper.a(d0, d1); + + if (d2 >= 0.009999999776482582D) { + d2 = (double) MathHelper.sqrt(d2); + d0 /= d2; + d1 /= d2; + double d3 = 1.0D / d2; + + if (d3 > 1.0D) { + d3 = 1.0D; + } + + d0 *= d3; + d1 *= d3; + d0 *= 0.05000000074505806D; + d1 *= 0.05000000074505806D; + d0 *= (double) (1.0F - this.R); + d1 *= (double) (1.0F - this.R); + if (!this.isVehicle()) { + this.f(-d0, 0.0D, -d1); + } + + if (!entity.isVehicle()) { + entity.f(d0, 0.0D, d1); + } + } + + } + } + } + + public void f(double d0, double d1, double d2) { + this.motX += d0; + this.motY += d1; + this.motZ += d2; + this.impulse = true; + } + + protected void ax() { + this.velocityChanged = true; + } + + public boolean damageEntity(DamageSource damagesource, float f) { + if (this.isInvulnerable(damagesource)) { + return false; + } else { + this.ax(); + return false; + } + } + + public Vec3D e(float f) { + if (f == 1.0F) { + return this.f(this.pitch, this.yaw); + } else { + float f1 = this.lastPitch + (this.pitch - this.lastPitch) * f; + float f2 = this.lastYaw + (this.yaw - this.lastYaw) * f; + + return this.f(f1, f2); + } + } + + protected final Vec3D f(float f, float f1) { + float f2 = MathHelper.cos(-f1 * 0.017453292F - 3.1415927F); + float f3 = MathHelper.sin(-f1 * 0.017453292F - 3.1415927F); + float f4 = -MathHelper.cos(-f * 0.017453292F); + float f5 = MathHelper.sin(-f * 0.017453292F); + + return new Vec3D((double) (f3 * f4), (double) f5, (double) (f2 * f4)); + } + + public Vec3D f(float f) { + if (f == 1.0F) { + return new Vec3D(this.locX, this.locY + (double) this.getHeadHeight(), this.locZ); + } else { + double d0 = this.lastX + (this.locX - this.lastX) * (double) f; + double d1 = this.lastY + (this.locY - this.lastY) * (double) f + (double) this.getHeadHeight(); + double d2 = this.lastZ + (this.locZ - this.lastZ) * (double) f; + + return new Vec3D(d0, d1, d2); + } + } + + public boolean isInteractable() { + return false; + } + + public boolean isCollidable() { + return false; + } + + public void a(Entity entity, int i, DamageSource damagesource) { + if (entity instanceof EntityPlayer) { + CriterionTriggers.c.a((EntityPlayer) entity, this, damagesource); + } + + } + + public boolean c(NBTTagCompound nbttagcompound) { + String s = this.getSaveID(); + + if (!this.dead && s != null) { + nbttagcompound.setString("id", s); + this.save(nbttagcompound); + return true; + } else { + return false; + } + } + + public boolean d(NBTTagCompound nbttagcompound) { + String s = this.getSaveID(); + + if (!this.dead && s != null && !this.isPassenger()) { + nbttagcompound.setString("id", s); + this.save(nbttagcompound); + return true; + } else { + return false; + } + } + + public static void b(DataConverterManager dataconvertermanager) { + dataconvertermanager.a(DataConverterTypes.ENTITY, new DataInspector() { + public NBTTagCompound a(DataConverter dataconverter, NBTTagCompound nbttagcompound, int i) { + if (nbttagcompound.hasKeyOfType("Passengers", 9)) { + NBTTagList nbttaglist = nbttagcompound.getList("Passengers", 10); + + for (int j = 0; j < nbttaglist.size(); ++j) { + nbttaglist.a(j, dataconverter.a(DataConverterTypes.ENTITY, nbttaglist.get(j), i)); + } + } + + return nbttagcompound; + } + }); + } + + public NBTTagCompound save(NBTTagCompound nbttagcompound) { + 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); + nbttagcompound.setShort("Air", (short) this.getAirTicks()); + nbttagcompound.setBoolean("OnGround", this.onGround); + nbttagcompound.setInt("Dimension", this.dimension); + 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", this.world.getDataManager().getUUID().getLeastSignificantBits()); + nbttagcompound.setLong("WorldUUIDMost", this.world.getDataManager().getUUID().getMostSignificantBits()); + nbttagcompound.setInt("Bukkit.updateLevel", CURRENT_LEVEL); + nbttagcompound.setInt("Spigot.ticksLived", this.ticksLived); + // CraftBukkit end + if (this.hasCustomName()) { + nbttagcompound.setString("CustomName", this.getCustomName()); + } + + if (this.getCustomNameVisible()) { + nbttagcompound.setBoolean("CustomNameVisible", this.getCustomNameVisible()); + } + + this.aG.b(nbttagcompound); + if (this.isSilent()) { + nbttagcompound.setBoolean("Silent", this.isSilent()); + } + + if (this.isNoGravity()) { + nbttagcompound.setBoolean("NoGravity", this.isNoGravity()); + } + + if (this.glowing) { + nbttagcompound.setBoolean("Glowing", this.glowing); + } + + NBTTagList nbttaglist; + Iterator iterator; + + if (!this.aH.isEmpty()) { + nbttaglist = new NBTTagList(); + iterator = this.aH.iterator(); + + while (iterator.hasNext()) { + String s = (String) iterator.next(); + + nbttaglist.add(new NBTTagString(s)); + } + + nbttagcompound.set("Tags", nbttaglist); + } + + this.b(nbttagcompound); + if (this.isVehicle()) { + nbttaglist = new NBTTagList(); + iterator = this.bF().iterator(); + + while (iterator.hasNext()) { + Entity entity = (Entity) iterator.next(); + NBTTagCompound nbttagcompound1 = new NBTTagCompound(); + + if (entity.c(nbttagcompound1)) { + nbttaglist.add(nbttagcompound1); + } + } + + if (!nbttaglist.isEmpty()) { + nbttagcompound.set("Passengers", nbttaglist); + } + } + + // Paper start - Save the entity's origin location + if (origin != null) { + nbttagcompound.set("Paper.Origin", this.createList(origin.getX(), origin.getY(), origin.getZ())); + } + // Save entity's from mob spawner status + if (spawnedViaMobSpawner) { + nbttagcompound.setBoolean("Paper.FromMobSpawner", true); + } + // Paper end + return nbttagcompound; + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Saving entity NBT"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being saved"); + + this.appendEntityCrashDetails(crashreportsystemdetails); + throw new ReportedException(crashreport); + } + } + + public void f(NBTTagCompound nbttagcompound) { + try { + NBTTagList nbttaglist = nbttagcompound.getList("Pos", 6); + NBTTagList nbttaglist1 = nbttagcompound.getList("Motion", 6); + NBTTagList nbttaglist2 = nbttagcompound.getList("Rotation", 5); + + this.motX = nbttaglist1.f(0); + this.motY = nbttaglist1.f(1); + this.motZ = nbttaglist1.f(2); + + /* CraftBukkit start - Moved section down + 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 */ + + this.locX = nbttaglist.f(0); + this.locY = nbttaglist.f(1); + this.locZ = nbttaglist.f(2); + this.M = this.locX; + this.N = this.locY; + this.O = this.locZ; + this.lastX = this.locX; + this.lastY = this.locY; + this.lastZ = this.locZ; + this.yaw = nbttaglist2.g(0); + this.pitch = nbttaglist2.g(1); + this.lastYaw = this.yaw; + this.lastPitch = this.pitch; + this.setHeadRotation(this.yaw); + this.h(this.yaw); + this.fallDistance = nbttagcompound.getFloat("FallDistance"); + this.fireTicks = nbttagcompound.getShort("Fire"); + this.setAirTicks(nbttagcompound.getShort("Air")); + this.onGround = nbttagcompound.getBoolean("OnGround"); + if (nbttagcompound.hasKey("Dimension")) { + this.dimension = nbttagcompound.getInt("Dimension"); + } + + this.invulnerable = nbttagcompound.getBoolean("Invulnerable"); + this.portalCooldown = nbttagcompound.getInt("PortalCooldown"); + if (nbttagcompound.b("UUID")) { + this.uniqueID = nbttagcompound.a("UUID"); + this.ar = this.uniqueID.toString(); + } + + this.setPosition(this.locX, this.locY, this.locZ); + this.setYawPitch(this.yaw, this.pitch); + if (nbttagcompound.hasKeyOfType("CustomName", 8)) { + this.setCustomName(nbttagcompound.getString("CustomName")); + } + + this.setCustomNameVisible(nbttagcompound.getBoolean("CustomNameVisible")); + this.aG.a(nbttagcompound); + this.setSilent(nbttagcompound.getBoolean("Silent")); + this.setNoGravity(nbttagcompound.getBoolean("NoGravity")); + this.g(nbttagcompound.getBoolean("Glowing")); + if (nbttagcompound.hasKeyOfType("Tags", 9)) { + this.aH.clear(); + NBTTagList nbttaglist3 = nbttagcompound.getList("Tags", 8); + int i = Math.min(nbttaglist3.size(), 1024); + + for (int j = 0; j < i; ++j) { + this.aH.add(nbttaglist3.getString(j)); + } + } + + this.a(nbttagcompound); + if (this.aA()) { + this.setPosition(this.locX, this.locY, this.locZ); + } + + // CraftBukkit start + if (this instanceof EntityLiving) { + EntityLiving entity = (EntityLiving) this; + + this.ticksLived = nbttagcompound.getInt("Spigot.ticksLived"); + + // 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 + double limit = getBukkitEntity() instanceof Vehicle ? 100.0D : 10.0D; + if (Math.abs(this.motX) > limit) { + this.motX = 0.0D; + } + + if (Math.abs(this.motY) > limit) { + this.motY = 0.0D; + } + + if (Math.abs(this.motZ) > limit) { + 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 + + // Paper start - Restore the entity's origin location + NBTTagList originTag = nbttagcompound.getList("Paper.Origin", 6); + if (!originTag.isEmpty()) { + origin = new Location(world.getWorld(), originTag.getDoubleAt(0), originTag.getDoubleAt(1), originTag.getDoubleAt(2)); + } + + spawnedViaMobSpawner = nbttagcompound.getBoolean("Paper.FromMobSpawner"); // Restore entity's from mob spawner status + // Paper end + + } catch (Throwable throwable) { + CrashReport crashreport = CrashReport.a(throwable, "Loading entity NBT"); + CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being loaded"); + + this.appendEntityCrashDetails(crashreportsystemdetails); + throw new ReportedException(crashreport); + } + } + + protected boolean aA() { + return true; + } + + // Paper start + private java.lang.ref.WeakReference currentChunk = null; + + public void setCurrentChunk(Chunk chunk) { + this.currentChunk = chunk != null ? new java.lang.ref.WeakReference<>(chunk) : null; + } + /** + * Returns the entities current registered chunk. If the entity is not added to a chunk yet, it will return null + */ + public Chunk getCurrentChunk() { + final Chunk chunk = currentChunk != null ? currentChunk.get() : null; + return chunk != null && chunk.isLoaded() ? chunk : null; + } + /** + * Returns the chunk at the location, using the entities local cache if avail + * Will only return null if the location specified is not loaded + */ + public Chunk getCurrentChunkAt(int x, int z) { + if (getChunkX() == x && getChunkZ() == z) { + Chunk chunk = getCurrentChunk(); + if (chunk != null) { + return chunk; + } + } + return world.getChunkIfLoaded(x, z); + } + /** + * Returns the chunk at the entities current location, using the entities local cache if avail + * Will only return null if the location specified is not loaded + */ + public Chunk getChunkAtLocation() { + return getCurrentChunkAt((int)Math.floor(locX) >> 4, (int)Math.floor(locZ) >> 4); + } + public final MinecraftKey entityKey = EntityTypes.getKey(this); + public final String entityKeyString = entityKey != null ? entityKey.toString() : null; + + @Override + public MinecraftKey getMinecraftKey() { + return entityKey; + } + + @Override + public String getMinecraftKeyString() { + return entityKeyString; + } + @Nullable + public final String getSaveID() { + return entityKeyString; + // Paper end + } + + protected abstract void a(NBTTagCompound nbttagcompound); + + protected abstract void b(NBTTagCompound nbttagcompound); + + protected NBTTagList createList(double... adouble) { return a(adouble); } // Paper - OBFHELPER + protected NBTTagList a(double... adouble) { + NBTTagList nbttaglist = new NBTTagList(); + double[] adouble1 = adouble; + int i = adouble.length; + + for (int j = 0; j < i; ++j) { + double d0 = adouble1[j]; + + nbttaglist.add(new NBTTagDouble(d0)); + } + + return nbttaglist; + } + + protected NBTTagList a(float... afloat) { + NBTTagList nbttaglist = new NBTTagList(); + float[] afloat1 = afloat; + int i = afloat.length; + + for (int j = 0; j < i; ++j) { + float f = afloat1[j]; + + nbttaglist.add(new NBTTagFloat(f)); + } + + return nbttaglist; + } + + @Nullable + public EntityItem a(Item item, int i) { + return this.a(item, i, 0.0F); + } + + @Nullable + public EntityItem a(Item item, int i, float f) { + return this.a(new ItemStack(item, i, 0), f); + } + + @Nullable public final EntityItem dropItem(ItemStack itemstack, float offset) { return this.a(itemstack, offset); } // Paper - OBFHELPER + @Nullable + public EntityItem a(ItemStack itemstack, float f) { + if (itemstack.isEmpty()) { + 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.q(); + this.world.addEntity(entityitem); + return entityitem; + } + } + + public boolean isAlive() { + return !this.dead; + } + + public boolean inBlock() { + if (this.noclip) { + return false; + } else { + BlockPosition.PooledBlockPosition blockposition_pooledblockposition = BlockPosition.PooledBlockPosition.s(); + + for (int i = 0; i < 8; ++i) { + int j = MathHelper.floor(this.locY + (double) (((float) ((i >> 0) % 2) - 0.5F) * 0.1F) + (double) this.getHeadHeight()); + int k = MathHelper.floor(this.locX + (double) (((float) ((i >> 1) % 2) - 0.5F) * this.width * 0.8F)); + int l = MathHelper.floor(this.locZ + (double) (((float) ((i >> 2) % 2) - 0.5F) * this.width * 0.8F)); + + if (blockposition_pooledblockposition.getX() != k || blockposition_pooledblockposition.getY() != j || blockposition_pooledblockposition.getZ() != l) { + blockposition_pooledblockposition.f(k, j, l); + if (this.world.getType(blockposition_pooledblockposition).r()) { + blockposition_pooledblockposition.t(); + return true; + } + } + } + + blockposition_pooledblockposition.t(); + return false; + } + } + + public boolean b(EntityHuman entityhuman, EnumHand enumhand) { + return false; + } + + @Nullable + public AxisAlignedBB j(Entity entity) { + return null; + } + + public void aE() { + Entity entity = this.bJ(); + + if (this.isPassenger() && entity.dead) { + this.stopRiding(); + } else { + this.motX = 0.0D; + this.motY = 0.0D; + this.motZ = 0.0D; + this.B_(); + if (this.isPassenger()) { + entity.k(this); + } + } + } + + public void k(Entity entity) { + if (this.w(entity)) { + entity.setPosition(this.locX, this.locY + this.aG() + entity.aF(), this.locZ); + } + } + + public double aF() { + return 0.0D; + } + + public double aG() { + return (double) this.length * 0.75D; + } + + public boolean startRiding(Entity entity) { + return this.a(entity, false); + } + + public boolean a(Entity entity, boolean flag) { + for (Entity entity1 = entity; entity1.au != null; entity1 = entity1.au) { + if (entity1.au == this) { + return false; + } + } + + if (!flag && (!this.n(entity) || !entity.q(this))) { + return false; + } else { + if (this.isPassenger()) { + this.stopRiding(); + } + + this.au = entity; + this.au.o(this); + return true; + } + } + + protected boolean n(Entity entity) { + return this.j <= 0; + } + + public void ejectPassengers() { + for (int i = this.passengers.size() - 1; i >= 0; --i) { + ((Entity) this.passengers.get(i)).stopRiding(); + } + + } + + public void stopRiding() { + if (this.au != null) { + Entity entity = this.au; + + this.au = null; + entity.p(this); + } + + } + + protected void o(Entity entity) { + if (entity == this) throw new IllegalArgumentException("Entities cannot become a passenger of themselves"); // Paper - issue 572 + if (entity.bJ() != 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 && entity.world.isChunkLoaded((int) entity.locX >> 4, (int) entity.locZ >> 4, false)) { // Boolean not used + VehicleEnterEvent event = new VehicleEnterEvent( + (Vehicle) getBukkitEntity(), + 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; + } + } + // CraftBukkit end + // Spigot start + org.spigotmc.event.entity.EntityMountEvent event = new org.spigotmc.event.entity.EntityMountEvent(entity.getBukkitEntity(), this.getBukkitEntity()); + Bukkit.getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + // Spigot end + if (!this.world.isClientSide && entity instanceof EntityHuman && !(this.bE() instanceof EntityHuman)) { + this.passengers.add(0, entity); + } else { + this.passengers.add(entity); + } + + } + } + + protected void p(Entity entity) { + if (entity.bJ() == this) { + throw new IllegalStateException("Use x.stopRiding(y), not y.removePassenger(x)"); + } else { + // CraftBukkit start + entity.setVehicle(this); // Paper - Set the vehicle back for the event + 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; + } + } + // CraftBukkit end + // Paper start - make EntityDismountEvent cancellable + if (!new org.spigotmc.event.entity.EntityDismountEvent(entity.getBukkitEntity(), this.getBukkitEntity()).callEvent()) { + return; + } + entity.setVehicle(null); + // Paper end + + this.passengers.remove(entity); + entity.j = 60; + } + } + + protected boolean q(Entity entity) { + return this.bF().size() < 1; + } + + public float aI() { + return 0.0F; + } + + public Vec3D aJ() { + return this.f(this.pitch, this.yaw); + } + + public void e(BlockPosition blockposition) { + if (this.portalCooldown > 0) { + this.portalCooldown = this.aM(); + } else { + if (!this.world.isClientSide && !blockposition.equals(this.an)) { + this.an = new BlockPosition(blockposition); + ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = Blocks.PORTAL.c(this.world, this.an); + double d0 = shapedetector_shapedetectorcollection.getFacing().k() == EnumDirection.EnumAxis.X ? (double) shapedetector_shapedetectorcollection.a().getZ() : (double) shapedetector_shapedetectorcollection.a().getX(); + double d1 = shapedetector_shapedetectorcollection.getFacing().k() == EnumDirection.EnumAxis.X ? this.locZ : this.locX; + + d1 = Math.abs(MathHelper.c(d1 - (double) (shapedetector_shapedetectorcollection.getFacing().e().c() == EnumDirection.EnumAxisDirection.NEGATIVE ? 1 : 0), d0, d0 - (double) shapedetector_shapedetectorcollection.d())); + double d2 = MathHelper.c(this.locY - 1.0D, (double) shapedetector_shapedetectorcollection.a().getY(), (double) (shapedetector_shapedetectorcollection.a().getY() - shapedetector_shapedetectorcollection.e())); + + this.ao = new Vec3D(d1, d2, 0.0D); + this.ap = shapedetector_shapedetectorcollection.getFacing(); + } + + this.ak = true; + } + } + + public int aM() { + return 300; + } + + public Iterable aO() { + return Entity.b; + } + + public Iterable getArmorItems() { + return Entity.b; + } + + public Iterable aQ() { + return Iterables.concat(this.aO(), this.getArmorItems()); + } + + public void setEquipment(EnumItemSlot enumitemslot, ItemStack itemstack) {} + + public boolean isBurning() { + boolean flag = this.world != null && this.world.isClientSide; + + return !this.fireProof && (this.fireTicks > 0 || flag && this.getFlag(0)); + } + + public boolean isPassenger() { + return this.bJ() != null; + } + + public boolean isVehicle() { + return !this.bF().isEmpty(); + } + + public boolean isSneaking() { + return this.getFlag(1); + } + + public void setSneaking(boolean flag) { + this.setFlag(1, flag); + } + + public boolean isSprinting() { + return this.getFlag(3); + } + + public void setSprinting(boolean flag) { + this.setFlag(3, flag); + } + + public boolean aW() { + return this.glowing || this.world.isClientSide && this.getFlag(6); + } + + public void g(boolean flag) { + this.glowing = flag; + if (!this.world.isClientSide) { + this.setFlag(6, this.glowing); + } + + } + + public boolean isInvisible() { + return this.getFlag(5); + } + + @Nullable public ScoreboardTeamBase getTeam() { return this.aY(); } // Paper - OBFHELPER + @Nullable + public ScoreboardTeamBase aY() { + if (!this.world.paperConfig.nonPlayerEntitiesOnScoreboards && !(this instanceof EntityHuman)) { return null; } // Paper + return this.world.getScoreboard().getPlayerTeam(this.bn()); + } + + public boolean r(Entity entity) { + return this.a(entity.aY()); + } + + public boolean a(ScoreboardTeamBase scoreboardteambase) { + return this.aY() != null ? this.aY().isAlly(scoreboardteambase) : false; + } + + public void setInvisible(boolean flag) { + this.setFlag(5, flag); + } + + public boolean getFlag(int i) { + return (((Byte) this.datawatcher.get(Entity.Z)).byteValue() & 1 << i) != 0; + } + + public void setFlag(int i, boolean flag) { + byte b0 = ((Byte) this.datawatcher.get(Entity.Z)).byteValue(); + + if (flag) { + this.datawatcher.set(Entity.Z, Byte.valueOf((byte) (b0 | 1 << i))); + } else { + this.datawatcher.set(Entity.Z, Byte.valueOf((byte) (b0 & ~(1 << i)))); + } + + } + + public int getAirTicks() { + return ((Integer) this.datawatcher.get(Entity.aA)).intValue(); + } + + public void setAirTicks(int i) { + // CraftBukkit start + EntityAirChangeEvent event = new EntityAirChangeEvent(this.getBukkitEntity(), i); + event.getEntity().getServer().getPluginManager().callEvent(event); + if (event.isCancelled()) { + return; + } + this.datawatcher.set(Entity.aA, Integer.valueOf(event.getAmount())); + // CraftBukkit end + } + + public void onLightningStrike(EntityLightning entitylightning) { + // 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); + pluginManager.callEvent(hangingEvent); + + if (hangingEvent.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) { + // 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 + } + + } + + public void b(EntityLiving entityliving) {} + + protected boolean i(double d0, double d1, double d2) { + BlockPosition blockposition = new BlockPosition(d0, d1, d2); + double d3 = d0 - (double) blockposition.getX(); + double d4 = d1 - (double) blockposition.getY(); + double d5 = d2 - (double) blockposition.getZ(); + + if (!this.world.a(this.getBoundingBox())) { + return false; + } else { + EnumDirection enumdirection = EnumDirection.UP; + double d6 = Double.MAX_VALUE; + + if (!this.world.t(blockposition.west()) && d3 < d6) { + d6 = d3; + enumdirection = EnumDirection.WEST; + } + + if (!this.world.t(blockposition.east()) && 1.0D - d3 < d6) { + d6 = 1.0D - d3; + enumdirection = EnumDirection.EAST; + } + + if (!this.world.t(blockposition.north()) && d5 < d6) { + d6 = d5; + enumdirection = EnumDirection.NORTH; + } + + if (!this.world.t(blockposition.south()) && 1.0D - d5 < d6) { + d6 = 1.0D - d5; + enumdirection = EnumDirection.SOUTH; + } + + if (!this.world.t(blockposition.up()) && 1.0D - d4 < d6) { + d6 = 1.0D - d4; + enumdirection = EnumDirection.UP; + } + + float f = this.random.nextFloat() * 0.2F + 0.1F; + float f1 = (float) enumdirection.c().a(); + + if (enumdirection.k() == EnumDirection.EnumAxis.X) { + this.motX = (double) (f1 * f); + this.motY *= 0.75D; + this.motZ *= 0.75D; + } else if (enumdirection.k() == EnumDirection.EnumAxis.Y) { + this.motX *= 0.75D; + this.motY = (double) (f1 * f); + this.motZ *= 0.75D; + } else if (enumdirection.k() == EnumDirection.EnumAxis.Z) { + this.motX *= 0.75D; + this.motY *= 0.75D; + this.motZ = (double) (f1 * f); + } + + return true; + } + } + + public void ba() { + this.E = true; + this.fallDistance = 0.0F; + } + + public String getName() { + if (this.hasCustomName()) { + return this.getCustomName(); + } else { + String s = EntityTypes.b(this); + + if (s == null) { + s = "generic"; + } + + return LocaleI18n.get("entity." + s + ".name"); + } + } + + @Nullable + public Entity[] bb() { + return null; + } + + public boolean s(Entity entity) { + return this == entity; + } + + public float getHeadRotation() { + return 0.0F; + } + + public void setHeadRotation(float f) {} + + public void h(float f) {} + + public boolean bd() { + return true; + } + + public boolean t(Entity entity) { + return false; + } + + public String toString() { + return String.format("%s[\'%s\'/%d, l=\'%s\', x=%.2f, y=%.2f, z=%.2f]", new Object[] { this.getClass().getSimpleName(), this.getName(), Integer.valueOf(this.id), this.world == null ? "~NULL~" : this.world.getWorldData().getName(), Double.valueOf(this.locX), Double.valueOf(this.locY), Double.valueOf(this.locZ)}); + } + + public boolean isInvulnerable(DamageSource damagesource) { + return this.invulnerable && damagesource != DamageSource.OUT_OF_WORLD && !damagesource.u(); + } + + public boolean be() { + return this.invulnerable; + } + + public void setInvulnerable(boolean flag) { + this.invulnerable = flag; + } + + public void u(Entity entity) { + this.setPositionRotation(entity.locX, entity.locY, entity.locZ, entity.yaw, entity.pitch); + } + + private void a(Entity entity) { + NBTTagCompound nbttagcompound = entity.save(new NBTTagCompound()); + + nbttagcompound.remove("Dimension"); + this.f(nbttagcompound); + this.portalCooldown = entity.portalCooldown; + this.an = entity.an; + this.ao = entity.ao; + this.ap = entity.ap; + } + + @Nullable + public Entity b(int i) { + if (!this.world.isClientSide && !this.dead) { + this.world.methodProfiler.a("changeDimension"); + MinecraftServer minecraftserver = this.C_(); + // CraftBukkit start - Move logic into new function "teleportTo(Location,boolean)" + // 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; + } + } + } + + BlockPosition blockposition = null; // PAIL: CHECK + Location enter = this.getBukkitEntity().getLocation(); + Location exit; + if (exitWorld != null) { + if (blockposition != null) { + exit = new Location(exitWorld.getWorld(), blockposition.getX(), blockposition.getY(), blockposition.getZ()); + } else { + exit = minecraftserver.getPlayerList().calculateTarget(enter, minecraftserver.getWorldServer(i)); + } + } + else { + exit = 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 + boolean oldCanCreate = agent.getCanCreatePortal(); + agent.setCanCreatePortal(false); // General entities cannot create portals + + 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()) { + agent.setCanCreatePortal(oldCanCreate); + return null; + } + exit = event.useTravelAgent() ? event.getPortalTravelAgent().findOrCreate(event.getTo()) : event.getTo(); + agent.setCanCreatePortal(oldCanCreate); + + // Need to make sure the profiler state is reset afterwards (but we still want to time the call) + Entity entity = this.teleportTo(exit, true); + this.world.methodProfiler.b(); + return entity; + } + return null; + } + + public Entity teleportTo(Location exit, boolean portal) { + if (!this.dead) { // Paper + 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.removeEntity(this); // Paper - Fully remove entity, can't have dupes in the UUID map + this.dead = false; + this.world.methodProfiler.a("reposition"); + /* CraftBukkit start - Handled in calculateTarget + BlockPosition blockposition; + + if (i == 1) { + blockposition = worldserver1.getDimensionSpawn(); + } else { + double d0 = this.locX; + double d1 = this.locZ; + double d2 = 8.0D; + + if (i == -1) { + d0 = MathHelper.a(d0 / 8.0D, worldserver1.getWorldBorder().b() + 16.0D, worldserver1.getWorldBorder().d() - 16.0D); + d1 = MathHelper.a(d1 / 8.0D, worldserver1.getWorldBorder().c() + 16.0D, worldserver1.getWorldBorder().e() - 16.0D); + } else if (i == 0) { + d0 = MathHelper.a(d0 * 8.0D, worldserver1.getWorldBorder().b() + 16.0D, worldserver1.getWorldBorder().d() - 16.0D); + d1 = MathHelper.a(d1 * 8.0D, worldserver1.getWorldBorder().c() + 16.0D, worldserver1.getWorldBorder().e() - 16.0D); + } + + d0 = (double) MathHelper.clamp((int) d0, -29999872, 29999872); + d1 = (double) MathHelper.clamp((int) d1, -29999872, 29999872); + float f = this.yaw; + + this.setPositionRotation(d0, this.locY, d1, 90.0F, 0.0F); + PortalTravelAgent portaltravelagent = worldserver1.getTravelAgent(); + + portaltravelagent.b(this, f); + blockposition = new BlockPosition(this); + } + + // CraftBukkit end */ + // 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); + worldserver1.getMinecraftServer().getPlayerList().repositionEntity(this, exit, portal); + // worldserver.entityJoinedWorld(this, false); // Handled in repositionEntity + // CraftBukkit end + this.world.methodProfiler.c("reloading"); + Entity entity = EntityTypes.a(this.getClass(), (World) worldserver1); + + if (entity != null) { + entity.a(this); + /* CraftBukkit start - We need to do this... + if (j == 1 && i == 1) { + BlockPosition blockposition1 = worldserver1.q(worldserver1.getSpawn()); + + entity.setPositionRotation(blockposition1, entity.yaw, entity.pitch); + } else { + entity.setPositionRotation(blockposition, entity.yaw, entity.pitch); + } + // CraftBukkit end */ + + boolean flag = entity.attachedToPlayer; + + entity.attachedToPlayer = true; + worldserver1.addEntity(entity); + entity.attachedToPlayer = flag; + worldserver1.entityJoinedWorld(entity, false); + // 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.dead = true; + this.world.methodProfiler.b(); + worldserver.m(); + worldserver1.m(); + // this.world.methodProfiler.b(); // CraftBukkit: Moved up to keep balanced + return entity; + } else { + return null; + } + } + + public boolean bf() { + return true; + } + + public float a(Explosion explosion, World world, BlockPosition blockposition, IBlockData iblockdata) { + return iblockdata.getBlock().a(this); + } + + public boolean a(Explosion explosion, World world, BlockPosition blockposition, IBlockData iblockdata, float f) { + return true; + } + + public int bg() { + return 3; + } + + public Vec3D getPortalOffset() { + return this.ao; + } + + public EnumDirection getPortalDirection() { + return this.ap; + } + + public boolean isIgnoreBlockTrigger() { + return false; + } + + public void appendEntityCrashDetails(CrashReportSystemDetails crashreportsystemdetails) { + crashreportsystemdetails.a("Entity Type", new CrashReportCallable() { + public String a() throws Exception { + return EntityTypes.a(Entity.this) + " (" + Entity.this.getClass().getCanonicalName() + ")"; + } + + public Object call() throws Exception { + return this.a(); + } + }); + crashreportsystemdetails.a("Entity ID", (Object) Integer.valueOf(this.id)); + crashreportsystemdetails.a("Entity Name", new CrashReportCallable() { + public String a() throws Exception { + return Entity.this.getName(); + } + + public Object call() throws Exception { + return this.a(); + } + }); + crashreportsystemdetails.a("Entity\'s Exact location", (Object) String.format("%.2f, %.2f, %.2f", new Object[] { Double.valueOf(this.locX), Double.valueOf(this.locY), Double.valueOf(this.locZ)})); + crashreportsystemdetails.a("Entity\'s Block location", (Object) CrashReportSystemDetails.a(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ))); + crashreportsystemdetails.a("Entity\'s Momentum", (Object) String.format("%.2f, %.2f, %.2f", new Object[] { Double.valueOf(this.motX), Double.valueOf(this.motY), Double.valueOf(this.motZ)})); + crashreportsystemdetails.a("Entity\'s Passengers", new CrashReportCallable() { + public String a() throws Exception { + return Entity.this.bF().toString(); + } + + public Object call() throws Exception { + return this.a(); + } + }); + crashreportsystemdetails.a("Entity\'s Vehicle", new CrashReportCallable() { + public String a() throws Exception { + return Entity.this.bJ().toString(); + } + + public Object call() throws Exception { + return this.a(); + } + }); + } + + public void a(UUID uuid) { + this.uniqueID = uuid; + this.ar = this.uniqueID.toString(); + } + + public UUID getUniqueID() { + return this.uniqueID; + } + + public String bn() { + return this.ar; + } + + public boolean bo() { + return this.pushedByWater(); + } + + public boolean pushedByWater() { + // Paper end + return true; + } + + public IChatBaseComponent getScoreboardDisplayName() { + ChatComponentText chatcomponenttext = new ChatComponentText(ScoreboardTeam.getPlayerDisplayName(this.aY(), this.getName())); + + chatcomponenttext.getChatModifier().setChatHoverable(this.bv()); + chatcomponenttext.getChatModifier().setInsertion(this.bn()); + return chatcomponenttext; + } + + public void setCustomName(String s) { + // CraftBukkit start - Add a sane limit for name length + if (s.length() > 256) { + s = s.substring(0, 256); + } + // CraftBukkit end + this.datawatcher.set(Entity.aB, s); + } + + public String getCustomName() { + return (String) this.datawatcher.get(Entity.aB); + } + + public boolean hasCustomName() { + return !((String) this.datawatcher.get(Entity.aB)).isEmpty(); + } + + public void setCustomNameVisible(boolean flag) { + this.datawatcher.set(Entity.aC, Boolean.valueOf(flag)); + } + + public boolean getCustomNameVisible() { + return ((Boolean) this.datawatcher.get(Entity.aC)).booleanValue(); + } + + public void enderTeleportTo(double d0, double d1, double d2) { + this.aI = true; + this.setPositionRotation(d0, d1, d2, this.yaw, this.pitch); + this.world.entityJoinedWorld(this, false); + } + + public void a(DataWatcherObject datawatcherobject) {} + + public EnumDirection getDirection() { + return EnumDirection.fromType2(MathHelper.floor((double) (this.yaw * 4.0F / 360.0F) + 0.5D) & 3); + } + + public EnumDirection bu() { + return this.getDirection(); + } + + protected ChatHoverable bv() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + MinecraftKey minecraftkey = EntityTypes.a(this); + + nbttagcompound.setString("id", this.bn()); + if (minecraftkey != null) { + nbttagcompound.setString("type", minecraftkey.toString()); + } + + nbttagcompound.setString("name", this.getName()); + return new ChatHoverable(ChatHoverable.EnumHoverAction.SHOW_ENTITY, new ChatComponentText(nbttagcompound.toString())); + } + + public boolean a(EntityPlayer entityplayer) { + return true; + } + + public AxisAlignedBB getBoundingBox() { + return this.boundingBox; + } + + public void a(AxisAlignedBB axisalignedbb) { + // CraftBukkit start - block invalid bounding boxes + double a = axisalignedbb.a, + b = axisalignedbb.b, + c = axisalignedbb.c, + d = axisalignedbb.d, + e = axisalignedbb.e, + f = axisalignedbb.f; + double len = axisalignedbb.d - axisalignedbb.a; + if (len < 0) d = a; + if (len > 64) d = a + 64.0; + + len = axisalignedbb.e - axisalignedbb.b; + if (len < 0) e = b; + if (len > 64) e = b + 64.0; + + len = axisalignedbb.f - axisalignedbb.c; + if (len < 0) f = c; + if (len > 64) f = c + 64.0; + this.boundingBox = new AxisAlignedBB(a, b, c, d, e, f); + // CraftBukkit end + } + + public float getHeadHeight() { + return this.length * 0.85F; + } + + public boolean bz() { + return this.aw; + } + + public void k(boolean flag) { + this.aw = flag; + } + + public boolean c(int i, ItemStack itemstack) { + return false; + } + + public void sendMessage(IChatBaseComponent ichatbasecomponent) {} + + public boolean a(int i, String s) { + return true; + } + + public BlockPosition getChunkCoordinates() { + return new BlockPosition(this.locX, this.locY + 0.5D, this.locZ); + } + + public Vec3D d() { + return new Vec3D(this.locX, this.locY, this.locZ); + } + + public World getWorld() { + return this.world; + } + + public Entity f() { + return this; + } + + public boolean getSendCommandFeedback() { + return false; + } + + public void a(CommandObjectiveExecutor.EnumCommandResult commandobjectiveexecutor_enumcommandresult, int i) { + if (this.world != null && !this.world.isClientSide) { + this.aG.a(this.world.getMinecraftServer(), this, commandobjectiveexecutor_enumcommandresult, i); + } + + } + + @Nullable + public MinecraftServer C_() { + return this.world.getMinecraftServer(); + } + + public CommandObjectiveExecutor bA() { + return this.aG; + } + + public void v(Entity entity) { + this.aG.a(entity.bA()); + } + + public EnumInteractionResult a(EntityHuman entityhuman, Vec3D vec3d, EnumHand enumhand) { + return EnumInteractionResult.PASS; + } + + public boolean bB() { + return false; + } + + protected void a(EntityLiving entityliving, Entity entity) { + if (entity instanceof EntityLiving) { + EnchantmentManager.a((EntityLiving) entity, (Entity) entityliving); + } + + EnchantmentManager.b(entityliving, entity); + } + + public void b(EntityPlayer entityplayer) {} + + public void c(EntityPlayer entityplayer) {} + + public float a(EnumBlockRotation enumblockrotation) { + float f = MathHelper.g(this.yaw); + + switch (enumblockrotation) { + case CLOCKWISE_180: + return f + 180.0F; + + case COUNTERCLOCKWISE_90: + return f + 270.0F; + + case CLOCKWISE_90: + return f + 90.0F; + + default: + return f; + } + } + + public float a(EnumBlockMirror enumblockmirror) { + float f = MathHelper.g(this.yaw); + + switch (enumblockmirror) { + case LEFT_RIGHT: + return -f; + + case FRONT_BACK: + return 180.0F - f; + + default: + return f; + } + } + + public boolean bC() { + return false; + } + + public boolean bD() { + boolean flag = this.aI; + + this.aI = false; + return flag; + } + + @Nullable + public Entity bE() { + return null; + } + + public List bF() { + return (List) (this.passengers.isEmpty() ? Collections.emptyList() : Lists.newArrayList(this.passengers)); + } + + public boolean w(Entity entity) { + Iterator iterator = this.bF().iterator(); + + Entity entity1; + + do { + if (!iterator.hasNext()) { + return false; + } + + entity1 = (Entity) iterator.next(); + } while (!entity1.equals(entity)); + + return true; + } + + public Collection bG() { + HashSet hashset = Sets.newHashSet(); + + this.a(Entity.class, (Set) hashset); + return hashset; + } + + public Collection b(Class oclass) { + HashSet hashset = Sets.newHashSet(); + + this.a(oclass, (Set) hashset); + return hashset; + } + + private void a(Class oclass, Set set) { + Entity entity; + + for (Iterator iterator = this.bF().iterator(); iterator.hasNext(); entity.a(oclass, set)) { + entity = (Entity) iterator.next(); + if (oclass.isAssignableFrom(entity.getClass())) { + set.add((T) entity); // CraftBukkit - decompile error + } + } + + } + + public Entity getVehicle() { + Entity entity; + + for (entity = this; entity.isPassenger(); entity = entity.bJ()) { + ; + } + + return entity; + } + + public boolean x(Entity entity) { + return this.getVehicle() == entity.getVehicle(); + } + + public boolean y(Entity entity) { + Iterator iterator = this.bF().iterator(); + + Entity entity1; + + do { + if (!iterator.hasNext()) { + return false; + } + + entity1 = (Entity) iterator.next(); + if (entity1.equals(entity)) { + return true; + } + } while (!entity1.y(entity)); + + return true; + } + + public boolean bI() { + Entity entity = this.bE(); + + return entity instanceof EntityHuman ? ((EntityHuman) entity).cZ() : !this.world.isClientSide; + } + + @Nullable + public Entity bJ() { + return this.au; + } + + public EnumPistonReaction getPushReaction() { + return EnumPistonReaction.NORMAL; + } + + public SoundCategory bK() { + return SoundCategory.NEUTRAL; + } + + public int getMaxFireTicks() { + return 1; + } +}