Newer
Older
void-pack-super-server / work / nms.old.1585251758016 / minecraft / server / Entity.java
Simon Lindgren on 26 Mar 2020 108 KB first commit
package net.minecraft.server;

import com.google.common.collect.ImmutableSet;
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.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Stream;
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.block.BlockFace;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Hanging;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Vehicle;
import org.bukkit.event.entity.EntityCombustByEntityEvent;
import org.bukkit.event.hanging.HangingBreakByEntityEvent;
import org.bukkit.event.vehicle.VehicleBlockCollisionEvent;
import org.bukkit.event.vehicle.VehicleEnterEvent;
import org.bukkit.event.vehicle.VehicleExitEvent;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.entity.CraftEntity;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.craftbukkit.event.CraftEventFactory;
import org.bukkit.entity.Pose;
import org.bukkit.event.entity.EntityAirChangeEvent;
import org.bukkit.event.entity.EntityCombustEvent;
import org.bukkit.event.entity.EntityDropItemEvent;
import org.bukkit.event.entity.EntityPortalEvent;
import org.bukkit.event.entity.EntityPoseChangeEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.plugin.PluginManager;
// CraftBukkit end

public abstract class Entity implements INamableTileEntity, ICommandListener {

    // CraftBukkit start
    private static final int CURRENT_LEVEL = 2;
    static boolean isLevelAtLeast(NBTTagCompound tag, int level) {
        return tag.hasKey("Bukkit.updateLevel") && tag.getInt("Bukkit.updateLevel") >= level;
    }

    private CraftEntity bukkitEntity;

    public CraftEntity getBukkitEntity() {
        if (bukkitEntity == null) {
            bukkitEntity = CraftEntity.getEntity(world.getServer(), this);
        }
        return bukkitEntity;
    }

    @Override
    public CommandSender getBukkitSender(CommandListenerWrapper wrapper) {
        return getBukkitEntity();
    }
    // CraftBukkit end

    protected static final Logger LOGGER = LogManager.getLogger();
    private static final AtomicInteger entityCount = new AtomicInteger();
    private static final List<ItemStack> c = Collections.emptyList();
    private static final AxisAlignedBB d = new AxisAlignedBB(0.0D, 0.0D, 0.0D, 0.0D, 0.0D, 0.0D);
    private static double e = 1.0D;
    private final EntityTypes<?> f;
    private int id;
    public boolean i;
    public final List<Entity> passengers;
    protected int j;
    @Nullable
    private Entity vehicle;
    public boolean attachedToPlayer;
    public World world;
    public double lastX;
    public double lastY;
    public double lastZ;
    private double locX;
    private double locY;
    private double locZ;
    private Vec3D mot;
    public float yaw;
    public float pitch;
    public float lastYaw;
    public float lastPitch;
    private AxisAlignedBB boundingBox;
    public boolean onGround;
    public boolean positionChanged;
    public boolean v;
    public boolean w;
    public boolean velocityChanged;
    protected Vec3D y;
    public boolean dead;
    public float A;
    public float B;
    public float C;
    public float fallDistance;
    private float av;
    private float aw;
    public double E;
    public double F;
    public double G;
    public float H;
    public boolean noclip;
    public float J;
    protected final Random random;
    public int ticksLived;
    public int fireTicks;
    public boolean inWater;
    protected double N;
    protected boolean O;
    protected boolean inLava;
    public int noDamageTicks;
    protected boolean justCreated;
    protected final DataWatcher datawatcher;
    protected static final DataWatcherObject<Byte> T = DataWatcher.a(Entity.class, DataWatcherRegistry.a);
    private static final DataWatcherObject<Integer> AIR_TICKS = DataWatcher.a(Entity.class, DataWatcherRegistry.b);
    private static final DataWatcherObject<Optional<IChatBaseComponent>> az = DataWatcher.a(Entity.class, DataWatcherRegistry.f);
    private static final DataWatcherObject<Boolean> aA = DataWatcher.a(Entity.class, DataWatcherRegistry.i);
    private static final DataWatcherObject<Boolean> aB = DataWatcher.a(Entity.class, DataWatcherRegistry.i);
    private static final DataWatcherObject<Boolean> aC = DataWatcher.a(Entity.class, DataWatcherRegistry.i);
    protected static final DataWatcherObject<EntityPose> POSE = DataWatcher.a(Entity.class, DataWatcherRegistry.s);
    public boolean inChunk;
    public int chunkX;
    public int chunkY;
    public int chunkZ;
    public long Z;
    public long aa;
    public long ab;
    public boolean ac;
    public boolean impulse;
    public int portalCooldown;
    protected boolean af;
    protected int ag;
    public DimensionManager dimension;
    protected BlockPosition ai;
    protected Vec3D aj;
    protected EnumDirection ak;
    private boolean invulnerable;
    protected UUID uniqueID;
    protected String am;
    public boolean glowing;
    private final Set<String> aE;
    private boolean aF;
    private final double[] aG;
    private long aH;
    private EntitySize size;
    private float headHeight;
    // CraftBukkit start
    public boolean persist = true;
    public boolean valid;
    public org.bukkit.projectiles.ProjectileSource projectileSource; // For projectiles only
    public boolean forceExplosionKnockback; // SPIGOT-949

    public float getBukkitYaw() {
        return this.yaw;
    }

    public boolean isChunkLoaded() {
        return world.isChunkLoaded((int) Math.floor(this.locX) >> 4, (int) Math.floor(this.locZ) >> 4);
    }
    // CraftBukkit end

    public Entity(EntityTypes<?> entitytypes, World world) {
        this.id = Entity.entityCount.incrementAndGet();
        this.passengers = Lists.newArrayList();
        this.mot = Vec3D.a;
        this.boundingBox = Entity.d;
        this.y = Vec3D.a;
        this.av = 1.0F;
        this.aw = 1.0F;
        this.random = new Random();
        this.fireTicks = -this.getMaxFireTicks();
        this.justCreated = true;
        this.uniqueID = MathHelper.a(this.random);
        this.am = this.uniqueID.toString();
        this.aE = Sets.newHashSet();
        this.aG = new double[]{0.0D, 0.0D, 0.0D};
        this.f = entitytypes;
        this.world = world;
        this.size = entitytypes.k();
        this.setPosition(0.0D, 0.0D, 0.0D);
        if (world != null) {
            this.dimension = world.worldProvider.getDimensionManager();
        }

        this.datawatcher = new DataWatcher(this);
        this.datawatcher.register(Entity.T, (byte) 0);
        this.datawatcher.register(Entity.AIR_TICKS, this.bw());
        this.datawatcher.register(Entity.aA, false);
        this.datawatcher.register(Entity.az, Optional.empty());
        this.datawatcher.register(Entity.aB, false);
        this.datawatcher.register(Entity.aC, false);
        this.datawatcher.register(Entity.POSE, EntityPose.STANDING);
        this.initDatawatcher();
        this.headHeight = this.getHeadHeight(EntityPose.STANDING, this.size);
    }

    public boolean isSpectator() {
        return false;
    }

    public final void decouple() {
        if (this.isVehicle()) {
            this.ejectPassengers();
        }

        if (this.isPassenger()) {
            this.stopRiding();
        }

    }

    public void c(double d0, double d1, double d2) {
        this.Z = PacketPlayOutEntity.a(d0);
        this.aa = PacketPlayOutEntity.a(d1);
        this.ab = PacketPlayOutEntity.a(d2);
    }

    public EntityTypes<?> getEntityType() {
        return this.f;
    }

    public int getId() {
        return this.id;
    }

    public void e(int i) {
        this.id = i;
    }

    public Set<String> getScoreboardTags() {
        return this.aE;
    }

    public boolean addScoreboardTag(String s) {
        return this.aE.size() >= 1024 ? false : this.aE.add(s);
    }

    public boolean removeScoreboardTag(String s) {
        return this.aE.remove(s);
    }

    public void killEntity() {
        this.die();
    }

