package net.minecraft.server.level;

import org.apache.logging.log4j.LogManager;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.HoverEvent;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.network.protocol.game.ClientboundAddPlayerPacket;
import net.minecraft.network.protocol.game.ClientboundSoundPacket;
import net.minecraft.sounds.SoundSource;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.network.protocol.game.ClientboundForgetLevelChunkPacket;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.network.protocol.game.ClientboundSetCameraPacket;
import net.minecraft.network.protocol.game.ClientboundResourcePackPacket;
import net.minecraft.world.entity.HumanoidArm;
import net.minecraft.network.protocol.game.ServerboundClientInformationPacket;
import java.util.UUID;
import net.minecraft.network.protocol.game.ClientboundRemoveMobEffectPacket;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.stats.RecipeBook;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.protocol.game.ClientboundPlayerLookAtPacket;
import net.minecraft.commands.arguments.EntityAnchorArgument;
import net.minecraft.network.protocol.game.ClientboundEntityEventPacket;
import net.minecraft.network.protocol.game.ClientboundChatPacket;
import net.minecraft.world.item.crafting.Recipe;
import java.util.Collection;
import net.minecraft.network.protocol.game.ClientboundContainerClosePacket;
import net.minecraft.network.protocol.game.ClientboundContainerSetDataPacket;
import net.minecraft.network.protocol.game.ClientboundContainerSetContentPacket;
import net.minecraft.core.NonNullList;
import net.minecraft.network.protocol.game.ClientboundContainerSetSlotPacket;
import net.minecraft.world.inventory.ResultSlot;
import net.minecraft.world.level.block.entity.CommandBlockEntity;
import net.minecraft.world.item.Item;
import net.minecraft.network.protocol.game.ClientboundOpenBookPacket;
import net.minecraft.world.item.WrittenBookItem;
import net.minecraft.world.item.Items;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.inventory.HorseInventoryMenu;
import net.minecraft.network.protocol.game.ClientboundHorseScreenOpenPacket;
import net.minecraft.world.Container;
import net.minecraft.world.entity.animal.horse.AbstractHorse;
import net.minecraft.network.protocol.game.ClientboundMerchantOffersPacket;
import net.minecraft.world.item.trading.MerchantOffers;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.network.protocol.game.ClientboundOpenScreenPacket;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.TranslatableComponent;
import java.util.OptionalInt;
import net.minecraft.world.MenuProvider;
import net.minecraft.network.protocol.game.ClientboundOpenSignEditorPacket;
import net.minecraft.world.level.block.entity.SignBlockEntity;
import net.minecraft.network.protocol.game.ClientboundAnimatePacket;
import net.minecraft.world.entity.monster.Monster;
import net.minecraft.core.Vec3i;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
import net.minecraft.core.Direction;
import net.minecraft.util.Unit;
import com.mojang.datafixers.util.Either;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.server.players.PlayerList;
import net.minecraft.world.level.storage.LevelData;
import net.minecraft.network.protocol.game.ClientboundLevelEventPacket;
import net.minecraft.network.protocol.game.ClientboundUpdateMobEffectPacket;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.network.protocol.game.ClientboundPlayerAbilitiesPacket;
import net.minecraft.network.protocol.game.ClientboundChangeDifficultyPacket;
import net.minecraft.network.protocol.game.ClientboundRespawnPacket;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.network.protocol.game.ClientboundGameEventPacket;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.damagesource.EntityDamageSource;
import net.minecraft.world.scores.PlayerTeam;
import net.minecraft.world.entity.NeutralMob;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.network.chat.Component;
import net.minecraft.stats.Stat;
import net.minecraft.stats.Stats;
import net.minecraft.world.scores.Score;
import net.minecraft.network.chat.ChatType;
import net.minecraft.world.scores.Team;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.CrashReportCategory;
import net.minecraft.ReportedException;
import net.minecraft.CrashReport;
import net.minecraft.network.protocol.game.ClientboundSetExperiencePacket;
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
import net.minecraft.network.protocol.game.ClientboundSetHealthPacket;
import net.minecraft.world.item.ComplexItem;
import java.util.Iterator;
import net.minecraft.network.protocol.game.ClientboundRemoveEntitiesPacket;
import net.minecraft.world.item.ServerItemCooldowns;
import net.minecraft.world.item.ItemCooldowns;
import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundPlayerCombatPacket;
import net.minecraft.world.item.ItemStack;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.nbt.Tag;
import java.util.function.Consumer;
import com.mojang.serialization.DynamicOps;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.CompoundTag;
import java.util.Random;
import net.minecraft.util.Mth;
import net.minecraft.world.level.GameType;
import net.minecraft.Util;
import com.google.common.collect.Lists;
import com.mojang.authlib.GameProfile;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.minecraft.resources.ResourceKey;
import net.minecraft.core.SectionPos;
import javax.annotation.Nullable;
import net.minecraft.world.phys.Vec3;
import net.minecraft.stats.ServerRecipeBook;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.ChatVisiblity;
import net.minecraft.stats.ServerStatsCounter;
import net.minecraft.server.PlayerAdvancements;
import java.util.List;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import org.apache.logging.log4j.Logger;
import net.minecraft.world.inventory.ContainerListener;
import net.minecraft.world.entity.player.Player;

public class ServerPlayer extends Player implements ContainerListener {
    private static final Logger LOGGER;
    public ServerGamePacketListenerImpl connection;
    public final MinecraftServer server;
    public final ServerPlayerGameMode gameMode;
    private final List<Integer> entitiesToRemove;
    private final PlayerAdvancements advancements;
    private final ServerStatsCounter stats;
    private float lastRecordedHealthAndAbsorption;
    private int lastRecordedFoodLevel;
    private int lastRecordedAirLevel;
    private int lastRecordedArmor;
    private int lastRecordedLevel;
    private int lastRecordedExperience;
    private float lastSentHealth;
    private int lastSentFood;
    private boolean lastFoodSaturationZero;
    private int lastSentExp;
    private int spawnInvulnerableTime;
    private ChatVisiblity chatVisibility;
    private boolean canChatColor;
    private long lastActionTime;
    private Entity camera;
    private boolean isChangingDimension;
    private boolean seenCredits;
    private final ServerRecipeBook recipeBook;
    private Vec3 levitationStartPos;
    private int levitationStartTime;
    private boolean disconnected;
    @Nullable
    private Vec3 enteredNetherPosition;
    private SectionPos lastSectionPos;
    private ResourceKey<Level> respawnDimension;
    @Nullable
    private BlockPos respawnPosition;
    private boolean respawnForced;
    private int containerCounter;
    public boolean ignoreSlotUpdateHack;
    public int latency;
    public boolean wonGame;
    
    public ServerPlayer(final MinecraftServer minecraftServer, final ServerLevel zd, final GameProfile gameProfile, final ServerPlayerGameMode zf) {
        super(zd, zd.getSharedSpawnPos(), gameProfile);
        this.entitiesToRemove = Lists.newLinkedList();
        this.lastRecordedHealthAndAbsorption = Float.MIN_VALUE;
        this.lastRecordedFoodLevel = Integer.MIN_VALUE;
        this.lastRecordedAirLevel = Integer.MIN_VALUE;
        this.lastRecordedArmor = Integer.MIN_VALUE;
        this.lastRecordedLevel = Integer.MIN_VALUE;
        this.lastRecordedExperience = Integer.MIN_VALUE;
        this.lastSentHealth = -1.0E8f;
        this.lastSentFood = -99999999;
        this.lastFoodSaturationZero = true;
        this.lastSentExp = -99999999;
        this.spawnInvulnerableTime = 60;
        this.canChatColor = true;
        this.lastActionTime = Util.getMillis();
        this.recipeBook = new ServerRecipeBook();
        this.lastSectionPos = SectionPos.of(0, 0, 0);
        this.respawnDimension = Level.OVERWORLD;
        zf.player = this;
        this.gameMode = zf;
        this.server = minecraftServer;
        this.stats = minecraftServer.getPlayerList().getPlayerStats(this);
        this.advancements = minecraftServer.getPlayerList().getPlayerAdvancements(this);
        this.maxUpStep = 1.0f;
        this.fudgeSpawnLocation(zd);
    }
    
