package net.minecraft.world.entity.player; import com.google.common.base.MoreObjects; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.math.IntMath; import com.mojang.authlib.GameProfile; import com.mojang.datafixers.util.Either; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.OptionalInt; import java.util.function.Predicate; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.GlobalPos; import net.minecraft.core.component.DataComponents; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.NbtUtils; import net.minecraft.network.chat.ClickEvent; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.stats.Stat; import net.minecraft.stats.Stats; import net.minecraft.tags.BlockTags; import net.minecraft.tags.DamageTypeTags; import net.minecraft.tags.EntityTypeTags; import net.minecraft.tags.FluidTags; import net.minecraft.tags.ItemTags; import net.minecraft.util.Mth; import net.minecraft.util.Unit; import net.minecraft.world.Container; import net.minecraft.world.Difficulty; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.MenuProvider; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffectUtil; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityAttachment; import net.minecraft.world.entity.EntityAttachments; import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.entity.EntityEquipment; import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EquipmentSlot; import net.minecraft.world.entity.HumanoidArm; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.MoverType; import net.minecraft.world.entity.Pose; import net.minecraft.world.entity.SlotAccess; import net.minecraft.world.entity.TamableAnimal; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.ai.attributes.AttributeSupplier.Builder; import net.minecraft.world.entity.animal.Parrot; import net.minecraft.world.entity.animal.horse.AbstractHorse; import net.minecraft.world.entity.boss.EnderDragonPart; import net.minecraft.world.entity.decoration.ArmorStand; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.monster.warden.WardenSpawnTracker; import net.minecraft.world.entity.projectile.FishingHook; import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.entity.projectile.ProjectileDeflection; import net.minecraft.world.food.FoodData; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.ClickAction; import net.minecraft.world.inventory.InventoryMenu; import net.minecraft.world.inventory.PlayerEnderChestContainer; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemCooldowns; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.ProjectileWeaponItem; import net.minecraft.world.item.component.BlocksAttacks; import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.item.crafting.RecipeHolder; import net.minecraft.world.item.enchantment.EnchantmentEffectComponents; import net.minecraft.world.item.enchantment.EnchantmentHelper; import net.minecraft.world.item.equipment.Equippable; import net.minecraft.world.item.trading.MerchantOffers; import net.minecraft.world.level.BaseCommandBlock; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.GameType; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.CommandBlockEntity; import net.minecraft.world.level.block.entity.JigsawBlockEntity; import net.minecraft.world.level.block.entity.SignBlockEntity; import net.minecraft.world.level.block.entity.StructureBlockEntity; import net.minecraft.world.level.block.entity.TestBlockEntity; import net.minecraft.world.level.block.entity.TestInstanceBlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.pattern.BlockInWorld; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.gameevent.GameEvent.Context; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import net.minecraft.world.scores.PlayerTeam; import net.minecraft.world.scores.Scoreboard; import net.minecraft.world.scores.Team; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public abstract class Player extends LivingEntity { public static final HumanoidArm DEFAULT_MAIN_HAND = HumanoidArm.RIGHT; public static final int DEFAULT_MODEL_CUSTOMIZATION = 0; public static final int MAX_HEALTH = 20; public static final int SLEEP_DURATION = 100; public static final int WAKE_UP_DURATION = 10; public static final int ENDER_SLOT_OFFSET = 200; public static final int HELD_ITEM_SLOT = 499; public static final int CRAFTING_SLOT_OFFSET = 500; public static final float DEFAULT_BLOCK_INTERACTION_RANGE = 4.5F; public static final float DEFAULT_ENTITY_INTERACTION_RANGE = 3.0F; public static final float CROUCH_BB_HEIGHT = 1.5F; public static final float SWIMMING_BB_WIDTH = 0.6F; public static final float SWIMMING_BB_HEIGHT = 0.6F; public static final float DEFAULT_EYE_HEIGHT = 1.62F; private static final int CURRENT_IMPULSE_CONTEXT_RESET_GRACE_TIME_TICKS = 40; public static final Vec3 DEFAULT_VEHICLE_ATTACHMENT = new Vec3(0.0, 0.6, 0.0); public static final EntityDimensions STANDING_DIMENSIONS = EntityDimensions.scalable(0.6F, 1.8F) .withEyeHeight(1.62F) .withAttachments(EntityAttachments.builder().attach(EntityAttachment.VEHICLE, DEFAULT_VEHICLE_ATTACHMENT)); private static final Map POSES = ImmutableMap.builder() .put(Pose.STANDING, STANDING_DIMENSIONS) .put(Pose.SLEEPING, SLEEPING_DIMENSIONS) .put(Pose.FALL_FLYING, EntityDimensions.scalable(0.6F, 0.6F).withEyeHeight(0.4F)) .put(Pose.SWIMMING, EntityDimensions.scalable(0.6F, 0.6F).withEyeHeight(0.4F)) .put(Pose.SPIN_ATTACK, EntityDimensions.scalable(0.6F, 0.6F).withEyeHeight(0.4F)) .put( Pose.CROUCHING, EntityDimensions.scalable(0.6F, 1.5F) .withEyeHeight(1.27F) .withAttachments(EntityAttachments.builder().attach(EntityAttachment.VEHICLE, DEFAULT_VEHICLE_ATTACHMENT)) ) .put(Pose.DYING, EntityDimensions.fixed(0.2F, 0.2F).withEyeHeight(1.62F)) .build(); private static final EntityDataAccessor DATA_PLAYER_ABSORPTION_ID = SynchedEntityData.defineId(Player.class, EntityDataSerializers.FLOAT); private static final EntityDataAccessor DATA_SCORE_ID = SynchedEntityData.defineId(Player.class, EntityDataSerializers.INT); protected static final EntityDataAccessor DATA_PLAYER_MODE_CUSTOMISATION = SynchedEntityData.defineId(Player.class, EntityDataSerializers.BYTE); protected static final EntityDataAccessor DATA_PLAYER_MAIN_HAND = SynchedEntityData.defineId(Player.class, EntityDataSerializers.BYTE); protected static final EntityDataAccessor DATA_SHOULDER_LEFT = SynchedEntityData.defineId(Player.class, EntityDataSerializers.COMPOUND_TAG); protected static final EntityDataAccessor DATA_SHOULDER_RIGHT = SynchedEntityData.defineId(Player.class, EntityDataSerializers.COMPOUND_TAG); public static final int CLIENT_LOADED_TIMEOUT_TIME = 60; private static final short DEFAULT_SLEEP_TIMER = 0; private static final float DEFAULT_EXPERIENCE_PROGRESS = 0.0F; private static final int DEFAULT_EXPERIENCE_LEVEL = 0; private static final int DEFAULT_TOTAL_EXPERIENCE = 0; private static final int NO_ENCHANTMENT_SEED = 0; private static final int DEFAULT_SELECTED_SLOT = 0; private static final int DEFAULT_SCORE = 0; private static final boolean DEFAULT_IGNORE_FALL_DAMAGE_FROM_CURRENT_IMPULSE = false; private static final int DEFAULT_CURRENT_IMPULSE_CONTEXT_RESET_GRACE_TIME = 0; private long timeEntitySatOnShoulder; final Inventory inventory; protected PlayerEnderChestContainer enderChestInventory = new PlayerEnderChestContainer(); public final InventoryMenu inventoryMenu; public AbstractContainerMenu containerMenu; protected FoodData foodData = new FoodData(); protected int jumpTriggerTime; private boolean clientLoaded = false; protected int clientLoadedTimeoutTimer = 60; public float oBob; public float bob; public int takeXpDelay; public double xCloakO; public double yCloakO; public double zCloakO; public double xCloak; public double yCloak; public double zCloak; private int sleepCounter = 0; protected boolean wasUnderwater; private final Abilities abilities = new Abilities(); public int experienceLevel = 0; public int totalExperience = 0; public float experienceProgress = 0.0F; protected int enchantmentSeed = 0; protected final float defaultFlySpeed = 0.02F; private int lastLevelUpTime; /** * The player's unique game profile */ private final GameProfile gameProfile; private boolean reducedDebugInfo; private ItemStack lastItemInMainHand = ItemStack.EMPTY; private final ItemCooldowns cooldowns = this.createItemCooldowns(); private Optional lastDeathLocation = Optional.empty(); @Nullable public FishingHook fishing; protected float hurtDir; @Nullable public Vec3 currentImpulseImpactPos; @Nullable public Entity currentExplosionCause; private boolean ignoreFallDamageFromCurrentImpulse = false; private int currentImpulseContextResetGraceTime = 0; public Player(Level level, BlockPos pos, float yRot, GameProfile gameProfile) { super(EntityType.PLAYER, level); this.setUUID(gameProfile.getId()); this.gameProfile = gameProfile; this.inventory = new Inventory(this, this.equipment); this.inventoryMenu = new InventoryMenu(this.inventory, !level.isClientSide, this); this.containerMenu = this.inventoryMenu; this.snapTo(pos.getX() + 0.5, pos.getY() + 1, pos.getZ() + 0.5, yRot, 0.0F); } @Override protected EntityEquipment createEquipment() { return new PlayerEquipment(this); } public boolean blockActionRestricted(Level level, BlockPos pos, GameType gameMode) { if (!gameMode.isBlockPlacingRestricted()) { return false; } else if (gameMode == GameType.SPECTATOR) { return true; } else if (this.mayBuild()) { return false; } else { ItemStack itemStack = this.getMainHandItem(); return itemStack.isEmpty() || !itemStack.canBreakBlockInAdventureMode(new BlockInWorld(level, pos, false)); } } public static Builder createAttributes() { return LivingEntity.createLivingAttributes() .add(Attributes.ATTACK_DAMAGE, 1.0) .add(Attributes.MOVEMENT_SPEED, 0.1F) .add(Attributes.ATTACK_SPEED) .add(Attributes.LUCK) .add(Attributes.BLOCK_INTERACTION_RANGE, 4.5) .add(Attributes.ENTITY_INTERACTION_RANGE, 3.0) .add(Attributes.BLOCK_BREAK_SPEED) .add(Attributes.SUBMERGED_MINING_SPEED) .add(Attributes.SNEAKING_SPEED) .add(Attributes.MINING_EFFICIENCY) .add(Attributes.SWEEPING_DAMAGE_RATIO); } @Override protected void defineSynchedData(net.minecraft.network.syncher.SynchedEntityData.Builder builder) { super.defineSynchedData(builder); builder.define(DATA_PLAYER_ABSORPTION_ID, 0.0F); builder.define(DATA_SCORE_ID, 0); builder.define(DATA_PLAYER_MODE_CUSTOMISATION, (byte)0); builder.define(DATA_PLAYER_MAIN_HAND, (byte)DEFAULT_MAIN_HAND.getId()); builder.define(DATA_SHOULDER_LEFT, new CompoundTag()); builder.define(DATA_SHOULDER_RIGHT, new CompoundTag()); } @Override public void tick() { this.noPhysics = this.isSpectator(); if (this.isSpectator() || this.isPassenger()) { this.setOnGround(false); } if (this.takeXpDelay > 0) { this.takeXpDelay--; } if (this.isSleeping()) { this.sleepCounter++; if (this.sleepCounter > 100) { this.sleepCounter = 100; } if (!this.level().isClientSide && this.level().isBrightOutside()) { this.stopSleepInBed(false, true); } } else if (this.sleepCounter > 0) { this.sleepCounter++; if (this.sleepCounter >= 110) { this.sleepCounter = 0; } } this.updateIsUnderwater(); super.tick(); if (!this.level().isClientSide && this.containerMenu != null && !this.containerMenu.stillValid(this)) { this.closeContainer(); this.containerMenu = this.inventoryMenu; } this.moveCloak(); if (this instanceof ServerPlayer serverPlayer) { this.foodData.tick(serverPlayer); this.awardStat(Stats.PLAY_TIME); this.awardStat(Stats.TOTAL_WORLD_TIME); if (this.isAlive()) { this.awardStat(Stats.TIME_SINCE_DEATH); } if (this.isDiscrete()) { this.awardStat(Stats.CROUCH_TIME); } if (!this.isSleeping()) { this.awardStat(Stats.TIME_SINCE_REST); } } int i = 29999999; double d = Mth.clamp(this.getX(), -2.9999999E7, 2.9999999E7); double e = Mth.clamp(this.getZ(), -2.9999999E7, 2.9999999E7); if (d != this.getX() || e != this.getZ()) { this.setPos(d, this.getY(), e); } this.attackStrengthTicker++; ItemStack itemStack = this.getMainHandItem(); if (!ItemStack.matches(this.lastItemInMainHand, itemStack)) { if (!ItemStack.isSameItem(this.lastItemInMainHand, itemStack)) { this.resetAttackStrengthTicker(); } this.lastItemInMainHand = itemStack.copy(); } if (!this.isEyeInFluid(FluidTags.WATER) && this.isEquipped(Items.TURTLE_HELMET)) { this.turtleHelmetTick(); } this.cooldowns.tick(); this.updatePlayerPose(); if (this.currentImpulseContextResetGraceTime > 0) { this.currentImpulseContextResetGraceTime--; } } @Override protected float getMaxHeadRotationRelativeToBody() { return this.isBlocking() ? 15.0F : super.getMaxHeadRotationRelativeToBody(); } public boolean isSecondaryUseActive() { return this.isShiftKeyDown(); } protected boolean wantsToStopRiding() { return this.isShiftKeyDown(); } protected boolean isStayingOnGroundSurface() { return this.isShiftKeyDown(); } protected boolean updateIsUnderwater() { this.wasUnderwater = this.isEyeInFluid(FluidTags.WATER); return this.wasUnderwater; } @Override public void onAboveBubbleColumn(boolean downwards, BlockPos pos) { if (!this.getAbilities().flying) { super.onAboveBubbleColumn(downwards, pos); } } @Override public void onInsideBubbleColumn(boolean downwards) { if (!this.getAbilities().flying) { super.onInsideBubbleColumn(downwards); } } private void turtleHelmetTick() { this.addEffect(new MobEffectInstance(MobEffects.WATER_BREATHING, 200, 0, false, false, true)); } private boolean isEquipped(Item item) { for (EquipmentSlot equipmentSlot : EquipmentSlot.VALUES) { ItemStack itemStack = this.getItemBySlot(equipmentSlot); Equippable equippable = itemStack.get(DataComponents.EQUIPPABLE); if (itemStack.is(item) && equippable != null && equippable.slot() == equipmentSlot) { return true; } } return false; } protected ItemCooldowns createItemCooldowns() { return new ItemCooldowns(); } private void moveCloak() { this.xCloakO = this.xCloak; this.yCloakO = this.yCloak; this.zCloakO = this.zCloak; double d = this.getX() - this.xCloak; double e = this.getY() - this.yCloak; double f = this.getZ() - this.zCloak; double g = 10.0; if (d > 10.0) { this.xCloak = this.getX(); this.xCloakO = this.xCloak; } if (f > 10.0) { this.zCloak = this.getZ(); this.zCloakO = this.zCloak; } if (e > 10.0) { this.yCloak = this.getY(); this.yCloakO = this.yCloak; } if (d < -10.0) { this.xCloak = this.getX(); this.xCloakO = this.xCloak; } if (f < -10.0) { this.zCloak = this.getZ(); this.zCloakO = this.zCloak; } if (e < -10.0) { this.yCloak = this.getY(); this.yCloakO = this.yCloak; } this.xCloak += d * 0.25; this.zCloak += f * 0.25; this.yCloak += e * 0.25; } protected void updatePlayerPose() { if (this.canPlayerFitWithinBlocksAndEntitiesWhen(Pose.SWIMMING)) { Pose pose = this.getDesiredPose(); Pose pose2; if (this.isSpectator() || this.isPassenger() || this.canPlayerFitWithinBlocksAndEntitiesWhen(pose)) { pose2 = pose; } else if (this.canPlayerFitWithinBlocksAndEntitiesWhen(Pose.CROUCHING)) { pose2 = Pose.CROUCHING; } else { pose2 = Pose.SWIMMING; } this.setPose(pose2); } } private Pose getDesiredPose() { if (this.isSleeping()) { return Pose.SLEEPING; } else if (this.isSwimming()) { return Pose.SWIMMING; } else if (this.isFallFlying()) { return Pose.FALL_FLYING; } else if (this.isAutoSpinAttack()) { return Pose.SPIN_ATTACK; } else { return this.isShiftKeyDown() && !this.abilities.flying ? Pose.CROUCHING : Pose.STANDING; } } protected boolean canPlayerFitWithinBlocksAndEntitiesWhen(Pose pose) { return this.level().noCollision(this, this.getDimensions(pose).makeBoundingBox(this.position()).deflate(1.0E-7)); } @Override protected SoundEvent getSwimSound() { return SoundEvents.PLAYER_SWIM; } @Override protected SoundEvent getSwimSplashSound() { return SoundEvents.PLAYER_SPLASH; } @Override protected SoundEvent getSwimHighSpeedSplashSound() { return SoundEvents.PLAYER_SPLASH_HIGH_SPEED; } @Override public int getDimensionChangingDelay() { return 10; } @Override public void playSound(SoundEvent sound, float volume, float pitch) { this.level().playSound(this, this.getX(), this.getY(), this.getZ(), sound, this.getSoundSource(), volume, pitch); } public void playNotifySound(SoundEvent sound, SoundSource source, float volume, float pitch) { } @Override public SoundSource getSoundSource() { return SoundSource.PLAYERS; } @Override protected int getFireImmuneTicks() { return 20; } @Override public void handleEntityEvent(byte id) { if (id == 9) { this.completeUsingItem(); } else if (id == 23) { this.reducedDebugInfo = false; } else if (id == 22) { this.reducedDebugInfo = true; } else { super.handleEntityEvent(id); } } /** * Sets the current crafting inventory back to the 2x2 square. */ protected void closeContainer() { this.containerMenu = this.inventoryMenu; } protected void doCloseContainer() { } @Override public void rideTick() { if (!this.level().isClientSide && this.wantsToStopRiding() && this.isPassenger()) { this.stopRiding(); this.setShiftKeyDown(false); } else { super.rideTick(); this.oBob = this.bob; this.bob = 0.0F; } } @Override public void aiStep() { if (this.jumpTriggerTime > 0) { this.jumpTriggerTime--; } this.tickRegeneration(); this.inventory.tick(); this.oBob = this.bob; if (this.abilities.flying && !this.isPassenger()) { this.resetFallDistance(); } super.aiStep(); this.updateSwingTime(); this.yHeadRot = this.getYRot(); this.setSpeed((float)this.getAttributeValue(Attributes.MOVEMENT_SPEED)); float f; if (this.onGround() && !this.isDeadOrDying() && !this.isSwimming()) { f = Math.min(0.1F, (float)this.getDeltaMovement().horizontalDistance()); } else { f = 0.0F; } this.bob = this.bob + (f - this.bob) * 0.4F; if (this.getHealth() > 0.0F && !this.isSpectator()) { AABB aABB; if (this.isPassenger() && !this.getVehicle().isRemoved()) { aABB = this.getBoundingBox().minmax(this.getVehicle().getBoundingBox()).inflate(1.0, 0.0, 1.0); } else { aABB = this.getBoundingBox().inflate(1.0, 0.5, 1.0); } List list = this.level().getEntities(this, aABB); List list2 = Lists.newArrayList(); for (Entity entity : list) { if (entity.getType() == EntityType.EXPERIENCE_ORB) { list2.add(entity); } else if (!entity.isRemoved()) { this.touch(entity); } } if (!list2.isEmpty()) { this.touch(Util.getRandom(list2, this.random)); } } this.playShoulderEntityAmbientSound(this.getShoulderEntityLeft()); this.playShoulderEntityAmbientSound(this.getShoulderEntityRight()); if (!this.level().isClientSide && (this.fallDistance > 0.5 || this.isInWater()) || this.abilities.flying || this.isSleeping() || this.isInPowderSnow) { this.removeEntitiesOnShoulder(); } } protected void tickRegeneration() { } private void playShoulderEntityAmbientSound(CompoundTag entityCompound) { if (!entityCompound.isEmpty() && !entityCompound.getBooleanOr("Silent", false)) { if (this.level().random.nextInt(200) == 0) { EntityType entityType = (EntityType)entityCompound.read("id", EntityType.CODEC).orElse(null); if (entityType == EntityType.PARROT && !Parrot.imitateNearbyMobs(this.level(), this)) { this.level() .playSound( null, this.getX(), this.getY(), this.getZ(), Parrot.getAmbient(this.level(), this.level().random), this.getSoundSource(), 1.0F, Parrot.getPitch(this.level().random) ); } } } } private void touch(Entity entity) { entity.playerTouch(this); } public int getScore() { return this.entityData.get(DATA_SCORE_ID); } /** * Set player's score */ public void setScore(int score) { this.entityData.set(DATA_SCORE_ID, score); } /** * Add to player's score */ public void increaseScore(int score) { int i = this.getScore(); this.entityData.set(DATA_SCORE_ID, i + score); } public void startAutoSpinAttack(int ticks, float damage, ItemStack itemStack) { this.autoSpinAttackTicks = ticks; this.autoSpinAttackDmg = damage; this.autoSpinAttackItemStack = itemStack; if (!this.level().isClientSide) { this.removeEntitiesOnShoulder(); this.setLivingEntityFlag(4, true); } } @NotNull @Override public ItemStack getWeaponItem() { return this.isAutoSpinAttack() && this.autoSpinAttackItemStack != null ? this.autoSpinAttackItemStack : super.getWeaponItem(); } @Override public void die(DamageSource damageSource) { super.die(damageSource); this.reapplyPosition(); if (!this.isSpectator() && this.level() instanceof ServerLevel serverLevel) { this.dropAllDeathLoot(serverLevel, damageSource); } if (damageSource != null) { this.setDeltaMovement( -Mth.cos((this.getHurtDir() + this.getYRot()) * (float) (Math.PI / 180.0)) * 0.1F, 0.1F, -Mth.sin((this.getHurtDir() + this.getYRot()) * (float) (Math.PI / 180.0)) * 0.1F ); } else { this.setDeltaMovement(0.0, 0.1, 0.0); } 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.setSharedFlagOnFire(false); this.setLastDeathLocation(Optional.of(GlobalPos.of(this.level().dimension(), this.blockPosition()))); } @Override protected void dropEquipment(ServerLevel level) { super.dropEquipment(level); if (!level.getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY)) { this.destroyVanishingCursedItems(); this.inventory.dropAll(); } } protected void destroyVanishingCursedItems() { for (int i = 0; i < this.inventory.getContainerSize(); i++) { ItemStack itemStack = this.inventory.getItem(i); if (!itemStack.isEmpty() && EnchantmentHelper.has(itemStack, EnchantmentEffectComponents.PREVENT_EQUIPMENT_DROP)) { this.inventory.removeItemNoUpdate(i); } } } @Override protected SoundEvent getHurtSound(DamageSource damageSource) { return damageSource.type().effects().sound(); } @Override protected SoundEvent getDeathSound() { return SoundEvents.PLAYER_DEATH; } public void handleCreativeModeItemDrop(ItemStack stack) { } /** * Drops an item into the world. */ @Nullable public ItemEntity drop(ItemStack itemStack, boolean includeThrowerName) { return this.drop(itemStack, false, includeThrowerName); } public float getDestroySpeed(BlockState state) { float f = this.inventory.getSelectedItem().getDestroySpeed(state); if (f > 1.0F) { f += (float)this.getAttributeValue(Attributes.MINING_EFFICIENCY); } if (MobEffectUtil.hasDigSpeed(this)) { f *= 1.0F + (MobEffectUtil.getDigSpeedAmplification(this) + 1) * 0.2F; } if (this.hasEffect(MobEffects.MINING_FATIGUE)) { float g = switch (this.getEffect(MobEffects.MINING_FATIGUE).getAmplifier()) { case 0 -> 0.3F; case 1 -> 0.09F; case 2 -> 0.0027F; default -> 8.1E-4F; }; f *= g; } f *= (float)this.getAttributeValue(Attributes.BLOCK_BREAK_SPEED); if (this.isEyeInFluid(FluidTags.WATER)) { f *= (float)this.getAttribute(Attributes.SUBMERGED_MINING_SPEED).getValue(); } if (!this.onGround()) { f /= 5.0F; } return f; } public boolean hasCorrectToolForDrops(BlockState state) { return !state.requiresCorrectToolForDrops() || this.inventory.getSelectedItem().isCorrectToolForDrops(state); } @Override public void readAdditionalSaveData(CompoundTag tag) { super.readAdditionalSaveData(tag); this.setUUID(this.gameProfile.getId()); ListTag listTag = tag.getListOrEmpty("Inventory"); this.inventory.load(listTag); this.inventory.setSelectedSlot(tag.getIntOr("SelectedItemSlot", 0)); this.sleepCounter = tag.getShortOr("SleepTimer", (short)0); this.experienceProgress = tag.getFloatOr("XpP", 0.0F); this.experienceLevel = tag.getIntOr("XpLevel", 0); this.totalExperience = tag.getIntOr("XpTotal", 0); this.enchantmentSeed = tag.getIntOr("XpSeed", 0); if (this.enchantmentSeed == 0) { this.enchantmentSeed = this.random.nextInt(); } this.setScore(tag.getIntOr("Score", 0)); this.foodData.readAdditionalSaveData(tag); this.abilities.loadSaveData(tag); this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.abilities.getWalkingSpeed()); tag.getList("EnderItems").ifPresent(listTagx -> this.enderChestInventory.fromTag(listTagx, this.registryAccess())); this.setShoulderEntityLeft(tag.getCompoundOrEmpty("ShoulderEntityLeft")); this.setShoulderEntityRight(tag.getCompoundOrEmpty("ShoulderEntityRight")); this.setLastDeathLocation(tag.read("LastDeathLocation", GlobalPos.CODEC)); this.currentImpulseImpactPos = (Vec3)tag.read("current_explosion_impact_pos", Vec3.CODEC).orElse(null); this.ignoreFallDamageFromCurrentImpulse = tag.getBooleanOr("ignore_fall_damage_from_current_explosion", false); this.currentImpulseContextResetGraceTime = tag.getIntOr("current_impulse_context_reset_grace_time", 0); } @Override public void addAdditionalSaveData(CompoundTag tag) { super.addAdditionalSaveData(tag); NbtUtils.addCurrentDataVersion(tag); tag.put("Inventory", this.inventory.save(new ListTag())); tag.putInt("SelectedItemSlot", this.inventory.getSelectedSlot()); tag.putShort("SleepTimer", (short)this.sleepCounter); tag.putFloat("XpP", this.experienceProgress); tag.putInt("XpLevel", this.experienceLevel); tag.putInt("XpTotal", this.totalExperience); tag.putInt("XpSeed", this.enchantmentSeed); tag.putInt("Score", this.getScore()); this.foodData.addAdditionalSaveData(tag); this.abilities.addSaveData(tag); tag.put("EnderItems", this.enderChestInventory.createTag(this.registryAccess())); if (!this.getShoulderEntityLeft().isEmpty()) { tag.put("ShoulderEntityLeft", this.getShoulderEntityLeft()); } if (!this.getShoulderEntityRight().isEmpty()) { tag.put("ShoulderEntityRight", this.getShoulderEntityRight()); } this.lastDeathLocation.ifPresent(globalPos -> tag.store("LastDeathLocation", GlobalPos.CODEC, globalPos)); tag.storeNullable("current_explosion_impact_pos", Vec3.CODEC, this.currentImpulseImpactPos); tag.putBoolean("ignore_fall_damage_from_current_explosion", this.ignoreFallDamageFromCurrentImpulse); tag.putInt("current_impulse_context_reset_grace_time", this.currentImpulseContextResetGraceTime); } @Override public boolean isInvulnerableTo(ServerLevel level, DamageSource damageSource) { if (super.isInvulnerableTo(level, damageSource)) { return true; } else if (damageSource.is(DamageTypeTags.IS_DROWNING)) { return !level.getGameRules().getBoolean(GameRules.RULE_DROWNING_DAMAGE); } else if (damageSource.is(DamageTypeTags.IS_FALL)) { return !level.getGameRules().getBoolean(GameRules.RULE_FALL_DAMAGE); } else if (damageSource.is(DamageTypeTags.IS_FIRE)) { return !level.getGameRules().getBoolean(GameRules.RULE_FIRE_DAMAGE); } else { return damageSource.is(DamageTypeTags.IS_FREEZING) ? !level.getGameRules().getBoolean(GameRules.RULE_FREEZE_DAMAGE) : false; } } @Override public boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) { if (this.isInvulnerableTo(level, damageSource)) { return false; } else if (this.abilities.invulnerable && !damageSource.is(DamageTypeTags.BYPASSES_INVULNERABILITY)) { return false; } else { this.noActionTime = 0; if (this.isDeadOrDying()) { return false; } else { this.removeEntitiesOnShoulder(); if (damageSource.scalesWithDifficulty()) { if (level.getDifficulty() == Difficulty.PEACEFUL) { amount = 0.0F; } if (level.getDifficulty() == Difficulty.EASY) { amount = Math.min(amount / 2.0F + 1.0F, amount); } if (level.getDifficulty() == Difficulty.HARD) { amount = amount * 3.0F / 2.0F; } } return amount == 0.0F ? false : super.hurtServer(level, damageSource, amount); } } } @Override protected void blockUsingItem(ServerLevel level, LivingEntity entity) { super.blockUsingItem(level, entity); ItemStack itemStack = this.getItemBlockingWith(); BlocksAttacks blocksAttacks = itemStack != null ? itemStack.get(DataComponents.BLOCKS_ATTACKS) : null; float f = entity.getSecondsToDisableBlocking(); if (f > 0.0F && blocksAttacks != null) { blocksAttacks.disable(level, this, f, itemStack); } } @Override public boolean canBeSeenAsEnemy() { return !this.getAbilities().invulnerable && super.canBeSeenAsEnemy(); } public boolean canHarmPlayer(Player other) { Team team = this.getTeam(); Team team2 = other.getTeam(); if (team == null) { return true; } else { return !team.isAlliedTo(team2) ? true : team.isAllowFriendlyFire(); } } @Override protected void hurtArmor(DamageSource damageSource, float damageAmount) { this.doHurtEquipment(damageSource, damageAmount, new EquipmentSlot[]{EquipmentSlot.FEET, EquipmentSlot.LEGS, EquipmentSlot.CHEST, EquipmentSlot.HEAD}); } @Override protected void hurtHelmet(DamageSource damageSource, float damageAmount) { this.doHurtEquipment(damageSource, damageAmount, new EquipmentSlot[]{EquipmentSlot.HEAD}); } @Override protected void actuallyHurt(ServerLevel level, DamageSource damageSource, float amount) { if (!this.isInvulnerableTo(level, damageSource)) { amount = this.getDamageAfterArmorAbsorb(damageSource, amount); amount = this.getDamageAfterMagicAbsorb(damageSource, amount); float var8 = Math.max(amount - this.getAbsorptionAmount(), 0.0F); this.setAbsorptionAmount(this.getAbsorptionAmount() - (amount - var8)); float g = amount - var8; if (g > 0.0F && g < 3.4028235E37F) { this.awardStat(Stats.DAMAGE_ABSORBED, Math.round(g * 10.0F)); } if (var8 != 0.0F) { this.causeFoodExhaustion(damageSource.getFoodExhaustion()); this.getCombatTracker().recordDamage(damageSource, var8); this.setHealth(this.getHealth() - var8); if (var8 < 3.4028235E37F) { this.awardStat(Stats.DAMAGE_TAKEN, Math.round(var8 * 10.0F)); } this.gameEvent(GameEvent.ENTITY_DAMAGE); } } } public boolean isTextFilteringEnabled() { return false; } public void openTextEdit(SignBlockEntity signEntity, boolean isFrontText) { } public void openMinecartCommandBlock(BaseCommandBlock commandEntity) { } public void openCommandBlock(CommandBlockEntity commandBlockEntity) { } public void openStructureBlock(StructureBlockEntity structureEntity) { } public void openTestBlock(TestBlockEntity testBlockEntity) { } public void openTestInstanceBlock(TestInstanceBlockEntity testInstanceBlockEntity) { } public void openJigsawBlock(JigsawBlockEntity jigsawBlockEntity) { } public void openHorseInventory(AbstractHorse horse, Container inventory) { } public OptionalInt openMenu(@Nullable MenuProvider menu) { return OptionalInt.empty(); } public void sendMerchantOffers(int containerId, MerchantOffers offers, int villagerLevel, int villagerXp, boolean showProgress, boolean canRestock) { } public void openItemGui(ItemStack stack, InteractionHand hand) { } public InteractionResult interactOn(Entity entityToInteractOn, InteractionHand hand) { if (this.isSpectator()) { if (entityToInteractOn instanceof MenuProvider) { this.openMenu((MenuProvider)entityToInteractOn); } return InteractionResult.PASS; } else { ItemStack itemStack = this.getItemInHand(hand); ItemStack itemStack2 = itemStack.copy(); InteractionResult interactionResult = entityToInteractOn.interact(this, hand); if (interactionResult.consumesAction()) { if (this.hasInfiniteMaterials() && itemStack == this.getItemInHand(hand) && itemStack.getCount() < itemStack2.getCount()) { itemStack.setCount(itemStack2.getCount()); } return interactionResult; } else { if (!itemStack.isEmpty() && entityToInteractOn instanceof LivingEntity) { if (this.hasInfiniteMaterials()) { itemStack = itemStack2; } InteractionResult interactionResult2 = itemStack.interactLivingEntity(this, (LivingEntity)entityToInteractOn, hand); if (interactionResult2.consumesAction()) { this.level().gameEvent(GameEvent.ENTITY_INTERACT, entityToInteractOn.position(), Context.of(this)); if (itemStack.isEmpty() && !this.hasInfiniteMaterials()) { this.setItemInHand(hand, ItemStack.EMPTY); } return interactionResult2; } } return InteractionResult.PASS; } } } @Override public void removeVehicle() { super.removeVehicle(); this.boardingCooldown = 0; } @Override protected boolean isImmobile() { return super.isImmobile() || this.isSleeping(); } @Override public boolean isAffectedByFluids() { return !this.abilities.flying; } @Override protected Vec3 maybeBackOffFromEdge(Vec3 vec, MoverType mover) { float f = this.maxUpStep(); if (!this.abilities.flying && !(vec.y > 0.0) && (mover == MoverType.SELF || mover == MoverType.PLAYER) && this.isStayingOnGroundSurface() && this.isAboveGround(f)) { double d = vec.x; double e = vec.z; double g = 0.05; double h = Math.signum(d) * 0.05; double i; for (i = Math.signum(e) * 0.05; d != 0.0 && this.canFallAtLeast(d, 0.0, f); d -= h) { if (Math.abs(d) <= 0.05) { d = 0.0; break; } } while (e != 0.0 && this.canFallAtLeast(0.0, e, f)) { if (Math.abs(e) <= 0.05) { e = 0.0; break; } e -= i; } while (d != 0.0 && e != 0.0 && this.canFallAtLeast(d, e, f)) { if (Math.abs(d) <= 0.05) { d = 0.0; } else { d -= h; } if (Math.abs(e) <= 0.05) { e = 0.0; } else { e -= i; } } return new Vec3(d, vec.y, e); } else { return vec; } } private boolean isAboveGround(float maxUpStep) { return this.onGround() || this.fallDistance < maxUpStep && !this.canFallAtLeast(0.0, 0.0, maxUpStep - this.fallDistance); } private boolean canFallAtLeast(double x, double z, double distance) { AABB aABB = this.getBoundingBox(); return this.level() .noCollision( this, new AABB(aABB.minX + 1.0E-7 + x, aABB.minY - distance - 1.0E-7, aABB.minZ + 1.0E-7 + z, aABB.maxX - 1.0E-7 + x, aABB.minY, aABB.maxZ - 1.0E-7 + z) ); } /** * Attacks for the player the targeted entity with the currently equipped item. The equipped item has hitEntity called on it. Args: targetEntity */ public void attack(Entity target) { if (target.isAttackable()) { if (!target.skipAttackInteraction(this)) { float f = this.isAutoSpinAttack() ? this.autoSpinAttackDmg : (float)this.getAttributeValue(Attributes.ATTACK_DAMAGE); ItemStack itemStack = this.getWeaponItem(); DamageSource damageSource = (DamageSource)Optional.ofNullable(itemStack.getItem().getDamageSource(this)).orElse(this.damageSources().playerAttack(this)); float g = this.getEnchantedDamage(target, f, damageSource) - f; float h = this.getAttackStrengthScale(0.5F); f *= 0.2F + h * h * 0.8F; g *= h; this.resetAttackStrengthTicker(); if (target.getType().is(EntityTypeTags.REDIRECTABLE_PROJECTILE) && target instanceof Projectile projectile && projectile.deflect(ProjectileDeflection.AIM_DEFLECT, this, this, true)) { this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_NODAMAGE, this.getSoundSource()); } else { if (f > 0.0F || g > 0.0F) { boolean bl = h > 0.9F; boolean bl2; if (this.isSprinting() && bl) { this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_KNOCKBACK, this.getSoundSource(), 1.0F, 1.0F); bl2 = true; } else { bl2 = false; } f += itemStack.getItem().getAttackDamageBonus(target, f, damageSource); boolean bl3 = bl && this.fallDistance > 0.0 && !this.onGround() && !this.onClimbable() && !this.isInWater() && !this.hasEffect(MobEffects.BLINDNESS) && !this.isPassenger() && target instanceof LivingEntity && !this.isSprinting(); if (bl3) { f *= 1.5F; } float i = f + g; boolean bl4 = false; if (bl && !bl3 && !bl2 && this.onGround()) { double d = this.getKnownMovement().horizontalDistanceSqr(); double e = this.getSpeed() * 2.5; if (d < Mth.square(e) && this.getItemInHand(InteractionHand.MAIN_HAND).is(ItemTags.SWORDS)) { bl4 = true; } } float j = 0.0F; if (target instanceof LivingEntity livingEntity) { j = livingEntity.getHealth(); } Vec3 vec3 = target.getDeltaMovement(); boolean bl5 = target.hurtOrSimulate(damageSource, i); if (bl5) { float k = this.getKnockback(target, damageSource) + (bl2 ? 1.0F : 0.0F); if (k > 0.0F) { if (target instanceof LivingEntity livingEntity2) { livingEntity2.knockback(k * 0.5F, Mth.sin(this.getYRot() * (float) (Math.PI / 180.0)), -Mth.cos(this.getYRot() * (float) (Math.PI / 180.0))); } else { target.push(-Mth.sin(this.getYRot() * (float) (Math.PI / 180.0)) * k * 0.5F, 0.1, Mth.cos(this.getYRot() * (float) (Math.PI / 180.0)) * k * 0.5F); } this.setDeltaMovement(this.getDeltaMovement().multiply(0.6, 1.0, 0.6)); this.setSprinting(false); } if (bl4) { float l = 1.0F + (float)this.getAttributeValue(Attributes.SWEEPING_DAMAGE_RATIO) * f; for (LivingEntity livingEntity3 : this.level().getEntitiesOfClass(LivingEntity.class, target.getBoundingBox().inflate(1.0, 0.25, 1.0))) { if (livingEntity3 != this && livingEntity3 != target && !this.isAlliedTo(livingEntity3) && !(livingEntity3 instanceof ArmorStand armorStand && armorStand.isMarker()) && this.distanceToSqr(livingEntity3) < 9.0) { float m = this.getEnchantedDamage(livingEntity3, l, damageSource) * h; if (this.level() instanceof ServerLevel serverLevel && livingEntity3.hurtServer(serverLevel, damageSource, m)) { livingEntity3.knockback(0.4F, Mth.sin(this.getYRot() * (float) (Math.PI / 180.0)), -Mth.cos(this.getYRot() * (float) (Math.PI / 180.0))); EnchantmentHelper.doPostAttackEffects(serverLevel, livingEntity3, damageSource); } } } this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_SWEEP, this.getSoundSource(), 1.0F, 1.0F); this.sweepAttack(); } if (target instanceof ServerPlayer && target.hurtMarked) { ((ServerPlayer)target).connection.send(new ClientboundSetEntityMotionPacket(target)); target.hurtMarked = false; target.setDeltaMovement(vec3); } if (bl3) { this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_CRIT, this.getSoundSource(), 1.0F, 1.0F); this.crit(target); } if (!bl3 && !bl4) { if (bl) { this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_STRONG, this.getSoundSource(), 1.0F, 1.0F); } else { this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_WEAK, this.getSoundSource(), 1.0F, 1.0F); } } if (g > 0.0F) { this.magicCrit(target); } this.setLastHurtMob(target); Entity entity = target; if (target instanceof EnderDragonPart) { entity = ((EnderDragonPart)target).parentMob; } boolean bl6 = false; if (this.level() instanceof ServerLevel serverLevel2) { if (entity instanceof LivingEntity livingEntity3x) { bl6 = itemStack.hurtEnemy(livingEntity3x, this); } EnchantmentHelper.doPostAttackEffects(serverLevel2, target, damageSource); } if (!this.level().isClientSide && !itemStack.isEmpty() && entity instanceof LivingEntity) { if (bl6) { itemStack.postHurtEnemy((LivingEntity)entity, this); } if (itemStack.isEmpty()) { if (itemStack == this.getMainHandItem()) { this.setItemInHand(InteractionHand.MAIN_HAND, ItemStack.EMPTY); } else { this.setItemInHand(InteractionHand.OFF_HAND, ItemStack.EMPTY); } } } if (target instanceof LivingEntity) { float n = j - ((LivingEntity)target).getHealth(); this.awardStat(Stats.DAMAGE_DEALT, Math.round(n * 10.0F)); if (this.level() instanceof ServerLevel && n > 2.0F) { int o = (int)(n * 0.5); ((ServerLevel)this.level()).sendParticles(ParticleTypes.DAMAGE_INDICATOR, target.getX(), target.getY(0.5), target.getZ(), o, 0.1, 0.0, 0.1, 0.2); } } this.causeFoodExhaustion(0.1F); } else { this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_ATTACK_NODAMAGE, this.getSoundSource(), 1.0F, 1.0F); } } } } } } protected float getEnchantedDamage(Entity entity, float damage, DamageSource damageSource) { return damage; } @Override protected void doAutoAttackOnTouch(LivingEntity target) { this.attack(target); } /** * Called when the entity is dealt a critical hit. */ public void crit(Entity entityHit) { } public void magicCrit(Entity entityHit) { } public void sweepAttack() { double d = -Mth.sin(this.getYRot() * (float) (Math.PI / 180.0)); double e = Mth.cos(this.getYRot() * (float) (Math.PI / 180.0)); if (this.level() instanceof ServerLevel) { ((ServerLevel)this.level()).sendParticles(ParticleTypes.SWEEP_ATTACK, this.getX() + d, this.getY(0.5), this.getZ() + e, 0, d, 0.0, e, 0.0); } } public void respawn() { } @Override public void remove(Entity.RemovalReason reason) { super.remove(reason); this.inventoryMenu.removed(this); if (this.containerMenu != null && this.hasContainerOpen()) { this.doCloseContainer(); } } @Override public boolean isClientAuthoritative() { return true; } @Override protected boolean isLocalClientAuthoritative() { return this.isLocalPlayer(); } /** * Returns whether this is a {@link net.minecraft.client.player.LocalPlayer}. */ public boolean isLocalPlayer() { return false; } @Override public boolean canSimulateMovement() { return !this.level().isClientSide || this.isLocalPlayer(); } @Override public boolean isEffectiveAi() { return !this.level().isClientSide || this.isLocalPlayer(); } /** * Returns the GameProfile for this player */ public GameProfile getGameProfile() { return this.gameProfile; } public Inventory getInventory() { return this.inventory; } public Abilities getAbilities() { return this.abilities; } @Override public boolean hasInfiniteMaterials() { return this.abilities.instabuild; } public boolean preventsBlockDrops() { return this.abilities.instabuild; } public void updateTutorialInventoryAction(ItemStack carried, ItemStack clicked, ClickAction action) { } public boolean hasContainerOpen() { return this.containerMenu != this.inventoryMenu; } public boolean canDropItems() { return true; } public Either startSleepInBed(BlockPos bedPos) { this.startSleeping(bedPos); this.sleepCounter = 0; return Either.right(Unit.INSTANCE); } public void stopSleepInBed(boolean wakeImmediately, boolean updateLevelForSleepingPlayers) { super.stopSleeping(); if (this.level() instanceof ServerLevel && updateLevelForSleepingPlayers) { ((ServerLevel)this.level()).updateSleepingPlayerList(); } this.sleepCounter = wakeImmediately ? 0 : 100; } @Override public void stopSleeping() { this.stopSleepInBed(true, true); } /** * Returns whether the player is asleep and the screen has fully faded. */ public boolean isSleepingLongEnough() { return this.isSleeping() && this.sleepCounter >= 100; } public int getSleepTimer() { return this.sleepCounter; } public void displayClientMessage(Component chatComponent, boolean actionBar) { } public void awardStat(ResourceLocation statKey) { this.awardStat(Stats.CUSTOM.get(statKey)); } public void awardStat(ResourceLocation stat, int increment) { this.awardStat(Stats.CUSTOM.get(stat), increment); } /** * Add a stat once */ public void awardStat(Stat stat) { this.awardStat(stat, 1); } /** * Adds a value to a statistic field. */ public void awardStat(Stat stat, int increment) { } public void resetStat(Stat stat) { } public int awardRecipes(Collection> recipes) { return 0; } public void triggerRecipeCrafted(RecipeHolder recipe, List items) { } public void awardRecipesByKey(List>> recipes) { } public int resetRecipes(Collection> recipes) { return 0; } @Override public void travel(Vec3 travelVector) { if (this.isPassenger()) { super.travel(travelVector); } else { if (this.isSwimming()) { double d = this.getLookAngle().y; double e = d < -0.2 ? 0.085 : 0.06; if (d <= 0.0 || this.jumping || !this.level().getFluidState(BlockPos.containing(this.getX(), this.getY() + 1.0 - 0.1, this.getZ())).isEmpty()) { Vec3 vec3 = this.getDeltaMovement(); this.setDeltaMovement(vec3.add(0.0, (d - vec3.y) * e, 0.0)); } } if (this.getAbilities().flying) { double d = this.getDeltaMovement().y; super.travel(travelVector); this.setDeltaMovement(this.getDeltaMovement().with(Direction.Axis.Y, d * 0.6)); } else { super.travel(travelVector); } } } @Override protected boolean canGlide() { return !this.abilities.flying && super.canGlide(); } @Override public void updateSwimming() { if (this.abilities.flying) { this.setSwimming(false); } else { super.updateSwimming(); } } protected boolean freeAt(BlockPos pos) { return !this.level().getBlockState(pos).isSuffocating(this.level(), pos); } @Override public float getSpeed() { return (float)this.getAttributeValue(Attributes.MOVEMENT_SPEED); } @Override public boolean causeFallDamage(double fallDistance, float damageMultiplier, DamageSource damageSource) { if (this.abilities.mayfly) { return false; } else { if (fallDistance >= 2.0) { this.awardStat(Stats.FALL_ONE_CM, (int)Math.round(fallDistance * 100.0)); } boolean bl = this.currentImpulseImpactPos != null && this.ignoreFallDamageFromCurrentImpulse; double d; if (bl) { d = Math.min(fallDistance, this.currentImpulseImpactPos.y - this.getY()); boolean bl2 = d <= 0.0; if (bl2) { this.resetCurrentImpulseContext(); } else { this.tryResetCurrentImpulseContext(); } } else { d = fallDistance; } if (d > 0.0 && super.causeFallDamage(d, damageMultiplier, damageSource)) { this.resetCurrentImpulseContext(); return true; } else { this.propagateFallToPassengers(fallDistance, damageMultiplier, damageSource); return false; } } } public boolean tryToStartFallFlying() { if (!this.isFallFlying() && this.canGlide() && !this.isInWater()) { this.startFallFlying(); return true; } else { return false; } } public void startFallFlying() { this.setSharedFlag(7, true); } @Override protected void doWaterSplashEffect() { if (!this.isSpectator()) { super.doWaterSplashEffect(); } } @Override protected void playStepSound(BlockPos pos, BlockState state) { if (this.isInWater()) { this.waterSwimSound(); this.playMuffledStepSound(state); } else { BlockPos blockPos = this.getPrimaryStepSoundBlockPos(pos); if (!pos.equals(blockPos)) { BlockState blockState = this.level().getBlockState(blockPos); if (blockState.is(BlockTags.COMBINATION_STEP_SOUND_BLOCKS)) { this.playCombinationStepSounds(blockState, state); } else { super.playStepSound(blockPos, blockState); } } else { super.playStepSound(pos, state); } } } @Override public LivingEntity.Fallsounds getFallSounds() { return new LivingEntity.Fallsounds(SoundEvents.PLAYER_SMALL_FALL, SoundEvents.PLAYER_BIG_FALL); } @Override public boolean killedEntity(ServerLevel level, LivingEntity entity) { this.awardStat(Stats.ENTITY_KILLED.get(entity.getType())); return true; } @Override public void makeStuckInBlock(BlockState state, Vec3 motionMultiplier) { if (!this.abilities.flying) { super.makeStuckInBlock(state, motionMultiplier); } this.tryResetCurrentImpulseContext(); } public void giveExperiencePoints(int xpPoints) { this.increaseScore(xpPoints); this.experienceProgress = this.experienceProgress + (float)xpPoints / this.getXpNeededForNextLevel(); this.totalExperience = Mth.clamp(this.totalExperience + xpPoints, 0, Integer.MAX_VALUE); while (this.experienceProgress < 0.0F) { float f = this.experienceProgress * this.getXpNeededForNextLevel(); if (this.experienceLevel > 0) { this.giveExperienceLevels(-1); this.experienceProgress = 1.0F + f / this.getXpNeededForNextLevel(); } else { this.giveExperienceLevels(-1); this.experienceProgress = 0.0F; } } while (this.experienceProgress >= 1.0F) { this.experienceProgress = (this.experienceProgress - 1.0F) * this.getXpNeededForNextLevel(); this.giveExperienceLevels(1); this.experienceProgress = this.experienceProgress / this.getXpNeededForNextLevel(); } } public int getEnchantmentSeed() { return this.enchantmentSeed; } public void onEnchantmentPerformed(ItemStack enchantedItem, int levelCost) { this.experienceLevel -= levelCost; if (this.experienceLevel < 0) { this.experienceLevel = 0; this.experienceProgress = 0.0F; this.totalExperience = 0; } this.enchantmentSeed = this.random.nextInt(); } /** * Add experience levels to this player. */ public void giveExperienceLevels(int levels) { this.experienceLevel = IntMath.saturatedAdd(this.experienceLevel, levels); if (this.experienceLevel < 0) { this.experienceLevel = 0; this.experienceProgress = 0.0F; this.totalExperience = 0; } if (levels > 0 && this.experienceLevel % 5 == 0 && this.lastLevelUpTime < this.tickCount - 100.0F) { float f = this.experienceLevel > 30 ? 1.0F : this.experienceLevel / 30.0F; this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_LEVELUP, this.getSoundSource(), f * 0.75F, 1.0F); this.lastLevelUpTime = this.tickCount; } } /** * This method returns the cap amount of experience that the experience bar can hold. With each level, the experience cap on the player's experience bar is raised by 10. */ public int getXpNeededForNextLevel() { if (this.experienceLevel >= 30) { return 112 + (this.experienceLevel - 30) * 9; } else { return this.experienceLevel >= 15 ? 37 + (this.experienceLevel - 15) * 5 : 7 + this.experienceLevel * 2; } } /** * Increases exhaustion level by the supplied amount. */ public void causeFoodExhaustion(float exhaustion) { if (!this.abilities.invulnerable) { if (!this.level().isClientSide) { this.foodData.addExhaustion(exhaustion); } } } public Optional getWardenSpawnTracker() { return Optional.empty(); } /** * Returns the player's FoodStats object. */ public FoodData getFoodData() { return this.foodData; } public boolean canEat(boolean canAlwaysEat) { return this.abilities.invulnerable || canAlwaysEat || this.foodData.needsFood(); } /** * Checks if the player's health is not full and not zero. */ public boolean isHurt() { return this.getHealth() > 0.0F && this.getHealth() < this.getMaxHealth(); } public boolean mayBuild() { return this.abilities.mayBuild; } /** * Returns whether this player can modify the block at a certain location with the given stack. *

* The position being queried is {@code pos.offset(facing.getOpposite())}. * * @return Whether this player may modify the queried location in the current world * @see ItemStack#canPlaceOn(Block) * @see ItemStack#canEditBlocks() * @see PlayerCapabilities#allowEdit */ public boolean mayUseItemAt(BlockPos pos, Direction facing, ItemStack stack) { if (this.abilities.mayBuild) { return true; } else { BlockPos blockPos = pos.relative(facing.getOpposite()); BlockInWorld blockInWorld = new BlockInWorld(this.level(), blockPos, false); return stack.canPlaceOnBlockInAdventureMode(blockInWorld); } } @Override protected int getBaseExperienceReward(ServerLevel level) { return !level.getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) && !this.isSpectator() ? Math.min(this.experienceLevel * 7, 100) : 0; } @Override protected boolean isAlwaysExperienceDropper() { return true; } @Override public boolean shouldShowName() { return true; } @Override protected Entity.MovementEmission getMovementEmission() { return this.abilities.flying || this.onGround() && this.isDiscrete() ? Entity.MovementEmission.NONE : Entity.MovementEmission.ALL; } /** * Sends the player's abilities to the server (if there is one). */ public void onUpdateAbilities() { } @Override public Component getName() { return Component.literal(this.gameProfile.getName()); } /** * Returns the InventoryEnderChest of this player. */ public PlayerEnderChestContainer getEnderChestInventory() { return this.enderChestInventory; } @Override protected boolean doesEmitEquipEvent(EquipmentSlot slot) { return slot.getType() == EquipmentSlot.Type.HUMANOID_ARMOR; } public boolean addItem(ItemStack stack) { return this.inventory.add(stack); } public boolean setEntityOnShoulder(CompoundTag entityCompound) { if (this.isPassenger() || !this.onGround() || this.isInWater() || this.isInPowderSnow) { return false; } else if (this.getShoulderEntityLeft().isEmpty()) { this.setShoulderEntityLeft(entityCompound); this.timeEntitySatOnShoulder = this.level().getGameTime(); return true; } else if (this.getShoulderEntityRight().isEmpty()) { this.setShoulderEntityRight(entityCompound); this.timeEntitySatOnShoulder = this.level().getGameTime(); return true; } else { return false; } } protected void removeEntitiesOnShoulder() { if (this.timeEntitySatOnShoulder + 20L < this.level().getGameTime()) { this.respawnEntityOnShoulder(this.getShoulderEntityLeft()); this.setShoulderEntityLeft(new CompoundTag()); this.respawnEntityOnShoulder(this.getShoulderEntityRight()); this.setShoulderEntityRight(new CompoundTag()); } } private void respawnEntityOnShoulder(CompoundTag entityCompound) { if (!this.level().isClientSide && !entityCompound.isEmpty()) { EntityType.create(entityCompound, this.level(), EntitySpawnReason.LOAD).ifPresent(entity -> { if (entity instanceof TamableAnimal tamableAnimal) { tamableAnimal.setOwner(this); } entity.setPos(this.getX(), this.getY() + 0.7F, this.getZ()); ((ServerLevel)this.level()).addWithUUID(entity); }); } } @Nullable public abstract GameType gameMode(); @Override public boolean isSpectator() { return this.gameMode() == GameType.SPECTATOR; } @Override public boolean canBeHitByProjectile() { return !this.isSpectator() && super.canBeHitByProjectile(); } @Override public boolean isSwimming() { return !this.abilities.flying && !this.isSpectator() && super.isSwimming(); } public boolean isCreative() { return this.gameMode() == GameType.CREATIVE; } @Override public boolean isPushedByFluid() { return !this.abilities.flying; } public Scoreboard getScoreboard() { return this.level().getScoreboard(); } @Override public Component getDisplayName() { MutableComponent mutableComponent = PlayerTeam.formatNameForTeam(this.getTeam(), this.getName()); return this.decorateDisplayNameComponent(mutableComponent); } private MutableComponent decorateDisplayNameComponent(MutableComponent displayName) { String string = this.getGameProfile().getName(); return displayName.withStyle( style -> style.withClickEvent(new ClickEvent.SuggestCommand("/tell " + string + " ")).withHoverEvent(this.createHoverEvent()).withInsertion(string) ); } @Override public String getScoreboardName() { return this.getGameProfile().getName(); } @Override protected void internalSetAbsorptionAmount(float absorptionAmount) { this.getEntityData().set(DATA_PLAYER_ABSORPTION_ID, absorptionAmount); } @Override public float getAbsorptionAmount() { return this.getEntityData().get(DATA_PLAYER_ABSORPTION_ID); } public boolean isModelPartShown(PlayerModelPart part) { return (this.getEntityData().get(DATA_PLAYER_MODE_CUSTOMISATION) & part.getMask()) == part.getMask(); } @Override public SlotAccess getSlot(int slot) { if (slot == 499) { return new SlotAccess() { @Override public ItemStack get() { return Player.this.containerMenu.getCarried(); } @Override public boolean set(ItemStack carried) { Player.this.containerMenu.setCarried(carried); return true; } }; } else { final int i = slot - 500; if (i >= 0 && i < 4) { return new SlotAccess() { @Override public ItemStack get() { return Player.this.inventoryMenu.getCraftSlots().getItem(i); } @Override public boolean set(ItemStack carried) { Player.this.inventoryMenu.getCraftSlots().setItem(i, carried); Player.this.inventoryMenu.slotsChanged(Player.this.inventory); return true; } }; } else if (slot >= 0 && slot < this.inventory.getNonEquipmentItems().size()) { return SlotAccess.forContainer(this.inventory, slot); } else { int j = slot - 200; return j >= 0 && j < this.enderChestInventory.getContainerSize() ? SlotAccess.forContainer(this.enderChestInventory, j) : super.getSlot(slot); } } } /** * Whether the "reducedDebugInfo" option is active for this player. */ public boolean isReducedDebugInfo() { return this.reducedDebugInfo; } public void setReducedDebugInfo(boolean reducedDebugInfo) { this.reducedDebugInfo = reducedDebugInfo; } @Override public void setRemainingFireTicks(int remainingFireTicks) { super.setRemainingFireTicks(this.abilities.invulnerable ? Math.min(remainingFireTicks, 1) : remainingFireTicks); } @Override public HumanoidArm getMainArm() { return this.entityData.get(DATA_PLAYER_MAIN_HAND) == 0 ? HumanoidArm.LEFT : HumanoidArm.RIGHT; } public void setMainArm(HumanoidArm hand) { this.entityData.set(DATA_PLAYER_MAIN_HAND, (byte)(hand == HumanoidArm.LEFT ? 0 : 1)); } public CompoundTag getShoulderEntityLeft() { return this.entityData.get(DATA_SHOULDER_LEFT); } protected void setShoulderEntityLeft(CompoundTag entityCompound) { this.entityData.set(DATA_SHOULDER_LEFT, entityCompound); } public CompoundTag getShoulderEntityRight() { return this.entityData.get(DATA_SHOULDER_RIGHT); } protected void setShoulderEntityRight(CompoundTag entityCompound) { this.entityData.set(DATA_SHOULDER_RIGHT, entityCompound); } public float getCurrentItemAttackStrengthDelay() { return (float)(1.0 / this.getAttributeValue(Attributes.ATTACK_SPEED) * 20.0); } /** * Returns the percentage of attack power available based on the cooldown (zero to one). */ public float getAttackStrengthScale(float adjustTicks) { return Mth.clamp((this.attackStrengthTicker + adjustTicks) / this.getCurrentItemAttackStrengthDelay(), 0.0F, 1.0F); } public void resetAttackStrengthTicker() { this.attackStrengthTicker = 0; } public ItemCooldowns getCooldowns() { return this.cooldowns; } @Override protected float getBlockSpeedFactor() { return !this.abilities.flying && !this.isFallFlying() ? super.getBlockSpeedFactor() : 1.0F; } @Override public float getLuck() { return (float)this.getAttributeValue(Attributes.LUCK); } public boolean canUseGameMasterBlocks() { return this.abilities.instabuild && this.getPermissionLevel() >= 2; } public int getPermissionLevel() { return 0; } public boolean hasPermissions(int permissionLevel) { return this.getPermissionLevel() >= permissionLevel; } @Override public EntityDimensions getDefaultDimensions(Pose pose) { return (EntityDimensions)POSES.getOrDefault(pose, STANDING_DIMENSIONS); } @Override public ImmutableList getDismountPoses() { return ImmutableList.of(Pose.STANDING, Pose.CROUCHING, Pose.SWIMMING); } @Override public ItemStack getProjectile(ItemStack weaponStack) { if (!(weaponStack.getItem() instanceof ProjectileWeaponItem)) { return ItemStack.EMPTY; } else { Predicate predicate = ((ProjectileWeaponItem)weaponStack.getItem()).getSupportedHeldProjectiles(); ItemStack itemStack = ProjectileWeaponItem.getHeldProjectile(this, predicate); if (!itemStack.isEmpty()) { return itemStack; } else { predicate = ((ProjectileWeaponItem)weaponStack.getItem()).getAllSupportedProjectiles(); for (int i = 0; i < this.inventory.getContainerSize(); i++) { ItemStack itemStack2 = this.inventory.getItem(i); if (predicate.test(itemStack2)) { return itemStack2; } } return this.hasInfiniteMaterials() ? new ItemStack(Items.ARROW) : ItemStack.EMPTY; } } } @Override public Vec3 getRopeHoldPosition(float partialTicks) { double d = 0.22 * (this.getMainArm() == HumanoidArm.RIGHT ? -1.0 : 1.0); float f = Mth.lerp(partialTicks * 0.5F, this.getXRot(), this.xRotO) * (float) (Math.PI / 180.0); float g = Mth.lerp(partialTicks, this.yBodyRotO, this.yBodyRot) * (float) (Math.PI / 180.0); if (this.isFallFlying() || this.isAutoSpinAttack()) { Vec3 vec3 = this.getViewVector(partialTicks); Vec3 vec32 = this.getDeltaMovement(); double e = vec32.horizontalDistanceSqr(); double h = vec3.horizontalDistanceSqr(); float k; if (e > 0.0 && h > 0.0) { double i = (vec32.x * vec3.x + vec32.z * vec3.z) / Math.sqrt(e * h); double j = vec32.x * vec3.z - vec32.z * vec3.x; k = (float)(Math.signum(j) * Math.acos(i)); } else { k = 0.0F; } return this.getPosition(partialTicks).add(new Vec3(d, -0.11, 0.85).zRot(-k).xRot(-f).yRot(-g)); } else if (this.isVisuallySwimming()) { return this.getPosition(partialTicks).add(new Vec3(d, 0.2, -0.15).xRot(-f).yRot(-g)); } else { double l = this.getBoundingBox().getYsize() - 1.0; double e = this.isCrouching() ? -0.2 : 0.07; return this.getPosition(partialTicks).add(new Vec3(d, l, e).yRot(-g)); } } @Override public boolean isAlwaysTicking() { return true; } public boolean isScoping() { return this.isUsingItem() && this.getUseItem().is(Items.SPYGLASS); } @Override public boolean shouldBeSaved() { return false; } public Optional getLastDeathLocation() { return this.lastDeathLocation; } public void setLastDeathLocation(Optional lastDeathLocation) { this.lastDeathLocation = lastDeathLocation; } @Override public float getHurtDir() { return this.hurtDir; } @Override public void animateHurt(float yaw) { super.animateHurt(yaw); this.hurtDir = yaw; } @Override public boolean canSprint() { return true; } @Override protected float getFlyingSpeed() { if (this.abilities.flying && !this.isPassenger()) { return this.isSprinting() ? this.abilities.getFlyingSpeed() * 2.0F : this.abilities.getFlyingSpeed(); } else { return this.isSprinting() ? 0.025999999F : 0.02F; } } public boolean hasClientLoaded() { return this.clientLoaded || this.clientLoadedTimeoutTimer <= 0; } public void tickClientLoadTimeout() { if (!this.clientLoaded) { this.clientLoadedTimeoutTimer--; } } public void setClientLoaded(boolean clientLoaded) { this.clientLoaded = clientLoaded; if (!this.clientLoaded) { this.clientLoadedTimeoutTimer = 60; } } public double blockInteractionRange() { return this.getAttributeValue(Attributes.BLOCK_INTERACTION_RANGE); } public double entityInteractionRange() { return this.getAttributeValue(Attributes.ENTITY_INTERACTION_RANGE); } public boolean canInteractWithEntity(Entity entity, double distance) { return entity.isRemoved() ? false : this.canInteractWithEntity(entity.getBoundingBox(), distance); } public boolean canInteractWithEntity(AABB boundingBox, double distance) { double d = this.entityInteractionRange() + distance; return boundingBox.distanceToSqr(this.getEyePosition()) < d * d; } public boolean canInteractWithBlock(BlockPos pos, double distance) { double d = this.blockInteractionRange() + distance; return new AABB(pos).distanceToSqr(this.getEyePosition()) < d * d; } public void setIgnoreFallDamageFromCurrentImpulse(boolean ignoreFallDamageFromCurrentImpulse) { this.ignoreFallDamageFromCurrentImpulse = ignoreFallDamageFromCurrentImpulse; if (ignoreFallDamageFromCurrentImpulse) { this.currentImpulseContextResetGraceTime = 40; } else { this.currentImpulseContextResetGraceTime = 0; } } public boolean isIgnoringFallDamageFromCurrentImpulse() { return this.ignoreFallDamageFromCurrentImpulse; } public void tryResetCurrentImpulseContext() { if (this.currentImpulseContextResetGraceTime == 0) { this.resetCurrentImpulseContext(); } } public void resetCurrentImpulseContext() { this.currentImpulseContextResetGraceTime = 0; this.currentExplosionCause = null; this.currentImpulseImpactPos = null; this.ignoreFallDamageFromCurrentImpulse = false; } public boolean shouldRotateWithMinecart() { return false; } @Override public boolean onClimbable() { return this.abilities.flying ? false : super.onClimbable(); } public String debugInfo() { return MoreObjects.toStringHelper(this) .add("name", this.getName().getString()) .add("id", this.getId()) .add("pos", this.position()) .add("mode", this.gameMode()) .add("permission", this.getPermissionLevel()) .toString(); } public static enum BedSleepingProblem { NOT_POSSIBLE_HERE, NOT_POSSIBLE_NOW(Component.translatable("block.minecraft.bed.no_sleep")), TOO_FAR_AWAY(Component.translatable("block.minecraft.bed.too_far_away")), OBSTRUCTED(Component.translatable("block.minecraft.bed.obstructed")), OTHER_PROBLEM, NOT_SAFE(Component.translatable("block.minecraft.bed.not_safe")); @Nullable private final Component message; private BedSleepingProblem() { this.message = null; } private BedSleepingProblem(final Component message) { this.message = message; } @Nullable public Component getMessage() { return this.message; } } }