Code/net/minecraft/world/entity/item/FallingBlockEntity.java

371 lines
17 KiB
Java
Raw Permalink Normal View History

2025-07-01 06:20:03 +00:00
package net.minecraft.world.entity.item;
import com.mojang.logging.LogUtils;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import net.minecraft.CrashReportCategory;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
import net.minecraft.network.protocol.game.ClientboundBlockUpdatePacket;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.resources.RegistryOps;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerEntity;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.FluidTags;
import net.minecraft.util.Mth;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySelector;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.MoverType;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.DirectionalPlaceContext;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.AnvilBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.ConcretePowderBlock;
import net.minecraft.world.level.block.Fallable;
import net.minecraft.world.level.block.FallingBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.portal.TeleportTransition;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import org.slf4j.Logger;
public class FallingBlockEntity extends Entity {
private static final Logger LOGGER = LogUtils.getLogger();
private static final BlockState DEFAULT_BLOCK_STATE = Blocks.SAND.defaultBlockState();
private static final int DEFAULT_TIME = 0;
private static final float DEFAULT_FALL_DAMAGE_PER_DISTANCE = 0.0F;
private static final int DEFAULT_MAX_FALL_DAMAGE = 40;
private static final boolean DEFAULT_DROP_ITEM = true;
private static final boolean DEFAULT_CANCEL_DROP = false;
private BlockState blockState = DEFAULT_BLOCK_STATE;
public int time = 0;
public boolean dropItem = true;
private boolean cancelDrop = false;
private boolean hurtEntities;
private int fallDamageMax = 40;
private float fallDamagePerDistance = 0.0F;
@Nullable
public CompoundTag blockData;
public boolean forceTickAfterTeleportToDuplicate;
protected static final EntityDataAccessor<BlockPos> DATA_START_POS = SynchedEntityData.defineId(FallingBlockEntity.class, EntityDataSerializers.BLOCK_POS);
public FallingBlockEntity(EntityType<? extends FallingBlockEntity> p_31950_, Level p_31951_) {
super(p_31950_, p_31951_);
}
private FallingBlockEntity(Level p_31953_, double p_31954_, double p_31955_, double p_31956_, BlockState p_31957_) {
this(EntityType.FALLING_BLOCK, p_31953_);
this.blockState = p_31957_;
this.blocksBuilding = true;
this.setPos(p_31954_, p_31955_, p_31956_);
this.setDeltaMovement(Vec3.ZERO);
this.xo = p_31954_;
this.yo = p_31955_;
this.zo = p_31956_;
this.setStartPos(this.blockPosition());
}
public static FallingBlockEntity fall(Level p_201972_, BlockPos p_201973_, BlockState p_201974_) {
FallingBlockEntity fallingblockentity = new FallingBlockEntity(
p_201972_,
p_201973_.getX() + 0.5,
p_201973_.getY(),
p_201973_.getZ() + 0.5,
p_201974_.hasProperty(BlockStateProperties.WATERLOGGED) ? p_201974_.setValue(BlockStateProperties.WATERLOGGED, false) : p_201974_
);
p_201972_.setBlock(p_201973_, p_201974_.getFluidState().createLegacyBlock(), 3);
p_201972_.addFreshEntity(fallingblockentity);
return fallingblockentity;
}
@Override
public boolean isAttackable() {
return false;
}
@Override
public final boolean hurtServer(ServerLevel p_369142_, DamageSource p_361302_, float p_361003_) {
if (!this.isInvulnerableToBase(p_361302_)) {
this.markHurt();
}
return false;
}
public void setStartPos(BlockPos p_31960_) {
this.entityData.set(DATA_START_POS, p_31960_);
}
public BlockPos getStartPos() {
return this.entityData.get(DATA_START_POS);
}
@Override
protected Entity.MovementEmission getMovementEmission() {
return Entity.MovementEmission.NONE;
}
@Override
protected void defineSynchedData(SynchedEntityData.Builder p_329911_) {
p_329911_.define(DATA_START_POS, BlockPos.ZERO);
}
@Override
public boolean isPickable() {
return !this.isRemoved();
}
@Override
protected double getDefaultGravity() {
return 0.04;
}
@Override
public void tick() {
if (this.blockState.isAir()) {
this.discard();
} else {
Block block = this.blockState.getBlock();
this.time++;
this.applyGravity();
this.move(MoverType.SELF, this.getDeltaMovement());
this.applyEffectsFromBlocks();
this.handlePortal();
if (this.level() instanceof ServerLevel serverlevel && (this.isAlive() || this.forceTickAfterTeleportToDuplicate)) {
BlockPos blockpos = this.blockPosition();
boolean flag = this.blockState.getBlock() instanceof ConcretePowderBlock;
boolean flag1 = flag && this.level().getFluidState(blockpos).is(FluidTags.WATER);
double d0 = this.getDeltaMovement().lengthSqr();
if (flag && d0 > 1.0) {
BlockHitResult blockhitresult = this.level()
.clip(
new ClipContext(
new Vec3(this.xo, this.yo, this.zo),
this.position(),
ClipContext.Block.COLLIDER,
ClipContext.Fluid.SOURCE_ONLY,
this
)
);
if (blockhitresult.getType() != HitResult.Type.MISS && this.level().getFluidState(blockhitresult.getBlockPos()).is(FluidTags.WATER)) {
blockpos = blockhitresult.getBlockPos();
flag1 = true;
}
}
if (!this.onGround() && !flag1) {
if (this.time > 100 && (blockpos.getY() <= this.level().getMinY() || blockpos.getY() > this.level().getMaxY())
|| this.time > 600) {
if (this.dropItem && serverlevel.getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) {
this.spawnAtLocation(serverlevel, block);
}
this.discard();
}
} else {
BlockState blockstate = this.level().getBlockState(blockpos);
this.setDeltaMovement(this.getDeltaMovement().multiply(0.7, -0.5, 0.7));
if (!blockstate.is(Blocks.MOVING_PISTON)) {
if (!this.cancelDrop) {
boolean flag2 = blockstate.canBeReplaced(
new DirectionalPlaceContext(this.level(), blockpos, Direction.DOWN, ItemStack.EMPTY, Direction.UP)
);
boolean flag3 = FallingBlock.isFree(this.level().getBlockState(blockpos.below())) && (!flag || !flag1);
boolean flag4 = this.blockState.canSurvive(this.level(), blockpos) && !flag3;
if (flag2 && flag4) {
if (this.blockState.hasProperty(BlockStateProperties.WATERLOGGED) && this.level().getFluidState(blockpos).getType() == Fluids.WATER) {
this.blockState = this.blockState.setValue(BlockStateProperties.WATERLOGGED, true);
}
if (this.level().setBlock(blockpos, this.blockState, 3)) {
((ServerLevel)this.level())
.getChunkSource()
.chunkMap
.broadcast(this, new ClientboundBlockUpdatePacket(blockpos, this.level().getBlockState(blockpos)));
this.discard();
if (block instanceof Fallable) {
((Fallable)block).onLand(this.level(), blockpos, this.blockState, blockstate, this);
}
if (this.blockData != null && this.blockState.hasBlockEntity()) {
BlockEntity blockentity = this.level().getBlockEntity(blockpos);
if (blockentity != null) {
CompoundTag compoundtag = blockentity.saveWithoutMetadata(this.level().registryAccess());
this.blockData.forEach((p_390676_, p_390677_) -> compoundtag.put(p_390676_, p_390677_.copy()));
try {
blockentity.loadWithComponents(compoundtag, this.level().registryAccess());
} catch (Exception exception) {
LOGGER.error("Failed to load block entity from falling block", (Throwable)exception);
}
blockentity.setChanged();
}
}
} else if (this.dropItem && serverlevel.getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) {
this.discard();
this.callOnBrokenAfterFall(block, blockpos);
this.spawnAtLocation(serverlevel, block);
}
} else {
this.discard();
if (this.dropItem && serverlevel.getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) {
this.callOnBrokenAfterFall(block, blockpos);
this.spawnAtLocation(serverlevel, block);
}
}
} else {
this.discard();
this.callOnBrokenAfterFall(block, blockpos);
}
}
}
}
this.setDeltaMovement(this.getDeltaMovement().scale(0.98));
}
}
public void callOnBrokenAfterFall(Block p_149651_, BlockPos p_149652_) {
if (p_149651_ instanceof Fallable) {
((Fallable)p_149651_).onBrokenAfterFall(this.level(), p_149652_, this);
}
}
@Override
public boolean causeFallDamage(double p_395056_, float p_149643_, DamageSource p_149645_) {
if (!this.hurtEntities) {
return false;
} else {
int i = Mth.ceil(p_395056_ - 1.0);
if (i < 0) {
return false;
} else {
Predicate<Entity> predicate = EntitySelector.NO_CREATIVE_OR_SPECTATOR.and(EntitySelector.LIVING_ENTITY_STILL_ALIVE);
DamageSource damagesource = this.blockState.getBlock() instanceof Fallable fallable ? fallable.getFallDamageSource(this) : this.damageSources().fallingBlock(this);
float f = Math.min(Mth.floor(i * this.fallDamagePerDistance), this.fallDamageMax);
this.level().getEntities(this, this.getBoundingBox(), predicate).forEach(p_359237_ -> p_359237_.hurt(damagesource, f));
boolean flag = this.blockState.is(BlockTags.ANVIL);
if (flag && f > 0.0F && this.random.nextFloat() < 0.05F + i * 0.05F) {
BlockState blockstate = AnvilBlock.damage(this.blockState);
if (blockstate == null) {
this.cancelDrop = true;
} else {
this.blockState = blockstate;
}
}
return false;
}
}
}
@Override
protected void addAdditionalSaveData(CompoundTag p_31973_) {
RegistryOps<Tag> registryops = this.registryAccess().createSerializationContext(NbtOps.INSTANCE);
p_31973_.store("BlockState", BlockState.CODEC, registryops, this.blockState);
p_31973_.putInt("Time", this.time);
p_31973_.putBoolean("DropItem", this.dropItem);
p_31973_.putBoolean("HurtEntities", this.hurtEntities);
p_31973_.putFloat("FallHurtAmount", this.fallDamagePerDistance);
p_31973_.putInt("FallHurtMax", this.fallDamageMax);
if (this.blockData != null) {
p_31973_.put("TileEntityData", this.blockData);
}
p_31973_.putBoolean("CancelDrop", this.cancelDrop);
}
@Override
protected void readAdditionalSaveData(CompoundTag p_31964_) {
RegistryOps<Tag> registryops = this.registryAccess().createSerializationContext(NbtOps.INSTANCE);
this.blockState = p_31964_.read("BlockState", BlockState.CODEC, registryops).orElse(DEFAULT_BLOCK_STATE);
this.time = p_31964_.getIntOr("Time", 0);
boolean flag = this.blockState.is(BlockTags.ANVIL);
this.hurtEntities = p_31964_.getBooleanOr("HurtEntities", flag);
this.fallDamagePerDistance = p_31964_.getFloatOr("FallHurtAmount", 0.0F);
this.fallDamageMax = p_31964_.getIntOr("FallHurtMax", 40);
this.dropItem = p_31964_.getBooleanOr("DropItem", true);
this.blockData = p_31964_.getCompound("TileEntityData").map(CompoundTag::copy).orElse(null);
this.cancelDrop = p_31964_.getBooleanOr("CancelDrop", false);
}
public void setHurtsEntities(float p_149657_, int p_149658_) {
this.hurtEntities = true;
this.fallDamagePerDistance = p_149657_;
this.fallDamageMax = p_149658_;
}
public void disableDrop() {
this.cancelDrop = true;
}
@Override
public boolean displayFireAnimation() {
return false;
}
@Override
public void fillCrashReportCategory(CrashReportCategory p_31962_) {
super.fillCrashReportCategory(p_31962_);
p_31962_.setDetail("Immitating BlockState", this.blockState.toString());
}
public BlockState getBlockState() {
return this.blockState;
}
@Override
protected Component getTypeName() {
return Component.translatable("entity.minecraft.falling_block_type", this.blockState.getBlock().getName());
}
@Override
public Packet<ClientGamePacketListener> getAddEntityPacket(ServerEntity p_342166_) {
return new ClientboundAddEntityPacket(this, p_342166_, Block.getId(this.getBlockState()));
}
@Override
public void recreateFromPacket(ClientboundAddEntityPacket p_149654_) {
super.recreateFromPacket(p_149654_);
this.blockState = Block.stateById(p_149654_.getData());
this.blocksBuilding = true;
double d0 = p_149654_.getX();
double d1 = p_149654_.getY();
double d2 = p_149654_.getZ();
this.setPos(d0, d1, d2);
this.setStartPos(this.blockPosition());
}
@Nullable
@Override
public Entity teleport(TeleportTransition p_367033_) {
ResourceKey<Level> resourcekey = p_367033_.newLevel().dimension();
ResourceKey<Level> resourcekey1 = this.level().dimension();
boolean flag = (resourcekey1 == Level.END || resourcekey == Level.END) && resourcekey1 != resourcekey;
Entity entity = super.teleport(p_367033_);
this.forceTickAfterTeleportToDuplicate = entity != null && flag;
return entity;
}
}