    private void fudgeSpawnLocation(final ServerLevel zd) {
        final BlockPos fu3 = zd.getSharedSpawnPos();
        if (zd.dimensionType().hasSkyLight() && zd.getServer().getWorldData().getGameType() != GameType.ADVENTURE) {
            int integer4 = Math.max(0, this.server.getSpawnRadius(zd));
            final int integer5 = Mth.floor(zd.getWorldBorder().getDistanceToBorder(fu3.getX(), fu3.getZ()));
            if (integer5 < integer4) {
                integer4 = integer5;
            }
            if (integer5 <= 1) {
                integer4 = 1;
            }
            final long long6 = integer4 * 2 + 1;
            final long long7 = long6 * long6;
            final int integer6 = (long7 > 2147483647L) ? Integer.MAX_VALUE : ((int)long7);
            final int integer7 = this.getCoprime(integer6);
            final int integer8 = new Random().nextInt(integer6);
            for (int integer9 = 0; integer9 < integer6; ++integer9) {
                final int integer10 = (integer8 + integer7 * integer9) % integer6;
                final int integer11 = integer10 % (integer4 * 2 + 1);
                final int integer12 = integer10 / (integer4 * 2 + 1);
                final BlockPos fu4 = PlayerRespawnLogic.getOverworldRespawnPos(zd, fu3.getX() + integer11 - integer4, fu3.getZ() + integer12 - integer4, false);
                if (fu4 != null) {
                    this.moveTo(fu4, 0.0f, 0.0f);
                    if (zd.noCollision(this)) {
                        break;
                    }
                }
            }
        }
        else {
            this.moveTo(fu3, 0.0f, 0.0f);
            while (!zd.noCollision(this) && this.getY() < 255.0) {
                this.setPos(this.getX(), this.getY() + 1.0, this.getZ());
            }
        }
    }
    
    private int getCoprime(final int integer) {
        return (integer <= 16) ? (integer - 1) : 17;
    }
    
    @Override
    public void readAdditionalSaveData(final CompoundTag le) {
        super.readAdditionalSaveData(le);
        if (le.contains("playerGameType", 99)) {
            if (this.getServer().getForceGameType()) {
                this.gameMode.setGameModeForPlayer(this.getServer().getDefaultGameType(), GameType.NOT_SET);
            }
            else {
                this.gameMode.setGameModeForPlayer(GameType.byId(le.getInt("playerGameType")), le.contains("previousPlayerGameType", 3) ? GameType.byId(le.getInt("previousPlayerGameType")) : GameType.NOT_SET);
            }
        }
        if (le.contains("enteredNetherPosition", 10)) {
            final CompoundTag le2 = le.getCompound("enteredNetherPosition");
            this.enteredNetherPosition = new Vec3(le2.getDouble("x"), le2.getDouble("y"), le2.getDouble("z"));
        }
        this.seenCredits = le.getBoolean("seenCredits");
        if (le.contains("recipeBook", 10)) {
            this.recipeBook.fromNbt(le.getCompound("recipeBook"), this.server.getRecipeManager());
        }
        if (this.isSleeping()) {
            this.stopSleeping();
        }
        if (le.contains("SpawnX", 99) && le.contains("SpawnY", 99) && le.contains("SpawnZ", 99)) {
            this.respawnPosition = new BlockPos(le.getInt("SpawnX"), le.getInt("SpawnY"), le.getInt("SpawnZ"));
            this.respawnForced = le.getBoolean("SpawnForced");
            if (le.contains("SpawnDimension")) {
                this.respawnDimension = Level.RESOURCE_KEY_CODEC.parse((DynamicOps)NbtOps.INSTANCE, le.get("SpawnDimension")).resultOrPartial((Consumer)ServerPlayer.LOGGER::error).orElse(Level.OVERWORLD);
            }
        }
    }
    
    @Override
    public void addAdditionalSaveData(final CompoundTag le) {
        super.addAdditionalSaveData(le);
        le.putInt("playerGameType", this.gameMode.getGameModeForPlayer().getId());
        le.putInt("previousPlayerGameType", this.gameMode.getPreviousGameModeForPlayer().getId());
        le.putBoolean("seenCredits", this.seenCredits);
        if (this.enteredNetherPosition != null) {
            final CompoundTag le2 = new CompoundTag();
            le2.putDouble("x", this.enteredNetherPosition.x);
            le2.putDouble("y", this.enteredNetherPosition.y);
            le2.putDouble("z", this.enteredNetherPosition.z);
            le.put("enteredNetherPosition", le2);
        }
        final Entity aom3 = this.getRootVehicle();
        final Entity aom4 = this.getVehicle();
        if (aom4 != null && aom3 != this && aom3.hasOnePlayerPassenger()) {
            final CompoundTag le3 = new CompoundTag();
            final CompoundTag le4 = new CompoundTag();
            aom3.save(le4);
            le3.putUUID("Attach", aom4.getUUID());
            le3.put("Entity", le4);
            le.put("RootVehicle", le3);
        }
        le.put("recipeBook", this.recipeBook.toNbt());
        le.putString("Dimension", this.level.dimension().location().toString());
        if (this.respawnPosition != null) {
            le.putInt("SpawnX", this.respawnPosition.getX());
            le.putInt("SpawnY", this.respawnPosition.getY());
            le.putInt("SpawnZ", this.respawnPosition.getZ());
            le.putBoolean("SpawnForced", this.respawnForced);
            ResourceLocation.CODEC.encodeStart((DynamicOps)NbtOps.INSTANCE, this.respawnDimension.location()).resultOrPartial((Consumer)ServerPlayer.LOGGER::error).ifPresent(lu -> le.put("SpawnDimension", lu));
        }
    }
    
    public void setExperiencePoints(final int integer) {
        final float float3 = (float)this.getXpNeededForNextLevel();
        final float float4 = (float3 - 1.0f) / float3;
        this.experienceProgress = Mth.clamp(integer / float3, 0.0f, float4);
        this.lastSentExp = -1;
    }
    
    public void setExperienceLevels(final int integer) {
        this.experienceLevel = integer;
        this.lastSentExp = -1;
    }
    
    @Override
    public void giveExperienceLevels(final int integer) {
        super.giveExperienceLevels(integer);
        this.lastSentExp = -1;
    }
    
    @Override
    public void onEnchantmentPerformed(final ItemStack bki, final int integer) {
        super.onEnchantmentPerformed(bki, integer);
        this.lastSentExp = -1;
    }
    
    public void initMenu() {
        this.containerMenu.addSlotListener(this);
    }
    
    @Override
    public void onEnterCombat() {
        super.onEnterCombat();
        this.connection.send(new ClientboundPlayerCombatPacket(this.getCombatTracker(), ClientboundPlayerCombatPacket.Event.ENTER_COMBAT));
    }
    
