Code/net/minecraft/world/level/block/entity/TheEndGatewayBlockEntity.java

274 lines
11 KiB
Java
Raw Normal View History

2025-07-01 06:20:03 +00:00
package net.minecraft.world.level.block.entity;
import com.mojang.logging.LogUtils;
import javax.annotation.Nullable;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.worldgen.features.EndFeatures;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.configurations.EndGatewayConfiguration;
import net.minecraft.world.phys.Vec3;
import org.slf4j.Logger;
public class TheEndGatewayBlockEntity extends TheEndPortalBlockEntity {
private static final Logger LOGGER = LogUtils.getLogger();
private static final int SPAWN_TIME = 200;
private static final int COOLDOWN_TIME = 40;
private static final int ATTENTION_INTERVAL = 2400;
private static final int EVENT_COOLDOWN = 1;
private static final int GATEWAY_HEIGHT_ABOVE_SURFACE = 10;
private static final long DEFAULT_AGE = 0L;
private static final boolean DEFAULT_EXACT_TELEPORT = false;
private long age = 0L;
private int teleportCooldown;
@Nullable
private BlockPos exitPortal;
private boolean exactTeleport = false;
public TheEndGatewayBlockEntity(BlockPos p_155813_, BlockState p_155814_) {
super(BlockEntityType.END_GATEWAY, p_155813_, p_155814_);
}
@Override
protected void saveAdditional(CompoundTag p_187527_, HolderLookup.Provider p_328092_) {
super.saveAdditional(p_187527_, p_328092_);
p_187527_.putLong("Age", this.age);
p_187527_.storeNullable("exit_portal", BlockPos.CODEC, this.exitPortal);
if (this.exactTeleport) {
p_187527_.putBoolean("ExactTeleport", true);
}
}
@Override
protected void loadAdditional(CompoundTag p_328247_, HolderLookup.Provider p_335607_) {
super.loadAdditional(p_328247_, p_335607_);
this.age = p_328247_.getLongOr("Age", 0L);
this.exitPortal = p_328247_.read("exit_portal", BlockPos.CODEC).filter(Level::isInSpawnableBounds).orElse(null);
this.exactTeleport = p_328247_.getBooleanOr("ExactTeleport", false);
}
public static void beamAnimationTick(Level p_155835_, BlockPos p_155836_, BlockState p_155837_, TheEndGatewayBlockEntity p_155838_) {
p_155838_.age++;
if (p_155838_.isCoolingDown()) {
p_155838_.teleportCooldown--;
}
}
public static void portalTick(Level p_344808_, BlockPos p_342267_, BlockState p_344200_, TheEndGatewayBlockEntity p_343419_) {
boolean flag = p_343419_.isSpawning();
boolean flag1 = p_343419_.isCoolingDown();
p_343419_.age++;
if (flag1) {
p_343419_.teleportCooldown--;
} else if (p_343419_.age % 2400L == 0L) {
triggerCooldown(p_344808_, p_342267_, p_344200_, p_343419_);
}
if (flag != p_343419_.isSpawning() || flag1 != p_343419_.isCoolingDown()) {
setChanged(p_344808_, p_342267_, p_344200_);
}
}
public boolean isSpawning() {
return this.age < 200L;
}
public boolean isCoolingDown() {
return this.teleportCooldown > 0;
}
public float getSpawnPercent(float p_59934_) {
return Mth.clamp(((float)this.age + p_59934_) / 200.0F, 0.0F, 1.0F);
}
public float getCooldownPercent(float p_59968_) {
return 1.0F - Mth.clamp((this.teleportCooldown - p_59968_) / 40.0F, 0.0F, 1.0F);
}
public ClientboundBlockEntityDataPacket getUpdatePacket() {
return ClientboundBlockEntityDataPacket.create(this);
}
@Override
public CompoundTag getUpdateTag(HolderLookup.Provider p_332673_) {
return this.saveCustomOnly(p_332673_);
}
public static void triggerCooldown(Level p_155850_, BlockPos p_155851_, BlockState p_155852_, TheEndGatewayBlockEntity p_155853_) {
if (!p_155850_.isClientSide) {
p_155853_.teleportCooldown = 40;
p_155850_.blockEvent(p_155851_, p_155852_.getBlock(), 1, 0);
setChanged(p_155850_, p_155851_, p_155852_);
}
}
@Override
public boolean triggerEvent(int p_59963_, int p_59964_) {
if (p_59963_ == 1) {
this.teleportCooldown = 40;
return true;
} else {
return super.triggerEvent(p_59963_, p_59964_);
}
}
@Nullable
public Vec3 getPortalPosition(ServerLevel p_342945_, BlockPos p_345486_) {
if (this.exitPortal == null && p_342945_.dimension() == Level.END) {
BlockPos blockpos = findOrCreateValidTeleportPos(p_342945_, p_345486_);
blockpos = blockpos.above(10);
LOGGER.debug("Creating portal at {}", blockpos);
spawnGatewayPortal(p_342945_, blockpos, EndGatewayConfiguration.knownExit(p_345486_, false));
this.setExitPosition(blockpos, this.exactTeleport);
}
if (this.exitPortal != null) {
BlockPos blockpos1 = this.exactTeleport ? this.exitPortal : findExitPosition(p_342945_, this.exitPortal);
return blockpos1.getBottomCenter();
} else {
return null;
}
}
private static BlockPos findExitPosition(Level p_155826_, BlockPos p_155827_) {
BlockPos blockpos = findTallestBlock(p_155826_, p_155827_.offset(0, 2, 0), 5, false);
LOGGER.debug("Best exit position for portal at {} is {}", p_155827_, blockpos);
return blockpos.above();
}
private static BlockPos findOrCreateValidTeleportPos(ServerLevel p_155819_, BlockPos p_155820_) {
Vec3 vec3 = findExitPortalXZPosTentative(p_155819_, p_155820_);
LevelChunk levelchunk = getChunk(p_155819_, vec3);
BlockPos blockpos = findValidSpawnInChunk(levelchunk);
if (blockpos == null) {
BlockPos blockpos1 = BlockPos.containing(vec3.x + 0.5, 75.0, vec3.z + 0.5);
LOGGER.debug("Failed to find a suitable block to teleport to, spawning an island on {}", blockpos1);
p_155819_.registryAccess()
.lookup(Registries.CONFIGURED_FEATURE)
.flatMap(p_360496_ -> p_360496_.get(EndFeatures.END_ISLAND))
.ifPresent(
p_256040_ -> p_256040_.value()
.place(p_155819_, p_155819_.getChunkSource().getGenerator(), RandomSource.create(blockpos1.asLong()), blockpos1)
);
blockpos = blockpos1;
} else {
LOGGER.debug("Found suitable block to teleport to: {}", blockpos);
}
return findTallestBlock(p_155819_, blockpos, 16, true);
}
private static Vec3 findExitPortalXZPosTentative(ServerLevel p_155842_, BlockPos p_155843_) {
Vec3 vec3 = new Vec3(p_155843_.getX(), 0.0, p_155843_.getZ()).normalize();
int i = 1024;
Vec3 vec31 = vec3.scale(1024.0);
for (int j = 16; !isChunkEmpty(p_155842_, vec31) && j-- > 0; vec31 = vec31.add(vec3.scale(-16.0))) {
LOGGER.debug("Skipping backwards past nonempty chunk at {}", vec31);
}
for (int k = 16; isChunkEmpty(p_155842_, vec31) && k-- > 0; vec31 = vec31.add(vec3.scale(16.0))) {
LOGGER.debug("Skipping forward past empty chunk at {}", vec31);
}
LOGGER.debug("Found chunk at {}", vec31);
return vec31;
}
private static boolean isChunkEmpty(ServerLevel p_155816_, Vec3 p_155817_) {
return getChunk(p_155816_, p_155817_).getHighestFilledSectionIndex() == -1;
}
private static BlockPos findTallestBlock(BlockGetter p_59943_, BlockPos p_59944_, int p_59945_, boolean p_59946_) {
BlockPos blockpos = null;
for (int i = -p_59945_; i <= p_59945_; i++) {
for (int j = -p_59945_; j <= p_59945_; j++) {
if (i != 0 || j != 0 || p_59946_) {
for (int k = p_59943_.getMaxY(); k > (blockpos == null ? p_59943_.getMinY() : blockpos.getY()); k--) {
BlockPos blockpos1 = new BlockPos(p_59944_.getX() + i, k, p_59944_.getZ() + j);
BlockState blockstate = p_59943_.getBlockState(blockpos1);
if (blockstate.isCollisionShapeFullBlock(p_59943_, blockpos1) && (p_59946_ || !blockstate.is(Blocks.BEDROCK))) {
blockpos = blockpos1;
break;
}
}
}
}
}
return blockpos == null ? p_59944_ : blockpos;
}
private static LevelChunk getChunk(Level p_59948_, Vec3 p_59949_) {
return p_59948_.getChunk(Mth.floor(p_59949_.x / 16.0), Mth.floor(p_59949_.z / 16.0));
}
@Nullable
private static BlockPos findValidSpawnInChunk(LevelChunk p_59954_) {
ChunkPos chunkpos = p_59954_.getPos();
BlockPos blockpos = new BlockPos(chunkpos.getMinBlockX(), 30, chunkpos.getMinBlockZ());
int i = p_59954_.getHighestSectionPosition() + 16 - 1;
BlockPos blockpos1 = new BlockPos(chunkpos.getMaxBlockX(), i, chunkpos.getMaxBlockZ());
BlockPos blockpos2 = null;
double d0 = 0.0;
for (BlockPos blockpos3 : BlockPos.betweenClosed(blockpos, blockpos1)) {
BlockState blockstate = p_59954_.getBlockState(blockpos3);
BlockPos blockpos4 = blockpos3.above();
BlockPos blockpos5 = blockpos3.above(2);
if (blockstate.is(Blocks.END_STONE)
&& !p_59954_.getBlockState(blockpos4).isCollisionShapeFullBlock(p_59954_, blockpos4)
&& !p_59954_.getBlockState(blockpos5).isCollisionShapeFullBlock(p_59954_, blockpos5)) {
double d1 = blockpos3.distToCenterSqr(0.0, 0.0, 0.0);
if (blockpos2 == null || d1 < d0) {
blockpos2 = blockpos3;
d0 = d1;
}
}
}
return blockpos2;
}
private static void spawnGatewayPortal(ServerLevel p_155822_, BlockPos p_155823_, EndGatewayConfiguration p_155824_) {
Feature.END_GATEWAY.place(p_155824_, p_155822_, p_155822_.getChunkSource().getGenerator(), RandomSource.create(), p_155823_);
}
@Override
public boolean shouldRenderFace(Direction p_59959_) {
return Block.shouldRenderFace(this.getBlockState(), this.level.getBlockState(this.getBlockPos().relative(p_59959_)), p_59959_);
}
public int getParticleAmount() {
int i = 0;
for (Direction direction : Direction.values()) {
i += this.shouldRenderFace(direction) ? 1 : 0;
}
return i;
}
public void setExitPosition(BlockPos p_59956_, boolean p_59957_) {
this.exactTeleport = p_59957_;
this.exitPortal = p_59956_;
this.setChanged();
}
}