/* * Decompiled with CFR 0.152. * * Could not load the following classes: * org.jspecify.annotations.Nullable */ package net.minecraft.world.entity.item; import java.util.List; import java.util.Objects; import java.util.UUID; import net.minecraft.core.BlockPos; import net.minecraft.core.UUIDUtil; import net.minecraft.network.chat.Component; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundSource; import net.minecraft.stats.Stats; import net.minecraft.tags.FluidTags; import net.minecraft.tags.ItemTags; import net.minecraft.util.Mth; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityReference; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.MoverType; import net.minecraft.world.entity.SlotAccess; import net.minecraft.world.entity.TraceableEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Explosion; import net.minecraft.world.level.Level; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.gamerules.GameRules; import net.minecraft.world.level.portal.TeleportTransition; import net.minecraft.world.level.storage.ValueInput; import net.minecraft.world.level.storage.ValueOutput; import net.minecraft.world.phys.Vec3; import org.jspecify.annotations.Nullable; public class ItemEntity extends Entity implements TraceableEntity { private static final EntityDataAccessor DATA_ITEM = SynchedEntityData.defineId(ItemEntity.class, EntityDataSerializers.ITEM_STACK); private static final float FLOAT_HEIGHT = 0.1f; public static final float EYE_HEIGHT = 0.2125f; private static final int LIFETIME = 6000; private static final int INFINITE_PICKUP_DELAY = Short.MAX_VALUE; private static final int INFINITE_LIFETIME = Short.MIN_VALUE; private static final int DEFAULT_HEALTH = 5; private static final short DEFAULT_AGE = 0; private static final short DEFAULT_PICKUP_DELAY = 0; private int age = 0; private int pickupDelay = 0; private int health = 5; private @Nullable EntityReference thrower; private @Nullable UUID target; public final float bobOffs = this.random.nextFloat() * (float)Math.PI * 2.0f; public ItemEntity(EntityType type, Level level) { super(type, level); this.setYRot(this.random.nextFloat() * 360.0f); } public ItemEntity(Level level, double x, double y, double z, ItemStack itemStack) { this(level, x, y, z, itemStack, level.random.nextDouble() * 0.2 - 0.1, 0.2, level.random.nextDouble() * 0.2 - 0.1); } public ItemEntity(Level level, double x, double y, double z, ItemStack itemStack, double deltaX, double deltaY, double deltaZ) { this((EntityType)EntityType.ITEM, level); this.setPos(x, y, z); this.setDeltaMovement(deltaX, deltaY, deltaZ); this.setItem(itemStack); } @Override public boolean dampensVibrations() { return this.getItem().is(ItemTags.DAMPENS_VIBRATIONS); } @Override public @Nullable Entity getOwner() { return EntityReference.getEntity(this.thrower, this.level()); } @Override public void restoreFrom(Entity oldEntity) { super.restoreFrom(oldEntity); if (oldEntity instanceof ItemEntity) { ItemEntity item = (ItemEntity)oldEntity; this.thrower = item.thrower; } } @Override protected Entity.MovementEmission getMovementEmission() { return Entity.MovementEmission.NONE; } @Override protected void defineSynchedData(SynchedEntityData.Builder entityData) { entityData.define(DATA_ITEM, ItemStack.EMPTY); } @Override protected double getDefaultGravity() { return 0.04; } @Override public void tick() { double value; int rate; if (this.getItem().isEmpty()) { this.discard(); return; } super.tick(); if (this.pickupDelay > 0 && this.pickupDelay != Short.MAX_VALUE) { --this.pickupDelay; } this.xo = this.getX(); this.yo = this.getY(); this.zo = this.getZ(); Vec3 oldMovement = this.getDeltaMovement(); if (this.isInWater() && this.getFluidHeight(FluidTags.WATER) > (double)0.1f) { this.setUnderwaterMovement(); } else if (this.isInLava() && this.getFluidHeight(FluidTags.LAVA) > (double)0.1f) { this.setUnderLavaMovement(); } else { this.applyGravity(); } if (this.level().isClientSide()) { this.noPhysics = false; } else { boolean bl = this.noPhysics = !this.level().noCollision(this, this.getBoundingBox().deflate(1.0E-7)); if (this.noPhysics) { this.moveTowardsClosestSpace(this.getX(), (this.getBoundingBox().minY + this.getBoundingBox().maxY) / 2.0, this.getZ()); } } if (!this.onGround() || this.getDeltaMovement().horizontalDistanceSqr() > (double)1.0E-5f || (this.tickCount + this.getId()) % 4 == 0) { this.move(MoverType.SELF, this.getDeltaMovement()); this.applyEffectsFromBlocks(); float friction = 0.98f; if (this.onGround()) { friction = this.level().getBlockState(this.getBlockPosBelowThatAffectsMyMovement()).getBlock().getFriction() * 0.98f; } this.setDeltaMovement(this.getDeltaMovement().multiply(friction, 0.98, friction)); if (this.onGround()) { Vec3 movement = this.getDeltaMovement(); if (movement.y < 0.0) { this.setDeltaMovement(movement.multiply(1.0, -0.5, 1.0)); } } } boolean moved = Mth.floor(this.xo) != Mth.floor(this.getX()) || Mth.floor(this.yo) != Mth.floor(this.getY()) || Mth.floor(this.zo) != Mth.floor(this.getZ()); int n = rate = moved ? 2 : 40; if (this.tickCount % rate == 0 && !this.level().isClientSide() && this.isMergable()) { this.mergeWithNeighbours(); } if (this.age != Short.MIN_VALUE) { ++this.age; } this.needsSync |= this.updateInWaterStateAndDoFluidPushing(); if (!this.level().isClientSide() && (value = this.getDeltaMovement().subtract(oldMovement).lengthSqr()) > 0.01) { this.needsSync = true; } if (!this.level().isClientSide() && this.age >= 6000) { this.discard(); } } @Override public BlockPos getBlockPosBelowThatAffectsMyMovement() { return this.getOnPos(0.999999f); } private void setUnderwaterMovement() { this.setFluidMovement(0.99f); } private void setUnderLavaMovement() { this.setFluidMovement(0.95f); } private void setFluidMovement(double multiplier) { Vec3 movement = this.getDeltaMovement(); this.setDeltaMovement(movement.x * multiplier, movement.y + (double)(movement.y < (double)0.06f ? 5.0E-4f : 0.0f), movement.z * multiplier); } private void mergeWithNeighbours() { if (!this.isMergable()) { return; } List items = this.level().getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(0.5, 0.0, 0.5), other -> other != this && other.isMergable()); for (ItemEntity entity : items) { if (!entity.isMergable()) continue; this.tryToMerge(entity); if (!this.isRemoved()) continue; break; } } private boolean isMergable() { ItemStack item = this.getItem(); return this.isAlive() && this.pickupDelay != Short.MAX_VALUE && this.age != Short.MIN_VALUE && this.age < 6000 && item.getCount() < item.getMaxStackSize(); } private void tryToMerge(ItemEntity other) { ItemStack thisItemStack = this.getItem(); ItemStack otherItemStack = other.getItem(); if (!Objects.equals(this.target, other.target) || !ItemEntity.areMergable(thisItemStack, otherItemStack)) { return; } if (otherItemStack.getCount() < thisItemStack.getCount()) { ItemEntity.merge(this, thisItemStack, other, otherItemStack); } else { ItemEntity.merge(other, otherItemStack, this, thisItemStack); } } public static boolean areMergable(ItemStack thisItemStack, ItemStack otherItemStack) { if (otherItemStack.getCount() + thisItemStack.getCount() > otherItemStack.getMaxStackSize()) { return false; } return ItemStack.isSameItemSameComponents(thisItemStack, otherItemStack); } public static ItemStack merge(ItemStack toStack, ItemStack fromStack, int maxCount) { int delta = Math.min(Math.min(toStack.getMaxStackSize(), maxCount) - toStack.getCount(), fromStack.getCount()); ItemStack newToStack = toStack.copyWithCount(toStack.getCount() + delta); fromStack.shrink(delta); return newToStack; } private static void merge(ItemEntity toItem, ItemStack toStack, ItemStack fromStack) { ItemStack newToStack = ItemEntity.merge(toStack, fromStack, 64); toItem.setItem(newToStack); } private static void merge(ItemEntity toItem, ItemStack toStack, ItemEntity fromItem, ItemStack fromStack) { ItemEntity.merge(toItem, toStack, fromStack); toItem.pickupDelay = Math.max(toItem.pickupDelay, fromItem.pickupDelay); toItem.age = Math.min(toItem.age, fromItem.age); if (fromStack.isEmpty()) { fromItem.discard(); } } @Override public boolean fireImmune() { return !this.getItem().canBeHurtBy(this.damageSources().inFire()) || super.fireImmune(); } @Override protected boolean shouldPlayLavaHurtSound() { if (this.health <= 0) { return true; } return this.tickCount % 10 == 0; } @Override public final boolean hurtClient(DamageSource source) { if (this.isInvulnerableToBase(source)) { return false; } return this.getItem().canBeHurtBy(source); } @Override public final boolean hurtServer(ServerLevel level, DamageSource source, float damage) { if (this.isInvulnerableToBase(source)) { return false; } if (!level.getGameRules().get(GameRules.MOB_GRIEFING).booleanValue() && source.getEntity() instanceof Mob) { return false; } if (!this.getItem().canBeHurtBy(source)) { return false; } this.markHurt(); this.health = (int)((float)this.health - damage); this.gameEvent(GameEvent.ENTITY_DAMAGE, source.getEntity()); if (this.health <= 0) { this.getItem().onDestroyed(this); this.discard(); } return true; } @Override public boolean ignoreExplosion(Explosion explosion) { if (explosion.shouldAffectBlocklikeEntities()) { return super.ignoreExplosion(explosion); } return true; } @Override protected void addAdditionalSaveData(ValueOutput output) { output.putShort("Health", (short)this.health); output.putShort("Age", (short)this.age); output.putShort("PickupDelay", (short)this.pickupDelay); EntityReference.store(this.thrower, output, "Thrower"); output.storeNullable("Owner", UUIDUtil.CODEC, this.target); if (!this.getItem().isEmpty()) { output.store("Item", ItemStack.CODEC, this.getItem()); } } @Override protected void readAdditionalSaveData(ValueInput input) { this.health = input.getShortOr("Health", (short)5); this.age = input.getShortOr("Age", (short)0); this.pickupDelay = input.getShortOr("PickupDelay", (short)0); this.target = input.read("Owner", UUIDUtil.CODEC).orElse(null); this.thrower = EntityReference.read(input, "Thrower"); this.setItem(input.read("Item", ItemStack.CODEC).orElse(ItemStack.EMPTY)); if (this.getItem().isEmpty()) { this.discard(); } } @Override public void playerTouch(Player player) { if (this.level().isClientSide()) { return; } ItemStack itemStack = this.getItem(); Item item = itemStack.getItem(); int orgCount = itemStack.getCount(); if (this.pickupDelay == 0 && (this.target == null || this.target.equals(player.getUUID())) && player.getInventory().add(itemStack)) { player.take(this, orgCount); if (itemStack.isEmpty()) { this.discard(); itemStack.setCount(orgCount); } player.awardStat(Stats.ITEM_PICKED_UP.get(item), orgCount); player.onItemPickup(this); } } @Override public Component getName() { Component name = this.getCustomName(); if (name != null) { return name; } return this.getItem().getItemName(); } @Override public boolean isAttackable() { return false; } @Override public @Nullable Entity teleport(TeleportTransition transition) { Entity entity = super.teleport(transition); if (!this.level().isClientSide() && entity instanceof ItemEntity) { ItemEntity item = (ItemEntity)entity; item.mergeWithNeighbours(); } return entity; } public ItemStack getItem() { return this.getEntityData().get(DATA_ITEM); } public void setItem(ItemStack itemStack) { this.getEntityData().set(DATA_ITEM, itemStack); } @Override public void onSyncedDataUpdated(EntityDataAccessor accessor) { super.onSyncedDataUpdated(accessor); if (DATA_ITEM.equals(accessor)) { this.getItem().setEntityRepresentation(this); } } public void setTarget(@Nullable UUID target) { this.target = target; } public void setThrower(Entity thrower) { this.thrower = EntityReference.of(thrower); } public int getAge() { return this.age; } public void setDefaultPickUpDelay() { this.pickupDelay = 10; } public void setNoPickUpDelay() { this.pickupDelay = 0; } public void setNeverPickUp() { this.pickupDelay = Short.MAX_VALUE; } public void setPickUpDelay(int ticks) { this.pickupDelay = ticks; } public boolean hasPickUpDelay() { return this.pickupDelay > 0; } public void setUnlimitedLifetime() { this.age = Short.MIN_VALUE; } public void setExtendedLifetime() { this.age = -6000; } public void makeFakeItem() { this.setNeverPickUp(); this.age = 5999; } public static float getSpin(float ageInTicks, float bobOffset) { return ageInTicks / 20.0f + bobOffset; } @Override public SoundSource getSoundSource() { return SoundSource.AMBIENT; } @Override public float getVisualRotationYInDegrees() { return 180.0f - ItemEntity.getSpin((float)this.getAge() + 0.5f, this.bobOffs) / ((float)Math.PI * 2) * 360.0f; } @Override public @Nullable SlotAccess getSlot(int slot) { if (slot == 0) { return SlotAccess.of(this::getItem, this::setItem); } return super.getSlot(slot); } }