    @Override
    public void onLeaveCombat() {
        super.onLeaveCombat();
        this.connection.send(new ClientboundPlayerCombatPacket(this.getCombatTracker(), ClientboundPlayerCombatPacket.Event.END_COMBAT));
    }
    
    @Override
    protected void onInsideBlock(final BlockState cfj) {
        CriteriaTriggers.ENTER_BLOCK.trigger(this, cfj);
    }
    
    @Override
    protected ItemCooldowns createItemCooldowns() {
        return new ServerItemCooldowns(this);
    }
    
    @Override
    public void tick() {
        this.gameMode.tick();
        --this.spawnInvulnerableTime;
        if (this.invulnerableTime > 0) {
            --this.invulnerableTime;
        }
        this.containerMenu.broadcastChanges();
        if (!this.level.isClientSide && !this.containerMenu.stillValid(this)) {
            this.closeContainer();
            this.containerMenu = this.inventoryMenu;
        }
        while (!this.entitiesToRemove.isEmpty()) {
            final int integer2 = Math.min(this.entitiesToRemove.size(), Integer.MAX_VALUE);
            final int[] arr3 = new int[integer2];
            final Iterator<Integer> iterator4 = this.entitiesToRemove.iterator();
            int integer3 = 0;
            while (iterator4.hasNext() && integer3 < integer2) {
                arr3[integer3++] = iterator4.next();
                iterator4.remove();
            }
            this.connection.send(new ClientboundRemoveEntitiesPacket(arr3));
        }
        final Entity aom2 = this.getCamera();
        if (aom2 != this) {
            if (aom2.isAlive()) {
                this.absMoveTo(aom2.getX(), aom2.getY(), aom2.getZ(), aom2.yRot, aom2.xRot);
                this.getLevel().getChunkSource().move(this);
                if (this.wantsToStopRiding()) {
                    this.setCamera(this);
                }
            }
            else {
                this.setCamera(this);
            }
        }
        CriteriaTriggers.TICK.trigger(this);
        if (this.levitationStartPos != null) {
            CriteriaTriggers.LEVITATION.trigger(this, this.levitationStartPos, this.tickCount - this.levitationStartTime);
        }
        this.advancements.flushDirty(this);
    }
    
    public void doTick() {
        try {
            if (!this.isSpectator() || this.level.hasChunkAt(this.blockPosition())) {
                super.tick();
            }
            for (int integer2 = 0; integer2 < this.inventory.getContainerSize(); ++integer2) {
                final ItemStack bki3 = this.inventory.getItem(integer2);
                if (bki3.getItem().isComplex()) {
                    final Packet<?> ni4 = ((ComplexItem)bki3.getItem()).getUpdatePacket(bki3, this.level, this);
                    if (ni4 != null) {
                        this.connection.send(ni4);
                    }
                }
            }
            if (this.getHealth() != this.lastSentHealth || this.lastSentFood != this.foodData.getFoodLevel() || this.foodData.getSaturationLevel() == 0.0f != this.lastFoodSaturationZero) {
                this.connection.send(new ClientboundSetHealthPacket(this.getHealth(), this.foodData.getFoodLevel(), this.foodData.getSaturationLevel()));
                this.lastSentHealth = this.getHealth();
                this.lastSentFood = this.foodData.getFoodLevel();
                this.lastFoodSaturationZero = (this.foodData.getSaturationLevel() == 0.0f);
            }
            if (this.getHealth() + this.getAbsorptionAmount() != this.lastRecordedHealthAndAbsorption) {
                this.lastRecordedHealthAndAbsorption = this.getHealth() + this.getAbsorptionAmount();
                this.updateScoreForCriteria(ObjectiveCriteria.HEALTH, Mth.ceil(this.lastRecordedHealthAndAbsorption));
            }
            if (this.foodData.getFoodLevel() != this.lastRecordedFoodLevel) {
                this.lastRecordedFoodLevel = this.foodData.getFoodLevel();
                this.updateScoreForCriteria(ObjectiveCriteria.FOOD, Mth.ceil((float)this.lastRecordedFoodLevel));
            }
            if (this.getAirSupply() != this.lastRecordedAirLevel) {
                this.lastRecordedAirLevel = this.getAirSupply();
                this.updateScoreForCriteria(ObjectiveCriteria.AIR, Mth.ceil((float)this.lastRecordedAirLevel));
            }
            if (this.getArmorValue() != this.lastRecordedArmor) {
                this.lastRecordedArmor = this.getArmorValue();
                this.updateScoreForCriteria(ObjectiveCriteria.ARMOR, Mth.ceil((float)this.lastRecordedArmor));
            }
            if (this.totalExperience != this.lastRecordedExperience) {
                this.lastRecordedExperience = this.totalExperience;
                this.updateScoreForCriteria(ObjectiveCriteria.EXPERIENCE, Mth.ceil((float)this.lastRecordedExperience));
            }
            if (this.experienceLevel != this.lastRecordedLevel) {
                this.lastRecordedLevel = this.experienceLevel;
                this.updateScoreForCriteria(ObjectiveCriteria.LEVEL, Mth.ceil((float)this.lastRecordedLevel));
            }
            if (this.totalExperience != this.lastSentExp) {
                this.lastSentExp = this.totalExperience;
                this.connection.send(new ClientboundSetExperiencePacket(this.experienceProgress, this.totalExperience, this.experienceLevel));
            }
            if (this.tickCount % 20 == 0) {
                CriteriaTriggers.LOCATION.trigger(this);
            }
        }
        catch (Throwable throwable2) {
            final CrashReport j3 = CrashReport.forThrowable(throwable2, "Ticking player");
            final CrashReportCategory k4 = j3.addCategory("Player being ticked");
            this.fillCrashReportCategory(k4);
            throw new ReportedException(j3);
        }
    }
    
    private void updateScoreForCriteria(final ObjectiveCriteria dfp, final int integer) {
        this.getScoreboard().forAllObjectives(dfp, this.getScoreboardName(), dfl -> dfl.setScore(integer));
    }
    
