365 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			365 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| package net.minecraft.world.entity.item;
 | |
| 
 | |
| import com.mojang.logging.LogUtils;
 | |
| import java.util.function.Predicate;
 | |
| import net.minecraft.CrashReportCategory;
 | |
| import net.minecraft.core.BlockPos;
 | |
| import net.minecraft.core.Direction;
 | |
| import net.minecraft.core.RegistryAccess;
 | |
| import net.minecraft.nbt.CompoundTag;
 | |
| 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.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.util.ProblemReporter;
 | |
| 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.level.storage.TagValueInput;
 | |
| import net.minecraft.world.level.storage.TagValueOutput;
 | |
| import net.minecraft.world.level.storage.ValueInput;
 | |
| import net.minecraft.world.level.storage.ValueOutput;
 | |
| import net.minecraft.world.phys.BlockHitResult;
 | |
| import net.minecraft.world.phys.HitResult;
 | |
| import net.minecraft.world.phys.Vec3;
 | |
| import org.jetbrains.annotations.Nullable;
 | |
| 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> entityType, Level level) {
 | |
| 		super(entityType, level);
 | |
| 	}
 | |
| 
 | |
| 	private FallingBlockEntity(Level level, double x, double y, double z, BlockState state) {
 | |
| 		this(EntityType.FALLING_BLOCK, level);
 | |
| 		this.blockState = state;
 | |
| 		this.blocksBuilding = true;
 | |
| 		this.setPos(x, y, z);
 | |
| 		this.setDeltaMovement(Vec3.ZERO);
 | |
| 		this.xo = x;
 | |
| 		this.yo = y;
 | |
| 		this.zo = z;
 | |
| 		this.setStartPos(this.blockPosition());
 | |
| 	}
 | |
| 
 | |
