Code/net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator.java

437 lines
20 KiB
Java

package net.minecraft.world.level.levelgen;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Suppliers;
import com.google.common.collect.Sets;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.mojang.serialization.codecs.RecordCodecBuilder.Instance;
import java.text.DecimalFormat;
import java.util.List;
import java.util.OptionalInt;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
import java.util.function.Supplier;
import javax.annotation.Nullable;
import net.minecraft.SharedConstants;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.QuartPos;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.util.Mth;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.LevelHeightAccessor;
import net.minecraft.world.level.NaturalSpawner;
import net.minecraft.world.level.NoiseColumn;
import net.minecraft.world.level.StructureManager;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeGenerationSettings;
import net.minecraft.world.level.biome.BiomeManager;
import net.minecraft.world.level.biome.BiomeResolver;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.CarvingMask;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.dimension.DimensionType;
import net.minecraft.world.level.levelgen.blending.Blender;
import net.minecraft.world.level.levelgen.carver.CarvingContext;
import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver;
import org.apache.commons.lang3.mutable.MutableObject;
public final class NoiseBasedChunkGenerator extends ChunkGenerator {
public static final MapCodec<NoiseBasedChunkGenerator> CODEC = RecordCodecBuilder.mapCodec(
p_255585_ -> p_255585_.group(
BiomeSource.CODEC.fieldOf("biome_source").forGetter(p_255584_ -> p_255584_.biomeSource),
NoiseGeneratorSettings.CODEC.fieldOf("settings").forGetter(p_224278_ -> p_224278_.settings)
)
.apply(p_255585_, p_255585_.stable(NoiseBasedChunkGenerator::new))
);
private static final BlockState AIR = Blocks.AIR.defaultBlockState();
private final Holder<NoiseGeneratorSettings> settings;
private final Supplier<Aquifer.FluidPicker> globalFluidPicker;
public NoiseBasedChunkGenerator(BiomeSource p_256415_, Holder<NoiseGeneratorSettings> p_256182_) {
super(p_256415_);
this.settings = p_256182_;
this.globalFluidPicker = Suppliers.memoize(() -> createFluidPicker(p_256182_.value()));
}
private static Aquifer.FluidPicker createFluidPicker(NoiseGeneratorSettings p_249264_) {
Aquifer.FluidStatus aquifer$fluidstatus = new Aquifer.FluidStatus(-54, Blocks.LAVA.defaultBlockState());
int i = p_249264_.seaLevel();
Aquifer.FluidStatus aquifer$fluidstatus1 = new Aquifer.FluidStatus(i, p_249264_.defaultFluid());
Aquifer.FluidStatus aquifer$fluidstatus2 = new Aquifer.FluidStatus(DimensionType.MIN_Y * 2, Blocks.AIR.defaultBlockState());
return (p_224274_, p_224275_, p_224276_) -> p_224275_ < Math.min(-54, i) ? aquifer$fluidstatus : aquifer$fluidstatus1;
}
@Override
public CompletableFuture<ChunkAccess> createBiomes(RandomState p_224299_, Blender p_224300_, StructureManager p_224301_, ChunkAccess p_224302_) {
return CompletableFuture.supplyAsync(() -> {
this.doCreateBiomes(p_224300_, p_224299_, p_224301_, p_224302_);
return p_224302_;
}, Util.backgroundExecutor().forName("init_biomes"));
}
private void doCreateBiomes(Blender p_224292_, RandomState p_224293_, StructureManager p_224294_, ChunkAccess p_224295_) {
NoiseChunk noisechunk = p_224295_.getOrCreateNoiseChunk(p_224340_ -> this.createNoiseChunk(p_224340_, p_224294_, p_224292_, p_224293_));
BiomeResolver biomeresolver = BelowZeroRetrogen.getBiomeResolver(p_224292_.getBiomeResolver(this.biomeSource), p_224295_);
p_224295_.fillBiomesFromNoise(biomeresolver, noisechunk.cachedClimateSampler(p_224293_.router(), this.settings.value().spawnTarget()));
}
private NoiseChunk createNoiseChunk(ChunkAccess p_224257_, StructureManager p_224258_, Blender p_224259_, RandomState p_224260_) {
return NoiseChunk.forChunk(
p_224257_, p_224260_, Beardifier.forStructuresInChunk(p_224258_, p_224257_.getPos()), this.settings.value(), this.globalFluidPicker.get(), p_224259_
);
}
@Override
protected MapCodec<? extends ChunkGenerator> codec() {
return CODEC;
}
public Holder<NoiseGeneratorSettings> generatorSettings() {
return this.settings;
}
public boolean stable(ResourceKey<NoiseGeneratorSettings> p_224222_) {
return this.settings.is(p_224222_);
}
@Override
public int getBaseHeight(int p_224216_, int p_224217_, Heightmap.Types p_224218_, LevelHeightAccessor p_224219_, RandomState p_224220_) {
return this.iterateNoiseColumn(p_224219_, p_224220_, p_224216_, p_224217_, null, p_224218_.isOpaque()).orElse(p_224219_.getMinY());
}
@Override
public NoiseColumn getBaseColumn(int p_224211_, int p_224212_, LevelHeightAccessor p_224213_, RandomState p_224214_) {
MutableObject<NoiseColumn> mutableobject = new MutableObject<>();
this.iterateNoiseColumn(p_224213_, p_224214_, p_224211_, p_224212_, mutableobject, null);
return mutableobject.getValue();
}
@Override
public void addDebugScreenInfo(List<String> p_224304_, RandomState p_224305_, BlockPos p_224306_) {
DecimalFormat decimalformat = new DecimalFormat("0.000");
NoiseRouter noiserouter = p_224305_.router();
DensityFunction.SinglePointContext densityfunction$singlepointcontext = new DensityFunction.SinglePointContext(
p_224306_.getX(), p_224306_.getY(), p_224306_.getZ()
);
double d0 = noiserouter.ridges().compute(densityfunction$singlepointcontext);
p_224304_.add(
"NoiseRouter T: "
+ decimalformat.format(noiserouter.temperature().compute(densityfunction$singlepointcontext))
+ " V: "
+ decimalformat.format(noiserouter.vegetation().compute(densityfunction$singlepointcontext))
+ " C: "
+ decimalformat.format(noiserouter.continents().compute(densityfunction$singlepointcontext))
+ " E: "
+ decimalformat.format(noiserouter.erosion().compute(densityfunction$singlepointcontext))
+ " D: "
+ decimalformat.format(noiserouter.depth().compute(densityfunction$singlepointcontext))
+ " W: "
+ decimalformat.format(d0)
+ " PV: "
+ decimalformat.format(NoiseRouterData.peaksAndValleys((float)d0))
+ " AS: "
+ decimalformat.format(noiserouter.initialDensityWithoutJaggedness().compute(densityfunction$singlepointcontext))
+ " N: "
+ decimalformat.format(noiserouter.finalDensity().compute(densityfunction$singlepointcontext))
);
}
private OptionalInt iterateNoiseColumn(
LevelHeightAccessor p_224240_,
RandomState p_224241_,
int p_224242_,
int p_224243_,
@Nullable MutableObject<NoiseColumn> p_224244_,
@Nullable Predicate<BlockState> p_224245_
) {
NoiseSettings noisesettings = this.settings.value().noiseSettings().clampToHeightAccessor(p_224240_);
int i = noisesettings.getCellHeight();
int j = noisesettings.minY();
int k = Mth.floorDiv(j, i);
int l = Mth.floorDiv(noisesettings.height(), i);
if (l <= 0) {
return OptionalInt.empty();
} else {
BlockState[] ablockstate;
if (p_224244_ == null) {
ablockstate = null;
} else {
ablockstate = new BlockState[noisesettings.height()];
p_224244_.setValue(new NoiseColumn(j, ablockstate));
}
int i1 = noisesettings.getCellWidth();
int j1 = Math.floorDiv(p_224242_, i1);
int k1 = Math.floorDiv(p_224243_, i1);
int l1 = Math.floorMod(p_224242_, i1);
int i2 = Math.floorMod(p_224243_, i1);
int j2 = j1 * i1;
int k2 = k1 * i1;
double d0 = (double)l1 / i1;
double d1 = (double)i2 / i1;
NoiseChunk noisechunk = new NoiseChunk(
1,
p_224241_,
j2,
k2,
noisesettings,
DensityFunctions.BeardifierMarker.INSTANCE,
this.settings.value(),
this.globalFluidPicker.get(),
Blender.empty()
);
noisechunk.initializeForFirstCellX();
noisechunk.advanceCellX(0);
for (int l2 = l - 1; l2 >= 0; l2--) {
noisechunk.selectCellYZ(l2, 0);
for (int i3 = i - 1; i3 >= 0; i3--) {
int j3 = (k + l2) * i + i3;
double d2 = (double)i3 / i;
noisechunk.updateForY(j3, d2);
noisechunk.updateForX(p_224242_, d0);
noisechunk.updateForZ(p_224243_, d1);
BlockState blockstate = noisechunk.getInterpolatedState();
BlockState blockstate1 = blockstate == null ? this.settings.value().defaultBlock() : blockstate;
if (ablockstate != null) {
int k3 = l2 * i + i3;
ablockstate[k3] = blockstate1;
}
if (p_224245_ != null && p_224245_.test(blockstate1)) {
noisechunk.stopInterpolation();
return OptionalInt.of(j3 + 1);
}
}
}
noisechunk.stopInterpolation();
return OptionalInt.empty();
}
}
@Override
public void buildSurface(WorldGenRegion p_224232_, StructureManager p_224233_, RandomState p_224234_, ChunkAccess p_224235_) {
if (!SharedConstants.debugVoidTerrain(p_224235_.getPos())) {
WorldGenerationContext worldgenerationcontext = new WorldGenerationContext(this, p_224232_);
this.buildSurface(
p_224235_,
worldgenerationcontext,
p_224234_,
p_224233_,
p_224232_.getBiomeManager(),
p_224232_.registryAccess().lookupOrThrow(Registries.BIOME),
Blender.of(p_224232_)
);
}
}
@VisibleForTesting
public void buildSurface(
ChunkAccess p_224262_,
WorldGenerationContext p_224263_,
RandomState p_224264_,
StructureManager p_224265_,
BiomeManager p_224266_,
Registry<Biome> p_224267_,
Blender p_224268_
) {
NoiseChunk noisechunk = p_224262_.getOrCreateNoiseChunk(p_224321_ -> this.createNoiseChunk(p_224321_, p_224265_, p_224268_, p_224264_));
NoiseGeneratorSettings noisegeneratorsettings = this.settings.value();
p_224264_.surfaceSystem()
.buildSurface(
p_224264_, p_224266_, p_224267_, noisegeneratorsettings.useLegacyRandomSource(), p_224263_, p_224262_, noisechunk, noisegeneratorsettings.surfaceRule()
);
}
@Override
public void applyCarvers(
WorldGenRegion p_224224_, long p_224225_, RandomState p_224226_, BiomeManager p_224227_, StructureManager p_224228_, ChunkAccess p_224229_
) {
BiomeManager biomemanager = p_224227_.withDifferentSource(
(p_255581_, p_255582_, p_255583_) -> this.biomeSource.getNoiseBiome(p_255581_, p_255582_, p_255583_, p_224226_.sampler())
);
WorldgenRandom worldgenrandom = new WorldgenRandom(new LegacyRandomSource(RandomSupport.generateUniqueSeed()));
int i = 8;
ChunkPos chunkpos = p_224229_.getPos();
NoiseChunk noisechunk = p_224229_.getOrCreateNoiseChunk(p_224250_ -> this.createNoiseChunk(p_224250_, p_224228_, Blender.of(p_224224_), p_224226_));
Aquifer aquifer = noisechunk.aquifer();
CarvingContext carvingcontext = new CarvingContext(
this, p_224224_.registryAccess(), p_224229_.getHeightAccessorForGeneration(), noisechunk, p_224226_, this.settings.value().surfaceRule()
);
CarvingMask carvingmask = ((ProtoChunk)p_224229_).getOrCreateCarvingMask();
for (int j = -8; j <= 8; j++) {
for (int k = -8; k <= 8; k++) {
ChunkPos chunkpos1 = new ChunkPos(chunkpos.x + j, chunkpos.z + k);
ChunkAccess chunkaccess = p_224224_.getChunk(chunkpos1.x, chunkpos1.z);
BiomeGenerationSettings biomegenerationsettings = chunkaccess.carverBiome(
() -> this.getBiomeGenerationSettings(
this.biomeSource.getNoiseBiome(QuartPos.fromBlock(chunkpos1.getMinBlockX()), 0, QuartPos.fromBlock(chunkpos1.getMinBlockZ()), p_224226_.sampler())
)
);
Iterable<Holder<ConfiguredWorldCarver<?>>> iterable = biomegenerationsettings.getCarvers();
int l = 0;
for (Holder<ConfiguredWorldCarver<?>> holder : iterable) {
ConfiguredWorldCarver<?> configuredworldcarver = holder.value();
worldgenrandom.setLargeFeatureSeed(p_224225_ + l, chunkpos1.x, chunkpos1.z);
if (configuredworldcarver.isStartChunk(worldgenrandom)) {
configuredworldcarver.carve(carvingcontext, p_224229_, biomemanager::getBiome, worldgenrandom, aquifer, chunkpos1, carvingmask);
}
l++;
}
}
}
}
@Override
public CompletableFuture<ChunkAccess> fillFromNoise(Blender p_224313_, RandomState p_224314_, StructureManager p_224315_, ChunkAccess p_224316_) {
NoiseSettings noisesettings = this.settings.value().noiseSettings().clampToHeightAccessor(p_224316_.getHeightAccessorForGeneration());
int i = noisesettings.minY();
int j = Mth.floorDiv(i, noisesettings.getCellHeight());
int k = Mth.floorDiv(noisesettings.height(), noisesettings.getCellHeight());
return k <= 0 ? CompletableFuture.completedFuture(p_224316_) : CompletableFuture.supplyAsync(() -> {
int l = p_224316_.getSectionIndex(k * noisesettings.getCellHeight() - 1 + i);
int i1 = p_224316_.getSectionIndex(i);
Set<LevelChunkSection> set = Sets.newHashSet();
for (int j1 = l; j1 >= i1; j1--) {
LevelChunkSection levelchunksection = p_224316_.getSection(j1);
levelchunksection.acquire();
set.add(levelchunksection);
}
ChunkAccess chunkaccess;
try {
chunkaccess = this.doFill(p_224313_, p_224315_, p_224314_, p_224316_, j, k);
} finally {
for (LevelChunkSection levelchunksection1 : set) {
levelchunksection1.release();
}
}
return chunkaccess;
}, Util.backgroundExecutor().forName("wgen_fill_noise"));
}
private ChunkAccess doFill(Blender p_224285_, StructureManager p_224286_, RandomState p_224287_, ChunkAccess p_224288_, int p_224289_, int p_224290_) {
NoiseChunk noisechunk = p_224288_.getOrCreateNoiseChunk(p_224255_ -> this.createNoiseChunk(p_224255_, p_224286_, p_224285_, p_224287_));
Heightmap heightmap = p_224288_.getOrCreateHeightmapUnprimed(Heightmap.Types.OCEAN_FLOOR_WG);
Heightmap heightmap1 = p_224288_.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE_WG);
ChunkPos chunkpos = p_224288_.getPos();
int i = chunkpos.getMinBlockX();
int j = chunkpos.getMinBlockZ();
Aquifer aquifer = noisechunk.aquifer();
noisechunk.initializeForFirstCellX();
BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos();
int k = noisechunk.cellWidth();
int l = noisechunk.cellHeight();
int i1 = 16 / k;
int j1 = 16 / k;
for (int k1 = 0; k1 < i1; k1++) {
noisechunk.advanceCellX(k1);
for (int l1 = 0; l1 < j1; l1++) {
int i2 = p_224288_.getSectionsCount() - 1;
LevelChunkSection levelchunksection = p_224288_.getSection(i2);
for (int j2 = p_224290_ - 1; j2 >= 0; j2--) {
noisechunk.selectCellYZ(j2, l1);
for (int k2 = l - 1; k2 >= 0; k2--) {
int l2 = (p_224289_ + j2) * l + k2;
int i3 = l2 & 15;
int j3 = p_224288_.getSectionIndex(l2);
if (i2 != j3) {
i2 = j3;
levelchunksection = p_224288_.getSection(j3);
}
double d0 = (double)k2 / l;
noisechunk.updateForY(l2, d0);
for (int k3 = 0; k3 < k; k3++) {
int l3 = i + k1 * k + k3;
int i4 = l3 & 15;
double d1 = (double)k3 / k;
noisechunk.updateForX(l3, d1);
for (int j4 = 0; j4 < k; j4++) {
int k4 = j + l1 * k + j4;
int l4 = k4 & 15;
double d2 = (double)j4 / k;
noisechunk.updateForZ(k4, d2);
BlockState blockstate = noisechunk.getInterpolatedState();
if (blockstate == null) {
blockstate = this.settings.value().defaultBlock();
}
blockstate = this.debugPreliminarySurfaceLevel(noisechunk, l3, l2, k4, blockstate);
if (blockstate != AIR && !SharedConstants.debugVoidTerrain(p_224288_.getPos())) {
levelchunksection.setBlockState(i4, i3, l4, blockstate, false);
heightmap.update(i4, l2, l4, blockstate);
heightmap1.update(i4, l2, l4, blockstate);
if (aquifer.shouldScheduleFluidUpdate() && !blockstate.getFluidState().isEmpty()) {
blockpos$mutableblockpos.set(l3, l2, k4);
p_224288_.markPosForPostprocessing(blockpos$mutableblockpos);
}
}
}
}
}
}
}
noisechunk.swapSlices();
}
noisechunk.stopInterpolation();
return p_224288_;
}
private BlockState debugPreliminarySurfaceLevel(NoiseChunk p_198232_, int p_198233_, int p_198234_, int p_198235_, BlockState p_198236_) {
return p_198236_;
}
@Override
public int getGenDepth() {
return this.settings.value().noiseSettings().height();
}
@Override
public int getSeaLevel() {
return this.settings.value().seaLevel();
}
@Override
public int getMinY() {
return this.settings.value().noiseSettings().minY();
}
@Override
public void spawnOriginalMobs(WorldGenRegion p_64379_) {
if (!this.settings.value().disableMobGeneration()) {
ChunkPos chunkpos = p_64379_.getCenter();
Holder<Biome> holder = p_64379_.getBiome(chunkpos.getWorldPosition().atY(p_64379_.getMaxY()));
WorldgenRandom worldgenrandom = new WorldgenRandom(new LegacyRandomSource(RandomSupport.generateUniqueSeed()));
worldgenrandom.setDecorationSeed(p_64379_.getSeed(), chunkpos.getMinBlockX(), chunkpos.getMinBlockZ());
NaturalSpawner.spawnMobsForChunkGeneration(p_64379_, holder, chunkpos, worldgenrandom);
}
}
}