    @Override
    public void die(final DamageSource anw) {
        final boolean boolean3 = this.level.getGameRules().getBoolean(GameRules.RULE_SHOWDEATHMESSAGES);
        if (boolean3) {
            final Component mr4 = this.getCombatTracker().getDeathMessage();
            this.connection.send(new ClientboundPlayerCombatPacket(this.getCombatTracker(), ClientboundPlayerCombatPacket.Event.ENTITY_DIED, mr4), (future -> {
                if (!future.isSuccess()) {
                    final int integer4 = 256;
                    final String string5 = mr4.getString(256);
                    final Component mr2 = new TranslatableComponent("death.attack.message_too_long", new Object[] { new TextComponent(string5).withStyle(ChatFormatting.YELLOW) });
                    final Component mr3 = new TranslatableComponent("death.attack.even_more_magic", new Object[] { this.getDisplayName() }).withStyle(nb -> nb.withHoverEvent(new HoverEvent((HoverEvent.Action<T>)HoverEvent.Action.SHOW_TEXT, (T)mr2)));
                    this.connection.send(new ClientboundPlayerCombatPacket(this.getCombatTracker(), ClientboundPlayerCombatPacket.Event.ENTITY_DIED, mr3));
                }
            }));
            final Team dfo5 = this.getTeam();
            if (dfo5 == null || dfo5.getDeathMessageVisibility() == Team.Visibility.ALWAYS) {
                this.server.getPlayerList().broadcastMessage(mr4, ChatType.SYSTEM, Util.NIL_UUID);
            }
            else if (dfo5.getDeathMessageVisibility() == Team.Visibility.HIDE_FOR_OTHER_TEAMS) {
                this.server.getPlayerList().broadcastToTeam(this, mr4);
            }
            else if (dfo5.getDeathMessageVisibility() == Team.Visibility.HIDE_FOR_OWN_TEAM) {
                this.server.getPlayerList().broadcastToAllExceptTeam(this, mr4);
            }
        }
        else {
            this.connection.send(new ClientboundPlayerCombatPacket(this.getCombatTracker(), ClientboundPlayerCombatPacket.Event.ENTITY_DIED));
        }
        this.removeEntitiesOnShoulder();
        if (this.level.getGameRules().getBoolean(GameRules.RULE_FORGIVE_DEAD_PLAYERS)) {
            this.tellNeutralMobsThatIDied();
        }
        if (!this.isSpectator()) {
            this.dropAllDeathLoot(anw);
        }
        this.getScoreboard().forAllObjectives(ObjectiveCriteria.DEATH_COUNT, this.getScoreboardName(), Score::increment);
        final LivingEntity aoy4 = this.getKillCredit();
        if (aoy4 != null) {
            this.awardStat(Stats.ENTITY_KILLED_BY.get(aoy4.getType()));
            aoy4.awardKillScore(this, this.deathScore, anw);
            this.createWitherRose(aoy4);
        }
        this.level.broadcastEntityEvent(this, (byte)3);
        this.awardStat(Stats.DEATHS);
        this.resetStat(Stats.CUSTOM.get(Stats.TIME_SINCE_DEATH));
        this.resetStat(Stats.CUSTOM.get(Stats.TIME_SINCE_REST));
        this.clearFire();
        this.setSharedFlag(0, false);
        this.getCombatTracker().recheckStatus();
    }
    
    private void tellNeutralMobsThatIDied() {
        final AABB deg2 = new AABB(this.blockPosition()).inflate(32.0, 10.0, 32.0);
        this.level.<Entity>getLoadedEntitiesOfClass(Mob.class, deg2).stream().filter(aoz -> aoz instanceof NeutralMob).forEach(aoz -> aoz.playerDied(this));
    }
    
    @Override
    public void awardKillScore(final Entity aom, final int integer, final DamageSource anw) {
        if (aom == this) {
            return;
        }
        super.awardKillScore(aom, integer, anw);
        this.increaseScore(integer);
        final String string5 = this.getScoreboardName();
        final String string6 = aom.getScoreboardName();
        this.getScoreboard().forAllObjectives(ObjectiveCriteria.KILL_COUNT_ALL, string5, Score::increment);
        if (aom instanceof Player) {
            this.awardStat(Stats.PLAYER_KILLS);
            this.getScoreboard().forAllObjectives(ObjectiveCriteria.KILL_COUNT_PLAYERS, string5, Score::increment);
        }
        else {
            this.awardStat(Stats.MOB_KILLS);
        }
        this.handleTeamKill(string5, string6, ObjectiveCriteria.TEAM_KILL);
        this.handleTeamKill(string6, string5, ObjectiveCriteria.KILLED_BY_TEAM);
        CriteriaTriggers.PLAYER_KILLED_ENTITY.trigger(this, aom, anw);
    }
    
    private void handleTeamKill(final String string1, final String string2, final ObjectiveCriteria[] arr) {
        final PlayerTeam dfk5 = this.getScoreboard().getPlayersTeam(string2);
        if (dfk5 != null) {
            final int integer6 = dfk5.getColor().getId();
            if (integer6 >= 0 && integer6 < arr.length) {
                this.getScoreboard().forAllObjectives(arr[integer6], string1, Score::increment);
            }
        }
    }
    
    @Override
    public boolean hurt(final DamageSource anw, final float float2) {
        if (this.isInvulnerableTo(anw)) {
            return false;
        }
        final boolean boolean4 = this.server.isDedicatedServer() && this.isPvpAllowed() && "fall".equals(anw.msgId);
        if (!boolean4 && this.spawnInvulnerableTime > 0 && anw != DamageSource.OUT_OF_WORLD) {
            return false;
        }
        if (anw instanceof EntityDamageSource) {
            final Entity aom5 = anw.getEntity();
            if (aom5 instanceof Player && !this.canHarmPlayer((Player)aom5)) {
                return false;
            }
            if (aom5 instanceof AbstractArrow) {
                final AbstractArrow beg6 = (AbstractArrow)aom5;
                final Entity aom6 = beg6.getOwner();
                if (aom6 instanceof Player && !this.canHarmPlayer((Player)aom6)) {
                    return false;
                }
            }
        }
        return super.hurt(anw, float2);
    }
    
    @Override
    public boolean canHarmPlayer(final Player bec) {
        return this.isPvpAllowed() && super.canHarmPlayer(bec);
    }
    
    private boolean isPvpAllowed() {
        return this.server.isPvpAllowed();
    }
    