    protected abstract void initDatawatcher();

    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;
    }

    protected void setPose(EntityPose entitypose) {
        // CraftBukkit start
        if (entitypose == this.getPose()) {
            return;
        }
        this.world.getServer().getPluginManager().callEvent(new EntityPoseChangeEvent(this.getBukkitEntity(), Pose.values()[entitypose.ordinal()]));
        // CraftBukkit end
        this.datawatcher.set(Entity.POSE, entitypose);
    }

    public EntityPose getPose() {
        return (EntityPose) this.datawatcher.get(Entity.POSE);
    }

    protected void setYawPitch(float f, float f1) {
        // CraftBukkit start - yaw was sometimes set to NaN, so we need to set it back to 0
        if (Float.isNaN(f)) {
            f = 0;
        }

        if (f == Float.POSITIVE_INFINITY || f == Float.NEGATIVE_INFINITY) {
            if (this instanceof EntityPlayer) {
                this.world.getServer().getLogger().warning(this.getName() + " was caught trying to crash the server with an invalid yaw");
                ((CraftPlayer) this.getBukkitEntity()).kickPlayer("Infinite yaw (Hacking?)");
            }
            f = 0;
        }

        // pitch was sometimes set to NaN, so we need to set it back to 0
        if (Float.isNaN(f1)) {
            f1 = 0;
        }

        if (f1 == Float.POSITIVE_INFINITY || f1 == Float.NEGATIVE_INFINITY) {
            if (this instanceof EntityPlayer) {
                this.world.getServer().getLogger().warning(this.getName() + " was caught trying to crash the server with an invalid pitch");
                ((CraftPlayer) this.getBukkitEntity()).kickPlayer("Infinite pitch (Hacking?)");
            }
            f1 = 0;
        }
        // CraftBukkit end

        this.yaw = f % 360.0F;
        this.pitch = f1 % 360.0F;
    }

    public void setPosition(double d0, double d1, double d2) {
        this.setPositionRaw(d0, d1, d2);
        float f = this.size.width / 2.0F;
        float f1 = this.size.height;

        this.a(new AxisAlignedBB(d0 - (double) f, d1, d2 - (double) f, d0 + (double) f, d1 + (double) f1, d2 + (double) f));
        if (valid) ((WorldServer) world).chunkCheck(this); // CraftBukkit
    }

    protected void Z() {
        this.setPosition(this.locX, this.locY, this.locZ);
    }

    public void tick() {
        if (!this.world.isClientSide) {
            this.setFlag(6, this.bt());
        }

        this.entityBaseTick();
    }

    // CraftBukkit start
    public void postTick() {
        // No clean way to break out of ticking once the entity has been copied to a new world, so instead we move the portalling later in the tick cycle
        if (!(this instanceof EntityPlayer)) {
            this.doPortalTick();
        }
    }
    // CraftBukkit end

    public void entityBaseTick() {
        this.world.getMethodProfiler().enter("entityBaseTick");
        if (this.isPassenger() && this.getVehicle().dead) {
            this.stopRiding();
        }

        if (this.j > 0) {
            --this.j;
        }

        this.A = this.B;
        this.lastPitch = this.pitch;
        this.lastYaw = this.yaw;
        if (this instanceof EntityPlayer) this.doPortalTick(); // CraftBukkit - // Moved up to postTick
        this.aE();
        this.m();
        if (this.world.isClientSide) {
            this.extinguish();
        } else if (this.fireTicks > 0) {
            if (this.isFireProof()) {
                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.aH()) {
            this.burnFromLava();
            this.fallDistance *= 0.5F;
        }

        if (this.locY() < -64.0D) {
            this.af();
        }

        if (!this.world.isClientSide) {
            this.setFlag(0, this.fireTicks > 0);
        }

        this.justCreated = false;
        this.world.getMethodProfiler().exit();
    }

    protected void E() {
        if (this.portalCooldown > 0) {
            --this.portalCooldown;
        }

    }

    public int ab() {
        return 1;
    }

    protected void burnFromLava() {
        if (!this.isFireProof()) {
            // CraftBukkit start - Fallen in lava TODO: this event spams!
            if (this instanceof EntityLiving && fireTicks <= 0) {
                // not on fire yet
                // TODO: shouldn't be sending null for the block
                org.bukkit.block.Block damager = null; // ((WorldServer) this.l).getWorld().getBlockAt(i, j, k);
                org.bukkit.entity.Entity damagee = this.getBukkitEntity();
                EntityCombustEvent combustEvent = new org.bukkit.event.entity.EntityCombustByBlockEvent(damager, damagee, 15);
                this.world.getServer().getPluginManager().callEvent(combustEvent);

                if (!combustEvent.isCancelled()) {
                    this.setOnFire(combustEvent.getDuration(), false);
                }
            } else {
                // This will be called every single tick the entity is in lava, so don't throw an event
                this.setOnFire(15, false);
            }
            // CraftBukkit end - we also don't throw an event unless the object in lava is living, to save on some event calls
            this.damageEntity(DamageSource.LAVA, 4.0F);
        }
    }

    public void setOnFire(int i) {
        // CraftBukkit start
        this.setOnFire(i, true);
    }

    public void setOnFire(int i, boolean callEvent) {
        if (callEvent) {
            EntityCombustEvent event = new EntityCombustEvent(this.getBukkitEntity(), i);
            this.world.getServer().getPluginManager().callEvent(event);

            if (event.isCancelled()) {
                return;
            }

            i = event.getDuration();
        }
        // CraftBukkit end
        int j = i * 20;

        if (this instanceof EntityLiving) {
            j = EnchantmentProtection.a((EntityLiving) this, j);
        }

        if (this.fireTicks < j) {
            this.fireTicks = j;
        }

    }

    public void g(int i) {
        this.fireTicks = i;
    }

    public int ad() {
        return this.fireTicks;
    }

    public void extinguish() {
        this.fireTicks = 0;
    }

    protected void af() {
        this.die();
    }

    public boolean e(double d0, double d1, double d2) {
        return this.b(this.getBoundingBox().d(d0, d1, d2));
    }

    private boolean b(AxisAlignedBB axisalignedbb) {
        return this.world.getCubes(this, axisalignedbb) && !this.world.containsLiquid(axisalignedbb);
    }

    public void move(EnumMoveType enummovetype, Vec3D vec3d) {
        if (this.noclip) {
            this.a(this.getBoundingBox().b(vec3d));
            this.recalcPosition();
        } else {
            if (enummovetype == EnumMoveType.PISTON) {
                vec3d = this.a(vec3d);
                if (vec3d.equals(Vec3D.a)) {
                    return;
                }
            }

            this.world.getMethodProfiler().enter("move");
            if (this.y.g() > 1.0E-7D) {
                vec3d = vec3d.h(this.y);
                this.y = Vec3D.a;
                this.setMot(Vec3D.a);
            }

            vec3d = this.a(vec3d, enummovetype);
            Vec3D vec3d1 = this.e(vec3d);

            if (vec3d1.g() > 1.0E-7D) {
                this.a(this.getBoundingBox().b(vec3d1));
                this.recalcPosition();
            }

            this.world.getMethodProfiler().exit();
            this.world.getMethodProfiler().enter("rest");
            this.positionChanged = !MathHelper.b(vec3d.x, vec3d1.x) || !MathHelper.b(vec3d.z, vec3d1.z);
            this.v = vec3d.y != vec3d1.y;
            this.onGround = this.v && vec3d.y < 0.0D;
            this.w = this.positionChanged || this.v;
            BlockPosition blockposition = this.ag();
            IBlockData iblockdata = this.world.getType(blockposition);

            this.a(vec3d1.y, this.onGround, iblockdata, blockposition);
            Vec3D vec3d2 = this.getMot();

            if (vec3d.x != vec3d1.x) {
                this.setMot(0.0D, vec3d2.y, vec3d2.z);
            }

            if (vec3d.z != vec3d1.z) {
                this.setMot(vec3d2.x, vec3d2.y, 0.0D);
            }

            Block block = iblockdata.getBlock();

            if (vec3d.y != vec3d1.y) {
                block.a((IBlockAccess) this.world, this);
            }

            // CraftBukkit start
            if (positionChanged && getBukkitEntity() instanceof Vehicle) {
                Vehicle vehicle = (Vehicle) this.getBukkitEntity();
                org.bukkit.block.Block bl = this.world.getWorld().getBlockAt(MathHelper.floor(this.locX), MathHelper.floor(this.locY), MathHelper.floor(this.locZ));

                if (vec3d.x > vec3d1.x) {
                    bl = bl.getRelative(BlockFace.EAST);
                } else if (vec3d.x < vec3d1.x) {
                    bl = bl.getRelative(BlockFace.WEST);
                } else if (vec3d.z > vec3d1.z) {
                    bl = bl.getRelative(BlockFace.SOUTH);
                } else if (vec3d.z < vec3d1.z) {
                    bl = bl.getRelative(BlockFace.NORTH);
                }

                if (!bl.getType().isAir()) {
                    VehicleBlockCollisionEvent event = new VehicleBlockCollisionEvent(vehicle, bl);
                    world.getServer().getPluginManager().callEvent(event);
                }
            }
            // CraftBukkit end

            if (this.onGround && !this.bk()) {
                block.stepOn(this.world, blockposition, this);
            }

            if (this.playStepSound() && !this.isPassenger()) {
                double d0 = vec3d1.x;
                double d1 = vec3d1.y;
                double d2 = vec3d1.z;

                if (block != Blocks.LADDER && block != Blocks.SCAFFOLDING) {
                    d1 = 0.0D;
                }

                this.B = (float) ((double) this.B + (double) MathHelper.sqrt(b(vec3d1)) * 0.6D);
                this.C = (float) ((double) this.C + (double) MathHelper.sqrt(d0 * d0 + d1 * d1 + d2 * d2) * 0.6D);
                if (this.C > this.av && !iblockdata.isAir()) {
                    this.av = this.ak();
                    if (this.isInWater()) {
                        Entity entity = this.isVehicle() && this.getRidingPassenger() != null ? this.getRidingPassenger() : this;
                        float f = entity == this ? 0.35F : 0.4F;
                        Vec3D vec3d3 = entity.getMot();
                        float f1 = MathHelper.sqrt(vec3d3.x * vec3d3.x * 0.20000000298023224D + vec3d3.y * vec3d3.y + vec3d3.z * vec3d3.z * 0.20000000298023224D) * f;

                        if (f1 > 1.0F) {
                            f1 = 1.0F;
                        }

                        this.d(f1);
                    } else {
                        this.a(blockposition, iblockdata);
                    }
                } else if (this.C > this.aw && this.aq() && iblockdata.isAir()) {
                    this.aw = this.e(this.C);
                }
            }

            try {
                this.inLava = false;
                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);
            }

            this.setMot(this.getMot().d((double) this.ai(), 1.0D, (double) this.ai()));
            boolean flag = this.ay();

            if (this.world.c(this.getBoundingBox().shrink(0.001D))) {
                if (!flag) {
                    ++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(), false);
                        }
                        // CraftBukkit end
                    }
                }

                this.burn(1);
            } else if (this.fireTicks <= 0) {
                this.fireTicks = -this.getMaxFireTicks();
            }

            if (flag && this.isBurning()) {
                this.a(SoundEffects.ENTITY_GENERIC_EXTINGUISH_FIRE, 0.7F, 1.6F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F);
                this.fireTicks = -this.getMaxFireTicks();
            }

            this.world.getMethodProfiler().exit();
        }
    }

    protected BlockPosition ag() {
        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);

        if (this.world.getType(blockposition).isAir()) {
            BlockPosition blockposition1 = blockposition.down();
            IBlockData iblockdata = this.world.getType(blockposition1);
            Block block = iblockdata.getBlock();

            if (block.a(TagsBlock.FENCES) || block.a(TagsBlock.WALLS) || block instanceof BlockFenceGate) {
                return blockposition1;
            }
        }

        return blockposition;
    }

    protected float ah() {
        float f = this.world.getType(new BlockPosition(this)).getBlock().n();
        float f1 = this.world.getType(this.aj()).getBlock().n();

        return (double) f == 1.0D ? f1 : f;
    }

    protected float ai() {
        Block block = this.world.getType(new BlockPosition(this)).getBlock();
        float f = block.m();

        return block != Blocks.WATER && block != Blocks.BUBBLE_COLUMN ? ((double) f == 1.0D ? this.world.getType(this.aj()).getBlock().m() : f) : f;
    }

    protected BlockPosition aj() {
        return new BlockPosition(this.locX, this.getBoundingBox().minY - 0.5000001D, this.locZ);
    }

    protected Vec3D a(Vec3D vec3d, EnumMoveType enummovetype) {
        return vec3d;
    }

    protected Vec3D a(Vec3D vec3d) {
        if (vec3d.g() <= 1.0E-7D) {
            return vec3d;
        } else {
            long i = this.world.getTime();

            if (i != this.aH) {
                Arrays.fill(this.aG, 0.0D);
                this.aH = i;
            }

            double d0;

            if (vec3d.x != 0.0D) {
                d0 = this.a(EnumDirection.EnumAxis.X, vec3d.x);
                return Math.abs(d0) <= 9.999999747378752E-6D ? Vec3D.a : new Vec3D(d0, 0.0D, 0.0D);
            } else if (vec3d.y != 0.0D) {
                d0 = this.a(EnumDirection.EnumAxis.Y, vec3d.y);
                return Math.abs(d0) <= 9.999999747378752E-6D ? Vec3D.a : new Vec3D(0.0D, d0, 0.0D);
            } else if (vec3d.z != 0.0D) {
                d0 = this.a(EnumDirection.EnumAxis.Z, vec3d.z);
                return Math.abs(d0) <= 9.999999747378752E-6D ? Vec3D.a : new Vec3D(0.0D, 0.0D, d0);
            } else {
                return Vec3D.a;
            }
        }
    }

    private double a(EnumDirection.EnumAxis enumdirection_enumaxis, double d0) {
        int i = enumdirection_enumaxis.ordinal();
        double d1 = MathHelper.a(d0 + this.aG[i], -0.51D, 0.51D);

        d0 = d1 - this.aG[i];
        this.aG[i] = d1;
        return d0;
    }

    private Vec3D e(Vec3D vec3d) {
        AxisAlignedBB axisalignedbb = this.getBoundingBox();
        VoxelShapeCollision voxelshapecollision = VoxelShapeCollision.a(this);
        VoxelShape voxelshape = this.world.getWorldBorder().a();
        Stream<VoxelShape> stream = VoxelShapes.c(voxelshape, VoxelShapes.a(axisalignedbb.shrink(1.0E-7D)), OperatorBoolean.AND) ? Stream.empty() : Stream.of(voxelshape);
        Stream<VoxelShape> stream1 = this.world.b(this, axisalignedbb.a(vec3d), (Set) ImmutableSet.of());
        StreamAccumulator<VoxelShape> streamaccumulator = new StreamAccumulator<>(Stream.concat(stream1, stream));
        Vec3D vec3d1 = vec3d.g() == 0.0D ? vec3d : a(this, vec3d, axisalignedbb, this.world, voxelshapecollision, streamaccumulator);
        boolean flag = vec3d.x != vec3d1.x;
        boolean flag1 = vec3d.y != vec3d1.y;
        boolean flag2 = vec3d.z != vec3d1.z;
        boolean flag3 = this.onGround || flag1 && vec3d.y < 0.0D;

        if (this.H > 0.0F && flag3 && (flag || flag2)) {
            Vec3D vec3d2 = a(this, new Vec3D(vec3d.x, (double) this.H, vec3d.z), axisalignedbb, this.world, voxelshapecollision, streamaccumulator);
            Vec3D vec3d3 = a(this, new Vec3D(0.0D, (double) this.H, 0.0D), axisalignedbb.b(vec3d.x, 0.0D, vec3d.z), this.world, voxelshapecollision, streamaccumulator);

            if (vec3d3.y < (double) this.H) {
                Vec3D vec3d4 = a(this, new Vec3D(vec3d.x, 0.0D, vec3d.z), axisalignedbb.b(vec3d3), this.world, voxelshapecollision, streamaccumulator).e(vec3d3);

                if (b(vec3d4) > b(vec3d2)) {
                    vec3d2 = vec3d4;
                }
            }

            if (b(vec3d2) > b(vec3d1)) {
                return vec3d2.e(a(this, new Vec3D(0.0D, -vec3d2.y + vec3d.y, 0.0D), axisalignedbb.b(vec3d2), this.world, voxelshapecollision, streamaccumulator));
            }
        }

        return vec3d1;
    }

    public static double b(Vec3D vec3d) {
        return vec3d.x * vec3d.x + vec3d.z * vec3d.z;
    }

    public static Vec3D a(@Nullable Entity entity, Vec3D vec3d, AxisAlignedBB axisalignedbb, World world, VoxelShapeCollision voxelshapecollision, StreamAccumulator<VoxelShape> streamaccumulator) {
        boolean flag = vec3d.x == 0.0D;
        boolean flag1 = vec3d.y == 0.0D;
        boolean flag2 = vec3d.z == 0.0D;

        if ((!flag || !flag1) && (!flag || !flag2) && (!flag1 || !flag2)) {
            StreamAccumulator<VoxelShape> streamaccumulator1 = new StreamAccumulator<>(Stream.concat(streamaccumulator.a(), world.b(entity, axisalignedbb.a(vec3d))));

            return a(vec3d, axisalignedbb, streamaccumulator1);
        } else {
            return a(vec3d, axisalignedbb, world, voxelshapecollision, streamaccumulator);
        }
    }

    public static Vec3D a(Vec3D vec3d, AxisAlignedBB axisalignedbb, StreamAccumulator<VoxelShape> streamaccumulator) {
        double d0 = vec3d.x;
        double d1 = vec3d.y;
        double d2 = vec3d.z;

        if (d1 != 0.0D) {
            d1 = VoxelShapes.a(EnumDirection.EnumAxis.Y, axisalignedbb, streamaccumulator.a(), d1);
            if (d1 != 0.0D) {
                axisalignedbb = axisalignedbb.d(0.0D, d1, 0.0D);
            }
        }

        boolean flag = Math.abs(d0) < Math.abs(d2);

        if (flag && d2 != 0.0D) {
            d2 = VoxelShapes.a(EnumDirection.EnumAxis.Z, axisalignedbb, streamaccumulator.a(), d2);
            if (d2 != 0.0D) {
                axisalignedbb = axisalignedbb.d(0.0D, 0.0D, d2);
            }
        }

        if (d0 != 0.0D) {
            d0 = VoxelShapes.a(EnumDirection.EnumAxis.X, axisalignedbb, streamaccumulator.a(), d0);
            if (!flag && d0 != 0.0D) {
                axisalignedbb = axisalignedbb.d(d0, 0.0D, 0.0D);
            }
        }

        if (!flag && d2 != 0.0D) {
            d2 = VoxelShapes.a(EnumDirection.EnumAxis.Z, axisalignedbb, streamaccumulator.a(), d2);
        }

        return new Vec3D(d0, d1, d2);
    }

    public static Vec3D a(Vec3D vec3d, AxisAlignedBB axisalignedbb, IWorldReader iworldreader, VoxelShapeCollision voxelshapecollision, StreamAccumulator<VoxelShape> streamaccumulator) {
        double d0 = vec3d.x;
        double d1 = vec3d.y;
        double d2 = vec3d.z;

        if (d1 != 0.0D) {
            d1 = VoxelShapes.a(EnumDirection.EnumAxis.Y, axisalignedbb, iworldreader, d1, voxelshapecollision, streamaccumulator.a());
            if (d1 != 0.0D) {
                axisalignedbb = axisalignedbb.d(0.0D, d1, 0.0D);
            }
        }

        boolean flag = Math.abs(d0) < Math.abs(d2);

        if (flag && d2 != 0.0D) {
            d2 = VoxelShapes.a(EnumDirection.EnumAxis.Z, axisalignedbb, iworldreader, d2, voxelshapecollision, streamaccumulator.a());
            if (d2 != 0.0D) {
                axisalignedbb = axisalignedbb.d(0.0D, 0.0D, d2);
            }
        }

        if (d0 != 0.0D) {
            d0 = VoxelShapes.a(EnumDirection.EnumAxis.X, axisalignedbb, iworldreader, d0, voxelshapecollision, streamaccumulator.a());
            if (!flag && d0 != 0.0D) {
                axisalignedbb = axisalignedbb.d(d0, 0.0D, 0.0D);
            }
        }

        if (!flag && d2 != 0.0D) {
            d2 = VoxelShapes.a(EnumDirection.EnumAxis.Z, axisalignedbb, iworldreader, d2, voxelshapecollision, streamaccumulator.a());
        }

        return new Vec3D(d0, d1, d2);
    }

    protected float ak() {
        return (float) ((int) this.C + 1);
    }

    public void recalcPosition() {
        AxisAlignedBB axisalignedbb = this.getBoundingBox();

        this.setPositionRaw((axisalignedbb.minX + axisalignedbb.maxX) / 2.0D, axisalignedbb.minY, (axisalignedbb.minZ + axisalignedbb.maxZ) / 2.0D);
        if (valid) ((WorldServer) world).chunkCheck(this); // CraftBukkit
    }

    protected SoundEffect getSoundSwim() {
        return SoundEffects.ENTITY_GENERIC_SWIM;
    }

    protected SoundEffect getSoundSplash() {
        return SoundEffects.ENTITY_GENERIC_SPLASH;
    }

    protected SoundEffect getSoundSplashHighSpeed() {
        return SoundEffects.ENTITY_GENERIC_SPLASH;
    }

    protected void checkBlockCollisions() {
        AxisAlignedBB axisalignedbb = this.getBoundingBox();
        BlockPosition.PooledBlockPosition blockposition_pooledblockposition = BlockPosition.PooledBlockPosition.d(axisalignedbb.minX + 0.001D, axisalignedbb.minY + 0.001D, axisalignedbb.minZ + 0.001D);
        Throwable throwable = null;

        try {
            BlockPosition.PooledBlockPosition blockposition_pooledblockposition1 = BlockPosition.PooledBlockPosition.d(axisalignedbb.maxX - 0.001D, axisalignedbb.maxY - 0.001D, axisalignedbb.maxZ - 0.001D);
            Throwable throwable1 = null;

            try {
                BlockPosition.PooledBlockPosition blockposition_pooledblockposition2 = BlockPosition.PooledBlockPosition.r();
                Throwable throwable2 = null;

                try {
                    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.d(i, j, k);
                                    IBlockData iblockdata = this.world.getType(blockposition_pooledblockposition2);

                                    try {
                                        iblockdata.a(this.world, blockposition_pooledblockposition2, this);
                                        this.a(iblockdata);
                                    } catch (Throwable throwable3) {
                                        CrashReport crashreport = CrashReport.a(throwable3, "Colliding entity with block");
                                        CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Block being collided with");

                                        CrashReportSystemDetails.a(crashreportsystemdetails, blockposition_pooledblockposition2, iblockdata);
                                        throw new ReportedException(crashreport);
                                    }
                                }
                            }
                        }
                    }
                } catch (Throwable throwable4) {
                    throwable2 = throwable4;
                    throw throwable4;
                } finally {
                    if (blockposition_pooledblockposition2 != null) {
                        if (throwable2 != null) {
                            try {
                                blockposition_pooledblockposition2.close();
                            } catch (Throwable throwable5) {
                                throwable2.addSuppressed(throwable5);
                            }
                        } else {
                            blockposition_pooledblockposition2.close();
                        }
                    }

                }
            } catch (Throwable throwable6) {
                throwable1 = throwable6;
                throw throwable6;
            } finally {
                if (blockposition_pooledblockposition1 != null) {
                    if (throwable1 != null) {
                        try {
                            blockposition_pooledblockposition1.close();
                        } catch (Throwable throwable7) {
                            throwable1.addSuppressed(throwable7);
                        }
                    } else {
                        blockposition_pooledblockposition1.close();
                    }
                }

            }
        } catch (Throwable throwable8) {
            throwable = throwable8;
            throw throwable8;
        } finally {
            if (blockposition_pooledblockposition != null) {
                if (throwable != null) {
                    try {
                        blockposition_pooledblockposition.close();
                    } catch (Throwable throwable9) {
                        throwable.addSuppressed(throwable9);
                    }
                } else {
                    blockposition_pooledblockposition.close();
                }
            }

        }

    }

    protected void a(IBlockData iblockdata) {}

    protected void a(BlockPosition blockposition, IBlockData iblockdata) {
        if (!iblockdata.getMaterial().isLiquid()) {
            IBlockData iblockdata1 = this.world.getType(blockposition.up());
            SoundEffectType soundeffecttype = iblockdata1.getBlock() == Blocks.SNOW ? iblockdata1.r() : iblockdata.r();

            this.a(soundeffecttype.d(), soundeffecttype.a() * 0.15F, soundeffecttype.b());
        }
    }

    protected void d(float f) {
        this.a(this.getSoundSwim(), f, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F);
    }

    protected float e(float f) {
        return 0.0F;
    }

    protected boolean aq() {
        return false;
    }

    public void a(SoundEffect soundeffect, float f, float f1) {
        if (!this.isSilent()) {
            this.world.playSound((EntityHuman) null, this.locX(), this.locY(), this.locZ(), soundeffect, this.getSoundCategory(), f, f1);
        }

    }

    public boolean isSilent() {
        return (Boolean) this.datawatcher.get(Entity.aB);
    }

    public void setSilent(boolean flag) {
        this.datawatcher.set(Entity.aB, flag);
    }

    public boolean isNoGravity() {
        return (Boolean) this.datawatcher.get(Entity.aC);
    }

    public void setNoGravity(boolean flag) {
        this.datawatcher.set(Entity.aC, 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 au() {
        return null;
    }

    protected void burn(float i) { // CraftBukkit - int -> float
        if (!this.isFireProof()) {
            this.damageEntity(DamageSource.FIRE, (float) i);
        }

    }

    public final boolean isFireProof() {
        return this.getEntityType().c();
    }

    public boolean b(float f, float f1) {
        if (this.isVehicle()) {
            Iterator iterator = this.getPassengers().iterator();

            while (iterator.hasNext()) {
                Entity entity = (Entity) iterator.next();

                entity.b(f, f1);
            }
        }

        return false;
    }

    public boolean isInWater() {
        return this.inWater;
    }

    private boolean isInRain() {
        BlockPosition.PooledBlockPosition blockposition_pooledblockposition = BlockPosition.PooledBlockPosition.b(this);
        Throwable throwable = null;

        boolean flag;

        try {
            flag = this.world.isRainingAt(blockposition_pooledblockposition) || this.world.isRainingAt(blockposition_pooledblockposition.c(this.locX(), this.locY() + (double) this.size.height, this.locZ()));
        } catch (Throwable throwable1) {
            throwable = throwable1;
            throw throwable1;
        } finally {
            if (blockposition_pooledblockposition != null) {
                if (throwable != null) {
                    try {
                        blockposition_pooledblockposition.close();
                    } catch (Throwable throwable2) {
                        throwable.addSuppressed(throwable2);
                    }
                } else {
                    blockposition_pooledblockposition.close();
                }
            }

        }

        return flag;
    }

    private boolean l() {
        return this.world.getType(new BlockPosition(this)).getBlock() == Blocks.BUBBLE_COLUMN;
    }

    public boolean isInWaterOrRain() {
        return this.isInWater() || this.isInRain();
    }

    public boolean ay() {
        return this.isInWater() || this.isInRain() || this.l();
    }

    public boolean az() {
        return this.isInWater() || this.l();
    }

    public boolean aA() {
        return this.O && this.isInWater();
    }

    private void m() {
        this.aC();
        this.n();
        this.aB();
    }

    public void aB() {
        if (this.isSwimming()) {
            this.setSwimming(this.isSprinting() && this.isInWater() && !this.isPassenger());
        } else {
            this.setSwimming(this.isSprinting() && this.aA() && !this.isPassenger());
        }

    }

    public boolean aC() {
        if (this.getVehicle() instanceof EntityBoat) {
            this.inWater = false;
        } else if (this.b(TagsFluid.WATER)) {
            if (!this.inWater && !this.justCreated) {
                this.aD();
            }

            this.fallDistance = 0.0F;
            this.inWater = true;
            this.extinguish();
        } else {
            this.inWater = false;
        }

        return this.inWater;
    }

    private void n() {
        this.O = this.a(TagsFluid.WATER, true);
    }

    protected void aD() {
        Entity entity = this.isVehicle() && this.getRidingPassenger() != null ? this.getRidingPassenger() : this;
        float f = entity == this ? 0.2F : 0.9F;
        Vec3D vec3d = entity.getMot();
        float f1 = MathHelper.sqrt(vec3d.x * vec3d.x * 0.20000000298023224D + vec3d.y * vec3d.y + vec3d.z * vec3d.z * 0.20000000298023224D) * f;

        if (f1 > 1.0F) {
            f1 = 1.0F;
        }

        if ((double) f1 < 0.25D) {
            this.a(this.getSoundSplash(), f1, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F);
        } else {
            this.a(this.getSoundSplashHighSpeed(), f1, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F);
        }

        float f2 = (float) MathHelper.floor(this.locY());

        float f3;
        float f4;
        int i;

        for (i = 0; (float) i < 1.0F + this.size.width * 20.0F; ++i) {
            f3 = (this.random.nextFloat() * 2.0F - 1.0F) * this.size.width;
            f4 = (this.random.nextFloat() * 2.0F - 1.0F) * this.size.width;
            this.world.addParticle(Particles.BUBBLE, this.locX() + (double) f3, (double) (f2 + 1.0F), this.locZ() + (double) f4, vec3d.x, vec3d.y - (double) (this.random.nextFloat() * 0.2F), vec3d.z);
        }

        for (i = 0; (float) i < 1.0F + this.size.width * 20.0F; ++i) {
            f3 = (this.random.nextFloat() * 2.0F - 1.0F) * this.size.width;
            f4 = (this.random.nextFloat() * 2.0F - 1.0F) * this.size.width;
            this.world.addParticle(Particles.SPLASH, this.locX() + (double) f3, (double) (f2 + 1.0F), this.locZ() + (double) f4, vec3d.x, vec3d.y, vec3d.z);
        }

    }

    public void aE() {
        if (this.isSprinting() && !this.isInWater()) {
            this.aF();
        }

    }

    protected void aF() {
        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.j() != EnumRenderType.INVISIBLE) {
            Vec3D vec3d = this.getMot();

            this.world.addParticle(new ParticleParamBlock(Particles.BLOCK, iblockdata), this.locX() + ((double) this.random.nextFloat() - 0.5D) * (double) this.size.width, this.locY() + 0.1D, this.locZ() + ((double) this.random.nextFloat() - 0.5D) * (double) this.size.width, vec3d.x * -4.0D, 1.5D, vec3d.z * -4.0D);
        }

    }

    public boolean a(Tag<FluidType> tag) {
        return this.a(tag, false);
    }

    public boolean a(Tag<FluidType> tag, boolean flag) {
        if (this.getVehicle() instanceof EntityBoat) {
            return false;
        } else {
            double d0 = this.getHeadY();
            BlockPosition blockposition = new BlockPosition(this.locX(), d0, this.locZ());

            if (flag && !this.world.isChunkLoaded(blockposition.getX() >> 4, blockposition.getZ() >> 4)) {
                return false;
            } else {
                Fluid fluid = this.world.getFluid(blockposition);

                return fluid.a(tag) && d0 < (double) ((float) blockposition.getY() + fluid.getHeight(this.world, blockposition) + 0.11111111F);
            }
        }
    }

    public void aG() {
        this.inLava = true;
    }

    public boolean aH() {
        return this.inLava;
    }

    public void a(float f, Vec3D vec3d) {
        Vec3D vec3d1 = a(vec3d, f, this.yaw);

        this.setMot(this.getMot().e(vec3d1));
    }

    private static Vec3D a(Vec3D vec3d, float f, float f1) {
        double d0 = vec3d.g();

        if (d0 < 1.0E-7D) {
            return Vec3D.a;
        } else {
            Vec3D vec3d1 = (d0 > 1.0D ? vec3d.d() : vec3d).a((double) f);
            float f2 = MathHelper.sin(f1 * 0.017453292F);
            float f3 = MathHelper.cos(f1 * 0.017453292F);

            return new Vec3D(vec3d1.x * (double) f3 - vec3d1.z * (double) f2, vec3d1.y, vec3d1.z * (double) f3 + vec3d1.x * (double) f2);
        }
    }

    public float aI() {
        BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition(this.locX(), 0.0D, this.locZ());

        if (this.world.isLoaded(blockposition_mutableblockposition)) {
            blockposition_mutableblockposition.p(MathHelper.floor(this.getHeadY()));
            return this.world.w(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) {
        double d3 = MathHelper.a(d0, -3.0E7D, 3.0E7D);
        double d4 = MathHelper.a(d2, -3.0E7D, 3.0E7D);

        this.lastX = d3;
        this.lastY = d1;
        this.lastZ = d4;
        this.setPosition(d3, d1, d4);
        this.yaw = f % 360.0F;
        this.pitch = MathHelper.a(f1, -90.0F, 90.0F) % 360.0F;
        this.lastYaw = this.yaw;
        this.lastPitch = this.pitch;
        world.getChunkAt((int) Math.floor(this.locX) >> 4, (int) Math.floor(this.locZ) >> 4); // CraftBukkit
    }

    public void 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.f(d0, d1, d2);
        this.yaw = f;
        this.pitch = f1;
        this.Z();
    }

    public void f(double d0, double d1, double d2) {
        this.setPositionRaw(d0, d1, d2);
        this.lastX = d0;
        this.lastY = d1;
        this.lastZ = d2;
        this.E = d0;
        this.F = d1;
        this.G = d2;
    }

    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 g(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 h(Entity entity) {
        return this.c(entity.getPositionVector());
    }

    public double c(Vec3D vec3d) {
        double d0 = this.locX() - vec3d.x;
        double d1 = this.locY() - vec3d.y;
        double d2 = this.locZ() - vec3d.z;

        return d0 * d0 + d1 * d1 + d2 * d2;
    }

    public void pickup(EntityHuman entityhuman) {}

    public void collide(Entity entity) {
        if (!this.isSameVehicle(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.J);
                    d1 *= (double) (1.0F - this.J);
                    if (!this.isVehicle()) {
                        this.h(-d0, 0.0D, -d1);
                    }

                    if (!entity.isVehicle()) {
                        entity.h(d0, 0.0D, d1);
                    }
                }

            }
        }
    }

    public void h(double d0, double d1, double d2) {
        this.setMot(this.getMot().add(d0, d1, d2));
        this.impulse = true;
    }

    protected void velocityChanged() {
        this.velocityChanged = true;
    }

    public boolean damageEntity(DamageSource damagesource, float f) {
        if (this.isInvulnerable(damagesource)) {
            return false;
        } else {
            this.velocityChanged();
            return false;
        }
    }

    public final Vec3D f(float f) {
        return this.c(this.g(f), this.h(f));
    }

    public float g(float f) {
        return f == 1.0F ? this.pitch : MathHelper.g(f, this.lastPitch, this.pitch);
    }

    public float h(float f) {
        return f == 1.0F ? this.yaw : MathHelper.g(f, this.lastYaw, this.yaw);
    }

    protected final Vec3D c(float f, float f1) {
        float f2 = f * 0.017453292F;
        float f3 = -f1 * 0.017453292F;
        float f4 = MathHelper.cos(f3);
        float f5 = MathHelper.sin(f3);
        float f6 = MathHelper.cos(f2);
        float f7 = MathHelper.sin(f2);

        return new Vec3D((double) (f5 * f6), (double) (-f7), (double) (f4 * f6));
    }

    public final Vec3D i(float f) {
        return this.d(this.g(f), this.h(f));
    }

    protected final Vec3D d(float f, float f1) {
        return this.c(f - 90.0F, f1);
    }

    public final Vec3D j(float f) {
        if (f == 1.0F) {
            return new Vec3D(this.locX(), this.getHeadY(), this.locZ());
        } else {
            double d0 = MathHelper.d((double) f, this.lastX, this.locX());
            double d1 = MathHelper.d((double) f, this.lastY, this.locY()) + (double) this.getHeadHeight();
            double d2 = MathHelper.d((double) f, this.lastZ, this.locZ());

            return new Vec3D(d0, d1, d2);
        }
    }

    public MovingObjectPosition a(double d0, float f, boolean flag) {
        Vec3D vec3d = this.j(f);
        Vec3D vec3d1 = this.f(f);
        Vec3D vec3d2 = vec3d.add(vec3d1.x * d0, vec3d1.y * d0, vec3d1.z * d0);

        return this.world.rayTrace(new RayTrace(vec3d, vec3d2, RayTrace.BlockCollisionOption.OUTLINE, flag ? RayTrace.FluidCollisionOption.ANY : RayTrace.FluidCollisionOption.NONE, this));
    }

    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.persist && !this.dead && s != null) { // CraftBukkit - persist flag
            nbttagcompound.setString("id", s);
            this.save(nbttagcompound);
            return true;
        } else {
            return false;
        }
    }

    public boolean d(NBTTagCompound nbttagcompound) {
        return this.isPassenger() ? false : this.c(nbttagcompound);
    }

    public NBTTagCompound save(NBTTagCompound nbttagcompound) {
        try {
            nbttagcompound.set("Pos", this.a(this.locX(), this.locY(), this.locZ()));
            Vec3D vec3d = this.getMot();

            nbttagcompound.set("Motion", this.a(vec3d.x, vec3d.y, vec3d.z));

            // CraftBukkit start - Checking for NaN pitch/yaw and resetting to zero
            // TODO: make sure this is the best way to address this.
            if (Float.isNaN(this.yaw)) {
                this.yaw = 0;
            }

            if (Float.isNaN(this.pitch)) {
                this.pitch = 0;
            }
            // CraftBukkit end

            nbttagcompound.set("Rotation", this.a(this.yaw, this.pitch));
            nbttagcompound.setFloat("FallDistance", this.fallDistance);
            nbttagcompound.setShort("Fire", (short) this.fireTicks);
            nbttagcompound.setShort("Air", (short) this.getAirTicks());
            nbttagcompound.setBoolean("OnGround", this.onGround);
            nbttagcompound.setInt("Dimension", this.dimension.getType().getDimensionID()); // CraftBukkit - preserve Vanilla compat
            nbttagcompound.setBoolean("Invulnerable", this.invulnerable);
            nbttagcompound.setInt("PortalCooldown", this.portalCooldown);
            nbttagcompound.a("UUID", this.getUniqueID());
            // CraftBukkit start
            // PAIL: Check above UUID reads 1.8 properly, ie: UUIDMost / UUIDLeast
            nbttagcompound.setLong("WorldUUIDLeast", ((WorldServer) this.world).getDataManager().getUUID().getLeastSignificantBits());
            nbttagcompound.setLong("WorldUUIDMost", ((WorldServer) this.world).getDataManager().getUUID().getMostSignificantBits());
            nbttagcompound.setInt("Bukkit.updateLevel", CURRENT_LEVEL);
            // CraftBukkit end
            IChatBaseComponent ichatbasecomponent = this.getCustomName();

            if (ichatbasecomponent != null) {
                nbttagcompound.setString("CustomName", IChatBaseComponent.ChatSerializer.a(ichatbasecomponent));
            }

            if (this.getCustomNameVisible()) {
                nbttagcompound.setBoolean("CustomNameVisible", this.getCustomNameVisible());
            }

            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.aE.isEmpty()) {
                nbttaglist = new NBTTagList();
                iterator = this.aE.iterator();

                while (iterator.hasNext()) {
                    String s = (String) iterator.next();

                    nbttaglist.add(NBTTagString.a(s));
                }

                nbttagcompound.set("Tags", nbttaglist);
            }

            this.b(nbttagcompound);
            if (this.isVehicle()) {
                nbttaglist = new NBTTagList();
                iterator = this.getPassengers().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);
                }
            }

            // CraftBukkit start - stores eventually existing bukkit values
            if (this.bukkitEntity != null) {
                this.bukkitEntity.storeBukkitValues(nbttagcompound);
            }
            // CraftBukkit end
            return nbttagcompound;
        } catch (Throwable throwable) {
            CrashReport crashreport = CrashReport.a(throwable, "Saving entity NBT");
            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);
            double d0 = nbttaglist1.h(0);
            double d1 = nbttaglist1.h(1);
            double d2 = nbttaglist1.h(2);

            this.setMot(Math.abs(d0) > 10.0D ? 0.0D : d0, Math.abs(d1) > 10.0D ? 0.0D : d1, Math.abs(d2) > 10.0D ? 0.0D : d2);
            this.f(nbttaglist.h(0), nbttaglist.h(1), nbttaglist.h(2));
            this.yaw = nbttaglist2.i(0);
            this.pitch = nbttaglist2.i(1);
            this.lastYaw = this.yaw;
            this.lastPitch = this.pitch;
            this.setHeadRotation(this.yaw);
            this.l(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 = DimensionManager.a(nbttagcompound.getInt("Dimension")); // CraftBukkit - redundant
            }

            this.invulnerable = nbttagcompound.getBoolean("Invulnerable");
            this.portalCooldown = nbttagcompound.getInt("PortalCooldown");
            if (nbttagcompound.b("UUID")) {
                this.uniqueID = nbttagcompound.a("UUID");
                this.am = this.uniqueID.toString();
            }

            if (Double.isFinite(this.locX()) && Double.isFinite(this.locY()) && Double.isFinite(this.locZ())) {
                if (Double.isFinite((double) this.yaw) && Double.isFinite((double) this.pitch)) {
                    this.Z();
                    this.setYawPitch(this.yaw, this.pitch);
                    if (nbttagcompound.hasKeyOfType("CustomName", 8)) {
                        this.setCustomName(IChatBaseComponent.ChatSerializer.a(nbttagcompound.getString("CustomName")));
                    }

                    this.setCustomNameVisible(nbttagcompound.getBoolean("CustomNameVisible"));
                    this.setSilent(nbttagcompound.getBoolean("Silent"));
                    this.setNoGravity(nbttagcompound.getBoolean("NoGravity"));
                    this.h(nbttagcompound.getBoolean("Glowing"));
                    if (nbttagcompound.hasKeyOfType("Tags", 9)) {
                        this.aE.clear();
                        NBTTagList nbttaglist3 = nbttagcompound.getList("Tags", 8);
                        int i = Math.min(nbttaglist3.size(), 1024);

                        for (int j = 0; j < i; ++j) {
                            this.aE.add(nbttaglist3.getString(j));
                        }
                    }

                    this.a(nbttagcompound);
                    if (this.aM()) {
                        this.Z();
                    }

                } else {
                    throw new IllegalStateException("Entity has invalid rotation");
                }
            } else {
                throw new IllegalStateException("Entity has invalid position");
            }

            // CraftBukkit start
            if (this instanceof EntityLiving) {
                EntityLiving entity = (EntityLiving) this;

                // Reset the persistence for tamed animals
                if (entity instanceof EntityTameableAnimal && !isLevelAtLeast(nbttagcompound, 2) && !nbttagcompound.getBoolean("PersistenceRequired")) {
                    EntityInsentient entityinsentient = (EntityInsentient) entity;
                    entityinsentient.persistent = !entityinsentient.isTypeNotPersistent(0);
                }
            }
            // CraftBukkit end

            // CraftBukkit start - Reset world
            if (this instanceof EntityPlayer) {
                Server server = Bukkit.getServer();
                org.bukkit.World bworld = null;

                // TODO: Remove World related checks, replaced with WorldUID
                String worldName = nbttagcompound.getString("world");

                if (nbttagcompound.hasKey("WorldUUIDMost") && nbttagcompound.hasKey("WorldUUIDLeast")) {
                    UUID uid = new UUID(nbttagcompound.getLong("WorldUUIDMost"), nbttagcompound.getLong("WorldUUIDLeast"));
                    bworld = server.getWorld(uid);
                } else {
                    bworld = server.getWorld(worldName);
                }

                if (bworld == null) {
                    bworld = ((org.bukkit.craftbukkit.CraftServer) server).getServer().getWorldServer(DimensionManager.OVERWORLD).getWorld();
                }

                spawnIn(bworld == null ? null : ((CraftWorld) bworld).getHandle());
            }
            this.getBukkitEntity().readBukkitValues(nbttagcompound);
            // CraftBukkit end

        } catch (Throwable throwable) {
            CrashReport crashreport = CrashReport.a(throwable, "Loading entity NBT");
            CrashReportSystemDetails crashreportsystemdetails = crashreport.a("Entity being loaded");

            this.appendEntityCrashDetails(crashreportsystemdetails);
            throw new ReportedException(crashreport);
        }
    }

    protected boolean aM() {
        return true;
    }

    @Nullable
    public final String getSaveID() {
        EntityTypes<?> entitytypes = this.getEntityType();
        MinecraftKey minecraftkey = EntityTypes.getName(entitytypes);

        return entitytypes.a() && minecraftkey != null ? minecraftkey.toString() : null;
    }

    protected abstract void a(NBTTagCompound nbttagcompound);

    protected abstract void b(NBTTagCompound nbttagcompound);

    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(NBTTagDouble.a(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(NBTTagFloat.a(f));
        }

        return nbttaglist;
    }

    @Nullable
    public EntityItem a(IMaterial imaterial) {
        return this.a(imaterial, 0);
    }

    @Nullable
    public EntityItem a(IMaterial imaterial, int i) {
        return this.a(new ItemStack(imaterial), (float) i);
    }

    @Nullable
    public EntityItem a(ItemStack itemstack) {
        return this.a(itemstack, 0.0F);
    }

    @Nullable
    public EntityItem a(ItemStack itemstack, float f) {
        if (itemstack.isEmpty()) {
            return null;
        } else if (this.world.isClientSide) {
            return null;
        } else {
            // CraftBukkit start - Capture drops for death event
            if (this instanceof EntityLiving && !((EntityLiving) this).forceDrops) {
                ((EntityLiving) this).drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(itemstack));
                return null;
            }
            // CraftBukkit end
            EntityItem entityitem = new EntityItem(this.world, this.locX(), this.locY() + (double) f, this.locZ(), itemstack);

            entityitem.defaultPickupDelay();
            // CraftBukkit start
            EntityDropItemEvent event = new EntityDropItemEvent(this.getBukkitEntity(), (org.bukkit.entity.Item) entityitem.getBukkitEntity());
            Bukkit.getPluginManager().callEvent(event);
            if (event.isCancelled()) {
                return null;
            }
            // CraftBukkit end
            this.world.addEntity(entityitem);
            return entityitem;
        }
    }

    public boolean isAlive() {
        return !this.dead;
    }

    public boolean inBlock() {
        if (this.noclip) {
            return false;
        } else {
            BlockPosition.PooledBlockPosition blockposition_pooledblockposition = BlockPosition.PooledBlockPosition.r();
            Throwable throwable = null;

            try {
                for (int i = 0; i < 8; ++i) {
                    int j = MathHelper.floor(this.locY() + (double) (((float) ((i >> 0) % 2) - 0.5F) * 0.1F) + (double) this.headHeight);
                    int k = MathHelper.floor(this.locX() + (double) (((float) ((i >> 1) % 2) - 0.5F) * this.size.width * 0.8F));
                    int l = MathHelper.floor(this.locZ() + (double) (((float) ((i >> 2) % 2) - 0.5F) * this.size.width * 0.8F));

                    if (blockposition_pooledblockposition.getX() != k || blockposition_pooledblockposition.getY() != j || blockposition_pooledblockposition.getZ() != l) {
                        blockposition_pooledblockposition.d(k, j, l);
                        if (this.world.getType(blockposition_pooledblockposition).m(this.world, blockposition_pooledblockposition)) {
                            boolean flag = true;

                            return flag;
                        }
                    }
                }

                return false;
            } catch (Throwable throwable1) {
                throwable = throwable1;
                throw throwable1;
            } finally {
                if (blockposition_pooledblockposition != null) {
                    if (throwable != null) {
                        try {
                            blockposition_pooledblockposition.close();
                        } catch (Throwable throwable2) {
                            throwable.addSuppressed(throwable2);
                        }
                    } else {
                        blockposition_pooledblockposition.close();
                    }
                }

            }
        }
    }

    public boolean b(EntityHuman entityhuman, EnumHand enumhand) {
        return false;
    }

    @Nullable
    public AxisAlignedBB j(Entity entity) {
        return null;
    }

    public void passengerTick() {
        this.setMot(Vec3D.a);
        this.tick();
        if (this.isPassenger()) {
            this.getVehicle().k(this);
        }
    }

    public void k(Entity entity) {
        this.a(entity, Entity::setPosition);
    }

    public void a(Entity entity, Entity.a entity_a) {
        if (this.w(entity)) {
            entity_a.accept(entity, this.locX(), this.locY() + this.aS() + entity.aR(), this.locZ());
        }
    }

    public double aR() {
        return 0.0D;
    }

    public double aS() {
        return (double) this.size.height * 0.75D;
    }

    public boolean startRiding(Entity entity) {
        return this.a(entity, false);
    }

    public boolean a(Entity entity, boolean flag) {
        for (Entity entity1 = entity; entity1.vehicle != null; entity1 = entity1.vehicle) {
            if (entity1.vehicle == this) {
                return false;
            }
        }

        if (!flag && (!this.n(entity) || !entity.q(this))) {
            return false;
        } else {
            if (this.isPassenger()) {
                this.stopRiding();
            }

            this.vehicle = entity;
            if (!this.vehicle.addPassenger(this)) this.vehicle = null; // CraftBukkit
            return true;
        }
    }

    protected boolean n(Entity entity) {
        return this.j <= 0;
    }

    protected boolean c(EntityPose entitypose) {
        return this.world.getCubes(this, this.d(entitypose));
    }

    public void ejectPassengers() {
        for (int i = this.passengers.size() - 1; i >= 0; --i) {
            ((Entity) this.passengers.get(i)).stopRiding();
        }

    }

    public void stopRiding() {
        if (this.vehicle != null) {
            Entity entity = this.vehicle;

            this.vehicle = null;
            if (!entity.removePassenger(this)) this.vehicle = entity; // CraftBukkit
        }

    }

    protected boolean addPassenger(Entity entity) { // CraftBukkit
        if (entity.getVehicle() != this) {
            throw new IllegalStateException("Use x.startRiding(y), not y.addPassenger(x)");
        } else {
            // CraftBukkit start
            com.google.common.base.Preconditions.checkState(!entity.passengers.contains(this), "Circular entity riding! %s %s", this, entity);

            CraftEntity craft = (CraftEntity) entity.getBukkitEntity().getVehicle();
            Entity orig = craft == null ? null : craft.getHandle();
            if (getBukkitEntity() instanceof Vehicle && entity.getBukkitEntity() instanceof LivingEntity) {
                VehicleEnterEvent event = new VehicleEnterEvent(
                        (Vehicle) getBukkitEntity(),
                         entity.getBukkitEntity()
                );
                // Suppress during worldgen
                if (this.valid) {
                    Bukkit.getPluginManager().callEvent(event);
                }
                CraftEntity craftn = (CraftEntity) entity.getBukkitEntity().getVehicle();
                Entity n = craftn == null ? null : craftn.getHandle();
                if (event.isCancelled() || n != orig) {
                    return false;
                }
            }
            // CraftBukkit end
            if (!this.world.isClientSide && entity instanceof EntityHuman && !(this.getRidingPassenger() instanceof EntityHuman)) {
                this.passengers.add(0, entity);
            } else {
                this.passengers.add(entity);
            }

        }
        return true; // CraftBukkit
    }

    protected boolean removePassenger(Entity entity) { // CraftBukkit
        if (entity.getVehicle() == this) {
            throw new IllegalStateException("Use x.stopRiding(y), not y.removePassenger(x)");
        } else {
            // CraftBukkit start
            CraftEntity craft = (CraftEntity) entity.getBukkitEntity().getVehicle();
            Entity orig = craft == null ? null : craft.getHandle();
            if (getBukkitEntity() instanceof Vehicle && entity.getBukkitEntity() instanceof LivingEntity) {
                VehicleExitEvent event = new VehicleExitEvent(
                        (Vehicle) getBukkitEntity(),
                        (LivingEntity) entity.getBukkitEntity()
                );
                Bukkit.getPluginManager().callEvent(event);
                CraftEntity craftn = (CraftEntity) entity.getBukkitEntity().getVehicle();
                Entity n = craftn == null ? null : craftn.getHandle();
                if (event.isCancelled() || n != orig) {
                    return false;
                }
            }
            // CraftBukkit end
            this.passengers.remove(entity);
            entity.j = 60;
        }
        return true; // CraftBukkit
    }

    protected boolean q(Entity entity) {
        return this.getPassengers().size() < 1;
    }

    public float aV() {
        return 0.0F;
    }

    public Vec3D getLookDirection() {
        return this.c(this.pitch, this.yaw);
    }

    public Vec2F aX() {
        return new Vec2F(this.pitch, this.yaw);
    }

    public void c(BlockPosition blockposition) {
        if (this.portalCooldown > 0) {
            this.portalCooldown = this.ba();
        } else {
            if (!this.world.isClientSide && !blockposition.equals(this.ai)) {
                this.ai = new BlockPosition(blockposition);
                BlockPortal blockportal = (BlockPortal) Blocks.NETHER_PORTAL;
                ShapeDetector.ShapeDetectorCollection shapedetector_shapedetectorcollection = BlockPortal.c((GeneratorAccess) this.world, this.ai);
                double d0 = shapedetector_shapedetectorcollection.getFacing().m() == EnumDirection.EnumAxis.X ? (double) shapedetector_shapedetectorcollection.a().getZ() : (double) shapedetector_shapedetectorcollection.a().getX();
                double d1 = Math.abs(MathHelper.c((shapedetector_shapedetectorcollection.getFacing().m() == EnumDirection.EnumAxis.X ? this.locZ() : this.locX()) - (double) (shapedetector_shapedetectorcollection.getFacing().f().d() == 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.aj = new Vec3D(d1, d2, 0.0D);
                this.ak = shapedetector_shapedetectorcollection.getFacing();
            }

            this.af = true;
        }
    }

    protected void doPortalTick() {
        if (this.world instanceof WorldServer) {
            int i = this.ab();

            if (this.af) {
                if ((true || this.world.getMinecraftServer().getAllowNether()) && !this.isPassenger() && this.ag++ >= i) { // CraftBukkit
                    this.world.getMethodProfiler().enter("portal");
                    this.ag = i;
                    this.portalCooldown = this.ba();
                    // CraftBukkit start
                    if (this instanceof EntityPlayer) {
                        ((EntityPlayer) this).a(this.world.worldProvider.getDimensionManager().getType() == DimensionManager.NETHER ? DimensionManager.OVERWORLD : DimensionManager.NETHER, PlayerTeleportEvent.TeleportCause.NETHER_PORTAL);
                    } else {
                        this.a(this.world.worldProvider.getDimensionManager().getType() == DimensionManager.NETHER ? DimensionManager.OVERWORLD : DimensionManager.NETHER);
                    }
                    // CraftBukkit end
                    this.world.getMethodProfiler().exit();
                }

                this.af = false;
            } else {
                if (this.ag > 0) {
                    this.ag -= 4;
                }

                if (this.ag < 0) {
                    this.ag = 0;
                }
            }

            this.E();
        }
    }

    public int ba() {
        return 300;
    }

    public Iterable<ItemStack> bc() {
        return Entity.c;
    }

    public Iterable<ItemStack> getArmorItems() {
        return Entity.c;
    }

    public Iterable<ItemStack> be() {
        return Iterables.concat(this.bc(), this.getArmorItems());
    }

    public void setEquipment(EnumItemSlot enumitemslot, ItemStack itemstack) {}

    public boolean isBurning() {
        boolean flag = this.world != null && this.world.isClientSide;

        return !this.isFireProof() && (this.fireTicks > 0 || flag && this.getFlag(0));
    }

    public boolean isPassenger() {
        return this.getVehicle() != null;
    }

    public boolean isVehicle() {
        return !this.getPassengers().isEmpty();
    }

    public boolean bi() {
        return true;
    }

    public void setSneaking(boolean flag) {
        this.setFlag(1, flag);
    }

    public boolean isSneaking() {
        return this.getFlag(1);
    }

    public boolean bk() {
        return this.isSneaking();
    }

    public boolean bl() {
        return this.isSneaking();
    }

    public boolean bm() {
        return this.isSneaking();
    }

    public boolean bn() {
        return this.isSneaking();
    }

    public boolean bo() {
        return this.getPose() == EntityPose.CROUCHING;
    }

    public boolean isSprinting() {
        return this.getFlag(3);
    }

    public void setSprinting(boolean flag) {
        this.setFlag(3, flag);
    }

    public boolean isSwimming() {
        return this.getFlag(4);
    }

    public boolean br() {
        return this.getPose() == EntityPose.SWIMMING;
    }

    public void setSwimming(boolean flag) {
        // CraftBukkit start
        if (this.isSwimming() != flag && this instanceof EntityLiving) {
            if (CraftEventFactory.callToggleSwimEvent((EntityLiving) this, flag).isCancelled()) {
                return;
            }
        }
        // CraftBukkit end
        this.setFlag(4, flag);
    }

    public boolean bt() {
        return this.glowing || this.world.isClientSide && this.getFlag(6);
    }

    public void h(boolean flag) {
        this.glowing = flag;
        if (!this.world.isClientSide) {
            this.setFlag(6, this.glowing);
        }

    }

    public boolean isInvisible() {
        return this.getFlag(5);
    }

    @Nullable
    public ScoreboardTeamBase getScoreboardTeam() {
        return this.world.getScoreboard().getPlayerTeam(this.getName());
    }

    public boolean r(Entity entity) {
        return this.a(entity.getScoreboardTeam());
    }

    public boolean a(ScoreboardTeamBase scoreboardteambase) {
        return this.getScoreboardTeam() != null ? this.getScoreboardTeam().isAlly(scoreboardteambase) : false;
    }

    public void setInvisible(boolean flag) {
        this.setFlag(5, flag);
    }

    public boolean getFlag(int i) {
        return ((Byte) this.datawatcher.get(Entity.T) & 1 << i) != 0;
    }

    public void setFlag(int i, boolean flag) {
        byte b0 = (Byte) this.datawatcher.get(Entity.T);

        if (flag) {
            this.datawatcher.set(Entity.T, (byte) (b0 | 1 << i));
        } else {
            this.datawatcher.set(Entity.T, (byte) (b0 & ~(1 << i)));
        }

    }

    public int bw() {
        return 300;
    }

    public int getAirTicks() {
        return (Integer) this.datawatcher.get(Entity.AIR_TICKS);
    }

    public void setAirTicks(int i) {
        // CraftBukkit start
        EntityAirChangeEvent event = new EntityAirChangeEvent(this.getBukkitEntity(), i);
        // Suppress during worldgen
        if (this.valid) {
            event.getEntity().getServer().getPluginManager().callEvent(event);
        }
        if (event.isCancelled()) {
            return;
        }
        this.datawatcher.set(Entity.AIR_TICKS, event.getAmount());
        // CraftBukkit end
    }

    public void onLightningStrike(EntityLightning entitylightning) {
        ++this.fireTicks;
        // CraftBukkit start
        final org.bukkit.entity.Entity thisBukkitEntity = this.getBukkitEntity();
        final org.bukkit.entity.Entity stormBukkitEntity = entitylightning.getBukkitEntity();
        final PluginManager pluginManager = Bukkit.getPluginManager();
        // CraftBukkit end

        if (this.fireTicks == 0) {
            // CraftBukkit start - Call a combust event when lightning strikes
            EntityCombustByEntityEvent entityCombustEvent = new EntityCombustByEntityEvent(stormBukkitEntity, thisBukkitEntity, 8);
            pluginManager.callEvent(entityCombustEvent);
            if (!entityCombustEvent.isCancelled()) {
                this.setOnFire(entityCombustEvent.getDuration(), false);
            }
            // CraftBukkit end
        }

        // CraftBukkit start
        if (thisBukkitEntity instanceof Hanging) {
            HangingBreakByEntityEvent hangingEvent = new HangingBreakByEntityEvent((Hanging) thisBukkitEntity, stormBukkitEntity);
            pluginManager.callEvent(hangingEvent);

            if (hangingEvent.isCancelled()) {
                return;
            }
        }

        if (this.isFireProof()) {
            return;
        }
        CraftEventFactory.entityDamage = entitylightning;
        if (!this.damageEntity(DamageSource.LIGHTNING, 5.0F)) {
            CraftEventFactory.entityDamage = null;
            return;
        }
        // CraftBukkit end
    }

    public void j(boolean flag) {
        Vec3D vec3d = this.getMot();
        double d0;

        if (flag) {
            d0 = Math.max(-0.9D, vec3d.y - 0.03D);
        } else {
            d0 = Math.min(1.8D, vec3d.y + 0.1D);
        }

        this.setMot(vec3d.x, d0, vec3d.z);
    }

    public void k(boolean flag) {
        Vec3D vec3d = this.getMot();
        double d0;

        if (flag) {
            d0 = Math.max(-0.3D, vec3d.y - 0.03D);
        } else {
            d0 = Math.min(0.7D, vec3d.y + 0.06D);
        }

        this.setMot(vec3d.x, d0, vec3d.z);
        this.fallDistance = 0.0F;
    }

    public void b(EntityLiving entityliving) {}

    protected void k(double d0, double d1, double d2) {
        BlockPosition blockposition = new BlockPosition(d0, d1, d2);
        Vec3D vec3d = new Vec3D(d0 - (double) blockposition.getX(), d1 - (double) blockposition.getY(), d2 - (double) blockposition.getZ());
        BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();
        EnumDirection enumdirection = EnumDirection.UP;
        double d3 = Double.MAX_VALUE;
        EnumDirection[] aenumdirection = new EnumDirection[]{EnumDirection.NORTH, EnumDirection.SOUTH, EnumDirection.WEST, EnumDirection.EAST, EnumDirection.UP};
        int i = aenumdirection.length;

        for (int j = 0; j < i; ++j) {
            EnumDirection enumdirection1 = aenumdirection[j];

            blockposition_mutableblockposition.g(blockposition).c(enumdirection1);
            if (!this.world.getType(blockposition_mutableblockposition).p(this.world, blockposition_mutableblockposition)) {
                double d4 = vec3d.a(enumdirection1.m());
                double d5 = enumdirection1.d() == EnumDirection.EnumAxisDirection.POSITIVE ? 1.0D - d4 : d4;

                if (d5 < d3) {
                    d3 = d5;
                    enumdirection = enumdirection1;
                }
            }
        }

        float f = this.random.nextFloat() * 0.2F + 0.1F;
        float f1 = (float) enumdirection.d().a();
        Vec3D vec3d1 = this.getMot().a(0.75D);

        if (enumdirection.m() == EnumDirection.EnumAxis.X) {
            this.setMot((double) (f1 * f), vec3d1.y, vec3d1.z);
        } else if (enumdirection.m() == EnumDirection.EnumAxis.Y) {
            this.setMot(vec3d1.x, (double) (f1 * f), vec3d1.z);
        } else if (enumdirection.m() == EnumDirection.EnumAxis.Z) {
            this.setMot(vec3d1.x, vec3d1.y, (double) (f1 * f));
        }

    }

    public void a(IBlockData iblockdata, Vec3D vec3d) {
        this.fallDistance = 0.0F;
        this.y = vec3d;
    }

    private static void c(IChatBaseComponent ichatbasecomponent) {
        ichatbasecomponent.a((chatmodifier) -> {
            chatmodifier.setChatClickable((ChatClickable) null);
        }).getSiblings().forEach(Entity::c);
    }

    @Override
    public IChatBaseComponent getDisplayName() {
        IChatBaseComponent ichatbasecomponent = this.getCustomName();

        if (ichatbasecomponent != null) {
            IChatBaseComponent ichatbasecomponent1 = ichatbasecomponent.h();

            c(ichatbasecomponent1);
            return ichatbasecomponent1;
        } else {
            return this.by();
        }
    }

    protected IChatBaseComponent by() {
        return this.f.g();
    }

    public boolean s(Entity entity) {
        return this == entity;
    }

    public float getHeadRotation() {
        return 0.0F;
    }

    public void setHeadRotation(float f) {}

    public void l(float f) {}

    public boolean bA() {
        return true;
    }

    public boolean t(Entity entity) {
        return false;
    }

    public String toString() {
        return String.format(Locale.ROOT, "%s['%s'/%d, l='%s', x=%.2f, y=%.2f, z=%.2f]", this.getClass().getSimpleName(), this.getDisplayName().getText(), this.id, this.world == null ? "~NULL~" : this.world.getWorldData().getName(), this.locX(), this.locY(), this.locZ());
    }

    public boolean isInvulnerable(DamageSource damagesource) {
        return this.invulnerable && damagesource != DamageSource.OUT_OF_WORLD && !damagesource.v();
    }

    public boolean isInvulnerable() {
        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);
    }

    public void v(Entity entity) {
        NBTTagCompound nbttagcompound = entity.save(new NBTTagCompound());

        nbttagcompound.remove("Dimension");
        this.f(nbttagcompound);
        this.portalCooldown = entity.portalCooldown;
        this.ai = entity.ai;
        this.aj = entity.aj;
        this.ak = entity.ak;
    }

    @Nullable
    public Entity a(DimensionManager dimensionmanager) {
        // CraftBukkit start
        return teleportTo(dimensionmanager, null);
    }

    @Nullable
    public Entity teleportTo(DimensionManager dimensionmanager, BlockPosition location) {
        // CraftBukkit end
        if (!this.world.isClientSide && !this.dead) {
            this.world.getMethodProfiler().enter("changeDimension");
            MinecraftServer minecraftserver = this.getMinecraftServer();
            DimensionManager dimensionmanager1 = this.dimension;
            WorldServer worldserver = minecraftserver.getWorldServer(dimensionmanager1);
            WorldServer worldserver1 = minecraftserver.getWorldServer(dimensionmanager);
            // CraftBukkit start
            if (worldserver1 == null){
                return null;
            }

            // this.dimension = dimensionmanager;
            // this.decouple();
            // CraftBukkit end
            this.world.getMethodProfiler().enter("reposition");
            Vec3D vec3d = this.getMot();
            float f = 0.0F;
            BlockPosition blockposition = location; // CraftBukkit

        if (blockposition == null) { // CraftBukkit
            if (dimensionmanager1.getType() == DimensionManager.THE_END && dimensionmanager == DimensionManager.OVERWORLD) { // CraftBukkit
                // CraftBukkit start
                EntityPortalEvent event = CraftEventFactory.callEntityPortalEvent(this, worldserver1, worldserver1.getHighestBlockYAt(HeightMap.Type.MOTION_BLOCKING_NO_LEAVES, worldserver1.getSpawn()), 0);
                if (event == null) {
                    return null;
                }
                worldserver1 = ((CraftWorld) event.getTo().getWorld()).getHandle();
                blockposition = new BlockPosition(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ());
                // CraftBukkit end
            } else if (dimensionmanager.getType() == DimensionManager.THE_END) { // CraftBukkit
                // CraftBukkit start
                EntityPortalEvent event = CraftEventFactory.callEntityPortalEvent(this, worldserver1, worldserver1.getDimensionSpawn() != null ? worldserver1.getDimensionSpawn() : worldserver1.getSpawn(), 0);
                if (event == null) {
                    return null;
                }
                worldserver1 = ((CraftWorld) event.getTo().getWorld()).getHandle();
                blockposition = new BlockPosition(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ());
                // CraftBukkit end
            } else {
                double d0 = this.locX();
                double d1 = this.locZ();
                double d2 = 8.0D;

                if (dimensionmanager1.getType() == DimensionManager.OVERWORLD && dimensionmanager.getType() == DimensionManager.NETHER) { // CraftBukkit
                    d0 /= 8.0D;
                    d1 /= 8.0D;
                } else if (dimensionmanager1.getType() == DimensionManager.NETHER && dimensionmanager.getType() == DimensionManager.OVERWORLD) { // CraftBukkit
                    d0 *= 8.0D;
                    d1 *= 8.0D;
                }

                double d3 = Math.min(-2.9999872E7D, worldserver1.getWorldBorder().c() + 16.0D);
                double d4 = Math.min(-2.9999872E7D, worldserver1.getWorldBorder().d() + 16.0D);
                double d5 = Math.min(2.9999872E7D, worldserver1.getWorldBorder().e() - 16.0D);
                double d6 = Math.min(2.9999872E7D, worldserver1.getWorldBorder().f() - 16.0D);

                d0 = MathHelper.a(d0, d3, d5);
                d1 = MathHelper.a(d1, d4, d6);
                Vec3D vec3d1 = this.getPortalOffset();

                blockposition = new BlockPosition(d0, this.locY(), d1);
                // CraftBukkit start
                EntityPortalEvent event = CraftEventFactory.callEntityPortalEvent(this, worldserver1, blockposition, 128);
                if (event == null) {
                    return null;
                }
                worldserver1 = ((CraftWorld) event.getTo().getWorld()).getHandle();
                blockposition = new BlockPosition(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ());
                int searchRadius = event.getSearchRadius();
                // CraftBukkit end
                ShapeDetector.Shape shapedetector_shape = worldserver1.getTravelAgent().findPortal(blockposition, vec3d, this.getPortalDirection(), vec3d1.x, vec3d1.y, this instanceof EntityHuman, searchRadius); // CraftBukkit - search radius

                if (shapedetector_shape == null) {
                    return null;
                }

                blockposition = new BlockPosition(shapedetector_shape.position);
                vec3d = shapedetector_shape.velocity;
                f = (float) shapedetector_shape.yaw;
            }
        } // CraftBukkit

            // CraftBukkit start

            this.dimension = dimensionmanager;
            this.decouple();
            // CraftBukkit end

            this.world.getMethodProfiler().exitEnter("reloading");
            Entity entity = this.getEntityType().a((World) worldserver1);

            if (entity != null) {
                entity.v(this);
                entity.setPositionRotation(blockposition, entity.yaw + f, entity.pitch);
                entity.setMot(vec3d);
                worldserver1.addEntityTeleport(entity);
                // 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.getMethodProfiler().exit();
            worldserver.resetEmptyTime();
            worldserver1.resetEmptyTime();
            this.world.getMethodProfiler().exit();
            return entity;
        } else {
            return null;
        }
    }

    public boolean canPortal() {
        return true;
    }

    public float a(Explosion explosion, IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, Fluid fluid, float f) {
        return f;
    }

    public boolean a(Explosion explosion, IBlockAccess iblockaccess, BlockPosition blockposition, IBlockData iblockdata, float f) {
        return true;
    }

    public int bD() {
        return 3;
    }

    public Vec3D getPortalOffset() {
        return this.aj;
    }

    public EnumDirection getPortalDirection() {
        return this.ak;
    }

    public boolean isIgnoreBlockTrigger() {
        return false;
    }

    public void appendEntityCrashDetails(CrashReportSystemDetails crashreportsystemdetails) {
        crashreportsystemdetails.a("Entity Type", () -> {
            return EntityTypes.getName(this.getEntityType()) + " (" + this.getClass().getCanonicalName() + ")";
        });
        crashreportsystemdetails.a("Entity ID", (Object) this.id);
        crashreportsystemdetails.a("Entity Name", () -> {
            return this.getDisplayName().getString();
        });
        crashreportsystemdetails.a("Entity's Exact location", (Object) String.format(Locale.ROOT, "%.2f, %.2f, %.2f", this.locX(), this.locY(), this.locZ()));
        crashreportsystemdetails.a("Entity's Block location", (Object) CrashReportSystemDetails.a(MathHelper.floor(this.locX()), MathHelper.floor(this.locY()), MathHelper.floor(this.locZ())));
        Vec3D vec3d = this.getMot();

        crashreportsystemdetails.a("Entity's Momentum", (Object) String.format(Locale.ROOT, "%.2f, %.2f, %.2f", vec3d.x, vec3d.y, vec3d.z));
        crashreportsystemdetails.a("Entity's Passengers", () -> {
            return this.getPassengers().toString();
        });
        crashreportsystemdetails.a("Entity's Vehicle", () -> {
            return this.getVehicle().toString();
        });
    }

    public void a(UUID uuid) {
        this.uniqueID = uuid;
        this.am = this.uniqueID.toString();
    }

    public UUID getUniqueID() {
        return this.uniqueID;
    }

    public String getUniqueIDString() {
        return this.am;
    }

    public String getName() {
        return this.am;
    }

    public boolean bM() {
        return true;
    }

    @Override
    public IChatBaseComponent getScoreboardDisplayName() {
        return ScoreboardTeam.a(this.getScoreboardTeam(), this.getDisplayName()).a((chatmodifier) -> {
            chatmodifier.setChatHoverable(this.bS()).setInsertion(this.getUniqueIDString());
        });
    }

    public void setCustomName(@Nullable IChatBaseComponent ichatbasecomponent) {
        this.datawatcher.set(Entity.az, Optional.ofNullable(ichatbasecomponent));
    }

    @Nullable
    @Override
    public IChatBaseComponent getCustomName() {
        return (IChatBaseComponent) ((Optional) this.datawatcher.get(Entity.az)).orElse((Object) null);
    }

    @Override
    public boolean hasCustomName() {
        return ((Optional) this.datawatcher.get(Entity.az)).isPresent();
    }

    public void setCustomNameVisible(boolean flag) {
        this.datawatcher.set(Entity.aA, flag);
    }

    public boolean getCustomNameVisible() {
        return (Boolean) this.datawatcher.get(Entity.aA);
    }

    public final void enderTeleportAndLoad(double d0, double d1, double d2) {
        if (this.world instanceof WorldServer) {
            ChunkCoordIntPair chunkcoordintpair = new ChunkCoordIntPair(new BlockPosition(d0, d1, d2));

            ((WorldServer) this.world).getChunkProvider().addTicket(TicketType.POST_TELEPORT, chunkcoordintpair, 0, this.getId());
            this.world.getChunkAt(chunkcoordintpair.x, chunkcoordintpair.z);
            this.enderTeleportTo(d0, d1, d2);
        }
    }

    public void enderTeleportTo(double d0, double d1, double d2) {
        if (this.world instanceof WorldServer) {
            WorldServer worldserver = (WorldServer) this.world;

            this.setPositionRotation(d0, d1, d2, this.yaw, this.pitch);
            this.cg().forEach((entity) -> {
                worldserver.chunkCheck(entity);
                entity.aF = true;
                entity.a(Entity::teleportAndSync);
            });
        }
    }

    public void a(DataWatcherObject<?> datawatcherobject) {
        if (Entity.POSE.equals(datawatcherobject)) {
            this.updateSize();
        }

    }

    public void updateSize() {
        EntitySize entitysize = this.size;
        EntityPose entitypose = this.getPose();
        EntitySize entitysize1 = this.a(entitypose);

        this.size = entitysize1;
        this.headHeight = this.getHeadHeight(entitypose, entitysize1);
        if (entitysize1.width < entitysize.width) {
            double d0 = (double) entitysize1.width / 2.0D;

            this.a(new AxisAlignedBB(this.locX() - d0, this.locY(), this.locZ() - d0, this.locX() + d0, this.locY() + (double) entitysize1.height, this.locZ() + d0));
        } else {
            AxisAlignedBB axisalignedbb = this.getBoundingBox();

            this.a(new AxisAlignedBB(axisalignedbb.minX, axisalignedbb.minY, axisalignedbb.minZ, axisalignedbb.minX + (double) entitysize1.width, axisalignedbb.minY + (double) entitysize1.height, axisalignedbb.minZ + (double) entitysize1.width));
            if (entitysize1.width > entitysize.width && !this.justCreated && !this.world.isClientSide) {
                float f = entitysize.width - entitysize1.width;

                this.move(EnumMoveType.SELF, new Vec3D((double) f, 0.0D, (double) f));
            }

        }
    }

    public EnumDirection getDirection() {
        return EnumDirection.fromAngle((double) this.yaw);
    }

    public EnumDirection getAdjustedDirection() {
        return this.getDirection();
    }

    protected ChatHoverable bS() {
        NBTTagCompound nbttagcompound = new NBTTagCompound();
        MinecraftKey minecraftkey = EntityTypes.getName(this.getEntityType());

        nbttagcompound.setString("id", this.getUniqueIDString());
        if (minecraftkey != null) {
            nbttagcompound.setString("type", minecraftkey.toString());
        }

        nbttagcompound.setString("name", IChatBaseComponent.ChatSerializer.a(this.getDisplayName()));
        return new ChatHoverable(ChatHoverable.EnumHoverAction.SHOW_ENTITY, new ChatComponentText(nbttagcompound.toString()));
    }

    public boolean a(EntityPlayer entityplayer) {
        return true;
    }

    public AxisAlignedBB getBoundingBox() {
        return this.boundingBox;
    }

    protected AxisAlignedBB d(EntityPose entitypose) {
        EntitySize entitysize = this.a(entitypose);
        float f = entitysize.width / 2.0F;
        Vec3D vec3d = new Vec3D(this.locX() - (double) f, this.locY(), this.locZ() - (double) f);
        Vec3D vec3d1 = new Vec3D(this.locX() + (double) f, this.locY() + (double) entitysize.height, this.locZ() + (double) f);

        return new AxisAlignedBB(vec3d, vec3d1);
    }

    public void a(AxisAlignedBB axisalignedbb) {
        // CraftBukkit start - block invalid bounding boxes
        double minX = axisalignedbb.minX,
                minY = axisalignedbb.minY,
                minZ = axisalignedbb.minZ,
                maxX = axisalignedbb.maxX,
                maxY = axisalignedbb.maxY,
                maxZ = axisalignedbb.maxZ;
        double len = axisalignedbb.maxX - axisalignedbb.minX;
        if (len < 0) maxX = minX;
        if (len > 64) maxX = minX + 64.0;

        len = axisalignedbb.maxY - axisalignedbb.minY;
        if (len < 0) maxY = minY;
        if (len > 64) maxY = minY + 64.0;

        len = axisalignedbb.maxZ - axisalignedbb.minZ;
        if (len < 0) maxZ = minZ;
        if (len > 64) maxZ = minZ + 64.0;
        this.boundingBox = new AxisAlignedBB(minX, minY, minZ, maxX, maxY, maxZ);
        // CraftBukkit end
    }

    protected float getHeadHeight(EntityPose entitypose, EntitySize entitysize) {
        return entitysize.height * 0.85F;
    }

    public final float getHeadHeight() {
        return this.headHeight;
    }

    public boolean a_(int i, ItemStack itemstack) {
        return false;
    }

    @Override
    public void sendMessage(IChatBaseComponent ichatbasecomponent) {}

    public BlockPosition getChunkCoordinates() {
        return new BlockPosition(this);
    }

    public Vec3D bX() {
        return this.getPositionVector();
    }

    public World getWorld() {
        return this.world;
    }

    @Nullable
    public MinecraftServer getMinecraftServer() {
        return this.world.getMinecraftServer();
    }

    public EnumInteractionResult a(EntityHuman entityhuman, Vec3D vec3d, EnumHand enumhand) {
        return EnumInteractionResult.PASS;
    }

    public boolean ca() {
        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 cb() {
        return false;
    }

    public boolean cc() {
        boolean flag = this.aF;

        this.aF = false;
        return flag;
    }

    @Nullable
    public Entity getRidingPassenger() {
        return null;
    }

    public List<Entity> getPassengers() {
        return (List) (this.passengers.isEmpty() ? Collections.emptyList() : Lists.newArrayList(this.passengers));
    }

    public boolean w(Entity entity) {
        Iterator iterator = this.getPassengers().iterator();

        Entity entity1;

        do {
            if (!iterator.hasNext()) {
                return false;
            }

            entity1 = (Entity) iterator.next();
        } while (!entity1.equals(entity));

        return true;
    }

    public boolean a(Class<? extends Entity> oclass) {
        Iterator iterator = this.getPassengers().iterator();

        Entity entity;

        do {
            if (!iterator.hasNext()) {
                return false;
            }

            entity = (Entity) iterator.next();
        } while (!oclass.isAssignableFrom(entity.getClass()));

        return true;
    }

    public Collection<Entity> getAllPassengers() {
        Set<Entity> set = Sets.newHashSet();
        Iterator iterator = this.getPassengers().iterator();

        while (iterator.hasNext()) {
            Entity entity = (Entity) iterator.next();

            set.add(entity);
            entity.a(false, set);
        }

        return set;
    }

    public Stream<Entity> cg() {
        return Stream.concat(Stream.of(this), this.passengers.stream().flatMap(Entity::cg));
    }

    public boolean hasSinglePlayerPassenger() {
        Set<Entity> set = Sets.newHashSet();

        this.a(true, set);
        return set.size() == 1;
    }

    private void a(boolean flag, Set<Entity> set) {
        Entity entity;

        for (Iterator iterator = this.getPassengers().iterator(); iterator.hasNext(); entity.a(flag, set)) {
            entity = (Entity) iterator.next();
            if (!flag || EntityPlayer.class.isAssignableFrom(entity.getClass())) {
                set.add(entity);
            }
        }

    }

    public Entity getRootVehicle() {
        Entity entity;

        for (entity = this; entity.isPassenger(); entity = entity.getVehicle()) {
            ;
        }

        return entity;
    }

    public boolean isSameVehicle(Entity entity) {
        return this.getRootVehicle() == entity.getRootVehicle();
    }

    public boolean y(Entity entity) {
        Iterator iterator = this.getPassengers().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 void a(Entity.a entity_a) {
        Iterator iterator = this.passengers.iterator();

        while (iterator.hasNext()) {
            Entity entity = (Entity) iterator.next();

            this.a(entity, entity_a);
        }

    }

    public boolean cj() {
        Entity entity = this.getRidingPassenger();

        return entity instanceof EntityHuman ? ((EntityHuman) entity).ec() : !this.world.isClientSide;
    }

    @Nullable
    public Entity getVehicle() {
        return this.vehicle;
    }

    public EnumPistonReaction getPushReaction() {
        return EnumPistonReaction.NORMAL;
    }

    public SoundCategory getSoundCategory() {
        return SoundCategory.NEUTRAL;
    }

    public int getMaxFireTicks() {
        return 1;
    }

    public CommandListenerWrapper getCommandListener() {
        return new CommandListenerWrapper(this, this.getPositionVector(), this.aX(), this.world instanceof WorldServer ? (WorldServer) this.world : null, this.y(), this.getDisplayName().getString(), this.getScoreboardDisplayName(), this.world.getMinecraftServer(), this);
    }

    protected int y() {
        return 0;
    }

    public boolean k(int i) {
        return this.y() >= i;
    }

    @Override
    public boolean shouldSendSuccess() {
        return this.world.getGameRules().getBoolean(GameRules.SEND_COMMAND_FEEDBACK);
    }

    @Override
    public boolean shouldSendFailure() {
        return true;
    }

    @Override
    public boolean shouldBroadcastCommands() {
        return true;
    }

    public void a(ArgumentAnchor.Anchor argumentanchor_anchor, Vec3D vec3d) {
        Vec3D vec3d1 = argumentanchor_anchor.a(this);
        double d0 = vec3d.x - vec3d1.x;
        double d1 = vec3d.y - vec3d1.y;
        double d2 = vec3d.z - vec3d1.z;
        double d3 = (double) MathHelper.sqrt(d0 * d0 + d2 * d2);

        this.pitch = MathHelper.g((float) (-(MathHelper.d(d1, d3) * 57.2957763671875D)));
        this.yaw = MathHelper.g((float) (MathHelper.d(d2, d0) * 57.2957763671875D) - 90.0F);
        this.setHeadRotation(this.yaw);
        this.lastPitch = this.pitch;
        this.lastYaw = this.yaw;
    }

    public boolean b(Tag<FluidType> tag) {
        AxisAlignedBB axisalignedbb = this.getBoundingBox().shrink(0.001D);
        int i = MathHelper.floor(axisalignedbb.minX);
        int j = MathHelper.f(axisalignedbb.maxX);
        int k = MathHelper.floor(axisalignedbb.minY);
        int l = MathHelper.f(axisalignedbb.maxY);
        int i1 = MathHelper.floor(axisalignedbb.minZ);
        int j1 = MathHelper.f(axisalignedbb.maxZ);

        if (!this.world.isAreaLoaded(i, k, i1, j, l, j1)) {
            return false;
        } else {
            double d0 = 0.0D;
            boolean flag = this.bM();
            boolean flag1 = false;
            Vec3D vec3d = Vec3D.a;
            int k1 = 0;
            BlockPosition.PooledBlockPosition blockposition_pooledblockposition = BlockPosition.PooledBlockPosition.r();
            Throwable throwable = null;

            try {
                for (int l1 = i; l1 < j; ++l1) {
                    for (int i2 = k; i2 < l; ++i2) {
                        for (int j2 = i1; j2 < j1; ++j2) {
                            blockposition_pooledblockposition.d(l1, i2, j2);
                            Fluid fluid = this.world.getFluid(blockposition_pooledblockposition);

                            if (fluid.a(tag)) {
                                double d1 = (double) ((float) i2 + fluid.getHeight(this.world, blockposition_pooledblockposition));

                                if (d1 >= axisalignedbb.minY) {
                                    flag1 = true;
                                    d0 = Math.max(d1 - axisalignedbb.minY, d0);
                                    if (flag) {
                                        Vec3D vec3d1 = fluid.c(this.world, blockposition_pooledblockposition);

                                        if (d0 < 0.4D) {
                                            vec3d1 = vec3d1.a(d0);
                                        }

                                        vec3d = vec3d.e(vec3d1);
                                        ++k1;
                                    }
                                }
                            }
                        }
                    }
                }
            } catch (Throwable throwable1) {
                throwable = throwable1;
                throw throwable1;
            } finally {
                if (blockposition_pooledblockposition != null) {
                    if (throwable != null) {
                        try {
                            blockposition_pooledblockposition.close();
                        } catch (Throwable throwable2) {
                            throwable.addSuppressed(throwable2);
                        }
                    } else {
                        blockposition_pooledblockposition.close();
                    }
                }

            }

            if (vec3d.f() > 0.0D) {
                if (k1 > 0) {
                    vec3d = vec3d.a(1.0D / (double) k1);
                }

                if (!(this instanceof EntityHuman)) {
                    vec3d = vec3d.d();
                }

                this.setMot(this.getMot().e(vec3d.a(0.014D)));
            }

            this.N = d0;
            return flag1;
        }
    }

    public double co() {
        return this.N;
    }

    public final float getWidth() {
        return this.size.width;
    }

    public final float getHeight() {
        return this.size.height;
    }

    public abstract Packet<?> L();

    public EntitySize a(EntityPose entitypose) {
        return this.f.k();
    }

    public Vec3D getPositionVector() {
        return new Vec3D(this.locX, this.locY, this.locZ);
    }

    public Vec3D getMot() {
        return this.mot;
    }

    public void setMot(Vec3D vec3d) {
        this.mot = vec3d;
    }

    public void setMot(double d0, double d1, double d2) {
        this.setMot(new Vec3D(d0, d1, d2));
    }

    public final double locX() {
        return this.locX;
    }

    public double c(double d0) {
        return this.locX + (double) this.getWidth() * d0;
    }

    public double d(double d0) {
        return this.c((2.0D * this.random.nextDouble() - 1.0D) * d0);
    }

    public final double locY() {
        return this.locY;
    }

    public double e(double d0) {
        return this.locY + (double) this.getHeight() * d0;
    }

    public double cv() {
        return this.e(this.random.nextDouble());
    }

    public double getHeadY() {
        return this.locY + (double) this.headHeight;
    }

    public final double locZ() {
        return this.locZ;
    }

    public double f(double d0) {
        return this.locZ + (double) this.getWidth() * d0;
    }

    public double g(double d0) {
        return this.f((2.0D * this.random.nextDouble() - 1.0D) * d0);
    }

    public void setPositionRaw(double d0, double d1, double d2) {
        this.locX = d0;
        this.locY = d1;
        this.locZ = d2;
    }

    public void checkDespawn() {}

    public void teleportAndSync(double d0, double d1, double d2) {
        this.setPositionRotation(d0, d1, d2, this.yaw, this.pitch);
    }

    @FunctionalInterface
    public interface a {

        void accept(Entity entity, double d0, double d1, double d2);
    }
}