package net.minecraft.world.entity.player; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.mojang.authlib.GameProfile; import com.mojang.datafixers.util.Either; import com.mojang.logging.LogUtils; 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.advancements.CriteriaTriggers; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.GlobalPos; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.NbtOps; 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.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.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.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.AttributeSupplier; import net.minecraft.world.entity.ai.attributes.Attributes; 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.food.FoodProperties; 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.ElytraItem; 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.SwordItem; 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.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.state.BlockState; import net.minecraft.world.level.block.state.pattern.BlockInWorld; import net.minecraft.world.level.gameevent.GameEvent; 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; import org.slf4j.Logger; public abstract class Player extends LivingEntity { private static final Logger LOGGER = LogUtils.getLogger(); 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); private long timeEntitySatOnShoulder; final Inventory inventory = new Inventory(this); protected PlayerEnderChestContainer enderChestInventory = new PlayerEnderChestContainer(); public final InventoryMenu inventoryMenu; public AbstractContainerMenu containerMenu; protected FoodData foodData = new FoodData(); protected int jumpTriggerTime; 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; protected boolean wasUnderwater; private final Abilities abilities = new Abilities(); public int experienceLevel; public int totalExperience; public float experienceProgress; protected int enchantmentSeed; 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; private int currentImpulseContextResetGraceTime; public Player(Level level, BlockPos pos, float yRot, GameProfile gameProfile) { super(EntityType.PLAYER, level); this.setUUID(gameProfile.getId()); this.gameProfile = gameProfile; this.inventoryMenu = new InventoryMenu(this.inventory, !level.isClientSide, this); this.containerMenu = this.inventoryMenu; this.moveTo(pos.getX() + 0.5, pos.getY() + 1, pos.getZ() + 0.5, yRot, 0.0F); this.rotOffs = 180.0F; } 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 AttributeSupplier.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(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.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().isDay()) { 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.level().isClientSide) { this.foodData.tick(this); 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(); } 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; } private void turtleHelmetTick() { ItemStack itemStack = this.getItemBySlot(EquipmentSlot.HEAD); if (itemStack.is(Items.TURTLE_HELMET) && !this.isEyeInFluid(FluidTags.WATER)) { this.addEffect(new MobEffectInstance(MobEffects.WATER_BREATHING, 200, 0, false, false, true)); } } 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; if (this.isFallFlying()) { pose = Pose.FALL_FLYING; } else if (this.isSleeping()) { pose = Pose.SLEEPING; } else if (this.isSwimming()) { pose = Pose.SWIMMING; } else if (this.isAutoSpinAttack()) { pose = Pose.SPIN_ATTACK; } else if (this.isShiftKeyDown() && !this.abilities.flying) { pose = Pose.CROUCHING; } else { pose = Pose.STANDING; } 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); } } 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 protected void serverAiStep() { super.serverAiStep(); this.updateSwingTime(); this.yHeadRot = this.getYRot(); } @Override public void aiStep() { if (this.jumpTriggerTime > 0) { this.jumpTriggerTime--; } if (this.level().getDifficulty() == Difficulty.PEACEFUL && this.level().getGameRules().getBoolean(GameRules.RULE_NATURAL_REGENERATION)) { if (this.getHealth() < this.getMaxHealth() && this.tickCount % 20 == 0) { this.heal(1.0F); } if (this.foodData.getSaturationLevel() < 20.0F && this.tickCount % 20 == 0) { this.foodData.setSaturation(this.foodData.getSaturationLevel() + 1.0F); } if (this.foodData.needsFood() && this.tickCount % 10 == 0) { this.foodData.setFoodLevel(this.foodData.getFoodLevel() + 1); } } this.inventory.tick(); this.oBob = this.bob; super.aiStep(); 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.5F || this.isInWater()) || this.abilities.flying || this.isSleeping() || this.isInPowderSnow) { this.removeEntitiesOnShoulder(); } } private void playShoulderEntityAmbientSound(@Nullable CompoundTag entityCompound) { if (entityCompound != null && (!entityCompound.contains("Silent") || !entityCompound.getBoolean("Silent")) && this.level().random.nextInt(200) == 0) { String string = entityCompound.getString("id"); EntityType.byString(string) .filter(entityType -> entityType == EntityType.PARROT) .ifPresent( entityType -> { if (!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); } /** * Starts the attack used by the Riptide enchantment. */ 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() { super.dropEquipment(); if (!this.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; } /** * Drops an item into the world. */ @Nullable public ItemEntity drop(ItemStack itemStack, boolean includeThrowerName) { return this.drop(itemStack, false, includeThrowerName); } /** * Creates and drops the provided item. Depending on the dropAround, it will drop the item around the player, instead of dropping the item from where the player is pointing at. Likewise, if includeThrowerName is true, the dropped item entity will have the thrower set as the player. */ @Nullable public ItemEntity drop(ItemStack droppedItem, boolean dropAround, boolean includeThrowerName) { if (droppedItem.isEmpty()) { return null; } else { if (this.level().isClientSide) { this.swing(InteractionHand.MAIN_HAND); } double d = this.getEyeY() - 0.3F; ItemEntity itemEntity = new ItemEntity(this.level(), this.getX(), d, this.getZ(), droppedItem); itemEntity.setPickUpDelay(40); if (includeThrowerName) { itemEntity.setThrower(this); } if (dropAround) { float f = this.random.nextFloat() * 0.5F; float g = this.random.nextFloat() * (float) (Math.PI * 2); itemEntity.setDeltaMovement(-Mth.sin(g) * f, 0.2F, Mth.cos(g) * f); } else { float f = 0.3F; float g = Mth.sin(this.getXRot() * (float) (Math.PI / 180.0)); float h = Mth.cos(this.getXRot() * (float) (Math.PI / 180.0)); float i = Mth.sin(this.getYRot() * (float) (Math.PI / 180.0)); float j = Mth.cos(this.getYRot() * (float) (Math.PI / 180.0)); float k = this.random.nextFloat() * (float) (Math.PI * 2); float l = 0.02F * this.random.nextFloat(); itemEntity.setDeltaMovement( -i * h * 0.3F + Math.cos(k) * l, -g * 0.3F + 0.1F + (this.random.nextFloat() - this.random.nextFloat()) * 0.1F, j * h * 0.3F + Math.sin(k) * l ); } return itemEntity; } } public float getDestroySpeed(BlockState state) { float f = this.inventory.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.DIG_SLOWDOWN)) { f *= switch (this.getEffect(MobEffects.DIG_SLOWDOWN).getAmplifier()) { case 0 -> 0.3F; case 1 -> 0.09F; case 2 -> 0.0027F; default -> 8.1E-4F; }; } 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.getSelected().isCorrectToolForDrops(state); } @Override public void readAdditionalSaveData(CompoundTag compound) { super.readAdditionalSaveData(compound); this.setUUID(this.gameProfile.getId()); ListTag listTag = compound.getList("Inventory", 10); this.inventory.load(listTag); this.inventory.selected = compound.getInt("SelectedItemSlot"); this.sleepCounter = compound.getShort("SleepTimer"); this.experienceProgress = compound.getFloat("XpP"); this.experienceLevel = compound.getInt("XpLevel"); this.totalExperience = compound.getInt("XpTotal"); this.enchantmentSeed = compound.getInt("XpSeed"); if (this.enchantmentSeed == 0) { this.enchantmentSeed = this.random.nextInt(); } this.setScore(compound.getInt("Score")); this.foodData.readAdditionalSaveData(compound); this.abilities.loadSaveData(compound); this.getAttribute(Attributes.MOVEMENT_SPEED).setBaseValue(this.abilities.getWalkingSpeed()); if (compound.contains("EnderItems", 9)) { this.enderChestInventory.fromTag(compound.getList("EnderItems", 10), this.registryAccess()); } if (compound.contains("ShoulderEntityLeft", 10)) { this.setShoulderEntityLeft(compound.getCompound("ShoulderEntityLeft")); } if (compound.contains("ShoulderEntityRight", 10)) { this.setShoulderEntityRight(compound.getCompound("ShoulderEntityRight")); } if (compound.contains("LastDeathLocation", 10)) { this.setLastDeathLocation(GlobalPos.CODEC.parse(NbtOps.INSTANCE, compound.get("LastDeathLocation")).resultOrPartial(LOGGER::error)); } if (compound.contains("current_explosion_impact_pos", 9)) { Vec3.CODEC .parse(NbtOps.INSTANCE, compound.get("current_explosion_impact_pos")) .resultOrPartial(LOGGER::error) .ifPresent(vec3 -> this.currentImpulseImpactPos = vec3); } this.ignoreFallDamageFromCurrentImpulse = compound.getBoolean("ignore_fall_damage_from_current_explosion"); this.currentImpulseContextResetGraceTime = compound.getInt("current_impulse_context_reset_grace_time"); } @Override public void addAdditionalSaveData(CompoundTag compound) { super.addAdditionalSaveData(compound); NbtUtils.addCurrentDataVersion(compound); compound.put("Inventory", this.inventory.save(new ListTag())); compound.putInt("SelectedItemSlot", this.inventory.selected); compound.putShort("SleepTimer", (short)this.sleepCounter); compound.putFloat("XpP", this.experienceProgress); compound.putInt("XpLevel", this.experienceLevel); compound.putInt("XpTotal", this.totalExperience); compound.putInt("XpSeed", this.enchantmentSeed); compound.putInt("Score", this.getScore()); this.foodData.addAdditionalSaveData(compound); this.abilities.addSaveData(compound); compound.put("EnderItems", this.enderChestInventory.createTag(this.registryAccess())); if (!this.getShoulderEntityLeft().isEmpty()) { compound.put("ShoulderEntityLeft", this.getShoulderEntityLeft()); } if (!this.getShoulderEntityRight().isEmpty()) { compound.put("ShoulderEntityRight", this.getShoulderEntityRight()); } this.getLastDeathLocation() .flatMap(globalPos -> GlobalPos.CODEC.encodeStart(NbtOps.INSTANCE, globalPos).resultOrPartial(LOGGER::error)) .ifPresent(tag -> compound.put("LastDeathLocation", tag)); if (this.currentImpulseImpactPos != null) { compound.put("current_explosion_impact_pos", Vec3.CODEC.encodeStart(NbtOps.INSTANCE, this.currentImpulseImpactPos).getOrThrow()); } compound.putBoolean("ignore_fall_damage_from_current_explosion", this.ignoreFallDamageFromCurrentImpulse); compound.putInt("current_impulse_context_reset_grace_time", this.currentImpulseContextResetGraceTime); } @Override public boolean isInvulnerableTo(DamageSource source) { if (super.isInvulnerableTo(source)) { return true; } else if (source.is(DamageTypeTags.IS_DROWNING)) { return !this.level().getGameRules().getBoolean(GameRules.RULE_DROWNING_DAMAGE); } else if (source.is(DamageTypeTags.IS_FALL)) { return !this.level().getGameRules().getBoolean(GameRules.RULE_FALL_DAMAGE); } else if (source.is(DamageTypeTags.IS_FIRE)) { return !this.level().getGameRules().getBoolean(GameRules.RULE_FIRE_DAMAGE); } else { return source.is(DamageTypeTags.IS_FREEZING) ? !this.level().getGameRules().getBoolean(GameRules.RULE_FREEZE_DAMAGE) : false; } } @Override public boolean hurt(DamageSource source, float amount) { if (this.isInvulnerableTo(source)) { return false; } else if (this.abilities.invulnerable && !source.is(DamageTypeTags.BYPASSES_INVULNERABILITY)) { return false; } else { this.noActionTime = 0; if (this.isDeadOrDying()) { return false; } else { if (!this.level().isClientSide) { this.removeEntitiesOnShoulder(); } if (source.scalesWithDifficulty()) { if (this.level().getDifficulty() == Difficulty.PEACEFUL) { amount = 0.0F; } if (this.level().getDifficulty() == Difficulty.EASY) { amount = Math.min(amount / 2.0F + 1.0F, amount); } if (this.level().getDifficulty() == Difficulty.HARD) { amount = amount * 3.0F / 2.0F; } } return amount == 0.0F ? false : super.hurt(source, amount); } } } @Override protected void blockUsingShield(LivingEntity attacker) { super.blockUsingShield(attacker); if (attacker.canDisableShield()) { this.disableShield(); } } @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 hurtCurrentlyUsedShield(float damageAmount) { if (this.useItem.is(Items.SHIELD)) { if (!this.level().isClientSide) { this.awardStat(Stats.ITEM_USED.get(this.useItem.getItem())); } if (damageAmount >= 3.0F) { int i = 1 + Mth.floor(damageAmount); InteractionHand interactionHand = this.getUsedItemHand(); this.useItem.hurtAndBreak(i, this, getSlotForHand(interactionHand)); if (this.useItem.isEmpty()) { if (interactionHand == InteractionHand.MAIN_HAND) { this.setItemSlot(EquipmentSlot.MAINHAND, ItemStack.EMPTY); } else { this.setItemSlot(EquipmentSlot.OFFHAND, ItemStack.EMPTY); } this.useItem = ItemStack.EMPTY; this.playSound(SoundEvents.SHIELD_BREAK, 0.8F, 0.8F + this.level().random.nextFloat() * 0.4F); } } } } @Override protected void actuallyHurt(DamageSource damageSource, float damageAmount) { if (!this.isInvulnerableTo(damageSource)) { damageAmount = this.getDamageAfterArmorAbsorb(damageSource, damageAmount); damageAmount = this.getDamageAfterMagicAbsorb(damageSource, damageAmount); float var7 = Math.max(damageAmount - this.getAbsorptionAmount(), 0.0F); this.setAbsorptionAmount(this.getAbsorptionAmount() - (damageAmount - var7)); float g = damageAmount - var7; if (g > 0.0F && g < 3.4028235E37F) { this.awardStat(Stats.DAMAGE_ABSORBED, Math.round(g * 10.0F)); } if (var7 != 0.0F) { this.causeFoodExhaustion(damageSource.getFoodExhaustion()); this.getCombatTracker().recordDamage(damageSource, var7); this.setHealth(this.getHealth() - var7); if (var7 < 3.4028235E37F) { this.awardStat(Stats.DAMAGE_TAKEN, Math.round(var7 * 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 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.abilities.instabuild && itemStack == this.getItemInHand(hand) && itemStack.getCount() < itemStack2.getCount()) { itemStack.setCount(itemStack2.getCount()); } return interactionResult; } else { if (!itemStack.isEmpty() && entityToInteractOn instanceof LivingEntity) { if (this.abilities.instabuild) { itemStack = itemStack2; } InteractionResult interactionResult2 = itemStack.interactLivingEntity(this, (LivingEntity)entityToInteractOn, hand); if (interactionResult2.consumesAction()) { this.level().gameEvent(GameEvent.ENTITY_INTERACT, entityToInteractOn.position(), GameEvent.Context.of(this)); if (itemStack.isEmpty() && !this.abilities.instabuild) { 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, float distance) { AABB aABB = this.getBoundingBox(); return this.level().noCollision(this, new AABB(aABB.minX + x, aABB.minY - distance - 1.0E-5F, aABB.minZ + z, aABB.maxX + x, aABB.minY, aABB.maxZ + 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 = 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.0F && !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; double d = this.walkDist - this.walkDistO; if (bl && !bl3 && !bl2 && this.onGround() && d < this.getSpeed()) { ItemStack itemStack2 = this.getItemInHand(InteractionHand.MAIN_HAND); if (itemStack2.getItem() instanceof SwordItem) { bl4 = true; } } float j = 0.0F; if (target instanceof LivingEntity livingEntity) { j = livingEntity.getHealth(); } Vec3 vec3 = target.getDeltaMovement(); boolean bl5 = target.hurt(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)livingEntity3).isMarker()) && this.distanceToSqr(livingEntity3) < 9.0) { float m = this.getEnchantedDamage(livingEntity3, l, damageSource) * h; livingEntity3.knockback(0.4F, Mth.sin(this.getYRot() * (float) (Math.PI / 180.0)), -Mth.cos(this.getYRot() * (float) (Math.PI / 180.0))); livingEntity3.hurt(damageSource, m); if (this.level() instanceof ServerLevel serverLevel) { 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); } public void disableShield() { this.getCooldowns().addCooldown(Items.SHIELD, 100); this.stopUsingItem(); this.level().broadcastEntityEvent(this, (byte)30); } /** * Called when the entity is dealt a critical hit. */ public void crit(Entity entityHit) { } /** * Called when the entity hit is dealt extra melee damage due to an enchantment. */ 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(); } } /** * Returns whether this is a {@link net.minecraft.client.player.LocalPlayer}. */ public boolean isLocalPlayer() { return false; } /** * 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 void updateTutorialInventoryAction(ItemStack carried, ItemStack clicked, ClickAction action) { } public boolean hasContainerOpen() { return this.containerMenu != this.inventoryMenu; } 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 jumpFromGround() { super.jumpFromGround(); this.awardStat(Stats.JUMP); if (this.isSprinting()) { this.causeFoodExhaustion(0.2F); } else { this.causeFoodExhaustion(0.05F); } } @Override public void travel(Vec3 travelVector) { if (this.isSwimming() && !this.isPassenger()) { double d = this.getLookAngle().y; double e = d < -0.2 ? 0.085 : 0.06; if (d <= 0.0 || this.jumping || !this.level().getBlockState(BlockPos.containing(this.getX(), this.getY() + 1.0 - 0.1, this.getZ())).getFluidState().isEmpty()) { Vec3 vec3 = this.getDeltaMovement(); this.setDeltaMovement(vec3.add(0.0, (d - vec3.y) * e, 0.0)); } } if (this.abilities.flying && !this.isPassenger()) { double d = this.getDeltaMovement().y; super.travel(travelVector); Vec3 vec32 = this.getDeltaMovement(); this.setDeltaMovement(vec32.x, d * 0.6, vec32.z); this.resetFallDistance(); this.setSharedFlag(7, false); } else { super.travel(travelVector); } } @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(float fallDistance, float multiplier, DamageSource source) { if (this.abilities.mayfly) { return false; } else { if (fallDistance >= 2.0F) { this.awardStat(Stats.FALL_ONE_CM, (int)Math.round(fallDistance * 100.0)); } boolean bl; if (this.ignoreFallDamageFromCurrentImpulse && this.currentImpulseImpactPos != null) { double d = this.currentImpulseImpactPos.y; this.tryResetCurrentImpulseContext(); if (d < this.getY()) { return false; } float f = Math.min(fallDistance, (float)(d - this.getY())); bl = super.causeFallDamage(f, multiplier, source); } else { bl = super.causeFallDamage(fallDistance, multiplier, source); } if (bl) { this.resetCurrentImpulseContext(); } return bl; } } public boolean tryToStartFallFlying() { if (!this.onGround() && !this.isFallFlying() && !this.isInWater() && !this.hasEffect(MobEffects.LEVITATION)) { ItemStack itemStack = this.getItemBySlot(EquipmentSlot.CHEST); if (itemStack.is(Items.ELYTRA) && ElytraItem.isFlyEnabled(itemStack)) { this.startFallFlying(); return true; } } return false; } public void startFallFlying() { this.setSharedFlag(7, true); } public void stopFallFlying() { this.setSharedFlag(7, true); this.setSharedFlag(7, false); } @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 += 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() { if (!this.level().getGameRules().getBoolean(GameRules.RULE_KEEPINVENTORY) && !this.isSpectator()) { int i = this.experienceLevel * 7; return i > 100 ? 100 : i; } else { return 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 public ItemStack getItemBySlot(EquipmentSlot slot) { if (slot == EquipmentSlot.MAINHAND) { return this.inventory.getSelected(); } else if (slot == EquipmentSlot.OFFHAND) { return this.inventory.offhand.get(0); } else { return slot.getType() == EquipmentSlot.Type.HUMANOID_ARMOR ? this.inventory.armor.get(slot.getIndex()) : ItemStack.EMPTY; } } @Override protected boolean doesEmitEquipEvent(EquipmentSlot slot) { return slot.getType() == EquipmentSlot.Type.HUMANOID_ARMOR; } @Override public void setItemSlot(EquipmentSlot slot, ItemStack stack) { this.verifyEquippedItem(stack); if (slot == EquipmentSlot.MAINHAND) { this.onEquipItem(slot, this.inventory.items.set(this.inventory.selected, stack), stack); } else if (slot == EquipmentSlot.OFFHAND) { this.onEquipItem(slot, this.inventory.offhand.set(0, stack), stack); } else if (slot.getType() == EquipmentSlot.Type.HUMANOID_ARMOR) { this.onEquipItem(slot, this.inventory.armor.set(slot.getIndex(), stack), stack); } } public boolean addItem(ItemStack stack) { return this.inventory.add(stack); } @Override public Iterable getHandSlots() { return Lists.newArrayList(this.getMainHandItem(), this.getOffhandItem()); } @Override public Iterable getArmorSlots() { return this.inventory.armor; } @Override public boolean canUseSlot(EquipmentSlot slot) { return slot != EquipmentSlot.BODY; } 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()).ifPresent(entity -> { if (entity instanceof TamableAnimal) { ((TamableAnimal)entity).setOwnerUUID(this.uuid); } entity.setPos(this.getX(), this.getY() + 0.7F, this.getZ()); ((ServerLevel)this.level()).addWithUUID(entity); }); } } @Override public abstract boolean isSpectator(); @Override public boolean canBeHitByProjectile() { return !this.isSpectator() && super.canBeHitByProjectile(); } @Override public boolean isSwimming() { return !this.abilities.flying && !this.isSpectator() && super.isSwimming(); } public abstract boolean isCreative(); @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(ClickEvent.Action.SUGGEST_COMMAND, "/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.items.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; } public float getLuck() { return (float)this.getAttributeValue(Attributes.LUCK); } public boolean canUseGameMasterBlocks() { return this.abilities.instabuild && this.getPermissionLevel() >= 2; } @Override public boolean canTakeItem(ItemStack stack) { EquipmentSlot equipmentSlot = this.getEquipmentSlotForItem(stack); return this.getItemBySlot(equipmentSlot).isEmpty(); } @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.abilities.instabuild ? new ItemStack(Items.ARROW) : ItemStack.EMPTY; } } } @Override public ItemStack eat(Level level, ItemStack food, FoodProperties foodProperties) { this.getFoodData().eat(foodProperties); this.awardStat(Stats.ITEM_USED.get(food.getItem())); level.playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.PLAYER_BURP, SoundSource.PLAYERS, 0.5F, level.random.nextFloat() * 0.1F + 0.9F); if (this instanceof ServerPlayer) { CriteriaTriggers.CONSUME_ITEM.trigger((ServerPlayer)this, food); } ItemStack itemStack = super.eat(level, food, foodProperties); Optional optional = foodProperties.usingConvertsTo(); if (optional.isPresent() && !this.hasInfiniteMaterials()) { if (itemStack.isEmpty()) { return ((ItemStack)optional.get()).copy(); } if (!this.level().isClientSide()) { this.getInventory().add(((ItemStack)optional.get()).copy()); } } return itemStack; } @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 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 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; } } }