    @Nullable
    @Override
    public Entity changeDimension(final ServerLevel zd) {
        this.isChangingDimension = true;
        final ServerLevel zd2 = this.getLevel();
        final ResourceKey<Level> ug4 = zd2.dimension();
        if (ug4 == Level.END && zd.dimension() == Level.OVERWORLD) {
            this.unRide();
            this.getLevel().removePlayerImmediately(this);
            if (!this.wonGame) {
                this.wonGame = true;
                this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.WIN_GAME, this.seenCredits ? 0.0f : 1.0f));
                this.seenCredits = true;
            }
            return this;
        }
        final LevelData dab5 = zd.getLevelData();
        this.connection.send(new ClientboundRespawnPacket(zd.dimensionTypeKey(), zd.dimension(), BiomeManager.obfuscateSeed(zd.getSeed()), this.gameMode.getGameModeForPlayer(), this.gameMode.getPreviousGameModeForPlayer(), zd.isDebug(), zd.isFlat(), true));
        this.connection.send(new ClientboundChangeDifficultyPacket(dab5.getDifficulty(), dab5.isDifficultyLocked()));
        final PlayerList abp6 = this.server.getPlayerList();
        abp6.sendPlayerPermissionLevel(this);
        zd2.removePlayerImmediately(this);
        this.removed = false;
        double double7 = this.getX();
        double double8 = this.getY();
        double double9 = this.getZ();
        float float13 = this.xRot;
        final float float15;
        float float14 = float15 = this.yRot;
        zd2.getProfiler().push("moving");
        if (zd.dimension() == Level.END) {
            final BlockPos fu16 = ServerLevel.END_SPAWN_POINT;
            double7 = fu16.getX();
            double8 = fu16.getY();
            double9 = fu16.getZ();
            float14 = 90.0f;
            float13 = 0.0f;
        }
        else {
            if (ug4 == Level.OVERWORLD && zd.dimension() == Level.NETHER) {
                this.enteredNetherPosition = this.position();
            }
            final DimensionType cif16 = zd2.dimensionType();
            final DimensionType cif17 = zd.dimensionType();
            final double double10 = 8.0;
            if (!cif16.shrunk() && cif17.shrunk()) {
                double7 /= 8.0;
                double9 /= 8.0;
            }
            else if (cif16.shrunk() && !cif17.shrunk()) {
                double7 *= 8.0;
                double9 *= 8.0;
            }
        }
        this.moveTo(double7, double8, double9, float14, float13);
        zd2.getProfiler().pop();
        zd2.getProfiler().push("placing");
        final double double11 = Math.min(-2.9999872E7, zd.getWorldBorder().getMinX() + 16.0);
        final double double10 = Math.min(-2.9999872E7, zd.getWorldBorder().getMinZ() + 16.0);
        final double double12 = Math.min(2.9999872E7, zd.getWorldBorder().getMaxX() - 16.0);
        final double double13 = Math.min(2.9999872E7, zd.getWorldBorder().getMaxZ() - 16.0);
        double7 = Mth.clamp(double7, double11, double12);
        double9 = Mth.clamp(double9, double10, double13);
        this.moveTo(double7, double8, double9, float14, float13);
        if (zd.dimension() == Level.END) {
            final int integer24 = Mth.floor(this.getX());
            final int integer25 = Mth.floor(this.getY()) - 1;
            final int integer26 = Mth.floor(this.getZ());
            ServerLevel.makeObsidianPlatform(zd);
            this.moveTo(integer24, integer25, integer26, float14, 0.0f);
            this.setDeltaMovement(Vec3.ZERO);
        }
        else if (!zd.getPortalForcer().findAndMoveToPortal(this, float15)) {
            zd.getPortalForcer().createPortal(this);
            zd.getPortalForcer().findAndMoveToPortal(this, float15);
        }
        zd2.getProfiler().pop();
        this.setLevel(zd);
        zd.addDuringPortalTeleport(this);
        this.triggerDimensionChangeTriggers(zd2);
        this.connection.teleport(this.getX(), this.getY(), this.getZ(), float14, float13);
        this.gameMode.setLevel(zd);
        this.connection.send(new ClientboundPlayerAbilitiesPacket(this.abilities));
        abp6.sendLevelInfo(this, zd);
        abp6.sendAllPlayerInfo(this);
        for (final MobEffectInstance aog25 : this.getActiveEffects()) {
            this.connection.send(new ClientboundUpdateMobEffectPacket(this.getId(), aog25));
        }
        this.connection.send(new ClientboundLevelEventPacket(1032, BlockPos.ZERO, 0, false));
        this.lastSentExp = -1;
        this.lastSentHealth = -1.0f;
        this.lastSentFood = -1;
        return this;
    }
    
    private void triggerDimensionChangeTriggers(final ServerLevel zd) {
        final ResourceKey<Level> ug3 = zd.dimension();
        final ResourceKey<Level> ug4 = this.level.dimension();
        CriteriaTriggers.CHANGED_DIMENSION.trigger(this, ug3, ug4);
        if (ug3 == Level.NETHER && ug4 == Level.OVERWORLD && this.enteredNetherPosition != null) {
            CriteriaTriggers.NETHER_TRAVEL.trigger(this, this.enteredNetherPosition);
        }
        if (ug4 != Level.NETHER) {
            this.enteredNetherPosition = null;
        }
    }
    
    @Override
    public boolean broadcastToPlayer(final ServerPlayer ze) {
        if (ze.isSpectator()) {
            return this.getCamera() == this;
        }
        return !this.isSpectator() && super.broadcastToPlayer(ze);
    }
    
    private void broadcast(final BlockEntity cdl) {
        if (cdl != null) {
            final ClientboundBlockEntityDataPacket nv3 = cdl.getUpdatePacket();
            if (nv3 != null) {
                this.connection.send(nv3);
            }
        }
    }
    
    @Override
    public void take(final Entity aom, final int integer) {
        super.take(aom, integer);
        this.containerMenu.broadcastChanges();
    }
    
    @Override
    public Either<BedSleepingProblem, Unit> startSleepInBed(final BlockPos fu) {
        final Direction fz3 = this.level.getBlockState(fu).<Direction>getValue((Property<Direction>)HorizontalDirectionalBlock.FACING);
        if (this.isSleeping() || !this.isAlive()) {
            return (Either<BedSleepingProblem, Unit>)Either.left(BedSleepingProblem.OTHER_PROBLEM);
        }
        if (!this.level.dimensionType().natural()) {
            return (Either<BedSleepingProblem, Unit>)Either.left(BedSleepingProblem.NOT_POSSIBLE_HERE);
        }
        if (!this.bedInRange(fu, fz3)) {
            return (Either<BedSleepingProblem, Unit>)Either.left(BedSleepingProblem.TOO_FAR_AWAY);
        }
        if (this.bedBlocked(fu, fz3)) {
            return (Either<BedSleepingProblem, Unit>)Either.left(BedSleepingProblem.OBSTRUCTED);
        }
        this.setRespawnPosition(this.level.dimension(), fu, false, true);
        if (this.level.isDay()) {
            return (Either<BedSleepingProblem, Unit>)Either.left(BedSleepingProblem.NOT_POSSIBLE_NOW);
        }
        if (!this.isCreative()) {
            final double double4 = 8.0;
            final double double5 = 5.0;
            final Vec3 dem8 = Vec3.atBottomCenterOf(fu);
            final List<Monster> list9 = this.level.<Monster>getEntitiesOfClass(Monster.class, new AABB(dem8.x() - 8.0, dem8.y() - 5.0, dem8.z() - 8.0, dem8.x() + 8.0, dem8.y() + 5.0, dem8.z() + 8.0), bcb -> bcb.isPreventingPlayerRest(this));
            if (!list9.isEmpty()) {
                return (Either<BedSleepingProblem, Unit>)Either.left(BedSleepingProblem.NOT_SAFE);
            }
        }
        final Either<BedSleepingProblem, Unit> either4 = (Either<BedSleepingProblem, Unit>)super.startSleepInBed(fu).ifRight(ael -> {
            this.awardStat(Stats.SLEEP_IN_BED);
            CriteriaTriggers.SLEPT_IN_BED.trigger(this);
            return;
        });
        ((ServerLevel)this.level).updateSleepingPlayerList();
        return either4;
    }
    
    @Override
    public void startSleeping(final BlockPos fu) {
        this.resetStat(Stats.CUSTOM.get(Stats.TIME_SINCE_REST));
        super.startSleeping(fu);
    }
    
    private boolean bedInRange(final BlockPos fu, final Direction fz) {
        return this.isReachableBedBlock(fu) || this.isReachableBedBlock(fu.relative(fz.getOpposite()));
    }
    
    private boolean isReachableBedBlock(final BlockPos fu) {
        final Vec3 dem3 = Vec3.atBottomCenterOf(fu);
        return Math.abs(this.getX() - dem3.x()) <= 3.0 && Math.abs(this.getY() - dem3.y()) <= 2.0 && Math.abs(this.getZ() - dem3.z()) <= 3.0;
    }
    
    private boolean bedBlocked(final BlockPos fu, final Direction fz) {
        final BlockPos fu2 = fu.above();
        return !this.freeAt(fu2) || !this.freeAt(fu2.relative(fz.getOpposite()));
    }
    
    @Override
    public void stopSleepInBed(final boolean boolean1, final boolean boolean2) {
        if (this.isSleeping()) {
            this.getLevel().getChunkSource().broadcastAndSend(this, new ClientboundAnimatePacket(this, 2));
        }
        super.stopSleepInBed(boolean1, boolean2);
        if (this.connection != null) {
            this.connection.teleport(this.getX(), this.getY(), this.getZ(), this.yRot, this.xRot);
        }
    }
    
    @Override
    public boolean startRiding(final Entity aom, final boolean boolean2) {
        final Entity aom2 = this.getVehicle();
        if (!super.startRiding(aom, boolean2)) {
            return false;
        }
        final Entity aom3 = this.getVehicle();
        if (aom3 != aom2 && this.connection != null) {
            this.connection.teleport(this.getX(), this.getY(), this.getZ(), this.yRot, this.xRot);
        }
        return true;
    }
    
    @Override
    public void stopRiding() {
        final Entity aom2 = this.getVehicle();
        super.stopRiding();
        final Entity aom3 = this.getVehicle();
        if (aom3 != aom2 && this.connection != null) {
            this.connection.teleport(this.getX(), this.getY(), this.getZ(), this.yRot, this.xRot);
        }
    }
    
    @Override
    public boolean isInvulnerableTo(final DamageSource anw) {
        return super.isInvulnerableTo(anw) || this.isChangingDimension() || (this.abilities.invulnerable && anw == DamageSource.WITHER);
    }
    
    @Override
    protected void checkFallDamage(final double double1, final boolean boolean2, final BlockState cfj, final BlockPos fu) {
    }
    
    @Override
    protected void onChangedBlock(final BlockPos fu) {
        if (!this.isSpectator()) {
            super.onChangedBlock(fu);
        }
    }
    
    public void doCheckFallDamage(final double double1, final boolean boolean2) {
        final BlockPos fu5 = this.getOnPos();
        if (!this.level.hasChunkAt(fu5)) {
            return;
        }
        super.checkFallDamage(double1, boolean2, this.level.getBlockState(fu5), fu5);
    }
    
    @Override
    public void openTextEdit(final SignBlockEntity ceh) {
        ceh.setAllowedPlayerEditor(this);
        this.connection.send(new ClientboundOpenSignEditorPacket(ceh.getBlockPos()));
    }
    
    private void nextContainerCounter() {
        this.containerCounter = this.containerCounter % 100 + 1;
    }
    
    @Override
    public OptionalInt openMenu(@Nullable final MenuProvider anj) {
        if (anj == null) {
            return OptionalInt.empty();
        }
        if (this.containerMenu != this.inventoryMenu) {
            this.closeContainer();
        }
        this.nextContainerCounter();
        final AbstractContainerMenu bgi3 = anj.createMenu(this.containerCounter, this.inventory, this);
        if (bgi3 == null) {
            if (this.isSpectator()) {
                this.displayClientMessage(new TranslatableComponent("container.spectatorCantOpen").withStyle(ChatFormatting.RED), true);
            }
            return OptionalInt.empty();
        }
        this.connection.send(new ClientboundOpenScreenPacket(bgi3.containerId, bgi3.getType(), anj.getDisplayName()));
        bgi3.addSlotListener(this);
        this.containerMenu = bgi3;
        return OptionalInt.of(this.containerCounter);
    }
    
    @Override
    public void sendMerchantOffers(final int integer1, final MerchantOffers bpa, final int integer3, final int integer4, final boolean boolean5, final boolean boolean6) {
        this.connection.send(new ClientboundMerchantOffersPacket(integer1, bpa, integer3, integer4, boolean5, boolean6));
    }
    
    @Override
    public void openHorseInventory(final AbstractHorse azm, final Container amz) {
        if (this.containerMenu != this.inventoryMenu) {
            this.closeContainer();
        }
        this.nextContainerCounter();
        this.connection.send(new ClientboundHorseScreenOpenPacket(this.containerCounter, amz.getContainerSize(), azm.getId()));
        (this.containerMenu = new HorseInventoryMenu(this.containerCounter, this.inventory, amz, azm)).addSlotListener(this);
    }
    
    @Override
    public void openItemGui(final ItemStack bki, final InteractionHand anf) {
        final Item bke4 = bki.getItem();
        if (bke4 == Items.WRITTEN_BOOK) {
            if (WrittenBookItem.resolveBookComponents(bki, this.createCommandSourceStack(), this)) {
                this.containerMenu.broadcastChanges();
            }
            this.connection.send(new ClientboundOpenBookPacket(anf));
        }
    }
    
    @Override
    public void openCommandBlock(final CommandBlockEntity cdq) {
        cdq.setSendToClient(true);
        this.broadcast(cdq);
    }
    
    @Override
    public void slotChanged(final AbstractContainerMenu bgi, final int integer, final ItemStack bki) {
        if (bgi.getSlot(integer) instanceof ResultSlot) {
            return;
        }
        if (bgi == this.inventoryMenu) {
            CriteriaTriggers.INVENTORY_CHANGED.trigger(this, this.inventory, bki);
        }
        if (this.ignoreSlotUpdateHack) {
            return;
        }
        this.connection.send(new ClientboundContainerSetSlotPacket(bgi.containerId, integer, bki));
    }
    
    public void refreshContainer(final AbstractContainerMenu bgi) {
        this.refreshContainer(bgi, bgi.getItems());
    }
    
    @Override
    public void refreshContainer(final AbstractContainerMenu bgi, final NonNullList<ItemStack> gi) {
        this.connection.send(new ClientboundContainerSetContentPacket(bgi.containerId, gi));
        this.connection.send(new ClientboundContainerSetSlotPacket(-1, -1, this.inventory.getCarried()));
    }
    
    @Override
    public void setContainerData(final AbstractContainerMenu bgi, final int integer2, final int integer3) {
        this.connection.send(new ClientboundContainerSetDataPacket(bgi.containerId, integer2, integer3));
    }
    
    public void closeContainer() {
        this.connection.send(new ClientboundContainerClosePacket(this.containerMenu.containerId));
        this.doCloseContainer();
    }
    
    public void broadcastCarriedItem() {
        if (this.ignoreSlotUpdateHack) {
            return;
        }
        this.connection.send(new ClientboundContainerSetSlotPacket(-1, -1, this.inventory.getCarried()));
    }
    
    public void doCloseContainer() {
        this.containerMenu.removed(this);
        this.containerMenu = this.inventoryMenu;
    }
    
    public void setPlayerInput(final float float1, final float float2, final boolean boolean3, final boolean boolean4) {
        if (this.isPassenger()) {
            if (float1 >= -1.0f && float1 <= 1.0f) {
                this.xxa = float1;
            }
            if (float2 >= -1.0f && float2 <= 1.0f) {
                this.zza = float2;
            }
            this.jumping = boolean3;
            this.setShiftKeyDown(boolean4);
        }
    }
    
    @Override
    public void awardStat(final Stat<?> acr, final int integer) {
        this.stats.increment(this, acr, integer);
        this.getScoreboard().forAllObjectives(acr, this.getScoreboardName(), dfl -> dfl.add(integer));
    }
    
    @Override
    public void resetStat(final Stat<?> acr) {
        this.stats.setValue(this, acr, 0);
        this.getScoreboard().forAllObjectives(acr, this.getScoreboardName(), Score::reset);
    }
    
    @Override
    public int awardRecipes(final Collection<Recipe<?>> collection) {
        return this.recipeBook.addRecipes(collection, this);
    }
    
    @Override
    public void awardRecipesByKey(final ResourceLocation[] arr) {
        final List<Recipe<?>> list3 = Lists.newArrayList();
        for (final ResourceLocation uh7 : arr) {
            this.server.getRecipeManager().byKey(uh7).ifPresent(list3::add);
        }
        this.awardRecipes(list3);
    }
    
    @Override
    public int resetRecipes(final Collection<Recipe<?>> collection) {
        return this.recipeBook.removeRecipes(collection, this);
    }
    
    @Override
    public void giveExperiencePoints(final int integer) {
        super.giveExperiencePoints(integer);
        this.lastSentExp = -1;
    }
    
    public void disconnect() {
        this.disconnected = true;
        this.ejectPassengers();
        if (this.isSleeping()) {
            this.stopSleepInBed(true, false);
        }
    }
    
    public boolean hasDisconnected() {
        return this.disconnected;
    }
    
    public void resetSentInfo() {
        this.lastSentHealth = -1.0E8f;
    }
    
    @Override
    public void displayClientMessage(final Component mr, final boolean boolean2) {
        this.connection.send(new ClientboundChatPacket(mr, boolean2 ? ChatType.GAME_INFO : ChatType.CHAT, Util.NIL_UUID));
    }
    
    @Override
    protected void completeUsingItem() {
        if (!this.useItem.isEmpty() && this.isUsingItem()) {
            this.connection.send(new ClientboundEntityEventPacket(this, (byte)9));
            super.completeUsingItem();
        }
    }
    
    @Override
    public void lookAt(final EntityAnchorArgument.Anchor a, final Vec3 dem) {
        super.lookAt(a, dem);
        this.connection.send(new ClientboundPlayerLookAtPacket(a, dem.x, dem.y, dem.z));
    }
    
    public void lookAt(final EntityAnchorArgument.Anchor a1, final Entity aom, final EntityAnchorArgument.Anchor a3) {
        final Vec3 dem5 = a3.apply(aom);
        super.lookAt(a1, dem5);
        this.connection.send(new ClientboundPlayerLookAtPacket(a1, aom, a3));
    }
    
    public void restoreFrom(final ServerPlayer ze, final boolean boolean2) {
        if (boolean2) {
            this.inventory.replaceWith(ze.inventory);
            this.setHealth(ze.getHealth());
            this.foodData = ze.foodData;
            this.experienceLevel = ze.experienceLevel;
            this.totalExperience = ze.totalExperience;
            this.experienceProgress = ze.experienceProgress;
            this.setScore(ze.getScore());
            this.portalEntranceBlock = ze.portalEntranceBlock;
            this.portalEntranceOffset = ze.portalEntranceOffset;
            this.portalEntranceForwards = ze.portalEntranceForwards;
        }
        else if (this.level.getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) || ze.isSpectator()) {
            this.inventory.replaceWith(ze.inventory);
            this.experienceLevel = ze.experienceLevel;
            this.totalExperience = ze.totalExperience;
            this.experienceProgress = ze.experienceProgress;
            this.setScore(ze.getScore());
        }
        this.enchantmentSeed = ze.enchantmentSeed;
        this.enderChestInventory = ze.enderChestInventory;
        this.getEntityData().<Byte>set(ServerPlayer.DATA_PLAYER_MODE_CUSTOMISATION, (Byte)ze.getEntityData().<T>get((EntityDataAccessor<T>)ServerPlayer.DATA_PLAYER_MODE_CUSTOMISATION));
        this.lastSentExp = -1;
        this.lastSentHealth = -1.0f;
        this.lastSentFood = -1;
        this.recipeBook.copyOverData(ze.recipeBook);
        this.entitiesToRemove.addAll(ze.entitiesToRemove);
        this.seenCredits = ze.seenCredits;
        this.enteredNetherPosition = ze.enteredNetherPosition;
        this.setShoulderEntityLeft(ze.getShoulderEntityLeft());
        this.setShoulderEntityRight(ze.getShoulderEntityRight());
    }
    
    @Override
    protected void onEffectAdded(final MobEffectInstance aog) {
        super.onEffectAdded(aog);
        this.connection.send(new ClientboundUpdateMobEffectPacket(this.getId(), aog));
        if (aog.getEffect() == MobEffects.LEVITATION) {
            this.levitationStartTime = this.tickCount;
            this.levitationStartPos = this.position();
        }
        CriteriaTriggers.EFFECTS_CHANGED.trigger(this);
    }
    
    @Override
    protected void onEffectUpdated(final MobEffectInstance aog, final boolean boolean2) {
        super.onEffectUpdated(aog, boolean2);
        this.connection.send(new ClientboundUpdateMobEffectPacket(this.getId(), aog));
        CriteriaTriggers.EFFECTS_CHANGED.trigger(this);
    }
    
    @Override
    protected void onEffectRemoved(final MobEffectInstance aog) {
        super.onEffectRemoved(aog);
        this.connection.send(new ClientboundRemoveMobEffectPacket(this.getId(), aog.getEffect()));
        if (aog.getEffect() == MobEffects.LEVITATION) {
            this.levitationStartPos = null;
        }
        CriteriaTriggers.EFFECTS_CHANGED.trigger(this);
    }
    
    @Override
    public void teleportTo(final double double1, final double double2, final double double3) {
        this.connection.teleport(double1, double2, double3, this.yRot, this.xRot);
    }
    
    @Override
    public void moveTo(final double double1, final double double2, final double double3) {
        this.connection.teleport(double1, double2, double3, this.yRot, this.xRot);
        this.connection.resetPosition();
    }
    
    @Override
    public void crit(final Entity aom) {
        this.getLevel().getChunkSource().broadcastAndSend(this, new ClientboundAnimatePacket(aom, 4));
    }
    
    @Override
    public void magicCrit(final Entity aom) {
        this.getLevel().getChunkSource().broadcastAndSend(this, new ClientboundAnimatePacket(aom, 5));
    }
    
    @Override
    public void onUpdateAbilities() {
        if (this.connection == null) {
            return;
        }
        this.connection.send(new ClientboundPlayerAbilitiesPacket(this.abilities));
        this.updateInvisibilityStatus();
    }
    
    public ServerLevel getLevel() {
        return (ServerLevel)this.level;
    }
    
    @Override
    public void setGameMode(final GameType bpy) {
        this.gameMode.setGameModeForPlayer(bpy);
        this.connection.send(new ClientboundGameEventPacket(ClientboundGameEventPacket.CHANGE_GAME_MODE, (float)bpy.getId()));
        if (bpy == GameType.SPECTATOR) {
            this.removeEntitiesOnShoulder();
            this.stopRiding();
        }
        else {
            this.setCamera(this);
        }
        this.onUpdateAbilities();
        this.updateEffectVisibility();
    }
    
    @Override
    public boolean isSpectator() {
        return this.gameMode.getGameModeForPlayer() == GameType.SPECTATOR;
    }
    
    @Override
    public boolean isCreative() {
        return this.gameMode.getGameModeForPlayer() == GameType.CREATIVE;
    }
    
    @Override
    public void sendMessage(final Component mr, final UUID uUID) {
        this.sendMessage(mr, ChatType.SYSTEM, uUID);
    }
    
    public void sendMessage(final Component mr, final ChatType mo, final UUID uUID) {
        this.connection.send(new ClientboundChatPacket(mr, mo, uUID), (future -> {
            if (!future.isSuccess() && (mo == ChatType.GAME_INFO || mo == ChatType.SYSTEM)) {
                final int integer6 = 256;
                final String string7 = mr.getString(256);
                final Component mr2 = new TextComponent(string7).withStyle(ChatFormatting.YELLOW);
                this.connection.send(new ClientboundChatPacket(new TranslatableComponent("multiplayer.message_not_delivered", new Object[] { mr2 }).withStyle(ChatFormatting.RED), ChatType.SYSTEM, uUID));
            }
        }));
    }
    
    public String getIpAddress() {
        String string2 = this.connection.connection.getRemoteAddress().toString();
        string2 = string2.substring(string2.indexOf("/") + 1);
        string2 = string2.substring(0, string2.indexOf(":"));
        return string2;
    }
    
    public void updateOptions(final ServerboundClientInformationPacket rf) {
        this.chatVisibility = rf.getChatVisibility();
        this.canChatColor = rf.getChatColors();
        this.getEntityData().<Byte>set(ServerPlayer.DATA_PLAYER_MODE_CUSTOMISATION, (byte)rf.getModelCustomisation());
        this.getEntityData().<Byte>set(ServerPlayer.DATA_PLAYER_MAIN_HAND, (byte)((rf.getMainHand() != HumanoidArm.LEFT) ? 1 : 0));
    }
    
    public ChatVisiblity getChatVisibility() {
        return this.chatVisibility;
    }
    
    public void sendTexturePack(final String string1, final String string2) {
        this.connection.send(new ClientboundResourcePackPacket(string1, string2));
    }
    
    @Override
    protected int getPermissionLevel() {
        return this.server.getProfilePermissions(this.getGameProfile());
    }
    
    public void resetLastActionTime() {
        this.lastActionTime = Util.getMillis();
    }
    
    public ServerStatsCounter getStats() {
        return this.stats;
    }
    
    public ServerRecipeBook getRecipeBook() {
        return this.recipeBook;
    }
    
    public void sendRemoveEntity(final Entity aom) {
        if (aom instanceof Player) {
            this.connection.send(new ClientboundRemoveEntitiesPacket(new int[] { aom.getId() }));
        }
        else {
            this.entitiesToRemove.add(aom.getId());
        }
    }
    
    public void cancelRemoveEntity(final Entity aom) {
        this.entitiesToRemove.remove(aom.getId());
    }
    
    @Override
    protected void updateInvisibilityStatus() {
        if (this.isSpectator()) {
            this.removeEffectParticles();
            this.setInvisible(true);
        }
        else {
            super.updateInvisibilityStatus();
        }
    }
    
    public Entity getCamera() {
        return (this.camera == null) ? this : this.camera;
    }
    
    public void setCamera(final Entity aom) {
        final Entity aom2 = this.getCamera();
        this.camera = ((aom == null) ? this : aom);
        if (aom2 != this.camera) {
            this.connection.send(new ClientboundSetCameraPacket(this.camera));
            this.teleportTo(this.camera.getX(), this.camera.getY(), this.camera.getZ());
        }
    }
    
    @Override
    protected void processDimensionDelay() {
        if (this.changingDimensionDelay > 0 && !this.isChangingDimension) {
            --this.changingDimensionDelay;
        }
    }
    
    @Override
    public void attack(final Entity aom) {
        if (this.gameMode.getGameModeForPlayer() == GameType.SPECTATOR) {
            this.setCamera(aom);
        }
        else {
            super.attack(aom);
        }
    }
    
    public long getLastActionTime() {
        return this.lastActionTime;
    }
    
    @Nullable
    public Component getTabListDisplayName() {
        return null;
    }
    
    @Override
    public void swing(final InteractionHand anf) {
        super.swing(anf);
        this.resetAttackStrengthTicker();
    }
    
    public boolean isChangingDimension() {
        return this.isChangingDimension;
    }
    
    public void hasChangedDimension() {
        this.isChangingDimension = false;
    }
    
    public PlayerAdvancements getAdvancements() {
        return this.advancements;
    }
    
    public void teleportTo(final ServerLevel zd, final double double2, final double double3, final double double4, final float float5, final float float6) {
        this.setCamera(this);
        this.stopRiding();
        if (zd == this.level) {
            this.connection.teleport(double2, double3, double4, float5, float6);
        }
        else {
            final ServerLevel zd2 = this.getLevel();
            final LevelData dab12 = zd.getLevelData();
            this.connection.send(new ClientboundRespawnPacket(zd.dimensionTypeKey(), zd.dimension(), BiomeManager.obfuscateSeed(zd.getSeed()), this.gameMode.getGameModeForPlayer(), this.gameMode.getPreviousGameModeForPlayer(), zd.isDebug(), zd.isFlat(), true));
            this.connection.send(new ClientboundChangeDifficultyPacket(dab12.getDifficulty(), dab12.isDifficultyLocked()));
            this.server.getPlayerList().sendPlayerPermissionLevel(this);
            zd2.removePlayerImmediately(this);
            this.removed = false;
            this.moveTo(double2, double3, double4, float5, float6);
            this.setLevel(zd);
            zd.addDuringCommandTeleport(this);
            this.triggerDimensionChangeTriggers(zd2);
            this.connection.teleport(double2, double3, double4, float5, float6);
            this.gameMode.setLevel(zd);
            this.server.getPlayerList().sendLevelInfo(this, zd);
            this.server.getPlayerList().sendAllPlayerInfo(this);
        }
    }
    
    @Nullable
    public BlockPos getRespawnPosition() {
        return this.respawnPosition;
    }
    
    public ResourceKey<Level> getRespawnDimension() {
        return this.respawnDimension;
    }
    
    public boolean isRespawnForced() {
        return this.respawnForced;
    }
    
    public void setRespawnPosition(final ResourceKey<Level> ug, @Nullable final BlockPos fu, final boolean boolean3, final boolean boolean4) {
        if (fu != null) {
            final boolean boolean5 = fu.equals(this.respawnPosition) && ug.equals(this.respawnDimension);
            if (boolean4 && !boolean5) {
                this.sendMessage(new TranslatableComponent("block.minecraft.set_spawn"), Util.NIL_UUID);
            }
            this.respawnPosition = fu;
            this.respawnDimension = ug;
            this.respawnForced = boolean3;
        }
        else {
            this.respawnPosition = null;
            this.respawnDimension = Level.OVERWORLD;
            this.respawnForced = false;
        }
    }
    
    public void trackChunk(final ChunkPos bph, final Packet<?> ni2, final Packet<?> ni3) {
        this.connection.send(ni3);
        this.connection.send(ni2);
    }
    
    public void untrackChunk(final ChunkPos bph) {
        if (this.isAlive()) {
            this.connection.send(new ClientboundForgetLevelChunkPacket(bph.x, bph.z));
        }
    }
    
    public SectionPos getLastSectionPos() {
        return this.lastSectionPos;
    }
    
    public void setLastSectionPos(final SectionPos go) {
        this.lastSectionPos = go;
    }
    
    @Override
    public void playNotifySound(final SoundEvent ack, final SoundSource acm, final float float3, final float float4) {
        this.connection.send(new ClientboundSoundPacket(ack, acm, this.getX(), this.getY(), this.getZ(), float3, float4));
    }
    
    @Override
    public Packet<?> getAddEntityPacket() {
        return new ClientboundAddPlayerPacket(this);
    }
    
    @Override
    public ItemEntity drop(final ItemStack bki, final boolean boolean2, final boolean boolean3) {
        final ItemEntity bbg5 = super.drop(bki, boolean2, boolean3);
        if (bbg5 == null) {
            return null;
        }
        this.level.addFreshEntity(bbg5);
        final ItemStack bki2 = bbg5.getItem();
        if (boolean3) {
            if (!bki2.isEmpty()) {
                this.awardStat(Stats.ITEM_DROPPED.get(bki2.getItem()), bki.getCount());
            }
            this.awardStat(Stats.DROP);
        }
        return bbg5;
    }
    
    static {
        LOGGER = LogManager.getLogger();
    }
}
