475 lines
15 KiB
Java
475 lines
15 KiB
Java
package net.minecraft.server.level;
|
|
|
|
import com.mojang.logging.LogUtils;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.concurrent.atomic.AtomicLong;
|
|
import java.util.function.Predicate;
|
|
import java.util.function.Supplier;
|
|
import net.minecraft.CrashReport;
|
|
import net.minecraft.CrashReportCategory;
|
|
import net.minecraft.CrashReportDetail;
|
|
import net.minecraft.ReportedException;
|
|
import net.minecraft.Util;
|
|
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.nbt.CompoundTag;
|
|
import net.minecraft.resources.ResourceLocation;
|
|
import net.minecraft.server.MinecraftServer;
|
|
import net.minecraft.sounds.SoundEvent;
|
|
import net.minecraft.sounds.SoundSource;
|
|
import net.minecraft.util.RandomSource;
|
|
import net.minecraft.util.StaticCache2D;
|
|
import net.minecraft.world.DifficultyInstance;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.flag.FeatureFlagSet;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import net.minecraft.world.level.LevelHeightAccessor;
|
|
import net.minecraft.world.level.WorldGenLevel;
|
|
import net.minecraft.world.level.biome.Biome;
|
|
import net.minecraft.world.level.biome.BiomeManager;
|
|
import net.minecraft.world.level.block.Block;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.EntityBlock;
|
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
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.ChunkSource;
|
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|
import net.minecraft.world.level.chunk.status.ChunkStep;
|
|
import net.minecraft.world.level.chunk.status.ChunkType;
|
|
import net.minecraft.world.level.dimension.DimensionType;
|
|
import net.minecraft.world.level.entity.EntityTypeTest;
|
|
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.Fluid;
|
|
import net.minecraft.world.level.material.FluidState;
|
|
import net.minecraft.world.level.storage.LevelData;
|
|
import net.minecraft.world.phys.AABB;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import net.minecraft.world.ticks.LevelTickAccess;
|
|
import net.minecraft.world.ticks.WorldGenTickAccess;
|
|
import org.jetbrains.annotations.Nullable;
|
|
import org.slf4j.Logger;
|
|
|
|
public class WorldGenRegion implements WorldGenLevel {
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
private final StaticCache2D<GenerationChunkHolder> cache;
|
|
private final ChunkAccess center;
|
|
private final ServerLevel level;
|
|
private final long seed;
|
|
private final LevelData levelData;
|
|
private final RandomSource random;
|
|
private final DimensionType dimensionType;
|
|
private final WorldGenTickAccess<Block> blockTicks = new WorldGenTickAccess<>(blockPos -> this.getChunk(blockPos).getBlockTicks());
|
|
private final WorldGenTickAccess<Fluid> fluidTicks = new WorldGenTickAccess<>(blockPos -> this.getChunk(blockPos).getFluidTicks());
|
|
private final BiomeManager biomeManager;
|
|
private final ChunkStep generatingStep;
|
|
@Nullable
|
|
private Supplier<String> currentlyGenerating;
|
|
private final AtomicLong subTickCount = new AtomicLong();
|
|
private static final ResourceLocation WORLDGEN_REGION_RANDOM = ResourceLocation.withDefaultNamespace("worldgen_region_random");
|
|
|
|
public WorldGenRegion(ServerLevel level, StaticCache2D<GenerationChunkHolder> cache, ChunkStep generatingStep, ChunkAccess center) {
|
|
this.generatingStep = generatingStep;
|
|
this.cache = cache;
|
|
this.center = center;
|
|
this.level = level;
|
|
this.seed = level.getSeed();
|
|
this.levelData = level.getLevelData();
|
|
this.random = level.getChunkSource().randomState().getOrCreateRandomFactory(WORLDGEN_REGION_RANDOM).at(this.center.getPos().getWorldPosition());
|
|
this.dimensionType = level.dimensionType();
|
|
this.biomeManager = new BiomeManager(this, BiomeManager.obfuscateSeed(this.seed));
|
|
}
|
|
|
|
public boolean isOldChunkAround(ChunkPos pos, int radius) {
|
|
return this.level.getChunkSource().chunkMap.isOldChunkAround(pos, radius);
|
|
}
|
|
|
|
public ChunkPos getCenter() {
|
|
return this.center.getPos();
|
|
}
|
|
|
|
@Override
|
|
public void setCurrentlyGenerating(@Nullable Supplier<String> currentlyGenerating) {
|
|
this.currentlyGenerating = currentlyGenerating;
|
|
}
|
|
|
|
@Override
|
|
public ChunkAccess getChunk(int chunkX, int chunkZ) {
|
|
return this.getChunk(chunkX, chunkZ, ChunkStatus.EMPTY);
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public ChunkAccess getChunk(int x, int z, ChunkStatus chunkStatus, boolean requireChunk) {
|
|
int i = this.center.getPos().getChessboardDistance(x, z);
|
|
ChunkStatus chunkStatus2 = i >= this.generatingStep.directDependencies().size() ? null : this.generatingStep.directDependencies().get(i);
|
|
GenerationChunkHolder generationChunkHolder;
|
|
if (chunkStatus2 != null) {
|
|
generationChunkHolder = this.cache.get(x, z);
|
|
if (chunkStatus.isOrBefore(chunkStatus2)) {
|
|
ChunkAccess chunkAccess = generationChunkHolder.getChunkIfPresentUnchecked(chunkStatus2);
|
|
if (chunkAccess != null) {
|
|
return chunkAccess;
|
|
}
|
|
}
|
|
} else {
|
|
generationChunkHolder = null;
|
|
}
|
|
|
|
CrashReport crashReport = CrashReport.forThrowable(
|
|
new IllegalStateException("Requested chunk unavailable during world generation"), "Exception generating new chunk"
|
|
);
|
|
CrashReportCategory crashReportCategory = crashReport.addCategory("Chunk request details");
|
|
crashReportCategory.setDetail("Requested chunk", String.format(Locale.ROOT, "%d, %d", x, z));
|
|
crashReportCategory.setDetail("Generating status", (CrashReportDetail<String>)(() -> this.generatingStep.targetStatus().getName()));
|
|
crashReportCategory.setDetail("Requested status", chunkStatus::getName);
|
|
crashReportCategory.setDetail(
|
|
"Actual status",
|
|
(CrashReportDetail<String>)(() -> generationChunkHolder == null ? "[out of cache bounds]" : generationChunkHolder.getPersistedStatus().getName())
|
|
);
|
|
crashReportCategory.setDetail("Maximum allowed status", (CrashReportDetail<String>)(() -> chunkStatus2 == null ? "null" : chunkStatus2.getName()));
|
|
crashReportCategory.setDetail("Dependencies", this.generatingStep.directDependencies()::toString);
|
|
crashReportCategory.setDetail("Requested distance", i);
|
|
crashReportCategory.setDetail("Generating chunk", this.center.getPos()::toString);
|
|
throw new ReportedException(crashReport);
|
|
}
|
|
|
|
@Override
|
|
public boolean hasChunk(int chunkX, int chunkZ) {
|
|
int i = this.center.getPos().getChessboardDistance(chunkX, chunkZ);
|
|
return i < this.generatingStep.directDependencies().size();
|
|
}
|
|
|
|
@Override
|
|
public BlockState getBlockState(BlockPos pos) {
|
|
return this.getChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ())).getBlockState(pos);
|
|
}
|
|
|
|
@Override
|
|
public FluidState getFluidState(BlockPos pos) {
|
|
return this.getChunk(pos).getFluidState(pos);
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public Player getNearestPlayer(double x, double y, double z, double distance, Predicate<Entity> predicate) {
|
|
return null;
|
|
}
|
|
|
|
@Override
|
|
public int getSkyDarken() {
|
|
return 0;
|
|
}
|
|
|
|
@Override
|
|
public BiomeManager getBiomeManager() {
|
|
return this.biomeManager;
|
|
}
|
|
|
|
@Override
|
|
public Holder<Biome> getUncachedNoiseBiome(int x, int y, int z) {
|
|
return this.level.getUncachedNoiseBiome(x, y, z);
|
|
}
|
|
|
|
@Override
|
|
public float getShade(Direction direction, boolean shade) {
|
|
return 1.0F;
|
|
}
|
|
|
|
@Override
|
|
public LevelLightEngine getLightEngine() {
|
|
return this.level.getLightEngine();
|
|
}
|
|
|
|
@Override
|
|
public boolean destroyBlock(BlockPos pos, boolean dropBlock, @Nullable Entity entity, int recursionLeft) {
|
|
BlockState blockState = this.getBlockState(pos);
|
|
if (blockState.isAir()) {
|
|
return false;
|
|
} else {
|
|
if (dropBlock) {
|
|
BlockEntity blockEntity = blockState.hasBlockEntity() ? this.getBlockEntity(pos) : null;
|
|
Block.dropResources(blockState, this.level, pos, blockEntity, entity, ItemStack.EMPTY);
|
|
}
|
|
|
|
return this.setBlock(pos, Blocks.AIR.defaultBlockState(), 3, recursionLeft);
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public BlockEntity getBlockEntity(BlockPos pos) {
|
|
ChunkAccess chunkAccess = this.getChunk(pos);
|
|
BlockEntity blockEntity = chunkAccess.getBlockEntity(pos);
|
|
if (blockEntity != null) {
|
|
return blockEntity;
|
|
} else {
|
|
CompoundTag compoundTag = chunkAccess.getBlockEntityNbt(pos);
|
|
BlockState blockState = chunkAccess.getBlockState(pos);
|
|
if (compoundTag != null) {
|
|
if ("DUMMY".equals(compoundTag.getStringOr("id", ""))) {
|
|
if (!blockState.hasBlockEntity()) {
|
|
return null;
|
|
}
|
|
|
|
blockEntity = ((EntityBlock)blockState.getBlock()).newBlockEntity(pos, blockState);
|
|
} else {
|
|
blockEntity = BlockEntity.loadStatic(pos, blockState, compoundTag, this.level.registryAccess());
|
|
}
|
|
|
|
if (blockEntity != null) {
|
|
chunkAccess.setBlockEntity(blockEntity);
|
|
return blockEntity;
|
|
}
|
|
}
|
|
|
|
if (blockState.hasBlockEntity()) {
|
|
LOGGER.warn("Tried to access a block entity before it was created. {}", pos);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean ensureCanWrite(BlockPos pos) {
|
|
int i = SectionPos.blockToSectionCoord(pos.getX());
|
|
int j = SectionPos.blockToSectionCoord(pos.getZ());
|
|
ChunkPos chunkPos = this.getCenter();
|
|
int k = Math.abs(chunkPos.x - i);
|
|
int l = Math.abs(chunkPos.z - j);
|
|
if (k <= this.generatingStep.blockStateWriteRadius() && l <= this.generatingStep.blockStateWriteRadius()) {
|
|
if (this.center.isUpgrading()) {
|
|
LevelHeightAccessor levelHeightAccessor = this.center.getHeightAccessorForGeneration();
|
|
if (levelHeightAccessor.isOutsideBuildHeight(pos.getY())) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
} else {
|
|
Util.logAndPauseIfInIde(
|
|
"Detected setBlock in a far chunk ["
|
|
+ i
|
|
+ ", "
|
|
+ j
|
|
+ "], pos: "
|
|
+ pos
|
|
+ ", status: "
|
|
+ this.generatingStep.targetStatus()
|
|
+ (this.currentlyGenerating == null ? "" : ", currently generating: " + (String)this.currentlyGenerating.get())
|
|
);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean setBlock(BlockPos pos, BlockState state, int flags, int recursionLeft) {
|
|
if (!this.ensureCanWrite(pos)) {
|
|
return false;
|
|
} else {
|
|
ChunkAccess chunkAccess = this.getChunk(pos);
|
|
BlockState blockState = chunkAccess.setBlockState(pos, state, flags);
|
|
if (blockState != null) {
|
|
this.level.updatePOIOnBlockStateChange(pos, blockState, state);
|
|
}
|
|
|
|
if (state.hasBlockEntity()) {
|
|
if (chunkAccess.getPersistedStatus().getChunkType() == ChunkType.LEVELCHUNK) {
|
|
BlockEntity blockEntity = ((EntityBlock)state.getBlock()).newBlockEntity(pos, state);
|
|
if (blockEntity != null) {
|
|
chunkAccess.setBlockEntity(blockEntity);
|
|
} else {
|
|
chunkAccess.removeBlockEntity(pos);
|
|
}
|
|
} else {
|
|
CompoundTag compoundTag = new CompoundTag();
|
|
compoundTag.putInt("x", pos.getX());
|
|
compoundTag.putInt("y", pos.getY());
|
|
compoundTag.putInt("z", pos.getZ());
|
|
compoundTag.putString("id", "DUMMY");
|
|
chunkAccess.setBlockEntityNbt(compoundTag);
|
|
}
|
|
} else if (blockState != null && blockState.hasBlockEntity()) {
|
|
chunkAccess.removeBlockEntity(pos);
|
|
}
|
|
|
|
if (state.hasPostProcess(this, pos) && (flags & 16) == 0) {
|
|
this.markPosForPostprocessing(pos);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
private void markPosForPostprocessing(BlockPos pos) {
|
|
this.getChunk(pos).markPosForPostprocessing(pos);
|
|
}
|
|
|
|
@Override
|
|
public boolean addFreshEntity(Entity entity) {
|
|
int i = SectionPos.blockToSectionCoord(entity.getBlockX());
|
|
int j = SectionPos.blockToSectionCoord(entity.getBlockZ());
|
|
this.getChunk(i, j).addEntity(entity);
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean removeBlock(BlockPos pos, boolean isMoving) {
|
|
return this.setBlock(pos, Blocks.AIR.defaultBlockState(), 3);
|
|
}
|
|
|
|
@Override
|
|
public WorldBorder getWorldBorder() {
|
|
return this.level.getWorldBorder();
|
|
}
|
|
|
|
@Override
|
|
public boolean isClientSide() {
|
|
return false;
|
|
}
|
|
|
|
@Deprecated
|
|
@Override
|
|
public ServerLevel getLevel() {
|
|
return this.level;
|
|
}
|
|
|
|
@Override
|
|
public RegistryAccess registryAccess() {
|
|
return this.level.registryAccess();
|
|
}
|
|
|
|
@Override
|
|
public FeatureFlagSet enabledFeatures() {
|
|
return this.level.enabledFeatures();
|
|
}
|
|
|
|
@Override
|
|
public LevelData getLevelData() {
|
|
return this.levelData;
|
|
}
|
|
|
|
@Override
|
|
public DifficultyInstance getCurrentDifficultyAt(BlockPos pos) {
|
|
if (!this.hasChunk(SectionPos.blockToSectionCoord(pos.getX()), SectionPos.blockToSectionCoord(pos.getZ()))) {
|
|
throw new RuntimeException("We are asking a region for a chunk out of bound");
|
|
} else {
|
|
return new DifficultyInstance(this.level.getDifficulty(), this.level.getDayTime(), 0L, this.level.getMoonBrightness());
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public MinecraftServer getServer() {
|
|
return this.level.getServer();
|
|
}
|
|
|
|
@Override
|
|
public ChunkSource getChunkSource() {
|
|
return this.level.getChunkSource();
|
|
}
|
|
|
|
@Override
|
|
public long getSeed() {
|
|
return this.seed;
|
|
}
|
|
|
|
@Override
|
|
public LevelTickAccess<Block> getBlockTicks() {
|
|
return this.blockTicks;
|
|
}
|
|
|
|
@Override
|
|
public LevelTickAccess<Fluid> getFluidTicks() {
|
|
return this.fluidTicks;
|
|
}
|
|
|
|
@Override
|
|
public int getSeaLevel() {
|
|
return this.level.getSeaLevel();
|
|
}
|
|
|
|
@Override
|
|
public RandomSource getRandom() {
|
|
return this.random;
|
|
}
|
|
|
|
@Override
|
|
public int getHeight(Heightmap.Types heightmapType, int x, int z) {
|
|
return this.getChunk(SectionPos.blockToSectionCoord(x), SectionPos.blockToSectionCoord(z)).getHeight(heightmapType, x & 15, z & 15) + 1;
|
|
}
|
|
|
|
@Override
|
|
public void playSound(@Nullable Entity entity, BlockPos pos, 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) {
|
|
}
|
|
|
|
@Override
|
|
public void levelEvent(@Nullable Entity entity, int type, BlockPos pos, int data) {
|
|
}
|
|
|
|
@Override
|
|
public void gameEvent(Holder<GameEvent> gameEvent, Vec3 pos, Context context) {
|
|
}
|
|
|
|
@Override
|
|
public DimensionType dimensionType() {
|
|
return this.dimensionType;
|
|
}
|
|
|
|
@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));
|
|
}
|
|
|
|
@Override
|
|
public <T extends Entity> List<T> getEntities(EntityTypeTest<Entity, T> entityTypeTest, AABB bounds, Predicate<? super T> predicate) {
|
|
return Collections.emptyList();
|
|
}
|
|
|
|
@Override
|
|
public List<Entity> getEntities(@Nullable Entity entity, AABB area, @Nullable Predicate<? super Entity> predicate) {
|
|
return Collections.emptyList();
|
|
}
|
|
|
|
@Override
|
|
public List<Player> players() {
|
|
return Collections.emptyList();
|
|
}
|
|
|
|
@Override
|
|
public int getMinY() {
|
|
return this.level.getMinY();
|
|
}
|
|
|
|
@Override
|
|
public int getHeight() {
|
|
return this.level.getHeight();
|
|
}
|
|
|
|
@Override
|
|
public long nextSubTickCount() {
|
|
return this.subTickCount.getAndIncrement();
|
|
}
|
|
}
|