minecraft-src/net/minecraft/world/level/Level.java
2025-07-04 03:45:38 +03:00

1075 lines
34 KiB
Java

package net.minecraft.world.level;
import com.google.common.collect.Lists;
import com.mojang.serialization.Codec;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import net.minecraft.CrashReport;
import net.minecraft.CrashReportCategory;
import net.minecraft.CrashReportDetail;
import net.minecraft.ReportedException;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.SectionPos;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.protocol.Packet;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.util.StringRepresentable;
import net.minecraft.util.AbortableIterationConsumer.Continuation;
import net.minecraft.util.profiling.Profiler;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.TickRateManager;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.damagesource.DamageSources;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySelector;
import net.minecraft.world.entity.boss.EnderDragonPart;
import net.minecraft.world.entity.boss.enderdragon.EnderDragon;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.alchemy.PotionBrewing;
import net.minecraft.world.item.component.FireworkExplosion;
import net.minecraft.world.item.crafting.RecipeAccess;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.block.BaseFireBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.FuelValues;
import net.minecraft.world.level.block.entity.TickingBlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.border.WorldBorder;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.entity.EntityTypeTest;
import net.minecraft.world.level.entity.LevelEntityGetter;
import net.minecraft.world.level.entity.UUIDLookup;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.gameevent.GameEvent.Context;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.redstone.CollectingNeighborUpdater;
import net.minecraft.world.level.redstone.NeighborUpdater;
import net.minecraft.world.level.redstone.Orientation;
import net.minecraft.world.level.saveddata.maps.MapId;
import net.minecraft.world.level.saveddata.maps.MapItemSavedData;
import net.minecraft.world.level.storage.LevelData;
import net.minecraft.world.level.storage.WritableLevelData;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.scores.Scoreboard;
import org.jetbrains.annotations.Nullable;
public abstract class Level implements LevelAccessor, UUIDLookup<Entity>, AutoCloseable {
public static final Codec<ResourceKey<Level>> RESOURCE_KEY_CODEC = ResourceKey.codec(Registries.DIMENSION);
public static final ResourceKey<Level> OVERWORLD = ResourceKey.create(Registries.DIMENSION, ResourceLocation.withDefaultNamespace("overworld"));
public static final ResourceKey<Level> NETHER = ResourceKey.create(Registries.DIMENSION, ResourceLocation.withDefaultNamespace("the_nether"));
public static final ResourceKey<Level> END = ResourceKey.create(Registries.DIMENSION, ResourceLocation.withDefaultNamespace("the_end"));
public static final int MAX_LEVEL_SIZE = 30000000;
public static final int LONG_PARTICLE_CLIP_RANGE = 512;
public static final int SHORT_PARTICLE_CLIP_RANGE = 32;
public static final int MAX_BRIGHTNESS = 15;
public static final int TICKS_PER_DAY = 24000;
public static final int MAX_ENTITY_SPAWN_Y = 20000000;
public static final int MIN_ENTITY_SPAWN_Y = -20000000;
protected final List<TickingBlockEntity> blockEntityTickers = Lists.<TickingBlockEntity>newArrayList();
protected final NeighborUpdater neighborUpdater;
private final List<TickingBlockEntity> pendingBlockEntityTickers = Lists.<TickingBlockEntity>newArrayList();
private boolean tickingBlockEntities;
private final Thread thread;
private final boolean isDebug;
private int skyDarken;
/**
* Contains the current Linear Congruential Generator seed for block updates. Used with an A value of 3 and a C value of 0x3c6ef35f, producing a highly planar series of values ill-suited for choosing random blocks in a 16x128x16 field.
*/
protected int randValue = RandomSource.create().nextInt();
protected final int addend = 1013904223;
protected float oRainLevel;
protected float rainLevel;
protected float oThunderLevel;
protected float thunderLevel;
public final RandomSource random = RandomSource.create();
@Deprecated
private final RandomSource threadSafeRandom = RandomSource.createThreadSafe();
private final Holder<DimensionType> dimensionTypeRegistration;
protected final WritableLevelData levelData;
public final boolean isClientSide;
private final WorldBorder worldBorder;
private final BiomeManager biomeManager;
private final ResourceKey<Level> dimension;
private final RegistryAccess registryAccess;
private final DamageSources damageSources;
private long subTickCount;
protected Level(
WritableLevelData levelData,
ResourceKey<Level> dimension,
RegistryAccess registryAccess,
Holder<DimensionType> dimensionTypeRegistration,
boolean isClientSide,
boolean isDebug,
long biomeZoomSeed,
int maxChainedNeighborUpdates
) {
this.levelData = levelData;
this.dimensionTypeRegistration = dimensionTypeRegistration;
final DimensionType dimensionType = dimensionTypeRegistration.value();
this.dimension = dimension;
this.isClientSide = isClientSide;
if (dimensionType.coordinateScale() != 1.0) {
this.worldBorder = new WorldBorder() {
@Override
public double getCenterX() {
return super.getCenterX() / dimensionType.coordinateScale();
}
@Override
public double getCenterZ() {
return super.getCenterZ() / dimensionType.coordinateScale();
}
};
} else {
this.worldBorder = new WorldBorder();
}
this.thread = Thread.currentThread();
this.biomeManager = new BiomeManager(this, biomeZoomSeed);
this.isDebug = isDebug;
this.neighborUpdater = new CollectingNeighborUpdater(this, maxChainedNeighborUpdates);
this.registryAccess = registryAccess;
this.damageSources = new DamageSources(registryAccess);
}
@Override
public boolean isClientSide() {
return this.isClientSide;
}
@Nullable
@Override
public MinecraftServer getServer() {
return null;
}
/**
* Check if the given BlockPos has valid coordinates
*/
public boolean isInWorldBounds(BlockPos pos) {
return !this.isOutsideBuildHeight(pos) && isInWorldBoundsHorizontal(pos);
}
public static boolean isInSpawnableBounds(BlockPos pos) {
return !isOutsideSpawnableHeight(pos.getY()) && isInWorldBoundsHorizontal(pos);
}
private static boolean isInWorldBoundsHorizontal(BlockPos pos) {
return pos.getX() >= -30000000 && pos.getZ() >= -30000000 && pos.getX() < 30000000 && pos.getZ() < 30000000;
}
private static boolean isOutsideSpawnableHeight(int y) {
return y < -20000000 || y >= 20000000;
}
public LevelChunk getChunkAt(BlockPos pos) {
return this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()));
}
public LevelChunk getChunk(int chunkX, int chunkZ) {
return (LevelChunk)this.getChunk(chunkX, chunkZ, ChunkStatus.FULL);
}
@Nullable
@Override
public ChunkAccess getChunk(int x, int z, ChunkStatus chunkStatus, boolean requireChunk) {
ChunkAccess chunkAccess = this.getChunkSource().getChunk(x, z, chunkStatus, requireChunk);
if (chunkAccess == null && requireChunk) {
throw new IllegalStateException("Should always be able to create a chunk!");
} else {
return chunkAccess;
}
}
@Override
public boolean setBlock(BlockPos pos, BlockState newState, int flags) {
return this.setBlock(pos, newState, flags, 512);
}
@Override
public boolean setBlock(BlockPos pos, BlockState state, int flags, int recursionLeft) {
if (this.isOutsideBuildHeight(pos)) {
return false;
} else if (!this.isClientSide && this.isDebug()) {
return false;
} else {
LevelChunk levelChunk = this.getChunkAt(pos);
Block block = state.getBlock();
BlockState blockState = levelChunk.setBlockState(pos, state, flags);
if (blockState == null) {
return false;
} else {
BlockState blockState2 = this.getBlockState(pos);
if (blockState2 == state) {
if (blockState != blockState2) {
this.setBlocksDirty(pos, blockState, blockState2);
}
if ((flags & 2) != 0
&& (!this.isClientSide || (flags & 4) == 0)
&& (this.isClientSide || levelChunk.getFullStatus() != null && levelChunk.getFullStatus().isOrAfter(FullChunkStatus.BLOCK_TICKING))) {
this.sendBlockUpdated(pos, blockState, state, flags);
}
if ((flags & 1) != 0) {
this.updateNeighborsAt(pos, blockState.getBlock());
if (!this.isClientSide && state.hasAnalogOutputSignal()) {
this.updateNeighbourForOutputSignal(pos, block);
}
}
if ((flags & 16) == 0 && recursionLeft > 0) {
int i = flags & -34;
blockState.updateIndirectNeighbourShapes(this, pos, i, recursionLeft - 1);
state.updateNeighbourShapes(this, pos, i, recursionLeft - 1);
state.updateIndirectNeighbourShapes(this, pos, i, recursionLeft - 1);
}
this.updatePOIOnBlockStateChange(pos, blockState, blockState2);
}
return true;
}
}
}
public void updatePOIOnBlockStateChange(BlockPos pos, BlockState oldState, BlockState newState) {
}
@Override
public boolean removeBlock(BlockPos pos, boolean isMoving) {
FluidState fluidState = this.getFluidState(pos);
return this.setBlock(pos, fluidState.createLegacyBlock(), 3 | (isMoving ? 64 : 0));
}
@Override
public boolean destroyBlock(BlockPos pos, boolean dropBlock, @Nullable Entity entity, int recursionLeft) {
BlockState blockState = this.getBlockState(pos);
if (blockState.isAir()) {
return false;
} else {
FluidState fluidState = this.getFluidState(pos);
if (!(blockState.getBlock() instanceof BaseFireBlock)) {
this.levelEvent(2001, pos, Block.getId(blockState));
}
if (dropBlock) {
BlockEntity blockEntity = blockState.hasBlockEntity() ? this.getBlockEntity(pos) : null;
Block.dropResources(blockState, this, pos, blockEntity, entity, ItemStack.EMPTY);
}
boolean bl = this.setBlock(pos, fluidState.createLegacyBlock(), 3, recursionLeft);
if (bl) {
this.gameEvent(GameEvent.BLOCK_DESTROY, pos, Context.of(entity, blockState));
}
return bl;
}
}
public void addDestroyBlockEffect(BlockPos pos, BlockState state) {
}
/**
* Convenience method to update the block on both the client and server
*/
public boolean setBlockAndUpdate(BlockPos pos, BlockState state) {
return this.setBlock(pos, state, 3);
}
/**
* Flags are as in setBlockState
*/
public abstract void sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags);
public void setBlocksDirty(BlockPos blockPos, BlockState oldState, BlockState newState) {
}
public void updateNeighborsAt(BlockPos pos, Block block, @Nullable Orientation orientation) {
}
public void updateNeighborsAtExceptFromFacing(BlockPos pos, Block block, Direction facing, @Nullable Orientation orientation) {
}
public void neighborChanged(BlockPos pos, Block block, @Nullable Orientation orientation) {
}
public void neighborChanged(BlockState state, BlockPos pos, Block block, @Nullable Orientation orientation, boolean movedByPiston) {
}
@Override
public void neighborShapeChanged(Direction direction, BlockPos pos, BlockPos neighborPos, BlockState neighborState, int flags, int recursionLeft) {
this.neighborUpdater.shapeUpdate(direction, neighborState, pos, neighborPos, flags, recursionLeft);
}
@Override
public int getHeight(Heightmap.Types heightmapType, int x, int z) {
int i;
if (x >= -30000000 && z >= -30000000 && x < 30000000 && z < 30000000) {
if (this.hasChunk(SectionPos.blockToSectionCoord(x), SectionPos.blockToSectionCoord(z))) {
i = this.getChunk(SectionPos.blockToSectionCoord(x), SectionPos.blockToSectionCoord(z)).getHeight(heightmapType, x & 15, z & 15) + 1;
} else {
i = this.getMinY();
}
} else {
i = this.getSeaLevel() + 1;
}
return i;
}
@Override
public LevelLightEngine getLightEngine() {
return this.getChunkSource().getLightEngine();
}
@Override
public BlockState getBlockState(BlockPos pos) {
if (this.isOutsideBuildHeight(pos)) {
return Blocks.VOID_AIR.defaultBlockState();
} else {
LevelChunk levelChunk = this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()));
return levelChunk.getBlockState(pos);
}
}
@Override
public FluidState getFluidState(BlockPos pos) {
if (this.isOutsideBuildHeight(pos)) {
return Fluids.EMPTY.defaultFluidState();
} else {
LevelChunk levelChunk = this.getChunkAt(pos);
return levelChunk.getFluidState(pos);
}
}
public boolean isBrightOutside() {
return !this.dimensionType().hasFixedTime() && this.skyDarken < 4;
}
public boolean isDarkOutside() {
return !this.dimensionType().hasFixedTime() && !this.isBrightOutside();
}
public boolean isMoonVisible() {
if (!this.dimensionType().natural()) {
return false;
} else {
int i = (int)(this.getDayTime() % 24000L);
return i >= 12600 && i <= 23400;
}
}
@Override
public void playSound(@Nullable Entity entity, BlockPos pos, SoundEvent sound, SoundSource source, float volume, float pitch) {
this.playSound(entity, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, sound, source, volume, pitch);
}
public abstract void playSeededSound(
@Nullable Entity entity, double x, double y, double z, Holder<SoundEvent> sound, SoundSource source, float volume, float pitch, long seed
);
public void playSeededSound(@Nullable Entity entity, double x, double y, double z, SoundEvent sound, SoundSource source, float volume, float pitch, long seed) {
this.playSeededSound(entity, x, y, z, BuiltInRegistries.SOUND_EVENT.wrapAsHolder(sound), source, volume, pitch, seed);
}
public abstract void playSeededSound(
@Nullable Entity entity, Entity sourceEntity, Holder<SoundEvent> sound, SoundSource source, float volume, float pitch, long seed
);
public void playSound(@Nullable Entity entity, double x, double y, double z, SoundEvent sound, SoundSource source) {
this.playSound(entity, x, y, z, sound, source, 1.0F, 1.0F);
}
public void playSound(@Nullable Entity entity, double x, double y, double z, SoundEvent sound, SoundSource source, float volume, float pitch) {
this.playSeededSound(entity, x, y, z, sound, source, volume, pitch, this.threadSafeRandom.nextLong());
}
public void playSound(@Nullable Entity entity, double x, double y, double z, Holder<SoundEvent> sound, SoundSource source, float volume, float pitch) {
this.playSeededSound(entity, x, y, z, sound, source, volume, pitch, this.threadSafeRandom.nextLong());
}
public void playSound(@Nullable Entity entity, Entity sourceEntity, SoundEvent sound, SoundSource source, float volume, float pitch) {
this.playSeededSound(entity, sourceEntity, BuiltInRegistries.SOUND_EVENT.wrapAsHolder(sound), source, volume, pitch, this.threadSafeRandom.nextLong());
}
public void playLocalSound(BlockPos pos, SoundEvent sound, SoundSource source, float volume, float pitch, boolean distanceDelay) {
this.playLocalSound(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, sound, source, volume, pitch, distanceDelay);
}
public void playLocalSound(Entity entity, SoundEvent sound, SoundSource source, float volume, float pitch) {
}
public void playLocalSound(double x, double y, double z, SoundEvent sound, SoundSource source, float volume, float pitch, boolean distanceDelay) {
}
public void playPlayerSound(SoundEvent sound, SoundSource source, float volume, float pitch) {
}
@Override
public void addParticle(ParticleOptions particle, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) {
}
public void addParticle(
ParticleOptions particle, boolean overrideLimiter, boolean alwaysShow, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed
) {
}
public void addAlwaysVisibleParticle(ParticleOptions particle, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) {
}
public void addAlwaysVisibleParticle(ParticleOptions particle, boolean ignoreRange, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed) {
}
/**
* Return getCelestialAngle()*2*PI
*/
public float getSunAngle(float partialTick) {
float f = this.getTimeOfDay(partialTick);
return f * (float) (Math.PI * 2);
}
public void addBlockEntityTicker(TickingBlockEntity ticker) {
(this.tickingBlockEntities ? this.pendingBlockEntityTickers : this.blockEntityTickers).add(ticker);
}
protected void tickBlockEntities() {
ProfilerFiller profilerFiller = Profiler.get();
profilerFiller.push("blockEntities");
this.tickingBlockEntities = true;
if (!this.pendingBlockEntityTickers.isEmpty()) {
this.blockEntityTickers.addAll(this.pendingBlockEntityTickers);
this.pendingBlockEntityTickers.clear();
}
Iterator<TickingBlockEntity> iterator = this.blockEntityTickers.iterator();
boolean bl = this.tickRateManager().runsNormally();
while (iterator.hasNext()) {
TickingBlockEntity tickingBlockEntity = (TickingBlockEntity)iterator.next();
if (tickingBlockEntity.isRemoved()) {
iterator.remove();
} else if (bl && this.shouldTickBlocksAt(tickingBlockEntity.getPos())) {
tickingBlockEntity.tick();
}
}
this.tickingBlockEntities = false;
profilerFiller.pop();
}
public <T extends Entity> void guardEntityTick(Consumer<T> consumerEntity, T entity) {
try {
consumerEntity.accept(entity);
} catch (Throwable var6) {
CrashReport crashReport = CrashReport.forThrowable(var6, "Ticking entity");
CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being ticked");
entity.fillCrashReportCategory(crashReportCategory);
throw new ReportedException(crashReport);
}
}
public boolean shouldTickDeath(Entity entity) {
return true;
}
public boolean shouldTickBlocksAt(long chunkPos) {
return true;
}
public boolean shouldTickBlocksAt(BlockPos pos) {
return this.shouldTickBlocksAt(ChunkPos.asLong(pos));
}
public void explode(@Nullable Entity source, double x, double y, double z, float radius, Level.ExplosionInteraction explosionInteraction) {
this.explode(
source,
Explosion.getDefaultDamageSource(this, source),
null,
x,
y,
z,
radius,
false,
explosionInteraction,
ParticleTypes.EXPLOSION,
ParticleTypes.EXPLOSION_EMITTER,
SoundEvents.GENERIC_EXPLODE
);
}
public void explode(@Nullable Entity source, double x, double y, double z, float radius, boolean fire, Level.ExplosionInteraction explosionInteraction) {
this.explode(
source,
Explosion.getDefaultDamageSource(this, source),
null,
x,
y,
z,
radius,
fire,
explosionInteraction,
ParticleTypes.EXPLOSION,
ParticleTypes.EXPLOSION_EMITTER,
SoundEvents.GENERIC_EXPLODE
);
}
public void explode(
@Nullable Entity source,
@Nullable DamageSource damageSource,
@Nullable ExplosionDamageCalculator damageCalculator,
Vec3 pos,
float radius,
boolean fire,
Level.ExplosionInteraction explosionInteraction
) {
this.explode(
source,
damageSource,
damageCalculator,
pos.x(),
pos.y(),
pos.z(),
radius,
fire,
explosionInteraction,
ParticleTypes.EXPLOSION,
ParticleTypes.EXPLOSION_EMITTER,
SoundEvents.GENERIC_EXPLODE
);
}
public void explode(
@Nullable Entity source,
@Nullable DamageSource damageSource,
@Nullable ExplosionDamageCalculator damageCalculator,
double x,
double y,
double z,
float radius,
boolean fire,
Level.ExplosionInteraction explosionInteraction
) {
this.explode(
source,
damageSource,
damageCalculator,
x,
y,
z,
radius,
fire,
explosionInteraction,
ParticleTypes.EXPLOSION,
ParticleTypes.EXPLOSION_EMITTER,
SoundEvents.GENERIC_EXPLODE
);
}
public abstract void explode(
@Nullable Entity source,
@Nullable DamageSource damageSource,
@Nullable ExplosionDamageCalculator damageCalculator,
double x,
double y,
double z,
float radius,
boolean fire,
Level.ExplosionInteraction explosionInteraction,
ParticleOptions smallExplosionParticles,
ParticleOptions largeExplosionParticles,
Holder<SoundEvent> explosionSound
);
/**
* Returns the name of the current chunk provider, by calling chunkprovider.makeString()
*/
public abstract String gatherChunkSourceStats();
@Nullable
@Override
public BlockEntity getBlockEntity(BlockPos pos) {
if (this.isOutsideBuildHeight(pos)) {
return null;
} else {
return !this.isClientSide && Thread.currentThread() != this.thread
? null
: this.getChunkAt(pos).getBlockEntity(pos, LevelChunk.EntityCreationType.IMMEDIATE);
}
}
public void setBlockEntity(BlockEntity blockEntity) {
BlockPos blockPos = blockEntity.getBlockPos();
if (!this.isOutsideBuildHeight(blockPos)) {
this.getChunkAt(blockPos).addAndRegisterBlockEntity(blockEntity);
}
}
public void removeBlockEntity(BlockPos pos) {
if (!this.isOutsideBuildHeight(pos)) {
this.getChunkAt(pos).removeBlockEntity(pos);
}
}
public boolean isLoaded(BlockPos pos) {
return this.isOutsideBuildHeight(pos)
? false
: this.getChunkSource().hasChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()));
}
public boolean loadedAndEntityCanStandOnFace(BlockPos pos, Entity entity, Direction direction) {
if (this.isOutsideBuildHeight(pos)) {
return false;
} else {
ChunkAccess chunkAccess = this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()), ChunkStatus.FULL, false);
return chunkAccess == null ? false : chunkAccess.getBlockState(pos).entityCanStandOnFace(this, pos, entity, direction);
}
}
public boolean loadedAndEntityCanStandOn(BlockPos pos, Entity entity) {
return this.loadedAndEntityCanStandOnFace(pos, entity, Direction.UP);
}
/**
* Called on the construction of the {@code Level} class to set up the initial skylight values.
*/
public void updateSkyBrightness() {
double d = 1.0 - this.getRainLevel(1.0F) * 5.0F / 16.0;
double e = 1.0 - this.getThunderLevel(1.0F) * 5.0F / 16.0;
double f = 0.5 + 2.0 * Mth.clamp((double)Mth.cos(this.getTimeOfDay(1.0F) * (float) (Math.PI * 2)), -0.25, 0.25);
this.skyDarken = (int)((1.0 - f * d * e) * 11.0);
}
public void setSpawnSettings(boolean spawnSettings) {
this.getChunkSource().setSpawnSettings(spawnSettings);
}
public BlockPos getSharedSpawnPos() {
BlockPos blockPos = this.levelData.getSpawnPos();
if (!this.getWorldBorder().isWithinBounds(blockPos)) {
blockPos = this.getHeightmapPos(
Heightmap.Types.MOTION_BLOCKING, BlockPos.containing(this.getWorldBorder().getCenterX(), 0.0, this.getWorldBorder().getCenterZ())
);
}
return blockPos;
}
public float getSharedSpawnAngle() {
return this.levelData.getSpawnAngle();
}
/**
* Called from World constructor to set rainingStrength and thunderingStrength
*/
protected void prepareWeather() {
if (this.levelData.isRaining()) {
this.rainLevel = 1.0F;
if (this.levelData.isThundering()) {
this.thunderLevel = 1.0F;
}
}
}
public void close() throws IOException {
this.getChunkSource().close();
}
@Nullable
@Override
public BlockGetter getChunkForCollisions(int chunkX, int chunkZ) {
return this.getChunk(chunkX, chunkZ, ChunkStatus.FULL, false);
}
@Override
public List<Entity> getEntities(@Nullable Entity entity, AABB area, Predicate<? super Entity> predicate) {
Profiler.get().incrementCounter("getEntities");
List<Entity> list = Lists.<Entity>newArrayList();
this.getEntities().get(area, entity2 -> {
if (entity2 != entity && predicate.test(entity2)) {
list.add(entity2);
}
});
for (EnderDragonPart enderDragonPart : this.dragonParts()) {
if (enderDragonPart != entity && enderDragonPart.parentMob != entity && predicate.test(enderDragonPart) && area.intersects(enderDragonPart.getBoundingBox())
)
{
list.add(enderDragonPart);
}
}
return list;
}
@Override
public <T extends Entity> List<T> getEntities(EntityTypeTest<Entity, T> entityTypeTest, AABB bounds, Predicate<? super T> predicate) {
List<T> list = Lists.<T>newArrayList();
this.getEntities(entityTypeTest, bounds, predicate, list);
return list;
}
public <T extends Entity> void getEntities(EntityTypeTest<Entity, T> entityTypeTest, AABB bounds, Predicate<? super T> predicate, List<? super T> output) {
this.getEntities(entityTypeTest, bounds, predicate, output, Integer.MAX_VALUE);
}
public <T extends Entity> void getEntities(
EntityTypeTest<Entity, T> entityTypeTest, AABB bounds, Predicate<? super T> predicate, List<? super T> output, int maxResults
) {
Profiler.get().incrementCounter("getEntities");
this.getEntities().get(entityTypeTest, bounds, entity -> {
if (predicate.test(entity)) {
output.add(entity);
if (output.size() >= maxResults) {
return Continuation.ABORT;
}
}
if (entity instanceof EnderDragon enderDragon) {
for (EnderDragonPart enderDragonPart : enderDragon.getSubEntities()) {
T entity2 = entityTypeTest.tryCast(enderDragonPart);
if (entity2 != null && predicate.test(entity2)) {
output.add(entity2);
if (output.size() >= maxResults) {
return Continuation.ABORT;
}
}
}
}
return Continuation.CONTINUE;
});
}
public List<Entity> getPushableEntities(Entity entity, AABB boundingBox) {
return this.getEntities(entity, boundingBox, EntitySelector.pushableBy(entity));
}
/**
* Returns the Entity with the given ID, or null if it doesn't exist in this Level.
*/
@Nullable
public abstract Entity getEntity(int id);
@Nullable
public Entity getEntity(UUID uUID) {
return this.getEntities().get(uUID);
}
public abstract Collection<EnderDragonPart> dragonParts();
public void blockEntityChanged(BlockPos pos) {
if (this.hasChunkAt(pos)) {
this.getChunkAt(pos).markUnsaved();
}
}
/**
* If on MP, sends a quitting packet.
*/
public void disconnect() {
}
public long getGameTime() {
return this.levelData.getGameTime();
}
public long getDayTime() {
return this.levelData.getDayTime();
}
public boolean mayInteract(Entity entity, BlockPos pos) {
return true;
}
/**
* Sends a {@link net.minecraft.network.protocol.game.ClientboundEntityEventPacket} to all tracked players of that entity.
*/
public void broadcastEntityEvent(Entity entity, byte state) {
}
public void broadcastDamageEvent(Entity entity, DamageSource damageSource) {
}
public void blockEvent(BlockPos pos, Block block, int eventID, int eventParam) {
this.getBlockState(pos).triggerEvent(this, pos, eventID, eventParam);
}
@Override
public LevelData getLevelData() {
return this.levelData;
}
public abstract TickRateManager tickRateManager();
public float getThunderLevel(float partialTick) {
return Mth.lerp(partialTick, this.oThunderLevel, this.thunderLevel) * this.getRainLevel(partialTick);
}
/**
* Sets the strength of the thunder.
*/
public void setThunderLevel(float strength) {
float f = Mth.clamp(strength, 0.0F, 1.0F);
this.oThunderLevel = f;
this.thunderLevel = f;
}
/**
* Returns rain strength.
*/
public float getRainLevel(float partialTick) {
return Mth.lerp(partialTick, this.oRainLevel, this.rainLevel);
}
/**
* Sets the strength of the rain.
*/
public void setRainLevel(float strength) {
float f = Mth.clamp(strength, 0.0F, 1.0F);
this.oRainLevel = f;
this.rainLevel = f;
}
private boolean canHaveWeather() {
return this.dimensionType().hasSkyLight() && !this.dimensionType().hasCeiling();
}
/**
* Returns {@code true} if the current thunder strength (weighted with the rain strength) is greater than 0.9
*/
public boolean isThundering() {
return this.canHaveWeather() && this.getThunderLevel(1.0F) > 0.9;
}
/**
* Returns {@code true} if the current rain strength is greater than 0.2
*/
public boolean isRaining() {
return this.canHaveWeather() && this.getRainLevel(1.0F) > 0.2;
}
/**
* Check if precipitation is currently happening at a position
*/
public boolean isRainingAt(BlockPos pos) {
if (!this.isRaining()) {
return false;
} else if (!this.canSeeSky(pos)) {
return false;
} else if (this.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, pos).getY() > pos.getY()) {
return false;
} else {
Biome biome = this.getBiome(pos).value();
return biome.getPrecipitationAt(pos, this.getSeaLevel()) == Biome.Precipitation.RAIN;
}
}
@Nullable
public abstract MapItemSavedData getMapData(MapId mapId);
public void globalLevelEvent(int id, BlockPos pos, int data) {
}
/**
* Adds some basic stats of the world to the given crash report.
*/
public CrashReportCategory fillReportDetails(CrashReport report) {
CrashReportCategory crashReportCategory = report.addCategory("Affected level", 1);
crashReportCategory.setDetail("All players", (CrashReportDetail<String>)(() -> {
List<? extends Player> list = this.players();
return list.size() + " total; " + (String)list.stream().map(Player::debugInfo).collect(Collectors.joining(", "));
}));
crashReportCategory.setDetail("Chunk stats", this.getChunkSource()::gatherStats);
crashReportCategory.setDetail("Level dimension", (CrashReportDetail<String>)(() -> this.dimension().location().toString()));
try {
this.levelData.fillCrashReportCategory(crashReportCategory, this);
} catch (Throwable var4) {
crashReportCategory.setDetailError("Level Data Unobtainable", var4);
}
return crashReportCategory;
}
public abstract void destroyBlockProgress(int breakerId, BlockPos pos, int progress);
public void createFireworks(double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, List<FireworkExplosion> explosions) {
}
public abstract Scoreboard getScoreboard();
public void updateNeighbourForOutputSignal(BlockPos pos, Block block) {
for (Direction direction : Direction.Plane.HORIZONTAL) {
BlockPos blockPos = pos.relative(direction);
if (this.hasChunkAt(blockPos)) {
BlockState blockState = this.getBlockState(blockPos);
if (blockState.is(Blocks.COMPARATOR)) {
this.neighborChanged(blockState, blockPos, block, null, false);
} else if (blockState.isRedstoneConductor(this, blockPos)) {
blockPos = blockPos.relative(direction);
blockState = this.getBlockState(blockPos);
if (blockState.is(Blocks.COMPARATOR)) {
this.neighborChanged(blockState, blockPos, block, null, false);
}
}
}
}
}
@Override
public DifficultyInstance getCurrentDifficultyAt(BlockPos pos) {
long l = 0L;
float f = 0.0F;
if (this.hasChunkAt(pos)) {
f = this.getMoonBrightness();
l = this.getChunkAt(pos).getInhabitedTime();
}
return new DifficultyInstance(this.getDifficulty(), this.getDayTime(), l, f);
}
@Override
public int getSkyDarken() {
return this.skyDarken;
}
public void setSkyFlashTime(int timeFlash) {
}
@Override
public WorldBorder getWorldBorder() {
return this.worldBorder;
}
public void sendPacketToServer(Packet<?> packet) {
throw new UnsupportedOperationException("Can't send packets to server unless you're on the client.");
}
@Override
public DimensionType dimensionType() {
return this.dimensionTypeRegistration.value();
}
public Holder<DimensionType> dimensionTypeRegistration() {
return this.dimensionTypeRegistration;
}
public ResourceKey<Level> dimension() {
return this.dimension;
}
@Override
public RandomSource getRandom() {
return this.random;
}
@Override
public boolean isStateAtPosition(BlockPos pos, Predicate<BlockState> state) {
return state.test(this.getBlockState(pos));
}
@Override
public boolean isFluidAtPosition(BlockPos pos, Predicate<FluidState> predicate) {
return predicate.test(this.getFluidState(pos));
}
public abstract RecipeAccess recipeAccess();
public BlockPos getBlockRandomPos(int x, int y, int z, int yMask) {
this.randValue = this.randValue * 3 + 1013904223;
int i = this.randValue >> 2;
return new BlockPos(x + (i & 15), y + (i >> 16 & yMask), z + (i >> 8 & 15));
}
public boolean noSave() {
return false;
}
@Override
public BiomeManager getBiomeManager() {
return this.biomeManager;
}
public final boolean isDebug() {
return this.isDebug;
}
protected abstract LevelEntityGetter<Entity> getEntities();
@Override
public long nextSubTickCount() {
return this.subTickCount++;
}
@Override
public RegistryAccess registryAccess() {
return this.registryAccess;
}
public DamageSources damageSources() {
return this.damageSources;
}
public abstract PotionBrewing potionBrewing();
public abstract FuelValues fuelValues();
public int getClientLeafTintColor(BlockPos pos) {
return 0;
}
public static enum ExplosionInteraction implements StringRepresentable {
NONE("none"),
BLOCK("block"),
MOB("mob"),
TNT("tnt"),
TRIGGER("trigger");
public static final Codec<Level.ExplosionInteraction> CODEC = StringRepresentable.fromEnum(Level.ExplosionInteraction::values);
private final String id;
private ExplosionInteraction(final String id) {
this.id = id;
}
@Override
public String getSerializedName() {
return this.id;
}
}
}