| 	public static FallingBlockEntity fall(Level level, BlockPos pos, BlockState blockState) {
 | |
| 		FallingBlockEntity fallingBlockEntity = new FallingBlockEntity(
 | |
| 			level,
 | |
| 			pos.getX() + 0.5,
 | |
| 			pos.getY(),
 | |
| 			pos.getZ() + 0.5,
 | |
| 			blockState.hasProperty(BlockStateProperties.WATERLOGGED) ? blockState.setValue(BlockStateProperties.WATERLOGGED, false) : blockState
 | |
| 		);
 | |
| 		level.setBlock(pos, blockState.getFluidState().createLegacyBlock(), 3);
 | |
| 		level.addFreshEntity(fallingBlockEntity);
 | |
| 		return fallingBlockEntity;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean isAttackable() {
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public final boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) {
 | |
| 		if (!this.isInvulnerableToBase(damageSource)) {
 | |
| 			this.markHurt();
 | |
| 		}
 | |
| 
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	public void setStartPos(BlockPos startPos) {
 | |
| 		this.entityData.set(DATA_START_POS, startPos);
 | |
| 	}
 | |
| 
 | |
| 	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 builder) {
 | |
| 		builder.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 bl = this.blockState.getBlock() instanceof ConcretePowderBlock;
 | |
| 				boolean bl2 = bl && this.level().getFluidState(blockPos).is(FluidTags.WATER);
 | |
| 				double d = this.getDeltaMovement().lengthSqr();
 | |
| 				if (bl && d > 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();
 | |
| 						bl2 = true;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				if (!this.onGround() && !bl2) {
 | |
| 					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 bl3 = blockState.canBeReplaced(new DirectionalPlaceContext(this.level(), blockPos, Direction.DOWN, ItemStack.EMPTY, Direction.UP));
 | |
| 							boolean bl4 = FallingBlock.isFree(this.level().getBlockState(blockPos.below())) && (!bl || !bl2);
 | |
| 							boolean bl5 = this.blockState.canSurvive(this.level(), blockPos) && !bl4;
 | |
| 							if (bl3 && bl5) {
 | |
| 								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) {
 | |
| 											try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(blockEntity.problemPath(), LOGGER)) {
 | |
| 												RegistryAccess registryAccess = this.level().registryAccess();
 | |
| 												TagValueOutput tagValueOutput = TagValueOutput.createWithContext(scopedCollector, registryAccess);
 | |
| 												blockEntity.saveWithoutMetadata(tagValueOutput);
 | |
| 												CompoundTag compoundTag = tagValueOutput.buildResult();
 | |
| 												this.blockData.forEach((string, tag) -> compoundTag.put(string, tag.copy()));
 | |
| 												blockEntity.loadWithComponents(TagValueInput.create(scopedCollector, registryAccess, compoundTag));
 | |
| 											} catch (Exception var19) {
 | |
| 												LOGGER.error("Failed to load block entity from falling block", (Throwable)var19);
 | |
| 											}
 | |
| 
 | |
| 											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 block, BlockPos pos) {
 | |
| 		if (block instanceof Fallable) {
 | |
| 			((Fallable)block).onBrokenAfterFall(this.level(), pos, this);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean causeFallDamage(double fallDistance, float damageMultiplier, DamageSource damageSource) {
 | |
| 		if (!this.hurtEntities) {
 | |
| 			return false;
 | |
| 		} else {
 | |
| 			int i = Mth.ceil(fallDistance - 1.0);
 | |
| 			if (i < 0) {
 | |
| 				return false;
 | |
| 			} else {
 | |
| 				Predicate<Entity> predicate = EntitySelector.NO_CREATIVE_OR_SPECTATOR.and(EntitySelector.LIVING_ENTITY_STILL_ALIVE);
 | |
| 				DamageSource damageSource2 = 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(entity -> entity.hurt(damageSource2, f));
 | |
| 				boolean bl = this.blockState.is(BlockTags.ANVIL);
 | |
| 				if (bl && 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(ValueOutput output) {
 | |
| 		output.store("BlockState", BlockState.CODEC, this.blockState);
 | |
| 		output.putInt("Time", this.time);
 | |
| 		output.putBoolean("DropItem", this.dropItem);
 | |
| 		output.putBoolean("HurtEntities", this.hurtEntities);
 | |
| 		output.putFloat("FallHurtAmount", this.fallDamagePerDistance);
 | |
| 		output.putInt("FallHurtMax", this.fallDamageMax);
 | |
| 		if (this.blockData != null) {
 | |
| 			output.store("TileEntityData", CompoundTag.CODEC, this.blockData);
 | |
| 		}
 | |
| 
 | |
| 		output.putBoolean("CancelDrop", this.cancelDrop);
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	protected void readAdditionalSaveData(ValueInput input) {
 | |
| 		this.blockState = (BlockState)input.read("BlockState", BlockState.CODEC).orElse(DEFAULT_BLOCK_STATE);
 | |
| 		this.time = input.getIntOr("Time", 0);
 | |
| 		boolean bl = this.blockState.is(BlockTags.ANVIL);
 | |
| 		this.hurtEntities = input.getBooleanOr("HurtEntities", bl);
 | |
| 		this.fallDamagePerDistance = input.getFloatOr("FallHurtAmount", 0.0F);
 | |
| 		this.fallDamageMax = input.getIntOr("FallHurtMax", 40);
 | |
| 		this.dropItem = input.getBooleanOr("DropItem", true);
 | |
| 		this.blockData = (CompoundTag)input.read("TileEntityData", CompoundTag.CODEC).orElse(null);
 | |
| 		this.cancelDrop = input.getBooleanOr("CancelDrop", false);
 | |
| 	}
 | |
| 
 | |
| 	public void setHurtsEntities(float fallDamagePerDistance, int fallDamageMax) {
 | |
| 		this.hurtEntities = true;
 | |
| 		this.fallDamagePerDistance = fallDamagePerDistance;
 | |
| 		this.fallDamageMax = fallDamageMax;
 | |
| 	}
 | |
| 
 | |
| 	public void disableDrop() {
 | |
| 		this.cancelDrop = true;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public boolean displayFireAnimation() {
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void fillCrashReportCategory(CrashReportCategory category) {
 | |
| 		super.fillCrashReportCategory(category);
 | |
| 		category.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 entity) {
 | |
| 		return new ClientboundAddEntityPacket(this, entity, Block.getId(this.getBlockState()));
 | |
| 	}
 | |
| 
 | |
| 	@Override
 | |
| 	public void recreateFromPacket(ClientboundAddEntityPacket packet) {
 | |
| 		super.recreateFromPacket(packet);
 | |
| 		this.blockState = Block.stateById(packet.getData());
 | |
| 		this.blocksBuilding = true;
 | |
| 		double d = packet.getX();
 | |
| 		double e = packet.getY();
 | |
| 		double f = packet.getZ();
 | |
| 		this.setPos(d, e, f);
 | |
| 		this.setStartPos(this.blockPosition());
 | |
| 	}
 | |
| 
 | |
| 	@Nullable
 | |
| 	@Override
 | |
| 	public Entity teleport(TeleportTransition teleportTransition) {
 | |
| 		ResourceKey<Level> resourceKey = teleportTransition.newLevel().dimension();
 | |
| 		ResourceKey<Level> resourceKey2 = this.level().dimension();
 | |
| 		boolean bl = (resourceKey2 == Level.END || resourceKey == Level.END) && resourceKey2 != resourceKey;
 | |
| 		Entity entity = super.teleport(teleportTransition);
 | |
| 		this.forceTickAfterTeleportToDuplicate = entity != null && bl;
 | |
| 		return entity;
 | |
| 	}
 | |
| }
 |