aboutsummaryrefslogtreecommitdiffhomepage
path: root/patch-remap/mache-spigotflower/net/minecraft/world/entity/Entity.java.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patch-remap/mache-spigotflower/net/minecraft/world/entity/Entity.java.patch')
-rw-r--r--patch-remap/mache-spigotflower/net/minecraft/world/entity/Entity.java.patch4014
1 files changed, 4014 insertions, 0 deletions
diff --git a/patch-remap/mache-spigotflower/net/minecraft/world/entity/Entity.java.patch b/patch-remap/mache-spigotflower/net/minecraft/world/entity/Entity.java.patch
new file mode 100644
index 0000000000..3ecafee161
--- /dev/null
+++ b/patch-remap/mache-spigotflower/net/minecraft/world/entity/Entity.java.patch
@@ -0,0 +1,4014 @@
+--- a/net/minecraft/world/entity/Entity.java
++++ b/net/minecraft/world/entity/Entity.java
+@@ -70,9 +70,9 @@
+ import net.minecraft.tags.TagKey;
+ import net.minecraft.util.Mth;
+ import net.minecraft.util.RandomSource;
+-import net.minecraft.world.InteractionHand;
++import net.minecraft.world.EnumHand;
++import net.minecraft.world.INamableTileEntity;
+ import net.minecraft.world.InteractionResult;
+-import net.minecraft.world.Nameable;
+ import net.minecraft.world.damagesource.DamageSource;
+ import net.minecraft.world.damagesource.DamageSources;
+ import net.minecraft.world.entity.item.ItemEntity;
+@@ -87,28 +87,29 @@
+ import net.minecraft.world.level.ClipContext;
+ import net.minecraft.world.level.Explosion;
+ import net.minecraft.world.level.GameRules;
+-import net.minecraft.world.level.ItemLike;
++import net.minecraft.world.level.IMaterial;
+ import net.minecraft.world.level.Level;
+ import net.minecraft.world.level.block.Block;
+ import net.minecraft.world.level.block.Blocks;
++import net.minecraft.world.level.block.EnumRenderType;
+ import net.minecraft.world.level.block.FenceGateBlock;
+ import net.minecraft.world.level.block.HoneyBlock;
+ import net.minecraft.world.level.block.Mirror;
+-import net.minecraft.world.level.block.RenderShape;
+ import net.minecraft.world.level.block.Rotation;
+ import net.minecraft.world.level.block.SoundType;
+-import net.minecraft.world.level.block.state.BlockState;
++import net.minecraft.world.level.block.state.IBlockData;
+ import net.minecraft.world.level.block.state.properties.BlockStateProperties;
+ import net.minecraft.world.level.border.WorldBorder;
+ import net.minecraft.world.level.dimension.DimensionType;
++import net.minecraft.world.level.dimension.LevelStem;
+ import net.minecraft.world.level.entity.EntityAccess;
+ import net.minecraft.world.level.entity.EntityInLevelCallback;
+ import net.minecraft.world.level.gameevent.DynamicGameEventListener;
+ import net.minecraft.world.level.gameevent.GameEvent;
+ import net.minecraft.world.level.levelgen.Heightmap;
++import net.minecraft.world.level.material.EnumPistonReaction;
+ import net.minecraft.world.level.material.Fluid;
+ import net.minecraft.world.level.material.FluidState;
+-import net.minecraft.world.level.material.PushReaction;
+ import net.minecraft.world.level.portal.PortalInfo;
+ import net.minecraft.world.level.portal.PortalShape;
+ import net.minecraft.world.phys.AABB;
+@@ -125,9 +126,63 @@
+ import net.minecraft.world.scores.Team;
+ import org.joml.Vector3f;
+ import org.slf4j.Logger;
++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.craftbukkit.event.CraftPortalEvent;
++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.craftbukkit.util.CraftLocation;
++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 Nameable, EntityAccess, CommandSource, ScoreHolder {
++public abstract class Entity implements INamableTileEntity, EntityAccess, CommandSource, ScoreHolder {
+
++ // CraftBukkit start
++ private static final int CURRENT_LEVEL = 2;
++ static boolean isLevelAtLeast(CompoundTag tag, int level) {
++ return tag.contains("Bukkit.updateLevel") && tag.getInt("Bukkit.updateLevel") >= level;
++ }
++
++ private CraftEntity bukkitEntity;
++
++ public CraftEntity getBukkitEntity() {
++ if (bukkitEntity == null) {
++ bukkitEntity = CraftEntity.getEntity(level.getCraftServer(), this);
++ }
++ return bukkitEntity;
++ }
++
++ @Override
++ public CommandSender getBukkitSender(CommandSourceStack wrapper) {
++ return getBukkitEntity();
++ }
++
++ // CraftBukkit - SPIGOT-6907: re-implement LivingEntity#setMaximumAir()
++ public int getDefaultMaxAirSupply() {
++ return TOTAL_AIR_SUPPLY;
++ }
++ // CraftBukkit end
++
+ private static final Logger LOGGER = LogUtils.getLogger();
+ public static final String ID_TAG = "id";
+ public static final String PASSENGERS_TAG = "Passengers";
+@@ -151,7 +206,7 @@
+ private final EntityType<?> type;
+ private int id;
+ public boolean blocksBuilding;
+- private ImmutableList<Entity> passengers;
++ public ImmutableList<Entity> passengers;
+ protected int boardingCooldown;
+ @Nullable
+ private Entity vehicle;
+@@ -168,7 +223,7 @@
+ public float yRotO;
+ public float xRotO;
+ private AABB bb;
+- private boolean onGround;
++ public boolean onGround;
+ public boolean horizontalCollision;
+ public boolean verticalCollision;
+ public boolean verticalCollisionBelow;
+@@ -190,10 +245,10 @@
+ public double zOld;
+ private float maxUpStep;
+ public boolean noPhysics;
+- protected final RandomSource random;
++ public final RandomSource random;
+ public int tickCount;
+ private int remainingFireTicks;
+- protected boolean wasTouchingWater;
++ public boolean wasTouchingWater;
+ protected Object2DoubleMap<TagKey<Fluid>> fluidHeight;
+ protected boolean wasEyeInWater;
+ private final Set<TagKey<Fluid>> fluidOnEyes;
+@@ -213,13 +268,13 @@
+ private static final EntityDataAccessor<Boolean> DATA_CUSTOM_NAME_VISIBLE = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.BOOLEAN);
+ private static final EntityDataAccessor<Boolean> DATA_SILENT = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.BOOLEAN);
+ private static final EntityDataAccessor<Boolean> DATA_NO_GRAVITY = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.BOOLEAN);
+- protected static final EntityDataAccessor<Pose> DATA_POSE = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.POSE);
++ protected static final EntityDataAccessor<EntityPose> DATA_POSE = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.POSE);
+ private static final EntityDataAccessor<Integer> DATA_TICKS_FROZEN = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.INT);
+ private EntityInLevelCallback levelCallback;
+ private final VecDeltaCodec packetPositionCodec;
+ public boolean noCulling;
+ public boolean hasImpulse;
+- private int portalCooldown;
++ public int portalCooldown;
+ protected boolean isInsidePortal;
+ protected int portalTime;
+ protected BlockPos portalEntrancePos;
+@@ -239,11 +294,34 @@
+ private boolean onGroundNoBlocks;
+ private float crystalSoundIntensity;
+ private int lastCrystalSoundPlayTick;
+- private boolean hasVisualFire;
++ public boolean hasVisualFire;
+ @Nullable
+- private BlockState feetBlockState;
++ private IBlockData feetBlockState;
++ // CraftBukkit start
++ public boolean persist = true;
++ public boolean visibleByDefault = true;
++ public boolean valid;
++ public boolean inWorld = false;
++ public boolean generation;
++ public int maxAirTicks = getDefaultMaxAirSupply(); // CraftBukkit - SPIGOT-6907: re-implement LivingEntity#setMaximumAir()
++ public org.bukkit.projectiles.ProjectileSource projectileSource; // For projectiles only
++ public boolean lastDamageCancelled; // SPIGOT-5339, SPIGOT-6252, SPIGOT-6777: Keep track if the event was canceled
++ public boolean persistentInvisibility = false;
++ public BlockPos lastLavaContact;
++ // Marks an entity, that it was removed by a plugin via Entity#remove
++ // Main use case currently is for SPIGOT-7487, preventing dropping of leash when leash is removed
++ public boolean pluginRemoved = false;
+
+- public Entity(EntityType<?> entitytype, Level level) {
++ public float getBukkitYaw() {
++ return this.yRot;
++ }
++
++ public boolean isChunkLoaded() {
++ return level.hasChunk((int) Math.floor(this.getX()) >> 4, (int) Math.floor(this.getZ()) >> 4);
++ }
++ // CraftBukkit end
++
++ public Entity(EntityType<?> entityType, Level level) {
+ this.id = Entity.ENTITY_COUNTER.incrementAndGet();
+ this.passengers = ImmutableList.of();
+ this.deltaMovement = Vec3.ZERO;
+@@ -264,9 +342,9 @@
+ this.mainSupportingBlockPos = Optional.empty();
+ this.onGroundNoBlocks = false;
+ this.feetBlockState = null;
+- this.type = entitytype;
++ this.type = entityType;
+ this.level = level;
+- this.dimensions = entitytype.getDimensions();
++ this.dimensions = entityType.getDimensions();
+ this.position = Vec3.ZERO;
+ this.blockPosition = BlockPos.ZERO;
+ this.chunkPosition = ChunkPos.ZERO;
+@@ -277,24 +355,24 @@
+ this.entityData.define(Entity.DATA_CUSTOM_NAME, Optional.empty());
+ this.entityData.define(Entity.DATA_SILENT, false);
+ this.entityData.define(Entity.DATA_NO_GRAVITY, false);
+- this.entityData.define(Entity.DATA_POSE, Pose.STANDING);
++ this.entityData.define(Entity.DATA_POSE, EntityPose.STANDING);
+ this.entityData.define(Entity.DATA_TICKS_FROZEN, 0);
+ this.defineSynchedData();
+ this.setPos(0.0D, 0.0D, 0.0D);
+- this.eyeHeight = this.getEyeHeight(Pose.STANDING, this.dimensions);
++ this.eyeHeight = this.getEyeHeight(EntityPose.STANDING, this.dimensions);
+ }
+
+- public boolean isColliding(BlockPos blockpos, BlockState blockstate) {
+- VoxelShape voxelshape = blockstate.getCollisionShape(this.level(), blockpos, CollisionContext.of(this));
+- VoxelShape voxelshape1 = voxelshape.move((double) blockpos.getX(), (double) blockpos.getY(), (double) blockpos.getZ());
++ public boolean isColliding(BlockPos pos, IBlockData state) {
++ VoxelShape voxelshape = state.getCollisionShape(this.level(), pos, CollisionContext.of(this));
++ VoxelShape voxelshape1 = voxelshape.move((double) pos.getX(), (double) pos.getY(), (double) pos.getZ());
+
+ return Shapes.joinIsNotEmpty(voxelshape1, Shapes.create(this.getBoundingBox()), BooleanOp.AND);
+ }
+
+ public int getTeamColor() {
+- PlayerTeam playerteam = this.getTeam();
++ PlayerTeam scoreboardteam = this.getTeam();
+
+- return playerteam != null && playerteam.getColor().getColor() != null ? playerteam.getColor().getColor() : 16777215;
++ return scoreboardteam != null && scoreboardteam.getColor().getColor() != null ? scoreboardteam.getColor().getColor() : 16777215;
+ }
+
+ public boolean isSpectator() {
+@@ -312,8 +390,8 @@
+
+ }
+
+- public void syncPacketPositionCodec(double d0, double d1, double d2) {
+- this.packetPositionCodec.setBase(new Vec3(d0, d1, d2));
++ public void syncPacketPositionCodec(double x, double d1, double y) {
++ this.packetPositionCodec.setBase(new Vec3(x, d1, y));
+ }
+
+ public VecDeltaCodec getPositionCodec() {
+@@ -325,25 +403,24 @@
+ }
+
+ @Override
+- @Override
+ public int getId() {
+ return this.id;
+ }
+
+- public void setId(int i) {
+- this.id = i;
++ public void setId(int id) {
++ this.id = id;
+ }
+
+ public Set<String> getTags() {
+ return this.tags;
+ }
+
+- public boolean addTag(String s) {
+- return this.tags.size() >= 1024 ? false : this.tags.add(s);
++ public boolean addTag(String tag) {
++ return this.tags.size() >= 1024 ? false : this.tags.add(tag);
+ }
+
+- public boolean removeTag(String s) {
+- return this.tags.remove(s);
++ public boolean removeTag(String tag) {
++ return this.tags.remove(tag);
+ }
+
+ public void kill() {
+@@ -361,57 +438,88 @@
+ return this.entityData;
+ }
+
+- @Override
+ public boolean equals(Object object) {
+ return object instanceof Entity ? ((Entity) object).id == this.id : false;
+ }
+
+- @Override
+ public int hashCode() {
+ return this.id;
+ }
+
+- public void remove(Entity.RemovalReason entity_removalreason) {
+- this.setRemoved(entity_removalreason);
++ public void remove(Entity.RemovalReason reason) {
++ this.setRemoved(reason);
+ }
+
+ public void onClientRemoval() {}
+
+- public void setPose(Pose pose) {
++ public void setPose(EntityPose pose) {
++ // CraftBukkit start
++ if (pose == this.getPose()) {
++ return;
++ }
++ this.level.getCraftServer().getPluginManager().callEvent(new EntityPoseChangeEvent(this.getBukkitEntity(), Pose.values()[pose.ordinal()]));
++ // CraftBukkit end
+ this.entityData.set(Entity.DATA_POSE, pose);
+ }
+
+- public Pose getPose() {
+- return (Pose) this.entityData.get(Entity.DATA_POSE);
++ public EntityPose getPose() {
++ return (EntityPose) this.entityData.get(Entity.DATA_POSE);
+ }
+
+- public boolean hasPose(Pose pose) {
++ public boolean hasPose(EntityPose pose) {
+ return this.getPose() == pose;
+ }
+
+- public boolean closerThan(Entity entity, double d0) {
+- return this.position().closerThan(entity.position(), d0);
++ public boolean closerThan(Entity entity, double distance) {
++ return this.position().closerThan(entity.position(), distance);
+ }
+
+- public boolean closerThan(Entity entity, double d0, double d1) {
++ public boolean closerThan(Entity entity, double horizontalDistance, double d1) {
+ double d2 = entity.getX() - this.getX();
+ double d3 = entity.getY() - this.getY();
+ double d4 = entity.getZ() - this.getZ();
+
+- return Mth.lengthSquared(d2, d4) < Mth.square(d0) && Mth.square(d3) < Mth.square(d1);
++ return Mth.lengthSquared(d2, d4) < Mth.square(horizontalDistance) && Mth.square(d3) < Mth.square(d1);
+ }
+
+- protected void setRot(float f, float f1) {
+- this.setYRot(f % 360.0F);
+- this.setXRot(f1 % 360.0F);
++ protected void setRot(float yRot, float xRot) {
++ // CraftBukkit start - yaw was sometimes set to NaN, so we need to set it back to 0
++ if (Float.isNaN(yRot)) {
++ yRot = 0;
++ }
++
++ if (yRot == Float.POSITIVE_INFINITY || yRot == Float.NEGATIVE_INFINITY) {
++ if (this instanceof ServerPlayer) {
++ this.level.getCraftServer().getLogger().warning(this.getScoreboardName() + " was caught trying to crash the server with an invalid yaw");
++ ((CraftPlayer) this.getBukkitEntity()).kickPlayer("Infinite yaw (Hacking?)");
++ }
++ yRot = 0;
++ }
++
++ // pitch was sometimes set to NaN, so we need to set it back to 0
++ if (Float.isNaN(xRot)) {
++ xRot = 0;
++ }
++
++ if (xRot == Float.POSITIVE_INFINITY || xRot == Float.NEGATIVE_INFINITY) {
++ if (this instanceof ServerPlayer) {
++ this.level.getCraftServer().getLogger().warning(this.getScoreboardName() + " was caught trying to crash the server with an invalid pitch");
++ ((CraftPlayer) this.getBukkitEntity()).kickPlayer("Infinite pitch (Hacking?)");
++ }
++ xRot = 0;
++ }
++ // CraftBukkit end
++
++ this.setYRot(yRot % 360.0F);
++ this.setXRot(xRot % 360.0F);
+ }
+
+- public final void setPos(Vec3 vec3) {
+- this.setPos(vec3.x(), vec3.y(), vec3.z());
++ public final void setPos(Vec3 pos) {
++ this.setPos(pos.x(), pos.y(), pos.z());
+ }
+
+- public void setPos(double d0, double d1, double d2) {
+- this.setPosRaw(d0, d1, d2);
++ public void setPos(double x, double d1, double y) {
++ this.setPosRaw(x, d1, y);
+ this.setBoundingBox(this.makeBoundingBox());
+ }
+
+@@ -423,9 +531,9 @@
+ this.setPos(this.position.x, this.position.y, this.position.z);
+ }
+
+- public void turn(double d0, double d1) {
++ public void turn(double yRot, double d1) {
+ float f = (float) d1 * 0.15F;
+- float f1 = (float) d0 * 0.15F;
++ float f1 = (float) yRot * 0.15F;
+
+ this.setXRot(this.getXRot() + f);
+ this.setYRot(this.getYRot() + f1);
+@@ -443,6 +551,15 @@
+ this.baseTick();
+ }
+
++ // 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 ServerPlayer)) {
++ this.handleNetherPortal();
++ }
++ }
++ // CraftBukkit end
++
+ public void baseTick() {
+ this.level().getProfiler().push("entityBaseTick");
+ this.feetBlockState = null;
+@@ -457,7 +574,7 @@
+ this.walkDistO = this.walkDist;
+ this.xRotO = this.getXRot();
+ this.yRotO = this.getYRot();
+- this.handleNetherPortal();
++ if (this instanceof ServerPlayer) this.handleNetherPortal(); // CraftBukkit - // Moved up to postTick
+ if (this.canSpawnSprintParticle()) {
+ this.spawnSprintParticle();
+ }
+@@ -492,6 +609,10 @@
+ if (this.isInLava()) {
+ this.lavaHurt();
+ this.fallDistance *= 0.5F;
++ // CraftBukkit start
++ } else {
++ this.lastLavaContact = null;
++ // CraftBukkit end
+ }
+
+ this.checkBelowWorld();
+@@ -503,8 +624,8 @@
+ this.level().getProfiler().pop();
+ }
+
+- public void setSharedFlagOnFire(boolean flag) {
+- this.setSharedFlag(0, flag || this.hasVisualFire);
++ public void setSharedFlagOnFire(boolean isOnFire) {
++ this.setSharedFlag(0, isOnFire || this.hasVisualFire);
+ }
+
+ public void checkBelowWorld() {
+@@ -518,8 +639,8 @@
+ this.portalCooldown = this.getDimensionChangingDelay();
+ }
+
+- public void setPortalCooldown(int i) {
+- this.portalCooldown = i;
++ public void setPortalCooldown(int portalCooldown) {
++ this.portalCooldown = portalCooldown;
+ }
+
+ public int getPortalCooldown() {
+@@ -543,19 +664,52 @@
+
+ public void lavaHurt() {
+ if (!this.fireImmune()) {
+- this.setSecondsOnFire(15);
++ // CraftBukkit start - Fallen in lava TODO: this event spams!
++ if (this instanceof net.minecraft.world.entity.LivingEntity && remainingFireTicks <= 0) {
++ // not on fire yet
++ org.bukkit.block.Block damager = (lastLavaContact == null) ? null : org.bukkit.craftbukkit.block.CraftBlock.at(level, lastLavaContact);
++ org.bukkit.entity.Entity damagee = this.getBukkitEntity();
++ EntityCombustEvent combustEvent = new org.bukkit.event.entity.EntityCombustByBlockEvent(damager, damagee, 15);
++ this.level.getCraftServer().getPluginManager().callEvent(combustEvent);
++
++ if (!combustEvent.isCancelled()) {
++ this.setSecondsOnFire(combustEvent.getDuration(), false);
++ }
++ } else {
++ // This will be called every single tick the entity is in lava, so don't throw an event
++ this.setSecondsOnFire(15, false);
++ }
++ CraftEventFactory.blockDamage = (lastLavaContact) == null ? null : org.bukkit.craftbukkit.block.CraftBlock.at(level, lastLavaContact);
+ if (this.hurt(this.damageSources().lava(), 4.0F)) {
+ this.playSound(SoundEvents.GENERIC_BURN, 0.4F, 2.0F + this.random.nextFloat() * 0.4F);
+ }
++ CraftEventFactory.blockDamage = null;
++ // CraftBukkit end - we also don't throw an event unless the object in lava is living, to save on some event calls
+
+ }
+ }
+
+- public void setSecondsOnFire(int i) {
++ public void setSecondsOnFire(int seconds) {
++ // CraftBukkit start
++ this.setSecondsOnFire(seconds, true);
++ }
++
++ public void setSecondsOnFire(int i, boolean callEvent) {
++ if (callEvent) {
++ EntityCombustEvent event = new EntityCombustEvent(this.getBukkitEntity(), i);
++ this.level.getCraftServer().getPluginManager().callEvent(event);
++
++ if (event.isCancelled()) {
++ return;
++ }
++
++ i = event.getDuration();
++ }
++ // CraftBukkit end
+ int j = i * 20;
+
+- if (this instanceof LivingEntity) {
+- j = ProtectionEnchantment.getFireAfterDampener((LivingEntity) this, j);
++ if (this instanceof net.minecraft.world.entity.LivingEntity) {
++ j = ProtectionEnchantment.getFireAfterDampener((net.minecraft.world.entity.LivingEntity) this, j);
+ }
+
+ if (this.remainingFireTicks < j) {
+@@ -564,8 +718,8 @@
+
+ }
+
+- public void setRemainingFireTicks(int i) {
+- this.remainingFireTicks = i;
++ public void setRemainingFireTicks(int remainingFireTicks) {
++ this.remainingFireTicks = remainingFireTicks;
+ }
+
+ public int getRemainingFireTicks() {
+@@ -580,39 +734,39 @@
+ this.discard();
+ }
+
+- public boolean isFree(double d0, double d1, double d2) {
+- return this.isFree(this.getBoundingBox().move(d0, d1, d2));
++ public boolean isFree(double x, double d1, double y) {
++ return this.isFree(this.getBoundingBox().move(x, d1, y));
+ }
+
+- private boolean isFree(AABB aabb) {
+- return this.level().noCollision(this, aabb) && !this.level().containsAnyLiquid(aabb);
++ private boolean isFree(AABB box) {
++ return this.level().noCollision(this, box) && !this.level().containsAnyLiquid(box);
+ }
+
+- public void setOnGround(boolean flag) {
+- this.onGround = flag;
+- this.checkSupportingBlock(flag, (Vec3) null);
++ public void setOnGround(boolean onGround) {
++ this.onGround = onGround;
++ this.checkSupportingBlock(onGround, (Vec3) null);
+ }
+
+- public void setOnGroundWithKnownMovement(boolean flag, Vec3 vec3) {
+- this.onGround = flag;
+- this.checkSupportingBlock(flag, vec3);
++ public void setOnGroundWithKnownMovement(boolean onGround, Vec3 movement) {
++ this.onGround = onGround;
++ this.checkSupportingBlock(onGround, movement);
+ }
+
+- public boolean isSupportedBy(BlockPos blockpos) {
+- return this.mainSupportingBlockPos.isPresent() && ((BlockPos) this.mainSupportingBlockPos.get()).equals(blockpos);
++ public boolean isSupportedBy(BlockPos pos) {
++ return this.mainSupportingBlockPos.isPresent() && ((BlockPos) this.mainSupportingBlockPos.get()).equals(pos);
+ }
+
+- protected void checkSupportingBlock(boolean flag, @Nullable Vec3 vec3) {
+- if (flag) {
+- AABB aabb = this.getBoundingBox();
+- AABB aabb1 = new AABB(aabb.minX, aabb.minY - 1.0E-6D, aabb.minZ, aabb.maxX, aabb.minY, aabb.maxZ);
+- Optional<BlockPos> optional = this.level.findSupportingBlock(this, aabb1);
++ protected void checkSupportingBlock(boolean onGround, @Nullable Vec3 movement) {
++ if (onGround) {
++ AABB axisalignedbb = this.getBoundingBox();
++ AABB axisalignedbb1 = new AABB(axisalignedbb.minX, axisalignedbb.minY - 1.0E-6D, axisalignedbb.minZ, axisalignedbb.maxX, axisalignedbb.minY, axisalignedbb.maxZ);
++ Optional<BlockPos> optional = this.level.findSupportingBlock(this, axisalignedbb1);
+
+ if (!optional.isPresent() && !this.onGroundNoBlocks) {
+- if (vec3 != null) {
+- AABB aabb2 = aabb1.move(-vec3.x, 0.0D, -vec3.z);
++ if (movement != null) {
++ AABB axisalignedbb2 = axisalignedbb1.move(-movement.x, 0.0D, -movement.z);
+
+- optional = this.level.findSupportingBlock(this, aabb2);
++ optional = this.level.findSupportingBlock(this, axisalignedbb2);
+ this.mainSupportingBlockPos = optional;
+ }
+ } else {
+@@ -633,103 +787,125 @@
+ return this.onGround;
+ }
+
+- public void move(MoverType movertype, Vec3 vec3) {
++ public void move(EnumMoveType type, Vec3 pos) {
+ if (this.noPhysics) {
+- this.setPos(this.getX() + vec3.x, this.getY() + vec3.y, this.getZ() + vec3.z);
++ this.setPos(this.getX() + pos.x, this.getY() + pos.y, this.getZ() + pos.z);
+ } else {
+ this.wasOnFire = this.isOnFire();
+- if (movertype == MoverType.PISTON) {
+- vec3 = this.limitPistonMovement(vec3);
+- if (vec3.equals(Vec3.ZERO)) {
++ if (type == EnumMoveType.PISTON) {
++ pos = this.limitPistonMovement(pos);
++ if (pos.equals(Vec3.ZERO)) {
+ return;
+ }
+ }
+
+ this.level().getProfiler().push("move");
+ if (this.stuckSpeedMultiplier.lengthSqr() > 1.0E-7D) {
+- vec3 = vec3.multiply(this.stuckSpeedMultiplier);
++ pos = pos.multiply(this.stuckSpeedMultiplier);
+ this.stuckSpeedMultiplier = Vec3.ZERO;
+ this.setDeltaMovement(Vec3.ZERO);
+ }
+
+- vec3 = this.maybeBackOffFromEdge(vec3, movertype);
+- Vec3 vec31 = this.collide(vec3);
+- double d0 = vec31.lengthSqr();
++ pos = this.maybeBackOffFromEdge(pos, type);
++ Vec3 vec3d1 = this.collide(pos);
++ double d0 = vec3d1.lengthSqr();
+
+ if (d0 > 1.0E-7D) {
+ if (this.fallDistance != 0.0F && d0 >= 1.0D) {
+- BlockHitResult blockhitresult = this.level().clip(new ClipContext(this.position(), this.position().add(vec31), ClipContext.Block.FALLDAMAGE_RESETTING, ClipContext.Fluid.WATER, this));
++ BlockHitResult movingobjectpositionblock = this.level().clip(new ClipContext(this.position(), this.position().add(vec3d1), ClipContext.Block.FALLDAMAGE_RESETTING, ClipContext.Fluid.WATER, this));
+
+- if (blockhitresult.getType() != HitResult.Type.MISS) {
++ if (movingobjectpositionblock.getType() != HitResult.EnumMovingObjectType.MISS) {
+ this.resetFallDistance();
+ }
+ }
+
+- this.setPos(this.getX() + vec31.x, this.getY() + vec31.y, this.getZ() + vec31.z);
++ this.setPos(this.getX() + vec3d1.x, this.getY() + vec3d1.y, this.getZ() + vec3d1.z);
+ }
+
+ this.level().getProfiler().pop();
+ this.level().getProfiler().push("rest");
+- boolean flag = !Mth.equal(vec3.x, vec31.x);
+- boolean flag1 = !Mth.equal(vec3.z, vec31.z);
++ boolean flag = !Mth.equal(pos.x, vec3d1.x);
++ boolean flag1 = !Mth.equal(pos.z, vec3d1.z);
+
+ this.horizontalCollision = flag || flag1;
+- this.verticalCollision = vec3.y != vec31.y;
+- this.verticalCollisionBelow = this.verticalCollision && vec3.y < 0.0D;
++ this.verticalCollision = pos.y != vec3d1.y;
++ this.verticalCollisionBelow = this.verticalCollision && pos.y < 0.0D;
+ if (this.horizontalCollision) {
+- this.minorHorizontalCollision = this.isHorizontalCollisionMinor(vec31);
++ this.minorHorizontalCollision = this.isHorizontalCollisionMinor(vec3d1);
+ } else {
+ this.minorHorizontalCollision = false;
+ }
+
+- this.setOnGroundWithKnownMovement(this.verticalCollisionBelow, vec31);
+- BlockPos blockpos = this.getOnPosLegacy();
+- BlockState blockstate = this.level().getBlockState(blockpos);
++ this.setOnGroundWithKnownMovement(this.verticalCollisionBelow, vec3d1);
++ BlockPos blockposition = this.getOnPosLegacy();
++ IBlockData iblockdata = this.level().getBlockState(blockposition);
+
+- this.checkFallDamage(vec31.y, this.onGround(), blockstate, blockpos);
++ this.checkFallDamage(vec3d1.y, this.onGround(), iblockdata, blockposition);
+ if (this.isRemoved()) {
+ this.level().getProfiler().pop();
+ } else {
+ if (this.horizontalCollision) {
+- Vec3 vec32 = this.getDeltaMovement();
++ Vec3 vec3d2 = this.getDeltaMovement();
+
+- this.setDeltaMovement(flag ? 0.0D : vec32.x, vec32.y, flag1 ? 0.0D : vec32.z);
++ this.setDeltaMovement(flag ? 0.0D : vec3d2.x, vec3d2.y, flag1 ? 0.0D : vec3d2.z);
+ }
+
+- Block block = blockstate.getBlock();
++ Block block = iblockdata.getBlock();
+
+- if (vec3.y != vec31.y) {
++ if (pos.y != vec3d1.y) {
+ block.updateEntityAfterFallOn(this.level(), this);
+ }
+
++ // CraftBukkit start
++ if (horizontalCollision && getBukkitEntity() instanceof Vehicle) {
++ Vehicle vehicle = (Vehicle) this.getBukkitEntity();
++ org.bukkit.block.Block bl = this.level.getWorld().getBlockAt(Mth.floor(this.getX()), Mth.floor(this.getY()), Mth.floor(this.getZ()));
++
++ if (pos.x > vec3d1.x) {
++ bl = bl.getRelative(BlockFace.EAST);
++ } else if (pos.x < vec3d1.x) {
++ bl = bl.getRelative(BlockFace.WEST);
++ } else if (pos.z > vec3d1.z) {
++ bl = bl.getRelative(BlockFace.SOUTH);
++ } else if (pos.z < vec3d1.z) {
++ bl = bl.getRelative(BlockFace.NORTH);
++ }
++
++ if (!bl.getType().isAir()) {
++ VehicleBlockCollisionEvent event = new VehicleBlockCollisionEvent(vehicle, bl);
++ level.getCraftServer().getPluginManager().callEvent(event);
++ }
++ }
++ // CraftBukkit end
++
+ if (this.onGround()) {
+- block.stepOn(this.level(), blockpos, blockstate, this);
++ block.stepOn(this.level(), blockposition, iblockdata, this);
+ }
+
+ Entity.MovementEmission entity_movementemission = this.getMovementEmission();
+
+ if (entity_movementemission.emitsAnything() && !this.isPassenger()) {
+- double d1 = vec31.x;
+- double d2 = vec31.y;
+- double d3 = vec31.z;
++ double d1 = vec3d1.x;
++ double d2 = vec3d1.y;
++ double d3 = vec3d1.z;
+
+- this.flyDist += (float) (vec31.length() * 0.6D);
+- BlockPos blockpos1 = this.getOnPos();
+- BlockState blockstate1 = this.level().getBlockState(blockpos1);
+- boolean flag2 = this.isStateClimbable(blockstate1);
++ this.flyDist += (float) (vec3d1.length() * 0.6D);
++ BlockPos blockposition1 = this.getOnPos();
++ IBlockData iblockdata1 = this.level().getBlockState(blockposition1);
++ boolean flag2 = this.isStateClimbable(iblockdata1);
+
+ if (!flag2) {
+ d2 = 0.0D;
+ }
+
+- this.walkDist += (float) vec31.horizontalDistance() * 0.6F;
++ this.walkDist += (float) vec3d1.horizontalDistance() * 0.6F;
+ this.moveDist += (float) Math.sqrt(d1 * d1 + d2 * d2 + d3 * d3) * 0.6F;
+- if (this.moveDist > this.nextStep && !blockstate1.isAir()) {
+- boolean flag3 = blockpos1.equals(blockpos);
+- boolean flag4 = this.vibrationAndSoundEffectsFromBlock(blockpos, blockstate, entity_movementemission.emitsSounds(), flag3, vec3);
++ if (this.moveDist > this.nextStep && !iblockdata1.isAir()) {
++ boolean flag3 = blockposition1.equals(blockposition);
++ boolean flag4 = this.vibrationAndSoundEffectsFromBlock(blockposition, iblockdata, entity_movementemission.emitsSounds(), flag3, pos);
+
+ if (!flag3) {
+- flag4 |= this.vibrationAndSoundEffectsFromBlock(blockpos1, blockstate1, false, entity_movementemission.emitsEvents(), vec3);
++ flag4 |= this.vibrationAndSoundEffectsFromBlock(blockposition1, iblockdata1, false, entity_movementemission.emitsEvents(), pos);
+ }
+
+ if (flag4) {
+@@ -744,7 +920,7 @@
+ this.gameEvent(GameEvent.SWIM);
+ }
+ }
+- } else if (blockstate1.isAir()) {
++ } else if (iblockdata1.isAir()) {
+ this.processFlappingMovement();
+ }
+ }
+@@ -753,8 +929,8 @@
+ float f = this.getBlockSpeedFactor();
+
+ this.setDeltaMovement(this.getDeltaMovement().multiply((double) f, 1.0D, (double) f));
+- if (this.level().getBlockStatesIfLoaded(this.getBoundingBox().deflate(1.0E-6D)).noneMatch((blockstate2) -> {
+- return blockstate2.is(BlockTags.FIRE) || blockstate2.is(Blocks.LAVA);
++ if (this.level().getBlockStatesIfLoaded(this.getBoundingBox().deflate(1.0E-6D)).noneMatch((iblockdata2) -> {
++ return iblockdata2.is(BlockTags.FIRE) || iblockdata2.is(Blocks.LAVA);
+ })) {
+ if (this.remainingFireTicks <= 0) {
+ this.setRemainingFireTicks(-this.getFireImmuneTicks());
+@@ -774,23 +950,23 @@
+ }
+ }
+
+- private boolean isStateClimbable(BlockState blockstate) {
+- return blockstate.is(BlockTags.CLIMBABLE) || blockstate.is(Blocks.POWDER_SNOW);
++ private boolean isStateClimbable(IBlockData state) {
++ return state.is(BlockTags.CLIMBABLE) || state.is(Blocks.POWDER_SNOW);
+ }
+
+- private boolean vibrationAndSoundEffectsFromBlock(BlockPos blockpos, BlockState blockstate, boolean flag, boolean flag1, Vec3 vec3) {
+- if (blockstate.isAir()) {
++ private boolean vibrationAndSoundEffectsFromBlock(BlockPos pos, IBlockData state, boolean playStepSound, boolean broadcastGameEvent, Vec3 vec3d) {
++ if (state.isAir()) {
+ return false;
+ } else {
+- boolean flag2 = this.isStateClimbable(blockstate);
++ boolean flag2 = this.isStateClimbable(state);
+
+- if ((this.onGround() || flag2 || this.isCrouching() && vec3.y == 0.0D || this.isOnRails()) && !this.isSwimming()) {
+- if (flag) {
+- this.walkingStepSound(blockpos, blockstate);
++ if ((this.onGround() || flag2 || this.isCrouching() && vec3d.y == 0.0D || this.isOnRails()) && !this.isSwimming()) {
++ if (playStepSound) {
++ this.walkingStepSound(pos, state);
+ }
+
+- if (flag1) {
+- this.level().gameEvent(GameEvent.STEP, this.position(), GameEvent.Context.of(this, blockstate));
++ if (broadcastGameEvent) {
++ this.level().gameEvent(GameEvent.STEP, this.position(), GameEvent.Context.of(this, state));
+ }
+
+ return true;
+@@ -800,7 +976,7 @@
+ }
+ }
+
+- protected boolean isHorizontalCollisionMinor(Vec3 vec3) {
++ protected boolean isHorizontalCollisionMinor(Vec3 deltaMovement) {
+ return false;
+ }
+
+@@ -809,9 +985,9 @@
+ this.checkInsideBlocks();
+ } catch (Throwable throwable) {
+ CrashReport crashreport = CrashReport.forThrowable(throwable, "Checking entity block collision");
+- CrashReportCategory crashreportcategory = crashreport.addCategory("Entity being checked for collision");
++ CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Entity being checked for collision");
+
+- this.fillCrashReportCategory(crashreportcategory);
++ this.fillCrashReportCategory(crashreportsystemdetails);
+ throw new ReportedException(crashreport);
+ }
+ }
+@@ -852,20 +1028,20 @@
+ return this.getOnPos(1.0E-5F);
+ }
+
+- protected BlockPos getOnPos(float f) {
++ protected BlockPos getOnPos(float yOffset) {
+ if (this.mainSupportingBlockPos.isPresent()) {
+- BlockPos blockpos = (BlockPos) this.mainSupportingBlockPos.get();
++ BlockPos blockposition = (BlockPos) this.mainSupportingBlockPos.get();
+
+- if (f <= 1.0E-5F) {
+- return blockpos;
++ if (yOffset <= 1.0E-5F) {
++ return blockposition;
+ } else {
+- BlockState blockstate = this.level().getBlockState(blockpos);
++ IBlockData iblockdata = this.level().getBlockState(blockposition);
+
+- return ((double) f > 0.5D || !blockstate.is(BlockTags.FENCES)) && !blockstate.is(BlockTags.WALLS) && !(blockstate.getBlock() instanceof FenceGateBlock) ? blockpos.atY(Mth.floor(this.position.y - (double) f)) : blockpos;
++ return ((double) yOffset > 0.5D || !iblockdata.is(BlockTags.FENCES)) && !iblockdata.is(BlockTags.WALLS) && !(iblockdata.getBlock() instanceof FenceGateBlock) ? blockposition.atY(Mth.floor(this.position.y - (double) yOffset)) : blockposition;
+ }
+ } else {
+ int i = Mth.floor(this.position.x);
+- int j = Mth.floor(this.position.y - (double) f);
++ int j = Mth.floor(this.position.y - (double) yOffset);
+ int k = Mth.floor(this.position.z);
+
+ return new BlockPos(i, j, k);
+@@ -880,19 +1056,19 @@
+ }
+
+ protected float getBlockSpeedFactor() {
+- BlockState blockstate = this.level().getBlockState(this.blockPosition());
+- float f = blockstate.getBlock().getSpeedFactor();
++ IBlockData iblockdata = this.level().getBlockState(this.blockPosition());
++ float f = iblockdata.getBlock().getSpeedFactor();
+
+- return !blockstate.is(Blocks.WATER) && !blockstate.is(Blocks.BUBBLE_COLUMN) ? ((double) f == 1.0D ? this.level().getBlockState(this.getBlockPosBelowThatAffectsMyMovement()).getBlock().getSpeedFactor() : f) : f;
++ return !iblockdata.is(Blocks.WATER) && !iblockdata.is(Blocks.BUBBLE_COLUMN) ? ((double) f == 1.0D ? this.level().getBlockState(this.getBlockPosBelowThatAffectsMyMovement()).getBlock().getSpeedFactor() : f) : f;
+ }
+
+- protected Vec3 maybeBackOffFromEdge(Vec3 vec3, MoverType movertype) {
+- return vec3;
++ protected Vec3 maybeBackOffFromEdge(Vec3 vec, EnumMoveType mover) {
++ return vec;
+ }
+
+- protected Vec3 limitPistonMovement(Vec3 vec3) {
+- if (vec3.lengthSqr() <= 1.0E-7D) {
+- return vec3;
++ protected Vec3 limitPistonMovement(Vec3 pos) {
++ if (pos.lengthSqr() <= 1.0E-7D) {
++ return pos;
+ } else {
+ long i = this.level().getGameTime();
+
+@@ -903,14 +1079,14 @@
+
+ double d0;
+
+- if (vec3.x != 0.0D) {
+- d0 = this.applyPistonMovementRestriction(Direction.Axis.X, vec3.x);
++ if (pos.x != 0.0D) {
++ d0 = this.applyPistonMovementRestriction(Direction.Axis.X, pos.x);
+ return Math.abs(d0) <= 9.999999747378752E-6D ? Vec3.ZERO : new Vec3(d0, 0.0D, 0.0D);
+- } else if (vec3.y != 0.0D) {
+- d0 = this.applyPistonMovementRestriction(Direction.Axis.Y, vec3.y);
++ } else if (pos.y != 0.0D) {
++ d0 = this.applyPistonMovementRestriction(Direction.Axis.Y, pos.y);
+ return Math.abs(d0) <= 9.999999747378752E-6D ? Vec3.ZERO : new Vec3(0.0D, d0, 0.0D);
+- } else if (vec3.z != 0.0D) {
+- d0 = this.applyPistonMovementRestriction(Direction.Axis.Z, vec3.z);
++ } else if (pos.z != 0.0D) {
++ d0 = this.applyPistonMovementRestriction(Direction.Axis.Z, pos.z);
+ return Math.abs(d0) <= 9.999999747378752E-6D ? Vec3.ZERO : new Vec3(0.0D, 0.0D, d0);
+ } else {
+ return Vec3.ZERO;
+@@ -918,95 +1094,95 @@
+ }
+ }
+
+- private double applyPistonMovementRestriction(Direction.Axis direction_axis, double d0) {
+- int i = direction_axis.ordinal();
+- double d1 = Mth.clamp(d0 + this.pistonDeltas[i], -0.51D, 0.51D);
++ private double applyPistonMovementRestriction(Direction.Axis axis, double distance) {
++ int i = axis.ordinal();
++ double d1 = Mth.clamp(distance + this.pistonDeltas[i], -0.51D, 0.51D);
+
+- d0 = d1 - this.pistonDeltas[i];
++ distance = d1 - this.pistonDeltas[i];
+ this.pistonDeltas[i] = d1;
+- return d0;
++ return distance;
+ }
+
+- private Vec3 collide(Vec3 vec3) {
+- AABB aabb = this.getBoundingBox();
+- List<VoxelShape> list = this.level().getEntityCollisions(this, aabb.expandTowards(vec3));
+- Vec3 vec31 = vec3.lengthSqr() == 0.0D ? vec3 : collideBoundingBox(this, vec3, aabb, this.level(), list);
+- boolean flag = vec3.x != vec31.x;
+- boolean flag1 = vec3.y != vec31.y;
+- boolean flag2 = vec3.z != vec31.z;
+- boolean flag3 = this.onGround() || flag1 && vec3.y < 0.0D;
++ private Vec3 collide(Vec3 vec) {
++ AABB axisalignedbb = this.getBoundingBox();
++ List<VoxelShape> list = this.level().getEntityCollisions(this, axisalignedbb.expandTowards(vec));
++ Vec3 vec3d1 = vec.lengthSqr() == 0.0D ? vec : collideBoundingBox(this, vec, axisalignedbb, this.level(), list);
++ boolean flag = vec.x != vec3d1.x;
++ boolean flag1 = vec.y != vec3d1.y;
++ boolean flag2 = vec.z != vec3d1.z;
++ boolean flag3 = this.onGround() || flag1 && vec.y < 0.0D;
+
+ if (this.maxUpStep() > 0.0F && flag3 && (flag || flag2)) {
+- Vec3 vec32 = collideBoundingBox(this, new Vec3(vec3.x, (double) this.maxUpStep(), vec3.z), aabb, this.level(), list);
+- Vec3 vec33 = collideBoundingBox(this, new Vec3(0.0D, (double) this.maxUpStep(), 0.0D), aabb.expandTowards(vec3.x, 0.0D, vec3.z), this.level(), list);
++ Vec3 vec3d2 = collideBoundingBox(this, new Vec3(vec.x, (double) this.maxUpStep(), vec.z), axisalignedbb, this.level(), list);
++ Vec3 vec3d3 = collideBoundingBox(this, new Vec3(0.0D, (double) this.maxUpStep(), 0.0D), axisalignedbb.expandTowards(vec.x, 0.0D, vec.z), this.level(), list);
+
+- if (vec33.y < (double) this.maxUpStep()) {
+- Vec3 vec34 = collideBoundingBox(this, new Vec3(vec3.x, 0.0D, vec3.z), aabb.move(vec33), this.level(), list).add(vec33);
++ if (vec3d3.y < (double) this.maxUpStep()) {
++ Vec3 vec3d4 = collideBoundingBox(this, new Vec3(vec.x, 0.0D, vec.z), axisalignedbb.move(vec3d3), this.level(), list).add(vec3d3);
+
+- if (vec34.horizontalDistanceSqr() > vec32.horizontalDistanceSqr()) {
+- vec32 = vec34;
++ if (vec3d4.horizontalDistanceSqr() > vec3d2.horizontalDistanceSqr()) {
++ vec3d2 = vec3d4;
+ }
+ }
+
+- if (vec32.horizontalDistanceSqr() > vec31.horizontalDistanceSqr()) {
+- return vec32.add(collideBoundingBox(this, new Vec3(0.0D, -vec32.y + vec3.y, 0.0D), aabb.move(vec32), this.level(), list));
++ if (vec3d2.horizontalDistanceSqr() > vec3d1.horizontalDistanceSqr()) {
++ return vec3d2.add(collideBoundingBox(this, new Vec3(0.0D, -vec3d2.y + vec.y, 0.0D), axisalignedbb.move(vec3d2), this.level(), list));
+ }
+ }
+
+- return vec31;
++ return vec3d1;
+ }
+
+- public static Vec3 collideBoundingBox(@Nullable Entity entity, Vec3 vec3, AABB aabb, Level level, List<VoxelShape> list) {
+- Builder<VoxelShape> builder = ImmutableList.builderWithExpectedSize(list.size() + 1);
++ public static Vec3 collideBoundingBox(@Nullable Entity entity, Vec3 vec, AABB collisionBox, Level level, List<VoxelShape> potentialHits) {
++ Builder<VoxelShape> builder = ImmutableList.builderWithExpectedSize(potentialHits.size() + 1);
+
+- if (!list.isEmpty()) {
+- builder.addAll(list);
++ if (!potentialHits.isEmpty()) {
++ builder.addAll(potentialHits);
+ }
+
+ WorldBorder worldborder = level.getWorldBorder();
+- boolean flag = entity != null && worldborder.isInsideCloseToBorder(entity, aabb.expandTowards(vec3));
++ boolean flag = entity != null && worldborder.isInsideCloseToBorder(entity, collisionBox.expandTowards(vec));
+
+ if (flag) {
+ builder.add(worldborder.getCollisionShape());
+ }
+
+- builder.addAll(level.getBlockCollisions(entity, aabb.expandTowards(vec3)));
+- return collideWithShapes(vec3, aabb, builder.build());
++ builder.addAll(level.getBlockCollisions(entity, collisionBox.expandTowards(vec)));
++ return collideWithShapes(vec, collisionBox, builder.build());
+ }
+
+- private static Vec3 collideWithShapes(Vec3 vec3, AABB aabb, List<VoxelShape> list) {
+- if (list.isEmpty()) {
+- return vec3;
++ private static Vec3 collideWithShapes(Vec3 deltaMovement, AABB entityBB, List<VoxelShape> shapes) {
++ if (shapes.isEmpty()) {
++ return deltaMovement;
+ } else {
+- double d0 = vec3.x;
+- double d1 = vec3.y;
+- double d2 = vec3.z;
++ double d0 = deltaMovement.x;
++ double d1 = deltaMovement.y;
++ double d2 = deltaMovement.z;
+
+ if (d1 != 0.0D) {
+- d1 = Shapes.collide(Direction.Axis.Y, aabb, list, d1);
++ d1 = Shapes.collide(Direction.Axis.Y, entityBB, shapes, d1);
+ if (d1 != 0.0D) {
+- aabb = aabb.move(0.0D, d1, 0.0D);
++ entityBB = entityBB.move(0.0D, d1, 0.0D);
+ }
+ }
+
+ boolean flag = Math.abs(d0) < Math.abs(d2);
+
+ if (flag && d2 != 0.0D) {
+- d2 = Shapes.collide(Direction.Axis.Z, aabb, list, d2);
++ d2 = Shapes.collide(Direction.Axis.Z, entityBB, shapes, d2);
+ if (d2 != 0.0D) {
+- aabb = aabb.move(0.0D, 0.0D, d2);
++ entityBB = entityBB.move(0.0D, 0.0D, d2);
+ }
+ }
+
+ if (d0 != 0.0D) {
+- d0 = Shapes.collide(Direction.Axis.X, aabb, list, d0);
++ d0 = Shapes.collide(Direction.Axis.X, entityBB, shapes, d0);
+ if (!flag && d0 != 0.0D) {
+- aabb = aabb.move(d0, 0.0D, 0.0D);
++ entityBB = entityBB.move(d0, 0.0D, 0.0D);
+ }
+ }
+
+ if (!flag && d2 != 0.0D) {
+- d2 = Shapes.collide(Direction.Axis.Z, aabb, list, d2);
++ d2 = Shapes.collide(Direction.Axis.Z, entityBB, shapes, d2);
+ }
+
+ return new Vec3(d0, d1, d2);
+@@ -1029,32 +1205,46 @@
+ return SoundEvents.GENERIC_SPLASH;
+ }
+
++ // CraftBukkit start - Add delegate methods
++ public SoundEvent getSwimSound0() {
++ return getSwimSound();
++ }
++
++ public SoundEvent getSwimSplashSound0() {
++ return getSwimSplashSound();
++ }
++
++ public SoundEvent getSwimHighSpeedSplashSound0() {
++ return getSwimHighSpeedSplashSound();
++ }
++ // CraftBukkit end
++
+ protected void checkInsideBlocks() {
+- AABB aabb = this.getBoundingBox();
+- BlockPos blockpos = BlockPos.containing(aabb.minX + 1.0E-7D, aabb.minY + 1.0E-7D, aabb.minZ + 1.0E-7D);
+- BlockPos blockpos1 = BlockPos.containing(aabb.maxX - 1.0E-7D, aabb.maxY - 1.0E-7D, aabb.maxZ - 1.0E-7D);
++ AABB axisalignedbb = this.getBoundingBox();
++ BlockPos blockposition = BlockPos.containing(axisalignedbb.minX + 1.0E-7D, axisalignedbb.minY + 1.0E-7D, axisalignedbb.minZ + 1.0E-7D);
++ BlockPos blockposition1 = BlockPos.containing(axisalignedbb.maxX - 1.0E-7D, axisalignedbb.maxY - 1.0E-7D, axisalignedbb.maxZ - 1.0E-7D);
+
+- if (this.level().hasChunksAt(blockpos, blockpos1)) {
+- BlockPos.MutableBlockPos blockpos_mutableblockpos = new BlockPos.MutableBlockPos();
++ if (this.level().hasChunksAt(blockposition, blockposition1)) {
++ BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
+
+- for (int i = blockpos.getX(); i <= blockpos1.getX(); ++i) {
+- for (int j = blockpos.getY(); j <= blockpos1.getY(); ++j) {
+- for (int k = blockpos.getZ(); k <= blockpos1.getZ(); ++k) {
++ for (int i = blockposition.getX(); i <= blockposition1.getX(); ++i) {
++ for (int j = blockposition.getY(); j <= blockposition1.getY(); ++j) {
++ for (int k = blockposition.getZ(); k <= blockposition1.getZ(); ++k) {
+ if (!this.isAlive()) {
+ return;
+ }
+
+- blockpos_mutableblockpos.set(i, j, k);
+- BlockState blockstate = this.level().getBlockState(blockpos_mutableblockpos);
++ blockposition_mutableblockposition.set(i, j, k);
++ IBlockData iblockdata = this.level().getBlockState(blockposition_mutableblockposition);
+
+ try {
+- blockstate.entityInside(this.level(), blockpos_mutableblockpos, this);
+- this.onInsideBlock(blockstate);
++ iblockdata.entityInside(this.level(), blockposition_mutableblockposition, this);
++ this.onInsideBlock(iblockdata);
+ } catch (Throwable throwable) {
+ CrashReport crashreport = CrashReport.forThrowable(throwable, "Colliding entity with block");
+- CrashReportCategory crashreportcategory = crashreport.addCategory("Block being collided with");
++ CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Block being collided with");
+
+- CrashReportCategory.populateBlockDetails(crashreportcategory, this.level(), blockpos_mutableblockpos, blockstate);
++ CrashReportCategory.populateBlockDetails(crashreportsystemdetails, this.level(), blockposition_mutableblockposition, iblockdata);
+ throw new ReportedException(crashreport);
+ }
+ }
+@@ -1064,19 +1254,19 @@
+
+ }
+
+- protected void onInsideBlock(BlockState blockstate) {}
++ protected void onInsideBlock(IBlockData state) {}
+
+- public void gameEvent(GameEvent gameevent, @Nullable Entity entity) {
+- this.level().gameEvent(entity, gameevent, this.position);
++ public void gameEvent(GameEvent event, @Nullable Entity entity) {
++ this.level().gameEvent(entity, event, this.position);
+ }
+
+- public void gameEvent(GameEvent gameevent) {
+- this.gameEvent(gameevent, this);
++ public void gameEvent(GameEvent event) {
++ this.gameEvent(event, this);
+ }
+
+- private void walkingStepSound(BlockPos blockpos, BlockState blockstate) {
+- this.playStepSound(blockpos, blockstate);
+- if (this.shouldPlayAmethystStepSound(blockstate)) {
++ private void walkingStepSound(BlockPos pos, IBlockData state) {
++ this.playStepSound(pos, state);
++ if (this.shouldPlayAmethystStepSound(state)) {
+ this.playAmethystStepSound();
+ }
+
+@@ -1085,40 +1275,40 @@
+ protected void waterSwimSound() {
+ Entity entity = (Entity) Objects.requireNonNullElse(this.getControllingPassenger(), this);
+ float f = entity == this ? 0.35F : 0.4F;
+- Vec3 vec3 = entity.getDeltaMovement();
+- float f1 = Math.min(1.0F, (float) Math.sqrt(vec3.x * vec3.x * 0.20000000298023224D + vec3.y * vec3.y + vec3.z * vec3.z * 0.20000000298023224D) * f);
++ Vec3 vec3d = entity.getDeltaMovement();
++ float f1 = Math.min(1.0F, (float) Math.sqrt(vec3d.x * vec3d.x * 0.20000000298023224D + vec3d.y * vec3d.y + vec3d.z * vec3d.z * 0.20000000298023224D) * f);
+
+ this.playSwimSound(f1);
+ }
+
+- protected BlockPos getPrimaryStepSoundBlockPos(BlockPos blockpos) {
+- BlockPos blockpos1 = blockpos.above();
+- BlockState blockstate = this.level().getBlockState(blockpos1);
++ protected BlockPos getPrimaryStepSoundBlockPos(BlockPos pos) {
++ BlockPos blockposition1 = pos.above();
++ IBlockData iblockdata = this.level().getBlockState(blockposition1);
+
+- return !blockstate.is(BlockTags.INSIDE_STEP_SOUND_BLOCKS) && !blockstate.is(BlockTags.COMBINATION_STEP_SOUND_BLOCKS) ? blockpos : blockpos1;
++ return !iblockdata.is(BlockTags.INSIDE_STEP_SOUND_BLOCKS) && !iblockdata.is(BlockTags.COMBINATION_STEP_SOUND_BLOCKS) ? pos : blockposition1;
+ }
+
+- protected void playCombinationStepSounds(BlockState blockstate, BlockState blockstate1) {
+- SoundType soundtype = blockstate.getSoundType();
++ protected void playCombinationStepSounds(IBlockData primaryState, IBlockData secondaryState) {
++ SoundType soundeffecttype = primaryState.getSoundType();
+
+- this.playSound(soundtype.getStepSound(), soundtype.getVolume() * 0.15F, soundtype.getPitch());
+- this.playMuffledStepSound(blockstate1);
++ this.playSound(soundeffecttype.getStepSound(), soundeffecttype.getVolume() * 0.15F, soundeffecttype.getPitch());
++ this.playMuffledStepSound(secondaryState);
+ }
+
+- protected void playMuffledStepSound(BlockState blockstate) {
+- SoundType soundtype = blockstate.getSoundType();
++ protected void playMuffledStepSound(IBlockData state) {
++ SoundType soundeffecttype = state.getSoundType();
+
+- this.playSound(soundtype.getStepSound(), soundtype.getVolume() * 0.05F, soundtype.getPitch() * 0.8F);
++ this.playSound(soundeffecttype.getStepSound(), soundeffecttype.getVolume() * 0.05F, soundeffecttype.getPitch() * 0.8F);
+ }
+
+- protected void playStepSound(BlockPos blockpos, BlockState blockstate) {
+- SoundType soundtype = blockstate.getSoundType();
++ protected void playStepSound(BlockPos pos, IBlockData state) {
++ SoundType soundeffecttype = state.getSoundType();
+
+- this.playSound(soundtype.getStepSound(), soundtype.getVolume() * 0.15F, soundtype.getPitch());
++ this.playSound(soundeffecttype.getStepSound(), soundeffecttype.getVolume() * 0.15F, soundeffecttype.getPitch());
+ }
+
+- private boolean shouldPlayAmethystStepSound(BlockState blockstate) {
+- return blockstate.is(BlockTags.CRYSTAL_SOUND_BLOCKS) && this.tickCount >= this.lastCrystalSoundPlayTick + 20;
++ private boolean shouldPlayAmethystStepSound(IBlockData state) {
++ return state.is(BlockTags.CRYSTAL_SOUND_BLOCKS) && this.tickCount >= this.lastCrystalSoundPlayTick + 20;
+ }
+
+ private void playAmethystStepSound() {
+@@ -1131,8 +1321,8 @@
+ this.lastCrystalSoundPlayTick = this.tickCount;
+ }
+
+- protected void playSwimSound(float f) {
+- this.playSound(this.getSwimSound(), f, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F);
++ protected void playSwimSound(float volume) {
++ this.playSound(this.getSwimSound(), volume, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F);
+ }
+
+ protected void onFlap() {}
+@@ -1141,16 +1331,16 @@
+ return false;
+ }
+
+- public void playSound(SoundEvent soundevent, float f, float f1) {
++ public void playSound(SoundEvent sound, float volume, float pitch) {
+ if (!this.isSilent()) {
+- this.level().playSound((Player) null, this.getX(), this.getY(), this.getZ(), soundevent, this.getSoundSource(), f, f1);
++ this.level().playSound((Player) null, this.getX(), this.getY(), this.getZ(), sound, this.getSoundSource(), volume, pitch);
+ }
+
+ }
+
+- public void playSound(SoundEvent soundevent) {
++ public void playSound(SoundEvent sound) {
+ if (!this.isSilent()) {
+- this.playSound(soundevent, 1.0F, 1.0F);
++ this.playSound(sound, 1.0F, 1.0F);
+ }
+
+ }
+@@ -1159,16 +1349,16 @@
+ return (Boolean) this.entityData.get(Entity.DATA_SILENT);
+ }
+
+- public void setSilent(boolean flag) {
+- this.entityData.set(Entity.DATA_SILENT, flag);
++ public void setSilent(boolean isSilent) {
++ this.entityData.set(Entity.DATA_SILENT, isSilent);
+ }
+
+ public boolean isNoGravity() {
+ return (Boolean) this.entityData.get(Entity.DATA_NO_GRAVITY);
+ }
+
+- public void setNoGravity(boolean flag) {
+- this.entityData.set(Entity.DATA_NO_GRAVITY, flag);
++ public void setNoGravity(boolean noGravity) {
++ this.entityData.set(Entity.DATA_NO_GRAVITY, noGravity);
+ }
+
+ protected Entity.MovementEmission getMovementEmission() {
+@@ -1179,18 +1369,18 @@
+ return false;
+ }
+
+- protected void checkFallDamage(double d0, boolean flag, BlockState blockstate, BlockPos blockpos) {
++ protected void checkFallDamage(double y, boolean flag, IBlockData onGround, BlockPos state) {
+ if (flag) {
+ if (this.fallDistance > 0.0F) {
+- blockstate.getBlock().fallOn(this.level(), blockstate, blockpos, this, this.fallDistance);
+- this.level().gameEvent(GameEvent.HIT_GROUND, this.position, GameEvent.Context.of(this, (BlockState) this.mainSupportingBlockPos.map((blockpos1) -> {
+- return this.level().getBlockState(blockpos1);
+- }).orElse(blockstate)));
++ onGround.getBlock().fallOn(this.level(), onGround, state, this, this.fallDistance);
++ this.level().gameEvent(GameEvent.HIT_GROUND, this.position, GameEvent.Context.of(this, (IBlockData) this.mainSupportingBlockPos.map((blockposition1) -> {
++ return this.level().getBlockState(blockposition1);
++ }).orElse(onGround)));
+ }
+
+ this.resetFallDistance();
+- } else if (d0 < 0.0D) {
+- this.fallDistance -= (float) d0;
++ } else if (y < 0.0D) {
++ this.fallDistance -= (float) y;
+ }
+
+ }
+@@ -1199,7 +1389,7 @@
+ return this.getType().fireImmune();
+ }
+
+- public boolean causeFallDamage(float f, float f1, DamageSource damagesource) {
++ public boolean causeFallDamage(float fallDistance, float multiplier, DamageSource source) {
+ if (this.type.is(EntityTypeTags.FALL_DAMAGE_IMMUNE)) {
+ return false;
+ } else {
+@@ -1209,7 +1399,7 @@
+ while (iterator.hasNext()) {
+ Entity entity = (Entity) iterator.next();
+
+- entity.causeFallDamage(f, f1, damagesource);
++ entity.causeFallDamage(fallDistance, multiplier, source);
+ }
+ }
+
+@@ -1222,9 +1412,9 @@
+ }
+
+ private boolean isInRain() {
+- BlockPos blockpos = this.blockPosition();
++ BlockPos blockposition = this.blockPosition();
+
+- return this.level().isRainingAt(blockpos) || this.level().isRainingAt(BlockPos.containing((double) blockpos.getX(), this.getBoundingBox().maxY, (double) blockpos.getZ()));
++ return this.level().isRainingAt(blockposition) || this.level().isRainingAt(BlockPos.containing((double) blockposition.getX(), this.getBoundingBox().maxY, (double) blockposition.getZ()));
+ }
+
+ private boolean isInBubbleColumn() {
+@@ -1273,9 +1463,9 @@
+ Entity entity = this.getVehicle();
+
+ if (entity instanceof Boat) {
+- Boat boat = (Boat) entity;
++ Boat entityboat = (Boat) entity;
+
+- if (!boat.isUnderWater()) {
++ if (!entityboat.isUnderWater()) {
+ this.wasTouchingWater = false;
+ return;
+ }
+@@ -1302,19 +1492,19 @@
+ Entity entity = this.getVehicle();
+
+ if (entity instanceof Boat) {
+- Boat boat = (Boat) entity;
++ Boat entityboat = (Boat) entity;
+
+- if (!boat.isUnderWater() && boat.getBoundingBox().maxY >= d0 && boat.getBoundingBox().minY <= d0) {
++ if (!entityboat.isUnderWater() && entityboat.getBoundingBox().maxY >= d0 && entityboat.getBoundingBox().minY <= d0) {
+ return;
+ }
+ }
+
+- BlockPos blockpos = BlockPos.containing(this.getX(), d0, this.getZ());
+- FluidState fluidstate = this.level().getFluidState(blockpos);
+- double d1 = (double) ((float) blockpos.getY() + fluidstate.getHeight(this.level(), blockpos));
++ BlockPos blockposition = BlockPos.containing(this.getX(), d0, this.getZ());
++ FluidState fluid = this.level().getFluidState(blockposition);
++ double d1 = (double) ((float) blockposition.getY() + fluid.getHeight(this.level(), blockposition));
+
+ if (d1 > d0) {
+- Stream stream = fluidstate.getTags();
++ Stream stream = fluid.getTags();
+ Set set = this.fluidOnEyes;
+
+ Objects.requireNonNull(this.fluidOnEyes);
+@@ -1326,8 +1516,8 @@
+ protected void doWaterSplashEffect() {
+ Entity entity = (Entity) Objects.requireNonNullElse(this.getControllingPassenger(), this);
+ float f = entity == this ? 0.2F : 0.9F;
+- Vec3 vec3 = entity.getDeltaMovement();
+- float f1 = Math.min(1.0F, (float) Math.sqrt(vec3.x * vec3.x * 0.20000000298023224D + vec3.y * vec3.y + vec3.z * vec3.z * 0.20000000298023224D) * f);
++ Vec3 vec3d = entity.getDeltaMovement();
++ float f1 = Math.min(1.0F, (float) Math.sqrt(vec3d.x * vec3d.x * 0.20000000298023224D + vec3d.y * vec3d.y + vec3d.z * vec3d.z * 0.20000000298023224D) * f);
+
+ if (f1 < 0.25F) {
+ this.playSound(this.getSwimSplashSound(), f1, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F);
+@@ -1344,13 +1534,13 @@
+ for (i = 0; (float) i < 1.0F + this.dimensions.width * 20.0F; ++i) {
+ d0 = (this.random.nextDouble() * 2.0D - 1.0D) * (double) this.dimensions.width;
+ d1 = (this.random.nextDouble() * 2.0D - 1.0D) * (double) this.dimensions.width;
+- this.level().addParticle(ParticleTypes.BUBBLE, this.getX() + d0, (double) (f2 + 1.0F), this.getZ() + d1, vec3.x, vec3.y - this.random.nextDouble() * 0.20000000298023224D, vec3.z);
++ this.level().addParticle(ParticleTypes.BUBBLE, this.getX() + d0, (double) (f2 + 1.0F), this.getZ() + d1, vec3d.x, vec3d.y - this.random.nextDouble() * 0.20000000298023224D, vec3d.z);
+ }
+
+ for (i = 0; (float) i < 1.0F + this.dimensions.width * 20.0F; ++i) {
+ d0 = (this.random.nextDouble() * 2.0D - 1.0D) * (double) this.dimensions.width;
+ d1 = (this.random.nextDouble() * 2.0D - 1.0D) * (double) this.dimensions.width;
+- this.level().addParticle(ParticleTypes.SPLASH, this.getX() + d0, (double) (f2 + 1.0F), this.getZ() + d1, vec3.x, vec3.y, vec3.z);
++ this.level().addParticle(ParticleTypes.SPLASH, this.getX() + d0, (double) (f2 + 1.0F), this.getZ() + d1, vec3d.x, vec3d.y, vec3d.z);
+ }
+
+ this.gameEvent(GameEvent.SPLASH);
+@@ -1358,11 +1548,11 @@
+
+ /** @deprecated */
+ @Deprecated
+- protected BlockState getBlockStateOnLegacy() {
++ protected IBlockData getBlockStateOnLegacy() {
+ return this.level().getBlockState(this.getOnPosLegacy());
+ }
+
+- public BlockState getBlockStateOn() {
++ public IBlockData getBlockStateOn() {
+ return this.level().getBlockState(this.getOnPos());
+ }
+
+@@ -1371,53 +1561,53 @@
+ }
+
+ protected void spawnSprintParticle() {
+- BlockPos blockpos = this.getOnPosLegacy();
+- BlockState blockstate = this.level().getBlockState(blockpos);
++ BlockPos blockposition = this.getOnPosLegacy();
++ IBlockData iblockdata = this.level().getBlockState(blockposition);
+
+- if (blockstate.getRenderShape() != RenderShape.INVISIBLE) {
+- Vec3 vec3 = this.getDeltaMovement();
+- BlockPos blockpos1 = this.blockPosition();
++ if (iblockdata.getRenderShape() != EnumRenderType.INVISIBLE) {
++ Vec3 vec3d = this.getDeltaMovement();
++ BlockPos blockposition1 = this.blockPosition();
+ double d0 = this.getX() + (this.random.nextDouble() - 0.5D) * (double) this.dimensions.width;
+ double d1 = this.getZ() + (this.random.nextDouble() - 0.5D) * (double) this.dimensions.width;
+
+- if (blockpos1.getX() != blockpos.getX()) {
+- d0 = Mth.clamp(d0, (double) blockpos.getX(), (double) blockpos.getX() + 1.0D);
++ if (blockposition1.getX() != blockposition.getX()) {
++ d0 = Mth.clamp(d0, (double) blockposition.getX(), (double) blockposition.getX() + 1.0D);
+ }
+
+- if (blockpos1.getZ() != blockpos.getZ()) {
+- d1 = Mth.clamp(d1, (double) blockpos.getZ(), (double) blockpos.getZ() + 1.0D);
++ if (blockposition1.getZ() != blockposition.getZ()) {
++ d1 = Mth.clamp(d1, (double) blockposition.getZ(), (double) blockposition.getZ() + 1.0D);
+ }
+
+- this.level().addParticle(new BlockParticleOption(ParticleTypes.BLOCK, blockstate), d0, this.getY() + 0.1D, d1, vec3.x * -4.0D, 1.5D, vec3.z * -4.0D);
++ this.level().addParticle(new BlockParticleOption(ParticleTypes.BLOCK, iblockdata), d0, this.getY() + 0.1D, d1, vec3d.x * -4.0D, 1.5D, vec3d.z * -4.0D);
+ }
+
+ }
+
+- public boolean isEyeInFluid(TagKey<Fluid> tagkey) {
+- return this.fluidOnEyes.contains(tagkey);
++ public boolean isEyeInFluid(TagKey<Fluid> fluidTag) {
++ return this.fluidOnEyes.contains(fluidTag);
+ }
+
+ public boolean isInLava() {
+ return !this.firstTick && this.fluidHeight.getDouble(FluidTags.LAVA) > 0.0D;
+ }
+
+- public void moveRelative(float f, Vec3 vec3) {
+- Vec3 vec31 = getInputVector(vec3, f, this.getYRot());
++ public void moveRelative(float amount, Vec3 relative) {
++ Vec3 vec3d1 = getInputVector(relative, amount, this.getYRot());
+
+- this.setDeltaMovement(this.getDeltaMovement().add(vec31));
++ this.setDeltaMovement(this.getDeltaMovement().add(vec3d1));
+ }
+
+- private static Vec3 getInputVector(Vec3 vec3, float f, float f1) {
+- double d0 = vec3.lengthSqr();
++ private static Vec3 getInputVector(Vec3 relative, float motionScaler, float facing) {
++ double d0 = relative.lengthSqr();
+
+ if (d0 < 1.0E-7D) {
+ return Vec3.ZERO;
+ } else {
+- Vec3 vec31 = (d0 > 1.0D ? vec3.normalize() : vec3).scale((double) f);
+- float f2 = Mth.sin(f1 * 0.017453292F);
+- float f3 = Mth.cos(f1 * 0.017453292F);
++ Vec3 vec3d1 = (d0 > 1.0D ? relative.normalize() : relative).scale((double) motionScaler);
++ float f2 = Mth.sin(facing * 0.017453292F);
++ float f3 = Mth.cos(facing * 0.017453292F);
+
+- return new Vec3(vec31.x * (double) f3 - vec31.z * (double) f2, vec31.y, vec31.z * (double) f3 + vec31.x * (double) f2);
++ return new Vec3(vec3d1.x * (double) f3 - vec3d1.z * (double) f2, vec3d1.y, vec3d1.z * (double) f3 + vec3d1.x * (double) f2);
+ }
+ }
+
+@@ -1427,40 +1617,41 @@
+ return this.level().hasChunkAt(this.getBlockX(), this.getBlockZ()) ? this.level().getLightLevelDependentMagicValue(BlockPos.containing(this.getX(), this.getEyeY(), this.getZ())) : 0.0F;
+ }
+
+- public void absMoveTo(double d0, double d1, double d2, float f, float f1) {
+- this.absMoveTo(d0, d1, d2);
++ public void absMoveTo(double x, double d1, double y, float f, float z) {
++ this.absMoveTo(x, d1, y);
+ this.setYRot(f % 360.0F);
+- this.setXRot(Mth.clamp(f1, -90.0F, 90.0F) % 360.0F);
++ this.setXRot(Mth.clamp(z, -90.0F, 90.0F) % 360.0F);
+ this.yRotO = this.getYRot();
+ this.xRotO = this.getXRot();
+ }
+
+- public void absMoveTo(double d0, double d1, double d2) {
+- double d3 = Mth.clamp(d0, -3.0E7D, 3.0E7D);
+- double d4 = Mth.clamp(d2, -3.0E7D, 3.0E7D);
++ public void absMoveTo(double x, double d1, double y) {
++ double d3 = Mth.clamp(x, -3.0E7D, 3.0E7D);
++ double d4 = Mth.clamp(y, -3.0E7D, 3.0E7D);
+
+ this.xo = d3;
+ this.yo = d1;
+ this.zo = d4;
+ this.setPos(d3, d1, d4);
++ if (valid) level.getChunk((int) Math.floor(this.getX()) >> 4, (int) Math.floor(this.getZ()) >> 4); // CraftBukkit
+ }
+
+- public void moveTo(Vec3 vec3) {
+- this.moveTo(vec3.x, vec3.y, vec3.z);
++ public void moveTo(Vec3 vec) {
++ this.moveTo(vec.x, vec.y, vec.z);
+ }
+
+- public void moveTo(double d0, double d1, double d2) {
+- this.moveTo(d0, d1, d2, this.getYRot(), this.getXRot());
++ public void moveTo(double x, double d1, double y) {
++ this.moveTo(x, d1, y, this.getYRot(), this.getXRot());
+ }
+
+- public void moveTo(BlockPos blockpos, float f, float f1) {
+- this.moveTo((double) blockpos.getX() + 0.5D, (double) blockpos.getY(), (double) blockpos.getZ() + 0.5D, f, f1);
++ public void moveTo(BlockPos pos, float yRot, float xRot) {
++ this.moveTo((double) pos.getX() + 0.5D, (double) pos.getY(), (double) pos.getZ() + 0.5D, yRot, xRot);
+ }
+
+- public void moveTo(double d0, double d1, double d2, float f, float f1) {
+- this.setPosRaw(d0, d1, d2);
++ public void moveTo(double x, double d1, double y, float f, float z) {
++ this.setPosRaw(x, d1, y);
+ this.setYRot(f);
+- this.setXRot(f1);
++ this.setXRot(z);
+ this.setOldPosAndRot();
+ this.reapplyPosition();
+ }
+@@ -1488,10 +1679,10 @@
+ return Mth.sqrt(f * f + f1 * f1 + f2 * f2);
+ }
+
+- public double distanceToSqr(double d0, double d1, double d2) {
+- double d3 = this.getX() - d0;
++ public double distanceToSqr(double x, double d1, double y) {
++ double d3 = this.getX() - x;
+ double d4 = this.getY() - d1;
+- double d5 = this.getZ() - d2;
++ double d5 = this.getZ() - y;
+
+ return d3 * d3 + d4 * d4 + d5 * d5;
+ }
+@@ -1500,10 +1691,10 @@
+ return this.distanceToSqr(entity.position());
+ }
+
+- public double distanceToSqr(Vec3 vec3) {
+- double d0 = this.getX() - vec3.x;
+- double d1 = this.getY() - vec3.y;
+- double d2 = this.getZ() - vec3.z;
++ public double distanceToSqr(Vec3 vec) {
++ double d0 = this.getX() - vec.x;
++ double d1 = this.getY() - vec.y;
++ double d2 = this.getZ() - vec.z;
+
+ return d0 * d0 + d1 * d1 + d2 * d2;
+ }
+@@ -1544,8 +1735,8 @@
+ }
+ }
+
+- public void push(double d0, double d1, double d2) {
+- this.setDeltaMovement(this.getDeltaMovement().add(d0, d1, d2));
++ public void push(double x, double d1, double y) {
++ this.setDeltaMovement(this.getDeltaMovement().add(x, d1, y));
+ this.hasImpulse = true;
+ }
+
+@@ -1553,8 +1744,8 @@
+ this.hurtMarked = true;
+ }
+
+- public boolean hurt(DamageSource damagesource, float f) {
+- if (this.isInvulnerableTo(damagesource)) {
++ public boolean hurt(DamageSource source, float amount) {
++ if (this.isInvulnerableTo(source)) {
+ return false;
+ } else {
+ this.markHurt();
+@@ -1562,21 +1753,21 @@
+ }
+ }
+
+- public final Vec3 getViewVector(float f) {
+- return this.calculateViewVector(this.getViewXRot(f), this.getViewYRot(f));
++ public final Vec3 getViewVector(float partialTicks) {
++ return this.calculateViewVector(this.getViewXRot(partialTicks), this.getViewYRot(partialTicks));
+ }
+
+- public float getViewXRot(float f) {
+- return f == 1.0F ? this.getXRot() : Mth.lerp(f, this.xRotO, this.getXRot());
++ public float getViewXRot(float partialTicks) {
++ return partialTicks == 1.0F ? this.getXRot() : Mth.lerp(partialTicks, this.xRotO, this.getXRot());
+ }
+
+- public float getViewYRot(float f) {
+- return f == 1.0F ? this.getYRot() : Mth.lerp(f, this.yRotO, this.getYRot());
++ public float getViewYRot(float partialTick) {
++ return partialTick == 1.0F ? this.getYRot() : Mth.lerp(partialTick, this.yRotO, this.getYRot());
+ }
+
+- protected final Vec3 calculateViewVector(float f, float f1) {
+- float f2 = f * 0.017453292F;
+- float f3 = -f1 * 0.017453292F;
++ protected final Vec3 calculateViewVector(float xRot, float yRot) {
++ float f2 = xRot * 0.017453292F;
++ float f3 = -yRot * 0.017453292F;
+ float f4 = Mth.cos(f3);
+ float f5 = Mth.sin(f3);
+ float f6 = Mth.cos(f2);
+@@ -1585,44 +1776,44 @@
+ return new Vec3((double) (f5 * f6), (double) (-f7), (double) (f4 * f6));
+ }
+
+- public final Vec3 getUpVector(float f) {
+- return this.calculateUpVector(this.getViewXRot(f), this.getViewYRot(f));
++ public final Vec3 getUpVector(float partialTicks) {
++ return this.calculateUpVector(this.getViewXRot(partialTicks), this.getViewYRot(partialTicks));
+ }
+
+- protected final Vec3 calculateUpVector(float f, float f1) {
+- return this.calculateViewVector(f - 90.0F, f1);
++ protected final Vec3 calculateUpVector(float xRot, float yRot) {
++ return this.calculateViewVector(xRot - 90.0F, yRot);
+ }
+
+ public final Vec3 getEyePosition() {
+ return new Vec3(this.getX(), this.getEyeY(), this.getZ());
+ }
+
+- public final Vec3 getEyePosition(float f) {
+- double d0 = Mth.lerp((double) f, this.xo, this.getX());
+- double d1 = Mth.lerp((double) f, this.yo, this.getY()) + (double) this.getEyeHeight();
+- double d2 = Mth.lerp((double) f, this.zo, this.getZ());
++ public final Vec3 getEyePosition(float partialTicks) {
++ double d0 = Mth.lerp((double) partialTicks, this.xo, this.getX());
++ double d1 = Mth.lerp((double) partialTicks, this.yo, this.getY()) + (double) this.getEyeHeight();
++ double d2 = Mth.lerp((double) partialTicks, this.zo, this.getZ());
+
+ return new Vec3(d0, d1, d2);
+ }
+
+- public Vec3 getLightProbePosition(float f) {
+- return this.getEyePosition(f);
++ public Vec3 getLightProbePosition(float partialTicks) {
++ return this.getEyePosition(partialTicks);
+ }
+
+- public final Vec3 getPosition(float f) {
+- double d0 = Mth.lerp((double) f, this.xo, this.getX());
+- double d1 = Mth.lerp((double) f, this.yo, this.getY());
+- double d2 = Mth.lerp((double) f, this.zo, this.getZ());
++ public final Vec3 getPosition(float partialTicks) {
++ double d0 = Mth.lerp((double) partialTicks, this.xo, this.getX());
++ double d1 = Mth.lerp((double) partialTicks, this.yo, this.getY());
++ double d2 = Mth.lerp((double) partialTicks, this.zo, this.getZ());
+
+ return new Vec3(d0, d1, d2);
+ }
+
+- public HitResult pick(double d0, float f, boolean flag) {
+- Vec3 vec3 = this.getEyePosition(f);
+- Vec3 vec31 = this.getViewVector(f);
+- Vec3 vec32 = vec3.add(vec31.x * d0, vec31.y * d0, vec31.z * d0);
++ public HitResult pick(double hitDistance, float f, boolean partialTicks) {
++ Vec3 vec3d = this.getEyePosition(f);
++ Vec3 vec3d1 = this.getViewVector(f);
++ Vec3 vec3d2 = vec3d.add(vec3d1.x * hitDistance, vec3d1.y * hitDistance, vec3d1.z * hitDistance);
+
+- return this.level().clip(new ClipContext(vec3, vec32, ClipContext.Block.OUTLINE, flag ? ClipContext.Fluid.ANY : ClipContext.Fluid.NONE, this));
++ return this.level().clip(new ClipContext(vec3d, vec3d2, ClipContext.Block.OUTLINE, partialTicks ? ClipContext.Fluid.ANY : ClipContext.Fluid.NONE, this));
+ }
+
+ public boolean canBeHitByProjectile() {
+@@ -1637,23 +1828,29 @@
+ return false;
+ }
+
+- public void awardKillScore(Entity entity, int i, DamageSource damagesource) {
+- if (entity instanceof ServerPlayer) {
+- CriteriaTriggers.ENTITY_KILLED_PLAYER.trigger((ServerPlayer) entity, this, damagesource);
++ // CraftBukkit start - collidable API
++ public boolean canCollideWithBukkit(Entity entity) {
++ return isPushable();
++ }
++ // CraftBukkit end
++
++ public void awardKillScore(Entity killed, int scoreValue, DamageSource source) {
++ if (killed instanceof ServerPlayer) {
++ CriteriaTriggers.ENTITY_KILLED_PLAYER.trigger((ServerPlayer) killed, this, source);
+ }
+
+ }
+
+- public boolean shouldRender(double d0, double d1, double d2) {
+- double d3 = this.getX() - d0;
++ public boolean shouldRender(double x, double d1, double y) {
++ double d3 = this.getX() - x;
+ double d4 = this.getY() - d1;
+- double d5 = this.getZ() - d2;
++ double d5 = this.getZ() - y;
+ double d6 = d3 * d3 + d4 * d4 + d5 * d5;
+
+ return this.shouldRenderAtSqrDistance(d6);
+ }
+
+- public boolean shouldRenderAtSqrDistance(double d0) {
++ public boolean shouldRenderAtSqrDistance(double distance) {
+ double d1 = this.getBoundingBox().getSize();
+
+ if (Double.isNaN(d1)) {
+@@ -1661,154 +1858,208 @@
+ }
+
+ d1 *= 64.0D * Entity.viewScale;
+- return d0 < d1 * d1;
++ return distance < d1 * d1;
+ }
+
+- public boolean saveAsPassenger(CompoundTag compoundtag) {
++ public boolean saveAsPassenger(CompoundTag compound) {
++ // CraftBukkit start - allow excluding certain data when saving
++ return saveAsPassenger(compound, true);
++ }
++
++ public boolean saveAsPassenger(CompoundTag nbttagcompound, boolean includeAll) {
++ // CraftBukkit end
+ if (this.removalReason != null && !this.removalReason.shouldSave()) {
+ return false;
+ } else {
+ String s = this.getEncodeId();
+
+- if (s == null) {
++ if (!this.persist || s == null) { // CraftBukkit - persist flag
+ return false;
+ } else {
+- compoundtag.putString("id", s);
+- this.saveWithoutId(compoundtag);
++ nbttagcompound.putString("id", s);
++ this.saveWithoutId(nbttagcompound, includeAll); // CraftBukkit - pass on includeAll
+ return true;
+ }
+ }
+ }
+
+- public boolean save(CompoundTag compoundtag) {
+- return this.isPassenger() ? false : this.saveAsPassenger(compoundtag);
++ public boolean save(CompoundTag compound) {
++ return this.isPassenger() ? false : this.saveAsPassenger(compound);
+ }
+
+- public CompoundTag saveWithoutId(CompoundTag compoundtag) {
++ public CompoundTag saveWithoutId(CompoundTag compound) {
++ // CraftBukkit start - allow excluding certain data when saving
++ return saveWithoutId(compound, true);
++ }
++
++ public CompoundTag saveWithoutId(CompoundTag nbttagcompound, boolean includeAll) {
++ // CraftBukkit end
+ try {
+- if (this.vehicle != null) {
+- compoundtag.put("Pos", this.newDoubleList(this.vehicle.getX(), this.getY(), this.vehicle.getZ()));
+- } else {
+- compoundtag.put("Pos", this.newDoubleList(this.getX(), this.getY(), this.getZ()));
++ // CraftBukkit start - selectively save position
++ if (includeAll) {
++ if (this.vehicle != null) {
++ nbttagcompound.put("Pos", this.newDoubleList(this.vehicle.getX(), this.getY(), this.vehicle.getZ()));
++ } else {
++ nbttagcompound.put("Pos", this.newDoubleList(this.getX(), this.getY(), this.getZ()));
++ }
+ }
++ // CraftBukkit end
+
+- Vec3 vec3 = this.getDeltaMovement();
++ Vec3 vec3d = this.getDeltaMovement();
+
+- compoundtag.put("Motion", this.newDoubleList(vec3.x, vec3.y, vec3.z));
+- compoundtag.put("Rotation", this.newFloatList(this.getYRot(), this.getXRot()));
+- compoundtag.putFloat("FallDistance", this.fallDistance);
+- compoundtag.putShort("Fire", (short) this.remainingFireTicks);
+- compoundtag.putShort("Air", (short) this.getAirSupply());
+- compoundtag.putBoolean("OnGround", this.onGround());
+- compoundtag.putBoolean("Invulnerable", this.invulnerable);
+- compoundtag.putInt("PortalCooldown", this.portalCooldown);
+- compoundtag.putUUID("UUID", this.getUUID());
+- Component component = this.getCustomName();
++ nbttagcompound.put("Motion", this.newDoubleList(vec3d.x, vec3d.y, vec3d.z));
+
+- if (component != null) {
+- compoundtag.putString("CustomName", Component.Serializer.toJson(component));
++ // 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.yRot)) {
++ this.yRot = 0;
+ }
+
++ if (Float.isNaN(this.xRot)) {
++ this.xRot = 0;
++ }
++ // CraftBukkit end
++
++ nbttagcompound.put("Rotation", this.newFloatList(this.getYRot(), this.getXRot()));
++ nbttagcompound.putFloat("FallDistance", this.fallDistance);
++ nbttagcompound.putShort("Fire", (short) this.remainingFireTicks);
++ nbttagcompound.putShort("Air", (short) this.getAirSupply());
++ nbttagcompound.putBoolean("OnGround", this.onGround());
++ nbttagcompound.putBoolean("Invulnerable", this.invulnerable);
++ nbttagcompound.putInt("PortalCooldown", this.portalCooldown);
++ // CraftBukkit start - selectively save uuid and world
++ if (includeAll) {
++ nbttagcompound.putUUID("UUID", this.getUUID());
++ // PAIL: Check above UUID reads 1.8 properly, ie: UUIDMost / UUIDLeast
++ nbttagcompound.putLong("WorldUUIDLeast", ((ServerLevel) this.level).getWorld().getUID().getLeastSignificantBits());
++ nbttagcompound.putLong("WorldUUIDMost", ((ServerLevel) this.level).getWorld().getUID().getMostSignificantBits());
++ }
++ nbttagcompound.putInt("Bukkit.updateLevel", CURRENT_LEVEL);
++ if (!this.persist) {
++ nbttagcompound.putBoolean("Bukkit.persist", this.persist);
++ }
++ if (!this.visibleByDefault) {
++ nbttagcompound.putBoolean("Bukkit.visibleByDefault", this.visibleByDefault);
++ }
++ if (this.persistentInvisibility) {
++ nbttagcompound.putBoolean("Bukkit.invisible", this.persistentInvisibility);
++ }
++ // SPIGOT-6907: re-implement LivingEntity#setMaximumAir()
++ if (maxAirTicks != getDefaultMaxAirSupply()) {
++ nbttagcompound.putInt("Bukkit.MaxAirSupply", getMaxAirSupply());
++ }
++ // CraftBukkit end
++ Component ichatbasecomponent = this.getCustomName();
++
++ if (ichatbasecomponent != null) {
++ nbttagcompound.putString("CustomName", Component.Serializer.toJson(ichatbasecomponent));
++ }
++
+ if (this.isCustomNameVisible()) {
+- compoundtag.putBoolean("CustomNameVisible", this.isCustomNameVisible());
++ nbttagcompound.putBoolean("CustomNameVisible", this.isCustomNameVisible());
+ }
+
+ if (this.isSilent()) {
+- compoundtag.putBoolean("Silent", this.isSilent());
++ nbttagcompound.putBoolean("Silent", this.isSilent());
+ }
+
+ if (this.isNoGravity()) {
+- compoundtag.putBoolean("NoGravity", this.isNoGravity());
++ nbttagcompound.putBoolean("NoGravity", this.isNoGravity());
+ }
+
+ if (this.hasGlowingTag) {
+- compoundtag.putBoolean("Glowing", true);
++ nbttagcompound.putBoolean("Glowing", true);
+ }
+
+ int i = this.getTicksFrozen();
+
+ if (i > 0) {
+- compoundtag.putInt("TicksFrozen", this.getTicksFrozen());
++ nbttagcompound.putInt("TicksFrozen", this.getTicksFrozen());
+ }
+
+ if (this.hasVisualFire) {
+- compoundtag.putBoolean("HasVisualFire", this.hasVisualFire);
++ nbttagcompound.putBoolean("HasVisualFire", this.hasVisualFire);
+ }
+
+- ListTag listtag;
++ ListTag nbttaglist;
+ Iterator iterator;
+
+ if (!this.tags.isEmpty()) {
+- listtag = new ListTag();
++ nbttaglist = new ListTag();
+ iterator = this.tags.iterator();
+
+ while (iterator.hasNext()) {
+ String s = (String) iterator.next();
+
+- listtag.add(StringTag.valueOf(s));
++ nbttaglist.add(StringTag.valueOf(s));
+ }
+
+- compoundtag.put("Tags", listtag);
++ nbttagcompound.put("Tags", nbttaglist);
+ }
+
+- this.addAdditionalSaveData(compoundtag);
++ this.addAdditionalSaveData(nbttagcompound, includeAll); // CraftBukkit - pass on includeAll
+ if (this.isVehicle()) {
+- listtag = new ListTag();
++ nbttaglist = new ListTag();
+ iterator = this.getPassengers().iterator();
+
+ while (iterator.hasNext()) {
+ Entity entity = (Entity) iterator.next();
+- CompoundTag compoundtag1 = new CompoundTag();
++ CompoundTag nbttagcompound1 = new CompoundTag();
+
+- if (entity.saveAsPassenger(compoundtag1)) {
+- listtag.add(compoundtag1);
++ if (entity.saveAsPassenger(nbttagcompound1, includeAll)) { // CraftBukkit - pass on includeAll
++ nbttaglist.add(nbttagcompound1);
+ }
+ }
+
+- if (!listtag.isEmpty()) {
+- compoundtag.put("Passengers", listtag);
++ if (!nbttaglist.isEmpty()) {
++ nbttagcompound.put("Passengers", nbttaglist);
+ }
+ }
+
+- return compoundtag;
++ // 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.forThrowable(throwable, "Saving entity NBT");
+- CrashReportCategory crashreportcategory = crashreport.addCategory("Entity being saved");
++ CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Entity being saved");
+
+- this.fillCrashReportCategory(crashreportcategory);
++ this.fillCrashReportCategory(crashreportsystemdetails);
+ throw new ReportedException(crashreport);
+ }
+ }
+
+- public void load(CompoundTag compoundtag) {
++ public void load(CompoundTag compound) {
+ try {
+- ListTag listtag = compoundtag.getList("Pos", 6);
+- ListTag listtag1 = compoundtag.getList("Motion", 6);
+- ListTag listtag2 = compoundtag.getList("Rotation", 5);
+- double d0 = listtag1.getDouble(0);
+- double d1 = listtag1.getDouble(1);
+- double d2 = listtag1.getDouble(2);
++ ListTag nbttaglist = compound.getList("Pos", 6);
++ ListTag nbttaglist1 = compound.getList("Motion", 6);
++ ListTag nbttaglist2 = compound.getList("Rotation", 5);
++ double d0 = nbttaglist1.getDouble(0);
++ double d1 = nbttaglist1.getDouble(1);
++ double d2 = nbttaglist1.getDouble(2);
+
+ this.setDeltaMovement(Math.abs(d0) > 10.0D ? 0.0D : d0, Math.abs(d1) > 10.0D ? 0.0D : d1, Math.abs(d2) > 10.0D ? 0.0D : d2);
+ double d3 = 3.0000512E7D;
+
+- this.setPosRaw(Mth.clamp(listtag.getDouble(0), -3.0000512E7D, 3.0000512E7D), Mth.clamp(listtag.getDouble(1), -2.0E7D, 2.0E7D), Mth.clamp(listtag.getDouble(2), -3.0000512E7D, 3.0000512E7D));
+- this.setYRot(listtag2.getFloat(0));
+- this.setXRot(listtag2.getFloat(1));
++ this.setPosRaw(Mth.clamp(nbttaglist.getDouble(0), -3.0000512E7D, 3.0000512E7D), Mth.clamp(nbttaglist.getDouble(1), -2.0E7D, 2.0E7D), Mth.clamp(nbttaglist.getDouble(2), -3.0000512E7D, 3.0000512E7D));
++ this.setYRot(nbttaglist2.getFloat(0));
++ this.setXRot(nbttaglist2.getFloat(1));
+ this.setOldPosAndRot();
+ this.setYHeadRot(this.getYRot());
+ this.setYBodyRot(this.getYRot());
+- this.fallDistance = compoundtag.getFloat("FallDistance");
+- this.remainingFireTicks = compoundtag.getShort("Fire");
+- if (compoundtag.contains("Air")) {
+- this.setAirSupply(compoundtag.getShort("Air"));
++ this.fallDistance = compound.getFloat("FallDistance");
++ this.remainingFireTicks = compound.getShort("Fire");
++ if (compound.contains("Air")) {
++ this.setAirSupply(compound.getShort("Air"));
+ }
+
+- this.onGround = compoundtag.getBoolean("OnGround");
+- this.invulnerable = compoundtag.getBoolean("Invulnerable");
+- this.portalCooldown = compoundtag.getInt("PortalCooldown");
+- if (compoundtag.hasUUID("UUID")) {
+- this.uuid = compoundtag.getUUID("UUID");
++ this.onGround = compound.getBoolean("OnGround");
++ this.invulnerable = compound.getBoolean("Invulnerable");
++ this.portalCooldown = compound.getInt("PortalCooldown");
++ if (compound.hasUUID("UUID")) {
++ this.uuid = compound.getUUID("UUID");
+ this.stringUUID = this.uuid.toString();
+ }
+
+@@ -1816,8 +2067,8 @@
+ if (Double.isFinite((double) this.getYRot()) && Double.isFinite((double) this.getXRot())) {
+ this.reapplyPosition();
+ this.setRot(this.getYRot(), this.getXRot());
+- if (compoundtag.contains("CustomName", 8)) {
+- String s = compoundtag.getString("CustomName");
++ if (compound.contains("CustomName", 8)) {
++ String s = compound.getString("CustomName");
+
+ try {
+ this.setCustomName(Component.Serializer.fromJson(s));
+@@ -1826,23 +2077,23 @@
+ }
+ }
+
+- this.setCustomNameVisible(compoundtag.getBoolean("CustomNameVisible"));
+- this.setSilent(compoundtag.getBoolean("Silent"));
+- this.setNoGravity(compoundtag.getBoolean("NoGravity"));
+- this.setGlowingTag(compoundtag.getBoolean("Glowing"));
+- this.setTicksFrozen(compoundtag.getInt("TicksFrozen"));
+- this.hasVisualFire = compoundtag.getBoolean("HasVisualFire");
+- if (compoundtag.contains("Tags", 9)) {
++ this.setCustomNameVisible(compound.getBoolean("CustomNameVisible"));
++ this.setSilent(compound.getBoolean("Silent"));
++ this.setNoGravity(compound.getBoolean("NoGravity"));
++ this.setGlowingTag(compound.getBoolean("Glowing"));
++ this.setTicksFrozen(compound.getInt("TicksFrozen"));
++ this.hasVisualFire = compound.getBoolean("HasVisualFire");
++ if (compound.contains("Tags", 9)) {
+ this.tags.clear();
+- ListTag listtag3 = compoundtag.getList("Tags", 8);
+- int i = Math.min(listtag3.size(), 1024);
++ ListTag nbttaglist3 = compound.getList("Tags", 8);
++ int i = Math.min(nbttaglist3.size(), 1024);
+
+ for (int j = 0; j < i; ++j) {
+- this.tags.add(listtag3.getString(j));
++ this.tags.add(nbttaglist3.getString(j));
+ }
+ }
+
+- this.readAdditionalSaveData(compoundtag);
++ this.readAdditionalSaveData(compound);
+ if (this.repositionEntityAfterLoad()) {
+ this.reapplyPosition();
+ }
+@@ -1853,11 +2104,50 @@
+ } else {
+ throw new IllegalStateException("Entity has invalid position");
+ }
++
++ // CraftBukkit start
++ this.persist = !compound.contains("Bukkit.persist") || compound.getBoolean("Bukkit.persist");
++ this.visibleByDefault = !compound.contains("Bukkit.visibleByDefault") || compound.getBoolean("Bukkit.visibleByDefault");
++ // SPIGOT-6907: re-implement LivingEntity#setMaximumAir()
++ if (compound.contains("Bukkit.MaxAirSupply")) {
++ maxAirTicks = compound.getInt("Bukkit.MaxAirSupply");
++ }
++ // CraftBukkit end
++
++ // CraftBukkit start - Reset world
++ if (this instanceof ServerPlayer) {
++ Server server = Bukkit.getServer();
++ org.bukkit.World bworld = null;
++
++ // TODO: Remove World related checks, replaced with WorldUID
++ String worldName = compound.getString("world");
++
++ if (compound.contains("WorldUUIDMost") && compound.contains("WorldUUIDLeast")) {
++ UUID uid = new UUID(compound.getLong("WorldUUIDMost"), compound.getLong("WorldUUIDLeast"));
++ bworld = server.getWorld(uid);
++ } else {
++ bworld = server.getWorld(worldName);
++ }
++
++ if (bworld == null) {
++ bworld = ((org.bukkit.craftbukkit.CraftServer) server).getServer().getLevel(Level.OVERWORLD).getWorld();
++ }
++
++ ((ServerPlayer) this).setLevel(bworld == null ? null : ((CraftWorld) bworld).getHandle());
++ }
++ this.getBukkitEntity().readBukkitValues(compound);
++ if (compound.contains("Bukkit.invisible")) {
++ boolean bukkitInvisible = compound.getBoolean("Bukkit.invisible");
++ this.setInvisible(bukkitInvisible);
++ this.persistentInvisibility = bukkitInvisible;
++ }
++ // CraftBukkit end
++
+ } catch (Throwable throwable) {
+ CrashReport crashreport = CrashReport.forThrowable(throwable, "Loading entity NBT");
+- CrashReportCategory crashreportcategory = crashreport.addCategory("Entity being loaded");
++ CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Entity being loaded");
+
+- this.fillCrashReportCategory(crashreportcategory);
++ this.fillCrashReportCategory(crashreportsystemdetails);
+ throw new ReportedException(crashreport);
+ }
+ }
+@@ -1867,72 +2157,91 @@
+ }
+
+ @Nullable
+- protected final String getEncodeId() {
+- EntityType<?> entitytype = this.getType();
+- ResourceLocation resourcelocation = EntityType.getKey(entitytype);
++ public final String getEncodeId() {
++ EntityType<?> entitytypes = this.getType();
++ ResourceLocation minecraftkey = EntityType.getKey(entitytypes);
+
+- return entitytype.canSerialize() && resourcelocation != null ? resourcelocation.toString() : null;
++ return entitytypes.canSerialize() && minecraftkey != null ? minecraftkey.toString() : null;
+ }
+
++ // CraftBukkit start - allow excluding certain data when saving
++ protected void addAdditionalSaveData(CompoundTag nbttagcompound, boolean includeAll) {
++ addAdditionalSaveData(nbttagcompound);
++ }
++ // CraftBukkit end
++
+ protected abstract void readAdditionalSaveData(CompoundTag compound);
+
+ protected abstract void addAdditionalSaveData(CompoundTag compound);
+
+- protected ListTag newDoubleList(double... adouble) {
+- ListTag listtag = new ListTag();
+- double[] adouble1 = adouble;
+- int i = adouble.length;
++ protected ListTag newDoubleList(double... numbers) {
++ ListTag nbttaglist = new ListTag();
++ double[] adouble1 = numbers;
++ int i = numbers.length;
+
+ for (int j = 0; j < i; ++j) {
+ double d0 = adouble1[j];
+
+- listtag.add(DoubleTag.valueOf(d0));
++ nbttaglist.add(DoubleTag.valueOf(d0));
+ }
+
+- return listtag;
++ return nbttaglist;
+ }
+
+- protected ListTag newFloatList(float... afloat) {
+- ListTag listtag = new ListTag();
+- float[] afloat1 = afloat;
+- int i = afloat.length;
++ protected ListTag newFloatList(float... numbers) {
++ ListTag nbttaglist = new ListTag();
++ float[] afloat1 = numbers;
++ int i = numbers.length;
+
+ for (int j = 0; j < i; ++j) {
+ float f = afloat1[j];
+
+- listtag.add(FloatTag.valueOf(f));
++ nbttaglist.add(FloatTag.valueOf(f));
+ }
+
+- return listtag;
++ return nbttaglist;
+ }
+
+ @Nullable
+- public ItemEntity spawnAtLocation(ItemLike itemlike) {
+- return this.spawnAtLocation(itemlike, 0);
++ public ItemEntity spawnAtLocation(IMaterial item) {
++ return this.spawnAtLocation(item, 0);
+ }
+
+ @Nullable
+- public ItemEntity spawnAtLocation(ItemLike itemlike, int i) {
+- return this.spawnAtLocation(new ItemStack(itemlike), (float) i);
++ public ItemEntity spawnAtLocation(IMaterial item, int offsetY) {
++ return this.spawnAtLocation(new ItemStack(item), (float) offsetY);
+ }
+
+ @Nullable
+- public ItemEntity spawnAtLocation(ItemStack itemstack) {
+- return this.spawnAtLocation(itemstack, 0.0F);
++ public ItemEntity spawnAtLocation(ItemStack stack) {
++ return this.spawnAtLocation(stack, 0.0F);
+ }
+
+ @Nullable
+- public ItemEntity spawnAtLocation(ItemStack itemstack, float f) {
+- if (itemstack.isEmpty()) {
++ public ItemEntity spawnAtLocation(ItemStack stack, float offsetY) {
++ if (stack.isEmpty()) {
+ return null;
+ } else if (this.level().isClientSide) {
+ return null;
+ } else {
+- ItemEntity itementity = new ItemEntity(this.level(), this.getX(), this.getY() + (double) f, this.getZ(), itemstack);
++ // CraftBukkit start - Capture drops for death event
++ if (this instanceof net.minecraft.world.entity.LivingEntity && !((net.minecraft.world.entity.LivingEntity) this).forceDrops) {
++ ((net.minecraft.world.entity.LivingEntity) this).drops.add(org.bukkit.craftbukkit.inventory.CraftItemStack.asBukkitCopy(stack));
++ return null;
++ }
++ // CraftBukkit end
++ ItemEntity entityitem = new ItemEntity(this.level(), this.getX(), this.getY() + (double) offsetY, this.getZ(), stack);
+
+- itementity.setDefaultPickUpDelay();
+- this.level().addFreshEntity(itementity);
+- return itementity;
++ entityitem.setDefaultPickUpDelay();
++ // 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.level().addFreshEntity(entityitem);
++ return entityitem;
+ }
+ }
+
+@@ -1945,17 +2254,17 @@
+ return false;
+ } else {
+ float f = this.dimensions.width * 0.8F;
+- AABB aabb = AABB.ofSize(this.getEyePosition(), (double) f, 1.0E-6D, (double) f);
++ AABB axisalignedbb = AABB.ofSize(this.getEyePosition(), (double) f, 1.0E-6D, (double) f);
+
+- return BlockPos.betweenClosedStream(aabb).anyMatch((blockpos) -> {
+- BlockState blockstate = this.level().getBlockState(blockpos);
++ return BlockPos.betweenClosedStream(axisalignedbb).anyMatch((blockposition) -> {
++ IBlockData iblockdata = this.level().getBlockState(blockposition);
+
+- return !blockstate.isAir() && blockstate.isSuffocating(this.level(), blockpos) && Shapes.joinIsNotEmpty(blockstate.getCollisionShape(this.level(), blockpos).move((double) blockpos.getX(), (double) blockpos.getY(), (double) blockpos.getZ()), Shapes.create(aabb), BooleanOp.AND);
++ return !iblockdata.isAir() && iblockdata.isSuffocating(this.level(), blockposition) && Shapes.joinIsNotEmpty(iblockdata.getCollisionShape(this.level(), blockposition).move((double) blockposition.getX(), (double) blockposition.getY(), (double) blockposition.getZ()), Shapes.create(axisalignedbb), BooleanOp.AND);
+ });
+ }
+ }
+
+- public InteractionResult interact(Player player, InteractionHand interactionhand) {
++ public InteractionResult interact(Player player, EnumHand hand) {
+ return InteractionResult.PASS;
+ }
+
+@@ -1975,19 +2284,19 @@
+ }
+ }
+
+- public final void positionRider(Entity entity) {
+- if (this.hasPassenger(entity)) {
+- this.positionRider(entity, Entity::setPos);
++ public final void positionRider(Entity passenger) {
++ if (this.hasPassenger(passenger)) {
++ this.positionRider(passenger, Entity::setPos);
+ }
+ }
+
+- protected void positionRider(Entity entity, Entity.MoveFunction entity_movefunction) {
+- Vec3 vec3 = this.getPassengerRidingPosition(entity);
++ protected void positionRider(Entity passenger, Entity.MoveFunction callback) {
++ Vec3 vec3d = this.getPassengerRidingPosition(passenger);
+
+- entity_movefunction.accept(entity, vec3.x, vec3.y + (double) entity.getMyRidingOffset(this), vec3.z);
++ callback.accept(passenger, vec3d.x, vec3d.y + (double) passenger.getMyRidingOffset(this), vec3d.z);
+ }
+
+- public void onPassengerTurned(Entity entity) {}
++ public void onPassengerTurned(Entity entityToUpdate) {}
+
+ public float getMyRidingOffset(Entity entity) {
+ return this.ridingOffset(entity);
+@@ -2001,41 +2310,53 @@
+ return (new Vec3(this.getPassengerAttachmentPoint(entity, this.dimensions, 1.0F).rotateY(-this.yRot * 0.017453292F))).add(this.position());
+ }
+
+- protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entitydimensions, float f) {
+- return new Vector3f(0.0F, entitydimensions.height, 0.0F);
++ protected Vector3f getPassengerAttachmentPoint(Entity entity, EntityDimensions entitysize, float f) {
++ return new Vector3f(0.0F, entitysize.height, 0.0F);
+ }
+
+- public boolean startRiding(Entity entity) {
+- return this.startRiding(entity, false);
++ public boolean startRiding(Entity vehicle) {
++ return this.startRiding(vehicle, false);
+ }
+
+ public boolean showVehicleHealth() {
+- return this instanceof LivingEntity;
++ return this instanceof net.minecraft.world.entity.LivingEntity;
+ }
+
+- public boolean startRiding(Entity entity, boolean flag) {
+- if (entity == this.vehicle) {
++ public boolean startRiding(Entity vehicle, boolean force) {
++ if (vehicle == this.vehicle) {
+ return false;
+- } else if (!entity.couldAcceptPassenger()) {
++ } else if (!vehicle.couldAcceptPassenger()) {
+ return false;
+ } else {
+- for (Entity entity1 = entity; entity1.vehicle != null; entity1 = entity1.vehicle) {
++ for (Entity entity1 = vehicle; entity1.vehicle != null; entity1 = entity1.vehicle) {
+ if (entity1.vehicle == this) {
+ return false;
+ }
+ }
+
+- if (!flag && (!this.canRide(entity) || !entity.canAddPassenger(this))) {
++ if (!force && (!this.canRide(vehicle) || !vehicle.canAddPassenger(this))) {
+ return false;
+ } else {
++ // CraftBukkit start
++ if (vehicle.getBukkitEntity() instanceof Vehicle && this.getBukkitEntity() instanceof LivingEntity) {
++ VehicleEnterEvent event = new VehicleEnterEvent((Vehicle) vehicle.getBukkitEntity(), this.getBukkitEntity());
++ // Suppress during worldgen
++ if (this.valid) {
++ Bukkit.getPluginManager().callEvent(event);
++ }
++ if (event.isCancelled()) {
++ return false;
++ }
++ }
++ // CraftBukkit end
+ if (this.isPassenger()) {
+ this.stopRiding();
+ }
+
+- this.setPose(Pose.STANDING);
+- this.vehicle = entity;
++ this.setPose(EntityPose.STANDING);
++ this.vehicle = vehicle;
+ this.vehicle.addPassenger(this);
+- entity.getIndirectPassengersStream().filter((entity2) -> {
++ vehicle.getIndirectPassengersStream().filter((entity2) -> {
+ return entity2 instanceof ServerPlayer;
+ }).forEach((entity2) -> {
+ CriteriaTriggers.START_RIDING_TRIGGER.trigger((ServerPlayer) entity2);
+@@ -2045,7 +2366,7 @@
+ }
+ }
+
+- protected boolean canRide(Entity entity) {
++ protected boolean canRide(Entity vehicle) {
+ return !this.isShiftKeyDown() && this.boardingCooldown <= 0;
+ }
+
+@@ -2061,7 +2382,7 @@
+ Entity entity = this.vehicle;
+
+ this.vehicle = null;
+- entity.removePassenger(this);
++ if (!entity.removePassenger(this)) this.vehicle = entity; // CraftBukkit
+ }
+
+ }
+@@ -2070,32 +2391,51 @@
+ this.removeVehicle();
+ }
+
+- protected void addPassenger(Entity entity) {
+- if (entity.getVehicle() != this) {
++ protected void addPassenger(Entity passenger) {
++ if (passenger.getVehicle() != this) {
+ throw new IllegalStateException("Use x.startRiding(y), not y.addPassenger(x)");
+ } else {
+ if (this.passengers.isEmpty()) {
+- this.passengers = ImmutableList.of(entity);
++ this.passengers = ImmutableList.of(passenger);
+ } else {
+ List<Entity> list = Lists.newArrayList(this.passengers);
+
+- if (!this.level().isClientSide && entity instanceof Player && !(this.getFirstPassenger() instanceof Player)) {
+- list.add(0, entity);
++ if (!this.level().isClientSide && passenger instanceof Player && !(this.getFirstPassenger() instanceof Player)) {
++ list.add(0, passenger);
+ } else {
+- list.add(entity);
++ list.add(passenger);
+ }
+
+ this.passengers = ImmutableList.copyOf(list);
+ }
+
+- this.gameEvent(GameEvent.ENTITY_MOUNT, entity);
++ this.gameEvent(GameEvent.ENTITY_MOUNT, passenger);
+ }
+ }
+
+- protected void removePassenger(Entity entity) {
++ protected boolean removePassenger(Entity entity) { // CraftBukkit
+ if (entity.getVehicle() == this) {
+ throw new IllegalStateException("Use x.stopRiding(y), not y.removePassenger(x)");
+ } else {
++ // CraftBukkit start
++ CraftEntity craft = (CraftEntity) entity.getBukkitEntity().getVehicle();
++ Entity orig = craft == null ? null : craft.getHandle();
++ if (getBukkitEntity() instanceof Vehicle && entity.getBukkitEntity() instanceof LivingEntity) {
++ VehicleExitEvent event = new VehicleExitEvent(
++ (Vehicle) getBukkitEntity(),
++ (LivingEntity) entity.getBukkitEntity()
++ );
++ // 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.passengers.size() == 1 && this.passengers.get(0) == entity) {
+ this.passengers = ImmutableList.of();
+ } else {
+@@ -2107,9 +2447,10 @@
+ entity.boardingCooldown = 60;
+ this.gameEvent(GameEvent.ENTITY_DISMOUNT, entity);
+ }
++ return true; // CraftBukkit
+ }
+
+- protected boolean canAddPassenger(Entity entity) {
++ protected boolean canAddPassenger(Entity passenger) {
+ return this.passengers.isEmpty();
+ }
+
+@@ -2142,8 +2483,8 @@
+ return this.getYRot();
+ }
+
+- public void lerpHeadTo(float f, int i) {
+- this.setYHeadRot(f);
++ public void lerpHeadTo(float yaw, int pitch) {
++ this.setYHeadRot(yaw);
+ }
+
+ public float getPickRadius() {
+@@ -2158,11 +2499,11 @@
+ if (!(this instanceof Player)) {
+ return Vec3.ZERO;
+ } else {
+- Player player = (Player) this;
+- boolean flag = player.getOffhandItem().is(item) && !player.getMainHandItem().is(item);
+- HumanoidArm humanoidarm = flag ? player.getMainArm().getOpposite() : player.getMainArm();
++ Player entityhuman = (Player) this;
++ boolean flag = entityhuman.getOffhandItem().is(item) && !entityhuman.getMainHandItem().is(item);
++ HumanoidArm enummainhand = flag ? entityhuman.getMainArm().getOpposite() : entityhuman.getMainArm();
+
+- return this.calculateViewVector(0.0F, this.getYRot() + (float) (humanoidarm == HumanoidArm.RIGHT ? 80 : -80)).scale(0.5D);
++ return this.calculateViewVector(0.0F, this.getYRot() + (float) (enummainhand == HumanoidArm.RIGHT ? 80 : -80)).scale(0.5D);
+ }
+ }
+
+@@ -2174,12 +2515,12 @@
+ return Vec3.directionFromRotation(this.getRotationVector());
+ }
+
+- public void handleInsidePortal(BlockPos blockpos) {
++ public void handleInsidePortal(BlockPos pos) {
+ if (this.isOnPortalCooldown()) {
+ this.setPortalCooldown();
+ } else {
+- if (!this.level().isClientSide && !blockpos.equals(this.portalEntrancePos)) {
+- this.portalEntrancePos = blockpos.immutable();
++ if (!this.level().isClientSide && !pos.equals(this.portalEntrancePos)) {
++ this.portalEntrancePos = pos.immutable();
+ }
+
+ this.isInsidePortal = true;
+@@ -2189,18 +2530,24 @@
+ protected void handleNetherPortal() {
+ if (this.level() instanceof ServerLevel) {
+ int i = this.getPortalWaitTime();
+- ServerLevel serverlevel = (ServerLevel) this.level();
++ ServerLevel worldserver = (ServerLevel) this.level();
+
+ if (this.isInsidePortal) {
+- MinecraftServer minecraftserver = serverlevel.getServer();
+- ResourceKey<Level> resourcekey = this.level().dimension() == Level.NETHER ? Level.OVERWORLD : Level.NETHER;
+- ServerLevel serverlevel1 = minecraftserver.getLevel(resourcekey);
++ MinecraftServer minecraftserver = worldserver.getServer();
++ ResourceKey<Level> resourcekey = this.level().getTypeKey() == LevelStem.NETHER ? Level.OVERWORLD : Level.NETHER; // CraftBukkit
++ ServerLevel worldserver1 = minecraftserver.getLevel(resourcekey);
+
+- if (serverlevel1 != null && minecraftserver.isNetherEnabled() && !this.isPassenger() && this.portalTime++ >= i) {
++ if (true && !this.isPassenger() && this.portalTime++ >= i) { // CraftBukkit
+ this.level().getProfiler().push("portal");
+ this.portalTime = i;
+ this.setPortalCooldown();
+- this.changeDimension(serverlevel1);
++ // CraftBukkit start
++ if (this instanceof ServerPlayer) {
++ ((ServerPlayer) this).changeDimension(worldserver1, PlayerTeleportEvent.TeleportCause.NETHER_PORTAL);
++ } else {
++ this.changeDimension(worldserver1);
++ }
++ // CraftBukkit end
+ this.level().getProfiler().pop();
+ }
+
+@@ -2223,21 +2570,21 @@
+ return 300;
+ }
+
+- public void lerpMotion(double d0, double d1, double d2) {
+- this.setDeltaMovement(d0, d1, d2);
++ public void lerpMotion(double x, double d1, double y) {
++ this.setDeltaMovement(x, d1, y);
+ }
+
+- public void handleDamageEvent(DamageSource damagesource) {}
++ public void handleDamageEvent(DamageSource damageSource) {}
+
+- public void handleEntityEvent(byte b0) {
+- switch (b0) {
++ public void handleEntityEvent(byte id) {
++ switch (id) {
+ case 53:
+ HoneyBlock.showSlideParticles(this);
+ default:
+ }
+ }
+
+- public void animateHurt(float f) {}
++ public void animateHurt(float yaw) {}
+
+ public Iterable<ItemStack> getHandSlots() {
+ return Entity.EMPTY_LIST;
+@@ -2251,7 +2598,7 @@
+ return Iterables.concat(this.getHandSlots(), this.getArmorSlots());
+ }
+
+- public void setItemSlot(EquipmentSlot equipmentslot, ItemStack itemstack) {}
++ public void setItemSlot(EquipmentSlot slot, ItemStack stack) {}
+
+ public boolean isOnFire() {
+ boolean flag = this.level() != null && this.level().isClientSide;
+@@ -2275,8 +2622,8 @@
+ return !this.getType().is(EntityTypeTags.NON_CONTROLLING_RIDER);
+ }
+
+- public void setShiftKeyDown(boolean flag) {
+- this.setSharedFlag(1, flag);
++ public void setShiftKeyDown(boolean keyDown) {
++ this.setSharedFlag(1, keyDown);
+ }
+
+ public boolean isShiftKeyDown() {
+@@ -2300,15 +2647,15 @@
+ }
+
+ public boolean isCrouching() {
+- return this.hasPose(Pose.CROUCHING);
++ return this.hasPose(EntityPose.CROUCHING);
+ }
+
+ public boolean isSprinting() {
+ return this.getSharedFlag(3);
+ }
+
+- public void setSprinting(boolean flag) {
+- this.setSharedFlag(3, flag);
++ public void setSprinting(boolean sprinting) {
++ this.setSharedFlag(3, sprinting);
+ }
+
+ public boolean isSwimming() {
+@@ -2316,23 +2663,30 @@
+ }
+
+ public boolean isVisuallySwimming() {
+- return this.hasPose(Pose.SWIMMING);
++ return this.hasPose(EntityPose.SWIMMING);
+ }
+
+ public boolean isVisuallyCrawling() {
+ return this.isVisuallySwimming() && !this.isInWater();
+ }
+
+- public void setSwimming(boolean flag) {
+- this.setSharedFlag(4, flag);
++ public void setSwimming(boolean swimming) {
++ // CraftBukkit start
++ if (valid && this.isSwimming() != swimming && this instanceof net.minecraft.world.entity.LivingEntity) {
++ if (CraftEventFactory.callToggleSwimEvent((net.minecraft.world.entity.LivingEntity) this, swimming).isCancelled()) {
++ return;
++ }
++ }
++ // CraftBukkit end
++ this.setSharedFlag(4, swimming);
+ }
+
+ public final boolean hasGlowingTag() {
+ return this.hasGlowingTag;
+ }
+
+- public final void setGlowingTag(boolean flag) {
+- this.hasGlowingTag = flag;
++ public final void setGlowingTag(boolean hasGlowingTag) {
++ this.hasGlowingTag = hasGlowingTag;
+ this.setSharedFlag(6, this.isCurrentlyGlowing());
+ }
+
+@@ -2348,9 +2702,9 @@
+ if (player.isSpectator()) {
+ return false;
+ } else {
+- PlayerTeam playerteam = this.getTeam();
++ PlayerTeam scoreboardteam = this.getTeam();
+
+- return playerteam != null && player != null && player.getTeam() == playerteam && playerteam.canSeeFriendlyInvisibles() ? false : this.isInvisible();
++ return scoreboardteam != null && player != null && player.getTeam() == scoreboardteam && scoreboardteam.canSeeFriendlyInvisibles() ? false : this.isInvisible();
+ }
+ }
+
+@@ -2358,7 +2712,7 @@
+ return false;
+ }
+
+- public void updateDynamicGameEventListener(BiConsumer<DynamicGameEventListener<?>, ServerLevel> biconsumer) {}
++ public void updateDynamicGameEventListener(BiConsumer<DynamicGameEventListener<?>, ServerLevel> listenerConsumer) {}
+
+ @Nullable
+ public PlayerTeam getTeam() {
+@@ -2373,43 +2727,58 @@
+ return this.getTeam() != null ? this.getTeam().isAlliedTo(team) : false;
+ }
+
+- public void setInvisible(boolean flag) {
+- this.setSharedFlag(5, flag);
++ // CraftBukkit - start
++ public void setInvisible(boolean invisible) {
++ if (!this.persistentInvisibility) { // Prevent Minecraft from removing our invisibility flag
++ this.setSharedFlag(5, invisible);
++ }
++ // CraftBukkit - end
+ }
+
+- protected boolean getSharedFlag(int i) {
+- return ((Byte) this.entityData.get(Entity.DATA_SHARED_FLAGS_ID) & 1 << i) != 0;
++ public boolean getSharedFlag(int flag) {
++ return ((Byte) this.entityData.get(Entity.DATA_SHARED_FLAGS_ID) & 1 << flag) != 0;
+ }
+
+- protected void setSharedFlag(int i, boolean flag) {
++ public void setSharedFlag(int flag, boolean set) {
+ byte b0 = (Byte) this.entityData.get(Entity.DATA_SHARED_FLAGS_ID);
+
+- if (flag) {
+- this.entityData.set(Entity.DATA_SHARED_FLAGS_ID, (byte) (b0 | 1 << i));
++ if (set) {
++ this.entityData.set(Entity.DATA_SHARED_FLAGS_ID, (byte) (b0 | 1 << flag));
+ } else {
+- this.entityData.set(Entity.DATA_SHARED_FLAGS_ID, (byte) (b0 & ~(1 << i)));
++ this.entityData.set(Entity.DATA_SHARED_FLAGS_ID, (byte) (b0 & ~(1 << flag)));
+ }
+
+ }
+
+ public int getMaxAirSupply() {
+- return 300;
++ return maxAirTicks; // CraftBukkit - SPIGOT-6907: re-implement LivingEntity#setMaximumAir()
+ }
+
+ public int getAirSupply() {
+ return (Integer) this.entityData.get(Entity.DATA_AIR_SUPPLY_ID);
+ }
+
+- public void setAirSupply(int i) {
+- this.entityData.set(Entity.DATA_AIR_SUPPLY_ID, i);
++ public void setAirSupply(int air) {
++ // CraftBukkit start
++ EntityAirChangeEvent event = new EntityAirChangeEvent(this.getBukkitEntity(), air);
++ // Suppress during worldgen
++ if (this.valid) {
++ event.getEntity().getServer().getPluginManager().callEvent(event);
++ }
++ if (event.isCancelled() && this.getAirSupply() != air) {
++ this.entityData.markDirty(Entity.DATA_AIR_SUPPLY_ID);
++ return;
++ }
++ this.entityData.set(Entity.DATA_AIR_SUPPLY_ID, event.getAmount());
++ // CraftBukkit end
+ }
+
+ public int getTicksFrozen() {
+ return (Integer) this.entityData.get(Entity.DATA_TICKS_FROZEN);
+ }
+
+- public void setTicksFrozen(int i) {
+- this.entityData.set(Entity.DATA_TICKS_FROZEN, i);
++ public void setTicksFrozen(int ticksFrozen) {
++ this.entityData.set(Entity.DATA_TICKS_FROZEN, ticksFrozen);
+ }
+
+ public float getPercentFrozen() {
+@@ -2426,43 +2795,73 @@
+ return 140;
+ }
+
+- public void thunderHit(ServerLevel serverlevel, LightningBolt lightningbolt) {
++ public void thunderHit(ServerLevel level, LightningBolt lightning) {
+ this.setRemainingFireTicks(this.remainingFireTicks + 1);
++ // CraftBukkit start
++ final org.bukkit.entity.Entity thisBukkitEntity = this.getBukkitEntity();
++ final org.bukkit.entity.Entity stormBukkitEntity = lightning.getBukkitEntity();
++ final PluginManager pluginManager = Bukkit.getPluginManager();
++ // CraftBukkit end
++
+ if (this.remainingFireTicks == 0) {
+- this.setSecondsOnFire(8);
++ // CraftBukkit start - Call a combust event when lightning strikes
++ EntityCombustByEntityEvent entityCombustEvent = new EntityCombustByEntityEvent(stormBukkitEntity, thisBukkitEntity, 8);
++ pluginManager.callEvent(entityCombustEvent);
++ if (!entityCombustEvent.isCancelled()) {
++ this.setSecondsOnFire(entityCombustEvent.getDuration(), false);
++ }
++ // CraftBukkit end
+ }
+
+- this.hurt(this.damageSources().lightningBolt(), 5.0F);
++ // CraftBukkit start
++ if (thisBukkitEntity instanceof Hanging) {
++ HangingBreakByEntityEvent hangingEvent = new HangingBreakByEntityEvent((Hanging) thisBukkitEntity, stormBukkitEntity);
++ pluginManager.callEvent(hangingEvent);
++
++ if (hangingEvent.isCancelled()) {
++ return;
++ }
++ }
++
++ if (this.fireImmune()) {
++ return;
++ }
++ CraftEventFactory.entityDamage = lightning;
++ if (!this.hurt(this.damageSources().lightningBolt(), 5.0F)) {
++ CraftEventFactory.entityDamage = null;
++ return;
++ }
++ // CraftBukkit end
+ }
+
+- public void onAboveBubbleCol(boolean flag) {
+- Vec3 vec3 = this.getDeltaMovement();
++ public void onAboveBubbleCol(boolean downwards) {
++ Vec3 vec3d = this.getDeltaMovement();
+ double d0;
+
+- if (flag) {
+- d0 = Math.max(-0.9D, vec3.y - 0.03D);
++ if (downwards) {
++ d0 = Math.max(-0.9D, vec3d.y - 0.03D);
+ } else {
+- d0 = Math.min(1.8D, vec3.y + 0.1D);
++ d0 = Math.min(1.8D, vec3d.y + 0.1D);
+ }
+
+- this.setDeltaMovement(vec3.x, d0, vec3.z);
++ this.setDeltaMovement(vec3d.x, d0, vec3d.z);
+ }
+
+- public void onInsideBubbleColumn(boolean flag) {
+- Vec3 vec3 = this.getDeltaMovement();
++ public void onInsideBubbleColumn(boolean downwards) {
++ Vec3 vec3d = this.getDeltaMovement();
+ double d0;
+
+- if (flag) {
+- d0 = Math.max(-0.3D, vec3.y - 0.03D);
++ if (downwards) {
++ d0 = Math.max(-0.3D, vec3d.y - 0.03D);
+ } else {
+- d0 = Math.min(0.7D, vec3.y + 0.06D);
++ d0 = Math.min(0.7D, vec3d.y + 0.06D);
+ }
+
+- this.setDeltaMovement(vec3.x, d0, vec3.z);
++ this.setDeltaMovement(vec3d.x, d0, vec3d.z);
+ this.resetFallDistance();
+ }
+
+- public boolean killedEntity(ServerLevel serverlevel, LivingEntity livingentity) {
++ public boolean killedEntity(ServerLevel level, net.minecraft.world.entity.LivingEntity entity) {
+ return true;
+ }
+
+@@ -2477,68 +2876,67 @@
+ this.fallDistance = 0.0F;
+ }
+
+- protected void moveTowardsClosestSpace(double d0, double d1, double d2) {
+- BlockPos blockpos = BlockPos.containing(d0, d1, d2);
+- Vec3 vec3 = new Vec3(d0 - (double) blockpos.getX(), d1 - (double) blockpos.getY(), d2 - (double) blockpos.getZ());
+- BlockPos.MutableBlockPos blockpos_mutableblockpos = new BlockPos.MutableBlockPos();
+- Direction direction = Direction.UP;
++ protected void moveTowardsClosestSpace(double x, double d1, double y) {
++ BlockPos blockposition = BlockPos.containing(x, d1, y);
++ Vec3 vec3d = new Vec3(x - (double) blockposition.getX(), d1 - (double) blockposition.getY(), y - (double) blockposition.getZ());
++ BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
++ Direction enumdirection = Direction.UP;
+ double d3 = Double.MAX_VALUE;
+- Direction[] adirection = new Direction[]{Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST, Direction.UP};
+- int i = adirection.length;
++ Direction[] aenumdirection = new Direction[]{Direction.NORTH, Direction.SOUTH, Direction.WEST, Direction.EAST, Direction.UP};
++ int i = aenumdirection.length;
+
+ for (int j = 0; j < i; ++j) {
+- Direction direction1 = adirection[j];
++ Direction enumdirection1 = aenumdirection[j];
+
+- blockpos_mutableblockpos.setWithOffset(blockpos, direction1);
+- if (!this.level().getBlockState(blockpos_mutableblockpos).isCollisionShapeFullBlock(this.level(), blockpos_mutableblockpos)) {
+- double d4 = vec3.get(direction1.getAxis());
+- double d5 = direction1.getAxisDirection() == Direction.AxisDirection.POSITIVE ? 1.0D - d4 : d4;
++ blockposition_mutableblockposition.setWithOffset(blockposition, enumdirection1);
++ if (!this.level().getBlockState(blockposition_mutableblockposition).isCollisionShapeFullBlock(this.level(), blockposition_mutableblockposition)) {
++ double d4 = vec3d.get(enumdirection1.getAxis());
++ double d5 = enumdirection1.getAxisDirection() == Direction.AxisDirection.POSITIVE ? 1.0D - d4 : d4;
+
+ if (d5 < d3) {
+ d3 = d5;
+- direction = direction1;
++ enumdirection = enumdirection1;
+ }
+ }
+ }
+
+ float f = this.random.nextFloat() * 0.2F + 0.1F;
+- float f1 = (float) direction.getAxisDirection().getStep();
+- Vec3 vec31 = this.getDeltaMovement().scale(0.75D);
++ float f1 = (float) enumdirection.getAxisDirection().getStep();
++ Vec3 vec3d1 = this.getDeltaMovement().scale(0.75D);
+
+- if (direction.getAxis() == Direction.Axis.X) {
+- this.setDeltaMovement((double) (f1 * f), vec31.y, vec31.z);
+- } else if (direction.getAxis() == Direction.Axis.Y) {
+- this.setDeltaMovement(vec31.x, (double) (f1 * f), vec31.z);
+- } else if (direction.getAxis() == Direction.Axis.Z) {
+- this.setDeltaMovement(vec31.x, vec31.y, (double) (f1 * f));
++ if (enumdirection.getAxis() == Direction.Axis.X) {
++ this.setDeltaMovement((double) (f1 * f), vec3d1.y, vec3d1.z);
++ } else if (enumdirection.getAxis() == Direction.Axis.Y) {
++ this.setDeltaMovement(vec3d1.x, (double) (f1 * f), vec3d1.z);
++ } else if (enumdirection.getAxis() == Direction.Axis.Z) {
++ this.setDeltaMovement(vec3d1.x, vec3d1.y, (double) (f1 * f));
+ }
+
+ }
+
+- public void makeStuckInBlock(BlockState blockstate, Vec3 vec3) {
++ public void makeStuckInBlock(IBlockData state, Vec3 motionMultiplier) {
+ this.resetFallDistance();
+- this.stuckSpeedMultiplier = vec3;
++ this.stuckSpeedMultiplier = motionMultiplier;
+ }
+
+- private static Component removeAction(Component component) {
+- MutableComponent mutablecomponent = component.plainCopy().setStyle(component.getStyle().withClickEvent((ClickEvent) null));
+- Iterator iterator = component.getSiblings().iterator();
++ private static Component removeAction(Component name) {
++ MutableComponent ichatmutablecomponent = name.plainCopy().setStyle(name.getStyle().withClickEvent((ClickEvent) null));
++ Iterator iterator = name.getSiblings().iterator();
+
+ while (iterator.hasNext()) {
+- Component component1 = (Component) iterator.next();
++ Component ichatbasecomponent1 = (Component) iterator.next();
+
+- mutablecomponent.append(removeAction(component1));
++ ichatmutablecomponent.append(removeAction(ichatbasecomponent1));
+ }
+
+- return mutablecomponent;
++ return ichatmutablecomponent;
+ }
+
+ @Override
+- @Override
+ public Component getName() {
+- Component component = this.getCustomName();
++ Component ichatbasecomponent = this.getCustomName();
+
+- return component != null ? removeAction(component) : this.getTypeName();
++ return ichatbasecomponent != null ? removeAction(ichatbasecomponent) : this.getTypeName();
+ }
+
+ protected Component getTypeName() {
+@@ -2553,9 +2951,9 @@
+ return 0.0F;
+ }
+
+- public void setYHeadRot(float f) {}
++ public void setYHeadRot(float yHeadRot) {}
+
+- public void setYBodyRot(float f) {}
++ public void setYBodyRot(float yBodyRot) {}
+
+ public boolean isAttackable() {
+ return true;
+@@ -2565,23 +2963,22 @@
+ return false;
+ }
+
+- @Override
+ public String toString() {
+ String s = this.level() == null ? "~NULL~" : this.level().toString();
+
+ return this.removalReason != null ? String.format(Locale.ROOT, "%s['%s'/%d, l='%s', x=%.2f, y=%.2f, z=%.2f, removed=%s]", this.getClass().getSimpleName(), this.getName().getString(), this.id, s, this.getX(), this.getY(), this.getZ(), this.removalReason) : String.format(Locale.ROOT, "%s['%s'/%d, l='%s', x=%.2f, y=%.2f, z=%.2f]", this.getClass().getSimpleName(), this.getName().getString(), this.id, s, this.getX(), this.getY(), this.getZ());
+ }
+
+- public boolean isInvulnerableTo(DamageSource damagesource) {
+- return this.isRemoved() || this.invulnerable && !damagesource.is(DamageTypeTags.BYPASSES_INVULNERABILITY) && !damagesource.isCreativePlayer() || damagesource.is(DamageTypeTags.IS_FIRE) && this.fireImmune() || damagesource.is(DamageTypeTags.IS_FALL) && this.getType().is(EntityTypeTags.FALL_DAMAGE_IMMUNE);
++ public boolean isInvulnerableTo(DamageSource source) {
++ return this.isRemoved() || this.invulnerable && !source.is(DamageTypeTags.BYPASSES_INVULNERABILITY) && !source.isCreativePlayer() || source.is(DamageTypeTags.IS_FIRE) && this.fireImmune() || source.is(DamageTypeTags.IS_FALL) && this.getType().is(EntityTypeTags.FALL_DAMAGE_IMMUNE);
+ }
+
+ public boolean isInvulnerable() {
+ return this.invulnerable;
+ }
+
+- public void setInvulnerable(boolean flag) {
+- this.invulnerable = flag;
++ public void setInvulnerable(boolean isInvulnerable) {
++ this.invulnerable = isInvulnerable;
+ }
+
+ public void copyPosition(Entity entity) {
+@@ -2589,42 +2986,77 @@
+ }
+
+ public void restoreFrom(Entity entity) {
+- CompoundTag compoundtag = entity.saveWithoutId(new CompoundTag());
++ CompoundTag nbttagcompound = entity.saveWithoutId(new CompoundTag());
+
+- compoundtag.remove("Dimension");
+- this.load(compoundtag);
++ nbttagcompound.remove("Dimension");
++ this.load(nbttagcompound);
+ this.portalCooldown = entity.portalCooldown;
+ this.portalEntrancePos = entity.portalEntrancePos;
+ }
+
+ @Nullable
+- public Entity changeDimension(ServerLevel serverlevel) {
++ public Entity changeDimension(ServerLevel destination) {
++ // CraftBukkit start
++ return teleportTo(destination, null);
++ }
++
++ @Nullable
++ public Entity teleportTo(ServerLevel worldserver, Vec3 location) {
++ // CraftBukkit end
+ if (this.level() instanceof ServerLevel && !this.isRemoved()) {
+ this.level().getProfiler().push("changeDimension");
+- this.unRide();
++ // CraftBukkit start
++ // this.unRide();
++ if (worldserver == null) {
++ return null;
++ }
++ // CraftBukkit end
+ this.level().getProfiler().push("reposition");
+- PortalInfo portalinfo = this.findDimensionEntryPoint(serverlevel);
++ PortalInfo shapedetectorshape = (location == null) ? this.findDimensionEntryPoint(worldserver) : new PortalInfo(new Vec3(location.x(), location.y(), location.z()), Vec3.ZERO, this.yRot, this.xRot, worldserver, null); // CraftBukkit
+
+- if (portalinfo == null) {
++ if (shapedetectorshape == null) {
+ return null;
+ } else {
++ // CraftBukkit start
++ worldserver = shapedetectorshape.world;
++ if (worldserver == level) {
++ // SPIGOT-6782: Just move the entity if a plugin changed the world to the one the entity is already in
++ moveTo(shapedetectorshape.pos.x, shapedetectorshape.pos.y, shapedetectorshape.pos.z, shapedetectorshape.yRot, shapedetectorshape.xRot);
++ setDeltaMovement(shapedetectorshape.speed);
++ return this;
++ }
++ this.unRide();
++ // CraftBukkit end
++
+ this.level().getProfiler().popPush("reloading");
+- Entity entity = this.getType().create(serverlevel);
++ Entity entity = this.getType().create(worldserver);
+
+ if (entity != null) {
+ entity.restoreFrom(this);
+- entity.moveTo(portalinfo.pos.x, portalinfo.pos.y, portalinfo.pos.z, portalinfo.yRot, entity.getXRot());
+- entity.setDeltaMovement(portalinfo.speed);
+- serverlevel.addDuringTeleport(entity);
+- if (serverlevel.dimension() == Level.END) {
+- ServerLevel.makeObsidianPlatform(serverlevel);
++ entity.moveTo(shapedetectorshape.pos.x, shapedetectorshape.pos.y, shapedetectorshape.pos.z, shapedetectorshape.yRot, entity.getXRot());
++ entity.setDeltaMovement(shapedetectorshape.speed);
++ // CraftBukkit start - Don't spawn the new entity if the current entity isn't spawned
++ if (this.inWorld) {
++ worldserver.addDuringTeleport(entity);
++ if (worldserver.getTypeKey() == LevelStem.END) { // CraftBukkit
++ ServerLevel.makeObsidianPlatform(worldserver, this); // CraftBukkit
++ }
+ }
++ // CraftBukkit end
++ // CraftBukkit start - Forward the CraftEntity to the new entity
++ this.getBukkitEntity().setHandle(entity);
++ entity.bukkitEntity = this.getBukkitEntity();
++
++ if (this instanceof Mob) {
++ ((Mob) this).dropLeash(true, false); // Unleash to prevent duping of leads.
++ }
++ // CraftBukkit end
+ }
+
+ this.removeAfterChangingDimensions();
+ this.level().getProfiler().pop();
+ ((ServerLevel) this.level()).resetEmptyTime();
+- serverlevel.resetEmptyTime();
++ worldserver.resetEmptyTime();
+ this.level().getProfiler().pop();
+ return entity;
+ }
+@@ -2638,70 +3070,105 @@
+ }
+
+ @Nullable
+- protected PortalInfo findDimensionEntryPoint(ServerLevel serverlevel) {
+- boolean flag = this.level().dimension() == Level.END && serverlevel.dimension() == Level.OVERWORLD;
+- boolean flag1 = serverlevel.dimension() == Level.END;
++ protected PortalInfo findDimensionEntryPoint(ServerLevel destination) {
++ // CraftBukkit start
++ if (destination == null) {
++ return null;
++ }
++ boolean flag = this.level().getTypeKey() == LevelStem.END && destination.getTypeKey() == LevelStem.OVERWORLD; // fromEndToOverworld
++ boolean flag1 = destination.getTypeKey() == LevelStem.END; // targetIsEnd
++ // CraftBukkit end
+
+ if (!flag && !flag1) {
+- boolean flag2 = serverlevel.dimension() == Level.NETHER;
++ boolean flag2 = destination.getTypeKey() == LevelStem.NETHER; // CraftBukkit
+
+- if (this.level().dimension() != Level.NETHER && !flag2) {
++ if (this.level().getTypeKey() != LevelStem.NETHER && !flag2) { // CraftBukkit
+ return null;
+ } else {
+- WorldBorder worldborder = serverlevel.getWorldBorder();
+- double d0 = DimensionType.getTeleportationScale(this.level().dimensionType(), serverlevel.dimensionType());
+- BlockPos blockpos = worldborder.clampToBounds(this.getX() * d0, this.getY(), this.getZ() * d0);
++ WorldBorder worldborder = destination.getWorldBorder();
++ double d0 = DimensionType.getTeleportationScale(this.level().dimensionType(), destination.dimensionType());
++ BlockPos blockposition = worldborder.clampToBounds(this.getX() * d0, this.getY(), this.getZ() * d0);
++ // CraftBukkit start
++ CraftPortalEvent event = callPortalEvent(this, destination, new Vec3(blockposition.getX(), blockposition.getY(), blockposition.getZ()), PlayerTeleportEvent.TeleportCause.NETHER_PORTAL, flag2 ? 16 : 128, 16);
++ if (event == null) {
++ return null;
++ }
++ final ServerLevel worldserverFinal = destination = ((CraftWorld) event.getTo().getWorld()).getHandle();
++ worldborder = worldserverFinal.getWorldBorder();
++ blockposition = worldborder.clampToBounds(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ());
+
+- return (PortalInfo) this.getExitPortal(serverlevel, blockpos, flag2, worldborder).map((blockutil_foundrectangle) -> {
+- BlockState blockstate = this.level().getBlockState(this.portalEntrancePos);
+- Direction.Axis direction_axis;
+- Vec3 vec3;
++ return (PortalInfo) this.getExitPortal(destination, blockposition, flag2, worldborder, event.getSearchRadius(), event.getCanCreatePortal(), event.getCreationRadius()).map((blockutil_rectangle) -> {
++ // CraftBukkit end
++ IBlockData iblockdata = this.level().getBlockState(this.portalEntrancePos);
++ Direction.Axis enumdirection_enumaxis;
++ Vec3 vec3d;
+
+- if (blockstate.hasProperty(BlockStateProperties.HORIZONTAL_AXIS)) {
+- direction_axis = (Direction.Axis) blockstate.getValue(BlockStateProperties.HORIZONTAL_AXIS);
+- BlockUtil.FoundRectangle blockutil_foundrectangle1 = BlockUtil.getLargestRectangleAround(this.portalEntrancePos, direction_axis, 21, Direction.Axis.Y, 21, (blockpos1) -> {
+- return this.level().getBlockState(blockpos1) == blockstate;
++ if (iblockdata.hasProperty(BlockStateProperties.HORIZONTAL_AXIS)) {
++ enumdirection_enumaxis = (Direction.Axis) iblockdata.getValue(BlockStateProperties.HORIZONTAL_AXIS);
++ BlockUtil.FoundRectangle blockutil_rectangle1 = BlockUtil.getLargestRectangleAround(this.portalEntrancePos, enumdirection_enumaxis, 21, Direction.Axis.Y, 21, (blockposition1) -> {
++ return this.level().getBlockState(blockposition1) == iblockdata;
+ });
+
+- vec3 = this.getRelativePortalPosition(direction_axis, blockutil_foundrectangle1);
++ vec3d = this.getRelativePortalPosition(enumdirection_enumaxis, blockutil_rectangle1);
+ } else {
+- direction_axis = Direction.Axis.X;
+- vec3 = new Vec3(0.5D, 0.0D, 0.0D);
++ enumdirection_enumaxis = Direction.Axis.X;
++ vec3d = new Vec3(0.5D, 0.0D, 0.0D);
+ }
+
+- return PortalShape.createPortalInfo(serverlevel, blockutil_foundrectangle, direction_axis, vec3, this, this.getDeltaMovement(), this.getYRot(), this.getXRot());
+- }).orElse((Object) null);
++ return PortalShape.createPortalInfo(worldserverFinal, blockutil_rectangle, enumdirection_enumaxis, vec3d, this, this.getDeltaMovement(), this.getYRot(), this.getXRot(), event); // CraftBukkit
++ }).orElse(null); // CraftBukkit - decompile error
+ }
+ } else {
+- BlockPos blockpos1;
++ BlockPos blockposition1;
+
+ if (flag1) {
+- blockpos1 = ServerLevel.END_SPAWN_POINT;
++ blockposition1 = ServerLevel.END_SPAWN_POINT;
+ } else {
+- blockpos1 = serverlevel.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, serverlevel.getSharedSpawnPos());
++ blockposition1 = destination.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, destination.getSharedSpawnPos());
+ }
++ // CraftBukkit start
++ CraftPortalEvent event = callPortalEvent(this, destination, new Vec3(blockposition1.getX() + 0.5D, blockposition1.getY(), blockposition1.getZ() + 0.5D), PlayerTeleportEvent.TeleportCause.END_PORTAL, 0, 0);
++ if (event == null) {
++ return null;
++ }
+
+- return new PortalInfo(new Vec3((double) blockpos1.getX() + 0.5D, (double) blockpos1.getY(), (double) blockpos1.getZ() + 0.5D), this.getDeltaMovement(), this.getYRot(), this.getXRot());
++ return new PortalInfo(new Vec3(event.getTo().getX(), event.getTo().getY(), event.getTo().getZ()), this.getDeltaMovement(), this.getYRot(), this.getXRot(), ((CraftWorld) event.getTo().getWorld()).getHandle(), event);
++ // CraftBukkit end
+ }
+ }
+
+- protected Vec3 getRelativePortalPosition(Direction.Axis direction_axis, BlockUtil.FoundRectangle blockutil_foundrectangle) {
+- return PortalShape.getRelativePosition(blockutil_foundrectangle, direction_axis, this.position(), this.getDimensions(this.getPose()));
++ protected Vec3 getRelativePortalPosition(Direction.Axis axis, BlockUtil.FoundRectangle portal) {
++ return PortalShape.getRelativePosition(portal, axis, this.position(), this.getDimensions(this.getPose()));
+ }
+
+- protected Optional<BlockUtil.FoundRectangle> getExitPortal(ServerLevel serverlevel, BlockPos blockpos, boolean flag, WorldBorder worldborder) {
+- return serverlevel.getPortalForcer().findPortalAround(blockpos, flag, worldborder);
++ // CraftBukkit start
++ protected CraftPortalEvent callPortalEvent(Entity entity, ServerLevel exitWorldServer, Vec3 exitPosition, PlayerTeleportEvent.TeleportCause cause, int searchRadius, int creationRadius) {
++ org.bukkit.entity.Entity bukkitEntity = entity.getBukkitEntity();
++ Location enter = bukkitEntity.getLocation();
++ Location exit = CraftLocation.toBukkit(exitPosition, exitWorldServer.getWorld());
++
++ EntityPortalEvent event = new EntityPortalEvent(bukkitEntity, enter, exit, searchRadius);
++ event.getEntity().getServer().getPluginManager().callEvent(event);
++ if (event.isCancelled() || event.getTo() == null || event.getTo().getWorld() == null || !entity.isAlive()) {
++ return null;
++ }
++ return new CraftPortalEvent(event);
+ }
+
++ protected Optional<BlockUtil.FoundRectangle> getExitPortal(ServerLevel worldserver, BlockPos blockposition, boolean flag, WorldBorder worldborder, int searchRadius, boolean canCreatePortal, int createRadius) {
++ return worldserver.getPortalForcer().findPortalAround(blockposition, worldborder, searchRadius);
++ // CraftBukkit end
++ }
++
+ public boolean canChangeDimensions() {
+ return !this.isPassenger() && !this.isVehicle();
+ }
+
+- public float getBlockExplosionResistance(Explosion explosion, BlockGetter blockgetter, BlockPos blockpos, BlockState blockstate, FluidState fluidstate, float f) {
+- return f;
++ public float getBlockExplosionResistance(Explosion explosion, BlockGetter level, BlockPos pos, IBlockData blockState, FluidState fluidState, float explosionPower) {
++ return explosionPower;
+ }
+
+- public boolean shouldBlockExplode(Explosion explosion, BlockGetter blockgetter, BlockPos blockpos, BlockState blockstate, float f) {
++ public boolean shouldBlockExplode(Explosion explosion, BlockGetter level, BlockPos pos, IBlockData blockState, float explosionPower) {
+ return true;
+ }
+
+@@ -2713,25 +3180,25 @@
+ return false;
+ }
+
+- public void fillCrashReportCategory(CrashReportCategory crashreportcategory) {
+- crashreportcategory.setDetail("Entity Type", () -> {
+- ResourceLocation resourcelocation = EntityType.getKey(this.getType());
++ public void fillCrashReportCategory(CrashReportCategory category) {
++ category.setDetail("Entity Type", () -> {
++ ResourceLocation minecraftkey = EntityType.getKey(this.getType());
+
+- return resourcelocation + " (" + this.getClass().getCanonicalName() + ")";
++ return minecraftkey + " (" + this.getClass().getCanonicalName() + ")";
+ });
+- crashreportcategory.setDetail("Entity ID", (Object) this.id);
+- crashreportcategory.setDetail("Entity Name", () -> {
++ category.setDetail("Entity ID", (Object) this.id);
++ category.setDetail("Entity Name", () -> {
+ return this.getName().getString();
+ });
+- crashreportcategory.setDetail("Entity's Exact location", (Object) String.format(Locale.ROOT, "%.2f, %.2f, %.2f", this.getX(), this.getY(), this.getZ()));
+- crashreportcategory.setDetail("Entity's Block location", (Object) CrashReportCategory.formatLocation(this.level(), Mth.floor(this.getX()), Mth.floor(this.getY()), Mth.floor(this.getZ())));
+- Vec3 vec3 = this.getDeltaMovement();
++ category.setDetail("Entity's Exact location", (Object) String.format(Locale.ROOT, "%.2f, %.2f, %.2f", this.getX(), this.getY(), this.getZ()));
++ category.setDetail("Entity's Block location", (Object) CrashReportCategory.formatLocation(this.level(), Mth.floor(this.getX()), Mth.floor(this.getY()), Mth.floor(this.getZ())));
++ Vec3 vec3d = this.getDeltaMovement();
+
+- crashreportcategory.setDetail("Entity's Momentum", (Object) String.format(Locale.ROOT, "%.2f, %.2f, %.2f", vec3.x, vec3.y, vec3.z));
+- crashreportcategory.setDetail("Entity's Passengers", () -> {
++ category.setDetail("Entity's Momentum", (Object) String.format(Locale.ROOT, "%.2f, %.2f, %.2f", vec3d.x, vec3d.y, vec3d.z));
++ category.setDetail("Entity's Passengers", () -> {
+ return this.getPassengers().toString();
+ });
+- crashreportcategory.setDetail("Entity's Vehicle", () -> {
++ category.setDetail("Entity's Vehicle", () -> {
+ return String.valueOf(this.getVehicle());
+ });
+ }
+@@ -2740,13 +3207,12 @@
+ return this.isOnFire() && !this.isSpectator();
+ }
+
+- public void setUUID(UUID uuid) {
+- this.uuid = uuid;
++ public void setUUID(UUID uniqueId) {
++ this.uuid = uniqueId;
+ this.stringUUID = this.uuid.toString();
+ }
+
+ @Override
+- @Override
+ public UUID getUUID() {
+ return this.uuid;
+ }
+@@ -2756,7 +3222,6 @@
+ }
+
+ @Override
+- @Override
+ public String getScoreboardName() {
+ return this.stringUUID;
+ }
+@@ -2769,85 +3234,92 @@
+ return Entity.viewScale;
+ }
+
+- public static void setViewScale(double d0) {
+- Entity.viewScale = d0;
++ public static void setViewScale(double renderDistWeight) {
++ Entity.viewScale = renderDistWeight;
+ }
+
+ @Override
+- @Override
+ public Component getDisplayName() {
+- return PlayerTeam.formatNameForTeam(this.getTeam(), this.getName()).withStyle((style) -> {
+- return style.withHoverEvent(this.createHoverEvent()).withInsertion(this.getStringUUID());
++ return PlayerTeam.formatNameForTeam(this.getTeam(), this.getName()).withStyle((chatmodifier) -> {
++ return chatmodifier.withHoverEvent(this.createHoverEvent()).withInsertion(this.getStringUUID());
+ });
+ }
+
+- public void setCustomName(@Nullable Component component) {
+- this.entityData.set(Entity.DATA_CUSTOM_NAME, Optional.ofNullable(component));
++ public void setCustomName(@Nullable Component name) {
++ this.entityData.set(Entity.DATA_CUSTOM_NAME, Optional.ofNullable(name));
+ }
+
+ @Nullable
+ @Override
+- @Override
+ public Component getCustomName() {
+ return (Component) ((Optional) this.entityData.get(Entity.DATA_CUSTOM_NAME)).orElse((Object) null);
+ }
+
+ @Override
+- @Override
+ public boolean hasCustomName() {
+ return ((Optional) this.entityData.get(Entity.DATA_CUSTOM_NAME)).isPresent();
+ }
+
+- public void setCustomNameVisible(boolean flag) {
+- this.entityData.set(Entity.DATA_CUSTOM_NAME_VISIBLE, flag);
++ public void setCustomNameVisible(boolean alwaysRenderNameTag) {
++ this.entityData.set(Entity.DATA_CUSTOM_NAME_VISIBLE, alwaysRenderNameTag);
+ }
+
+ public boolean isCustomNameVisible() {
+ return (Boolean) this.entityData.get(Entity.DATA_CUSTOM_NAME_VISIBLE);
+ }
+
+- public final void teleportToWithTicket(double d0, double d1, double d2) {
++ public final void teleportToWithTicket(double x, double d1, double y) {
+ if (this.level() instanceof ServerLevel) {
+- ChunkPos chunkpos = new ChunkPos(BlockPos.containing(d0, d1, d2));
++ ChunkPos chunkcoordintpair = new ChunkPos(BlockPos.containing(x, d1, y));
+
+- ((ServerLevel) this.level()).getChunkSource().addRegionTicket(TicketType.POST_TELEPORT, chunkpos, 0, this.getId());
+- this.level().getChunk(chunkpos.x, chunkpos.z);
+- this.teleportTo(d0, d1, d2);
++ ((ServerLevel) this.level()).getChunkSource().addRegionTicket(TicketType.POST_TELEPORT, chunkcoordintpair, 0, this.getId());
++ this.level().getChunk(chunkcoordintpair.x, chunkcoordintpair.z);
++ this.teleportTo(x, d1, y);
+ }
+ }
+
+- public boolean teleportTo(ServerLevel serverlevel, double d0, double d1, double d2, Set<RelativeMovement> set, float f, float f1) {
++ // CraftBukkit start
++ public boolean teleportTo(ServerLevel worldserver, double d0, double d1, double d2, Set<RelativeMovement> set, float f, float f1, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) {
++ return this.teleportTo(worldserver, d0, d1, d2, set, f, f1);
++ }
++ // CraftBukkit end
++
++ public boolean teleportTo(ServerLevel level, double x, double d1, double y, Set<RelativeMovement> set, float z, float f1) {
+ float f2 = Mth.clamp(f1, -90.0F, 90.0F);
+
+- if (serverlevel == this.level()) {
+- this.moveTo(d0, d1, d2, f, f2);
++ if (level == this.level()) {
++ this.moveTo(x, d1, y, z, f2);
+ this.teleportPassengers();
+- this.setYHeadRot(f);
++ this.setYHeadRot(z);
+ } else {
+ this.unRide();
+- Entity entity = this.getType().create(serverlevel);
++ Entity entity = this.getType().create(level);
+
+ if (entity == null) {
+ return false;
+ }
+
+ entity.restoreFrom(this);
+- entity.moveTo(d0, d1, d2, f, f2);
+- entity.setYHeadRot(f);
++ entity.moveTo(x, d1, y, z, f2);
++ entity.setYHeadRot(z);
+ this.setRemoved(Entity.RemovalReason.CHANGED_DIMENSION);
+- serverlevel.addDuringTeleport(entity);
++ // CraftBukkit start - Don't spawn the new entity if the current entity isn't spawned
++ if (inWorld) {
++ level.addDuringTeleport(entity);
++ }
++ // CraftBukkit end
+ }
+
+ return true;
+ }
+
+- public void dismountTo(double d0, double d1, double d2) {
+- this.teleportTo(d0, d1, d2);
++ public void dismountTo(double x, double d1, double y) {
++ this.teleportTo(x, d1, y);
+ }
+
+- public void teleportTo(double d0, double d1, double d2) {
++ public void teleportTo(double x, double d1, double y) {
+ if (this.level() instanceof ServerLevel) {
+- this.moveTo(d0, d1, d2, this.getYRot(), this.getXRot());
++ this.moveTo(x, d1, y, this.getYRot(), this.getXRot());
+ this.teleportPassengers();
+ }
+ }
+@@ -2865,18 +3337,18 @@
+ });
+ }
+
+- public void teleportRelative(double d0, double d1, double d2) {
+- this.teleportTo(this.getX() + d0, this.getY() + d1, this.getZ() + d2);
++ public void teleportRelative(double dx, double d1, double dy) {
++ this.teleportTo(this.getX() + dx, this.getY() + d1, this.getZ() + dy);
+ }
+
+ public boolean shouldShowName() {
+ return this.isCustomNameVisible();
+ }
+
+- public void onSyncedDataUpdated(List<SynchedEntityData.DataValue<?>> list) {}
++ public void onSyncedDataUpdated(List<SynchedEntityData.DataValue<?>> dataValues) {}
+
+- public void onSyncedDataUpdated(EntityDataAccessor<?> entitydataaccessor) {
+- if (Entity.DATA_POSE.equals(entitydataaccessor)) {
++ public void onSyncedDataUpdated(EntityDataAccessor<?> key) {
++ if (Entity.DATA_POSE.equals(key)) {
+ this.refreshDimensions();
+ }
+
+@@ -2885,31 +3357,31 @@
+ /** @deprecated */
+ @Deprecated
+ protected void fixupDimensions() {
+- Pose pose = this.getPose();
+- EntityDimensions entitydimensions = this.getDimensions(pose);
++ EntityPose entitypose = this.getPose();
++ EntityDimensions entitysize = this.getDimensions(entitypose);
+
+- this.dimensions = entitydimensions;
+- this.eyeHeight = this.getEyeHeight(pose, entitydimensions);
++ this.dimensions = entitysize;
++ this.eyeHeight = this.getEyeHeight(entitypose, entitysize);
+ }
+
+ public void refreshDimensions() {
+- EntityDimensions entitydimensions = this.dimensions;
+- Pose pose = this.getPose();
+- EntityDimensions entitydimensions1 = this.getDimensions(pose);
++ EntityDimensions entitysize = this.dimensions;
++ EntityPose entitypose = this.getPose();
++ EntityDimensions entitysize1 = this.getDimensions(entitypose);
+
+- this.dimensions = entitydimensions1;
+- this.eyeHeight = this.getEyeHeight(pose, entitydimensions1);
++ this.dimensions = entitysize1;
++ this.eyeHeight = this.getEyeHeight(entitypose, entitysize1);
+ this.reapplyPosition();
+- boolean flag = (double) entitydimensions1.width <= 4.0D && (double) entitydimensions1.height <= 4.0D;
++ boolean flag = (double) entitysize1.width <= 4.0D && (double) entitysize1.height <= 4.0D;
+
+- if (!this.level().isClientSide && !this.firstTick && !this.noPhysics && flag && (entitydimensions1.width > entitydimensions.width || entitydimensions1.height > entitydimensions.height) && !(this instanceof Player)) {
+- Vec3 vec3 = this.position().add(0.0D, (double) entitydimensions.height / 2.0D, 0.0D);
+- double d0 = (double) Math.max(0.0F, entitydimensions1.width - entitydimensions.width) + 1.0E-6D;
+- double d1 = (double) Math.max(0.0F, entitydimensions1.height - entitydimensions.height) + 1.0E-6D;
+- VoxelShape voxelshape = Shapes.create(AABB.ofSize(vec3, d0, d1, d0));
++ if (!this.level().isClientSide && !this.firstTick && !this.noPhysics && flag && (entitysize1.width > entitysize.width || entitysize1.height > entitysize.height) && !(this instanceof Player)) {
++ Vec3 vec3d = this.position().add(0.0D, (double) entitysize.height / 2.0D, 0.0D);
++ double d0 = (double) Math.max(0.0F, entitysize1.width - entitysize.width) + 1.0E-6D;
++ double d1 = (double) Math.max(0.0F, entitysize1.height - entitysize.height) + 1.0E-6D;
++ VoxelShape voxelshape = Shapes.create(AABB.ofSize(vec3d, d0, d1, d0));
+
+- this.level().findFreePosition(this, voxelshape, vec3, (double) entitydimensions1.width, (double) entitydimensions1.height, (double) entitydimensions1.width).ifPresent((vec31) -> {
+- this.setPos(vec31.add(0.0D, (double) (-entitydimensions1.height) / 2.0D, 0.0D));
++ this.level().findFreePosition(this, voxelshape, vec3d, (double) entitysize1.width, (double) entitysize1.height, (double) entitysize1.width).ifPresent((vec3d1) -> {
++ this.setPos(vec3d1.add(0.0D, (double) (-entitysize1.height) / 2.0D, 0.0D));
+ });
+ }
+
+@@ -2927,12 +3399,11 @@
+ return new HoverEvent(HoverEvent.Action.SHOW_ENTITY, new HoverEvent.EntityTooltipInfo(this.getType(), this.getUUID(), this.getName()));
+ }
+
+- public boolean broadcastToPlayer(ServerPlayer serverplayer) {
++ public boolean broadcastToPlayer(ServerPlayer player) {
+ return true;
+ }
+
+ @Override
+- @Override
+ public final AABB getBoundingBox() {
+ return this.bb;
+ }
+@@ -2941,15 +3412,34 @@
+ return this.getBoundingBox();
+ }
+
+- public final void setBoundingBox(AABB aabb) {
+- this.bb = aabb;
++ public final void setBoundingBox(AABB bb) {
++ // CraftBukkit start - block invalid bounding boxes
++ double minX = bb.minX,
++ minY = bb.minY,
++ minZ = bb.minZ,
++ maxX = bb.maxX,
++ maxY = bb.maxY,
++ maxZ = bb.maxZ;
++ double len = bb.maxX - bb.minX;
++ if (len < 0) maxX = minX;
++ if (len > 64) maxX = minX + 64.0;
++
++ len = bb.maxY - bb.minY;
++ if (len < 0) maxY = minY;
++ if (len > 64) maxY = minY + 64.0;
++
++ len = bb.maxZ - bb.minZ;
++ if (len < 0) maxZ = minZ;
++ if (len > 64) maxZ = minZ + 64.0;
++ this.bb = new AABB(minX, minY, minZ, maxX, maxY, maxZ);
++ // CraftBukkit end
+ }
+
+- protected float getEyeHeight(Pose pose, EntityDimensions entitydimensions) {
+- return entitydimensions.height * 0.85F;
++ protected float getEyeHeight(EntityPose pose, EntityDimensions dimensions) {
++ return dimensions.height * 0.85F;
+ }
+
+- public float getEyeHeight(Pose pose) {
++ public float getEyeHeight(EntityPose pose) {
+ return this.getEyeHeight(pose, this.getDimensions(pose));
+ }
+
+@@ -2957,7 +3447,7 @@
+ return this.eyeHeight;
+ }
+
+- public Vec3 getLeashOffset(float f) {
++ public Vec3 getLeashOffset(float partialTick) {
+ return this.getLeashOffset();
+ }
+
+@@ -2965,12 +3455,11 @@
+ return new Vec3(0.0D, (double) this.getEyeHeight(), (double) (this.getBbWidth() * 0.4F));
+ }
+
+- public SlotAccess getSlot(int i) {
++ public SlotAccess getSlot(int slot) {
+ return SlotAccess.NULL;
+ }
+
+ @Override
+- @Override
+ public void sendSystemMessage(Component component) {}
+
+ public Level getCommandSenderWorld() {
+@@ -2982,7 +3471,7 @@
+ return this.level().getServer();
+ }
+
+- public InteractionResult interactAt(Player player, Vec3 vec3, InteractionHand interactionhand) {
++ public InteractionResult interactAt(Player player, Vec3 vec, EnumHand hand) {
+ return InteractionResult.PASS;
+ }
+
+@@ -2990,22 +3479,22 @@
+ return false;
+ }
+
+- public void doEnchantDamageEffects(LivingEntity livingentity, Entity entity) {
+- if (entity instanceof LivingEntity) {
+- EnchantmentHelper.doPostHurtEffects((LivingEntity) entity, livingentity);
++ public void doEnchantDamageEffects(net.minecraft.world.entity.LivingEntity attacker, Entity target) {
++ if (target instanceof net.minecraft.world.entity.LivingEntity) {
++ EnchantmentHelper.doPostHurtEffects((net.minecraft.world.entity.LivingEntity) target, attacker);
+ }
+
+- EnchantmentHelper.doPostDamageEffects(livingentity, entity);
++ EnchantmentHelper.doPostDamageEffects(attacker, target);
+ }
+
+- public void startSeenByPlayer(ServerPlayer serverplayer) {}
++ public void startSeenByPlayer(ServerPlayer serverPlayer) {}
+
+- public void stopSeenByPlayer(ServerPlayer serverplayer) {}
++ public void stopSeenByPlayer(ServerPlayer serverPlayer) {}
+
+- public float rotate(Rotation rotation) {
++ public float rotate(Rotation transformRotation) {
+ float f = Mth.wrapDegrees(this.getYRot());
+
+- switch (rotation) {
++ switch (transformRotation) {
+ case CLOCKWISE_180:
+ return f + 180.0F;
+ case COUNTERCLOCKWISE_90:
+@@ -3017,10 +3506,10 @@
+ }
+ }
+
+- public float mirror(Mirror mirror) {
++ public float mirror(Mirror transformMirror) {
+ float f = Mth.wrapDegrees(this.getYRot());
+
+- switch (mirror) {
++ switch (transformMirror) {
+ case FRONT_BACK:
+ return -f;
+ case LEFT_RIGHT:
+@@ -3035,7 +3524,7 @@
+ }
+
+ @Nullable
+- public LivingEntity getControllingPassenger() {
++ public net.minecraft.world.entity.LivingEntity getControllingPassenger() {
+ return null;
+ }
+
+@@ -3077,13 +3566,11 @@
+ }
+
+ @Override
+- @Override
+ public Stream<Entity> getSelfAndPassengers() {
+ return Stream.concat(Stream.of(this), this.getIndirectPassengersStream());
+ }
+
+ @Override
+- @Override
+ public Stream<Entity> getPassengersAndSelf() {
+ return Stream.concat(this.passengers.stream().flatMap(Entity::getPassengersAndSelf), Stream.of(this));
+ }
+@@ -3129,12 +3616,12 @@
+ }
+
+ public boolean isControlledByLocalInstance() {
+- LivingEntity livingentity = this.getControllingPassenger();
++ net.minecraft.world.entity.LivingEntity entityliving = this.getControllingPassenger();
+
+- if (livingentity instanceof Player) {
+- Player player = (Player) livingentity;
++ if (entityliving instanceof Player) {
++ Player entityhuman = (Player) entityliving;
+
+- return player.isLocalPlayer();
++ return entityhuman.isLocalPlayer();
+ } else {
+ return this.isEffectiveAi();
+ }
+@@ -3144,16 +3631,16 @@
+ return !this.level().isClientSide;
+ }
+
+- protected static Vec3 getCollisionHorizontalEscapeVector(double d0, double d1, float f) {
+- double d2 = (d0 + d1 + 9.999999747378752E-6D) / 2.0D;
+- float f1 = -Mth.sin(f * 0.017453292F);
+- float f2 = Mth.cos(f * 0.017453292F);
++ protected static Vec3 getCollisionHorizontalEscapeVector(double vehicleWidth, double d1, float passengerWidth) {
++ double d2 = (vehicleWidth + d1 + 9.999999747378752E-6D) / 2.0D;
++ float f1 = -Mth.sin(passengerWidth * 0.017453292F);
++ float f2 = Mth.cos(passengerWidth * 0.017453292F);
+ float f3 = Math.max(Math.abs(f1), Math.abs(f2));
+
+ return new Vec3((double) f1 * d2 / (double) f3, 0.0D, (double) f2 * d2 / (double) f3);
+ }
+
+- public Vec3 getDismountLocationForPassenger(LivingEntity livingentity) {
++ public Vec3 getDismountLocationForPassenger(net.minecraft.world.entity.LivingEntity passenger) {
+ return new Vec3(this.getX(), this.getBoundingBox().maxY, this.getZ());
+ }
+
+@@ -3167,15 +3654,15 @@
+ return this.vehicle != null && this.vehicle.getControllingPassenger() == this ? this.vehicle : null;
+ }
+
+- public PushReaction getPistonPushReaction() {
+- return PushReaction.NORMAL;
++ public EnumPistonReaction getPistonPushReaction() {
++ return EnumPistonReaction.NORMAL;
+ }
+
+ public SoundSource getSoundSource() {
+ return SoundSource.NEUTRAL;
+ }
+
+- protected int getFireImmuneTicks() {
++ public int getFireImmuneTicks() {
+ return 1;
+ }
+
+@@ -3187,33 +3674,30 @@
+ return 0;
+ }
+
+- public boolean hasPermissions(int i) {
+- return this.getPermissionLevel() >= i;
++ public boolean hasPermissions(int level) {
++ return this.getPermissionLevel() >= level;
+ }
+
+ @Override
+- @Override
+ public boolean acceptsSuccess() {
+ return this.level().getGameRules().getBoolean(GameRules.RULE_SENDCOMMANDFEEDBACK);
+ }
+
+ @Override
+- @Override
+ public boolean acceptsFailure() {
+ return true;
+ }
+
+ @Override
+- @Override
+ public boolean shouldInformAdmins() {
+ return true;
+ }
+
+- public void lookAt(EntityAnchorArgument.Anchor entityanchorargument_anchor, Vec3 vec3) {
+- Vec3 vec31 = entityanchorargument_anchor.apply(this);
+- double d0 = vec3.x - vec31.x;
+- double d1 = vec3.y - vec31.y;
+- double d2 = vec3.z - vec31.z;
++ public void lookAt(EntityAnchorArgument.Anchor anchor, Vec3 target) {
++ Vec3 vec3d1 = anchor.apply(this);
++ double d0 = target.x - vec3d1.x;
++ double d1 = target.y - vec3d1.y;
++ double d2 = target.z - vec3d1.z;
+ double d3 = Math.sqrt(d0 * d0 + d2 * d2);
+
+ this.setXRot(Mth.wrapDegrees((float) (-(Mth.atan2(d1, d3) * 57.2957763671875D))));
+@@ -3223,90 +3707,95 @@
+ this.yRotO = this.getYRot();
+ }
+
+- public boolean updateFluidHeightAndDoFluidPushing(TagKey<Fluid> tagkey, double d0) {
++ public boolean updateFluidHeightAndDoFluidPushing(TagKey<Fluid> fluidTag, double motionScale) {
+ if (this.touchingUnloadedChunk()) {
+ return false;
+ } else {
+- AABB aabb = this.getBoundingBox().deflate(0.001D);
+- int i = Mth.floor(aabb.minX);
+- int j = Mth.ceil(aabb.maxX);
+- int k = Mth.floor(aabb.minY);
+- int l = Mth.ceil(aabb.maxY);
+- int i1 = Mth.floor(aabb.minZ);
+- int j1 = Mth.ceil(aabb.maxZ);
++ AABB axisalignedbb = this.getBoundingBox().deflate(0.001D);
++ int i = Mth.floor(axisalignedbb.minX);
++ int j = Mth.ceil(axisalignedbb.maxX);
++ int k = Mth.floor(axisalignedbb.minY);
++ int l = Mth.ceil(axisalignedbb.maxY);
++ int i1 = Mth.floor(axisalignedbb.minZ);
++ int j1 = Mth.ceil(axisalignedbb.maxZ);
+ double d1 = 0.0D;
+ boolean flag = this.isPushedByFluid();
+ boolean flag1 = false;
+- Vec3 vec3 = Vec3.ZERO;
++ Vec3 vec3d = Vec3.ZERO;
+ int k1 = 0;
+- BlockPos.MutableBlockPos blockpos_mutableblockpos = new BlockPos.MutableBlockPos();
++ BlockPos.MutableBlockPos blockposition_mutableblockposition = new BlockPos.MutableBlockPos();
+
+ for (int l1 = i; l1 < j; ++l1) {
+ for (int i2 = k; i2 < l; ++i2) {
+ for (int j2 = i1; j2 < j1; ++j2) {
+- blockpos_mutableblockpos.set(l1, i2, j2);
+- FluidState fluidstate = this.level().getFluidState(blockpos_mutableblockpos);
++ blockposition_mutableblockposition.set(l1, i2, j2);
++ FluidState fluid = this.level().getFluidState(blockposition_mutableblockposition);
+
+- if (fluidstate.is(tagkey)) {
+- double d2 = (double) ((float) i2 + fluidstate.getHeight(this.level(), blockpos_mutableblockpos));
++ if (fluid.is(fluidTag)) {
++ double d2 = (double) ((float) i2 + fluid.getHeight(this.level(), blockposition_mutableblockposition));
+
+- if (d2 >= aabb.minY) {
++ if (d2 >= axisalignedbb.minY) {
+ flag1 = true;
+- d1 = Math.max(d2 - aabb.minY, d1);
++ d1 = Math.max(d2 - axisalignedbb.minY, d1);
+ if (flag) {
+- Vec3 vec31 = fluidstate.getFlow(this.level(), blockpos_mutableblockpos);
++ Vec3 vec3d1 = fluid.getFlow(this.level(), blockposition_mutableblockposition);
+
+ if (d1 < 0.4D) {
+- vec31 = vec31.scale(d1);
++ vec3d1 = vec3d1.scale(d1);
+ }
+
+- vec3 = vec3.add(vec31);
++ vec3d = vec3d.add(vec3d1);
+ ++k1;
+ }
++ // CraftBukkit start - store last lava contact location
++ if (fluidTag == FluidTags.LAVA) {
++ this.lastLavaContact = blockposition_mutableblockposition.immutable();
++ }
++ // CraftBukkit end
+ }
+ }
+ }
+ }
+ }
+
+- if (vec3.length() > 0.0D) {
++ if (vec3d.length() > 0.0D) {
+ if (k1 > 0) {
+- vec3 = vec3.scale(1.0D / (double) k1);
++ vec3d = vec3d.scale(1.0D / (double) k1);
+ }
+
+ if (!(this instanceof Player)) {
+- vec3 = vec3.normalize();
++ vec3d = vec3d.normalize();
+ }
+
+- Vec3 vec32 = this.getDeltaMovement();
++ Vec3 vec3d2 = this.getDeltaMovement();
+
+- vec3 = vec3.scale(d0 * 1.0D);
++ vec3d = vec3d.scale(motionScale * 1.0D);
+ double d3 = 0.003D;
+
+- if (Math.abs(vec32.x) < 0.003D && Math.abs(vec32.z) < 0.003D && vec3.length() < 0.0045000000000000005D) {
+- vec3 = vec3.normalize().scale(0.0045000000000000005D);
++ if (Math.abs(vec3d2.x) < 0.003D && Math.abs(vec3d2.z) < 0.003D && vec3d.length() < 0.0045000000000000005D) {
++ vec3d = vec3d.normalize().scale(0.0045000000000000005D);
+ }
+
+- this.setDeltaMovement(this.getDeltaMovement().add(vec3));
++ this.setDeltaMovement(this.getDeltaMovement().add(vec3d));
+ }
+
+- this.fluidHeight.put(tagkey, d1);
++ this.fluidHeight.put(fluidTag, d1);
+ return flag1;
+ }
+ }
+
+ public boolean touchingUnloadedChunk() {
+- AABB aabb = this.getBoundingBox().inflate(1.0D);
+- int i = Mth.floor(aabb.minX);
+- int j = Mth.ceil(aabb.maxX);
+- int k = Mth.floor(aabb.minZ);
+- int l = Mth.ceil(aabb.maxZ);
++ AABB axisalignedbb = this.getBoundingBox().inflate(1.0D);
++ int i = Mth.floor(axisalignedbb.minX);
++ int j = Mth.ceil(axisalignedbb.maxX);
++ int k = Mth.floor(axisalignedbb.minZ);
++ int l = Mth.ceil(axisalignedbb.maxZ);
+
+ return !this.level().hasChunksAt(i, k, j, l);
+ }
+
+- public double getFluidHeight(TagKey<Fluid> tagkey) {
+- return this.fluidHeight.getDouble(tagkey);
++ public double getFluidHeight(TagKey<Fluid> fluidTag) {
++ return this.fluidHeight.getDouble(fluidTag);
+ }
+
+ public double getFluidJumpThreshold() {
+@@ -3329,7 +3818,7 @@
+ return new ClientboundAddEntityPacket(this);
+ }
+
+- public EntityDimensions getDimensions(Pose pose) {
++ public EntityDimensions getDimensions(EntityPose pose) {
+ return this.type.getDimensions();
+ }
+
+@@ -3342,12 +3831,11 @@
+ }
+
+ @Override
+- @Override
+ public BlockPos blockPosition() {
+ return this.blockPosition;
+ }
+
+- public BlockState getFeetBlockState() {
++ public IBlockData getFeetBlockState() {
+ if (this.feetBlockState == null) {
+ this.feetBlockState = this.level().getBlockState(this.blockPosition());
+ }
+@@ -3363,16 +3851,16 @@
+ return this.deltaMovement;
+ }
+
+- public void setDeltaMovement(Vec3 vec3) {
+- this.deltaMovement = vec3;
++ public void setDeltaMovement(Vec3 deltaMovement) {
++ this.deltaMovement = deltaMovement;
+ }
+
+- public void addDeltaMovement(Vec3 vec3) {
+- this.setDeltaMovement(this.getDeltaMovement().add(vec3));
++ public void addDeltaMovement(Vec3 addend) {
++ this.setDeltaMovement(this.getDeltaMovement().add(addend));
+ }
+
+- public void setDeltaMovement(double d0, double d1, double d2) {
+- this.setDeltaMovement(new Vec3(d0, d1, d2));
++ public void setDeltaMovement(double x, double d1, double y) {
++ this.setDeltaMovement(new Vec3(x, d1, y));
+ }
+
+ public final int getBlockX() {
+@@ -3383,12 +3871,12 @@
+ return this.position.x;
+ }
+
+- public double getX(double d0) {
+- return this.position.x + (double) this.getBbWidth() * d0;
++ public double getX(double scale) {
++ return this.position.x + (double) this.getBbWidth() * scale;
+ }
+
+- public double getRandomX(double d0) {
+- return this.getX((2.0D * this.random.nextDouble() - 1.0D) * d0);
++ public double getRandomX(double scale) {
++ return this.getX((2.0D * this.random.nextDouble() - 1.0D) * scale);
+ }
+
+ public final int getBlockY() {
+@@ -3399,8 +3887,8 @@
+ return this.position.y;
+ }
+
+- public double getY(double d0) {
+- return this.position.y + (double) this.getBbHeight() * d0;
++ public double getY(double scale) {
++ return this.position.y + (double) this.getBbHeight() * scale;
+ }
+
+ public double getRandomY() {
+@@ -3419,20 +3907,20 @@
+ return this.position.z;
+ }
+
+- public double getZ(double d0) {
+- return this.position.z + (double) this.getBbWidth() * d0;
++ public double getZ(double scale) {
++ return this.position.z + (double) this.getBbWidth() * scale;
+ }
+
+- public double getRandomZ(double d0) {
+- return this.getZ((2.0D * this.random.nextDouble() - 1.0D) * d0);
++ public double getRandomZ(double scale) {
++ return this.getZ((2.0D * this.random.nextDouble() - 1.0D) * scale);
+ }
+
+- public final void setPosRaw(double d0, double d1, double d2) {
+- if (this.position.x != d0 || this.position.y != d1 || this.position.z != d2) {
+- this.position = new Vec3(d0, d1, d2);
+- int i = Mth.floor(d0);
++ public final void setPosRaw(double x, double d1, double y) {
++ if (this.position.x != x || this.position.y != d1 || this.position.z != y) {
++ this.position = new Vec3(x, d1, y);
++ int i = Mth.floor(x);
+ int j = Mth.floor(d1);
+- int k = Mth.floor(d2);
++ int k = Mth.floor(y);
+
+ if (i != this.blockPosition.getX() || j != this.blockPosition.getY() || k != this.blockPosition.getZ()) {
+ this.blockPosition = new BlockPos(i, j, k);
+@@ -3449,22 +3937,22 @@
+
+ public void checkDespawn() {}
+
+- public Vec3 getRopeHoldPosition(float f) {
+- return this.getPosition(f).add(0.0D, (double) this.eyeHeight * 0.7D, 0.0D);
++ public Vec3 getRopeHoldPosition(float partialTicks) {
++ return this.getPosition(partialTicks).add(0.0D, (double) this.eyeHeight * 0.7D, 0.0D);
+ }
+
+- public void recreateFromPacket(ClientboundAddEntityPacket clientboundaddentitypacket) {
+- int i = clientboundaddentitypacket.getId();
+- double d0 = clientboundaddentitypacket.getX();
+- double d1 = clientboundaddentitypacket.getY();
+- double d2 = clientboundaddentitypacket.getZ();
++ public void recreateFromPacket(ClientboundAddEntityPacket packet) {
++ int i = packet.getId();
++ double d0 = packet.getX();
++ double d1 = packet.getY();
++ double d2 = packet.getZ();
+
+ this.syncPacketPositionCodec(d0, d1, d2);
+ this.moveTo(d0, d1, d2);
+- this.setXRot(clientboundaddentitypacket.getXRot());
+- this.setYRot(clientboundaddentitypacket.getYRot());
++ this.setXRot(packet.getXRot());
++ this.setYRot(packet.getYRot());
+ this.setId(i);
+- this.setUUID(clientboundaddentitypacket.getUUID());
++ this.setUUID(packet.getUUID());
+ }
+
+ @Nullable
+@@ -3472,8 +3960,8 @@
+ return null;
+ }
+
+- public void setIsInPowderSnow(boolean flag) {
+- this.isInPowderSnow = flag;
++ public void setIsInPowderSnow(boolean isInPowderSnow) {
++ this.isInPowderSnow = isInPowderSnow;
+ }
+
+ public boolean canFreeze() {
+@@ -3492,11 +3980,11 @@
+ return this.getYRot();
+ }
+
+- public void setYRot(float f) {
+- if (!Float.isFinite(f)) {
+- Util.logAndPauseIfInIde("Invalid entity rotation: " + f + ", discarding.");
++ public void setYRot(float yRot) {
++ if (!Float.isFinite(yRot)) {
++ Util.logAndPauseIfInIde("Invalid entity rotation: " + yRot + ", discarding.");
+ } else {
+- this.yRot = f;
++ this.yRot = yRot;
+ }
+ }
+
+@@ -3504,11 +3992,11 @@
+ return this.xRot;
+ }
+
+- public void setXRot(float f) {
+- if (!Float.isFinite(f)) {
+- Util.logAndPauseIfInIde("Invalid entity rotation: " + f + ", discarding.");
++ public void setXRot(float xRot) {
++ if (!Float.isFinite(xRot)) {
++ Util.logAndPauseIfInIde("Invalid entity rotation: " + xRot + ", discarding.");
+ } else {
+- this.xRot = f;
++ this.xRot = xRot;
+ }
+ }
+
+@@ -3520,8 +4008,8 @@
+ return this.maxUpStep;
+ }
+
+- public void setMaxUpStep(float f) {
+- this.maxUpStep = f;
++ public void setMaxUpStep(float maxUpStep) {
++ this.maxUpStep = maxUpStep;
+ }
+
+ public final boolean isRemoved() {
+@@ -3534,10 +4022,9 @@
+ }
+
+ @Override
+- @Override
+- public final void setRemoved(Entity.RemovalReason entity_removalreason) {
++ public final void setRemoved(Entity.RemovalReason removalReason) {
+ if (this.removalReason == null) {
+- this.removalReason = entity_removalreason;
++ this.removalReason = removalReason;
+ }
+
+ if (this.removalReason.shouldDestroy()) {
+@@ -3545,32 +4032,29 @@
+ }
+
+ this.getPassengers().forEach(Entity::stopRiding);
+- this.levelCallback.onRemove(entity_removalreason);
++ this.levelCallback.onRemove(removalReason);
+ }
+
+- protected void unsetRemoved() {
++ public void unsetRemoved() {
+ this.removalReason = null;
+ }
+
+ @Override
+- @Override
+- public void setLevelCallback(EntityInLevelCallback entityinlevelcallback) {
+- this.levelCallback = entityinlevelcallback;
++ public void setLevelCallback(EntityInLevelCallback levelCallback) {
++ this.levelCallback = levelCallback;
+ }
+
+ @Override
+- @Override
+ public boolean shouldBeSaved() {
+ return this.removalReason != null && !this.removalReason.shouldSave() ? false : (this.isPassenger() ? false : !this.isVehicle() || !this.hasExactlyOnePlayerPassenger());
+ }
+
+ @Override
+- @Override
+ public boolean isAlwaysTicking() {
+ return false;
+ }
+
+- public boolean mayInteract(Level level, BlockPos blockpos) {
++ public boolean mayInteract(Level level, BlockPos pos) {
+ return true;
+ }
+
+@@ -3647,6 +4131,6 @@
+ @FunctionalInterface
+ public interface MoveFunction {
+
+- void accept(Entity entity, double x, double d, double y);
++ void accept(Entity entity, double x, double d1, double y);
+ }
+ }