634 lines
25 KiB
Java
634 lines
25 KiB
Java
package net.minecraft.world.level.chunk;
|
|
|
|
import com.google.common.base.Suppliers;
|
|
import com.mojang.datafixers.util.Pair;
|
|
import com.mojang.serialization.Codec;
|
|
import com.mojang.serialization.MapCodec;
|
|
import it.unimi.dsi.fastutil.ints.IntArraySet;
|
|
import it.unimi.dsi.fastutil.ints.IntSet;
|
|
import it.unimi.dsi.fastutil.longs.LongSet;
|
|
import it.unimi.dsi.fastutil.objects.Object2ObjectArrayMap;
|
|
import it.unimi.dsi.fastutil.objects.ObjectArraySet;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.Collections;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.Set;
|
|
import java.util.Map.Entry;
|
|
import java.util.concurrent.CompletableFuture;
|
|
import java.util.function.Function;
|
|
import java.util.function.Predicate;
|
|
import java.util.function.Supplier;
|
|
import java.util.stream.Collectors;
|
|
import net.minecraft.CrashReport;
|
|
import net.minecraft.CrashReportCategory;
|
|
import net.minecraft.CrashReportDetail;
|
|
import net.minecraft.ReportedException;
|
|
import net.minecraft.SharedConstants;
|
|
import net.minecraft.Util;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Holder;
|
|
import net.minecraft.core.HolderLookup;
|
|
import net.minecraft.core.HolderSet;
|
|
import net.minecraft.core.Registry;
|
|
import net.minecraft.core.RegistryAccess;
|
|
import net.minecraft.core.SectionPos;
|
|
import net.minecraft.core.registries.BuiltInRegistries;
|
|
import net.minecraft.core.registries.Registries;
|
|
import net.minecraft.network.protocol.game.DebugPackets;
|
|
import net.minecraft.resources.ResourceKey;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.server.level.WorldGenRegion;
|
|
import net.minecraft.util.random.WeightedList;
|
|
import net.minecraft.world.entity.MobCategory;
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.LevelHeightAccessor;
|
|
import net.minecraft.world.level.LevelReader;
|
|
import net.minecraft.world.level.NoiseColumn;
|
|
import net.minecraft.world.level.StructureManager;
|
|
import net.minecraft.world.level.WorldGenLevel;
|
|
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.BiomeSource;
|
|
import net.minecraft.world.level.biome.FeatureSorter;
|
|
import net.minecraft.world.level.biome.MobSpawnSettings;
|
|
import net.minecraft.world.level.biome.FeatureSorter.StepFeatureData;
|
|
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
|
import net.minecraft.world.level.levelgen.GenerationStep;
|
|
import net.minecraft.world.level.levelgen.Heightmap;
|
|
import net.minecraft.world.level.levelgen.LegacyRandomSource;
|
|
import net.minecraft.world.level.levelgen.RandomState;
|
|
import net.minecraft.world.level.levelgen.RandomSupport;
|
|
import net.minecraft.world.level.levelgen.WorldgenRandom;
|
|
import net.minecraft.world.level.levelgen.XoroshiroRandomSource;
|
|
import net.minecraft.world.level.levelgen.blending.Blender;
|
|
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
|
|
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
|
import net.minecraft.world.level.levelgen.structure.Structure;
|
|
import net.minecraft.world.level.levelgen.structure.StructureCheckResult;
|
|
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
|
import net.minecraft.world.level.levelgen.structure.StructureSpawnOverride;
|
|
import net.minecraft.world.level.levelgen.structure.StructureStart;
|
|
import net.minecraft.world.level.levelgen.structure.StructureSet.StructureSelectionEntry;
|
|
import net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement;
|
|
import net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement;
|
|
import net.minecraft.world.level.levelgen.structure.placement.StructurePlacement;
|
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
|
import org.apache.commons.lang3.mutable.MutableBoolean;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public abstract class ChunkGenerator {
|
|
public static final Codec<ChunkGenerator> CODEC = BuiltInRegistries.CHUNK_GENERATOR.byNameCodec().dispatchStable(ChunkGenerator::codec, Function.identity());
|
|
protected final BiomeSource biomeSource;
|
|
private final Supplier<List<StepFeatureData>> featuresPerStep;
|
|
private final Function<Holder<Biome>, BiomeGenerationSettings> generationSettingsGetter;
|
|
|
|
public ChunkGenerator(BiomeSource biomeSource) {
|
|
this(biomeSource, holder -> ((Biome)holder.value()).getGenerationSettings());
|
|
}
|
|
|
|
public ChunkGenerator(BiomeSource biomeSource, Function<Holder<Biome>, BiomeGenerationSettings> generationSettingsGetter) {
|
|
this.biomeSource = biomeSource;
|
|
this.generationSettingsGetter = generationSettingsGetter;
|
|
this.featuresPerStep = Suppliers.memoize(
|
|
() -> FeatureSorter.buildFeaturesPerStep(
|
|
List.copyOf(biomeSource.possibleBiomes()), holder -> ((BiomeGenerationSettings)generationSettingsGetter.apply(holder)).features(), true
|
|
)
|
|
);
|
|
}
|
|
|
|
public void validate() {
|
|
this.featuresPerStep.get();
|
|
}
|
|
|
|
protected abstract MapCodec<? extends ChunkGenerator> codec();
|
|
|
|
public ChunkGeneratorStructureState createState(HolderLookup<StructureSet> structureSetLookup, RandomState randomState, long seed) {
|
|
return ChunkGeneratorStructureState.createForNormal(randomState, seed, this.biomeSource, structureSetLookup);
|
|
}
|
|
|
|
public Optional<ResourceKey<MapCodec<? extends ChunkGenerator>>> getTypeNameForDataFixer() {
|
|
return BuiltInRegistries.CHUNK_GENERATOR.getResourceKey(this.codec());
|
|
}
|
|
|
|
public CompletableFuture<ChunkAccess> createBiomes(RandomState randomState, Blender blender, StructureManager structureManager, ChunkAccess chunk) {
|
|
return CompletableFuture.supplyAsync(() -> {
|
|
chunk.fillBiomesFromNoise(this.biomeSource, randomState.sampler());
|
|
return chunk;
|
|
}, Util.backgroundExecutor().forName("init_biomes"));
|
|
}
|
|
|
|
public abstract void applyCarvers(
|
|
WorldGenRegion level, long seed, RandomState random, BiomeManager biomeManager, StructureManager structureManager, ChunkAccess chunk
|
|
);
|
|
|
|
@Nullable
|
|
public Pair<BlockPos, Holder<Structure>> findNearestMapStructure(
|
|
ServerLevel level, HolderSet<Structure> structure, BlockPos pos, int searchRadius, boolean skipKnownStructures
|
|
) {
|
|
ChunkGeneratorStructureState chunkGeneratorStructureState = level.getChunkSource().getGeneratorState();
|
|
Map<StructurePlacement, Set<Holder<Structure>>> map = new Object2ObjectArrayMap<>();
|
|
|
|
for (Holder<Structure> holder : structure) {
|
|
for (StructurePlacement structurePlacement : chunkGeneratorStructureState.getPlacementsForStructure(holder)) {
|
|
((Set)map.computeIfAbsent(structurePlacement, structurePlacementx -> new ObjectArraySet())).add(holder);
|
|
}
|
|
}
|
|
|
|
if (map.isEmpty()) {
|
|
return null;
|
|
} else {
|
|
Pair<BlockPos, Holder<Structure>> pair = null;
|
|
double d = Double.MAX_VALUE;
|
|
StructureManager structureManager = level.structureManager();
|
|
List<Entry<StructurePlacement, Set<Holder<Structure>>>> list = new ArrayList(map.size());
|
|
|
|
for (Entry<StructurePlacement, Set<Holder<Structure>>> entry : map.entrySet()) {
|
|
StructurePlacement structurePlacement2 = (StructurePlacement)entry.getKey();
|
|
if (structurePlacement2 instanceof ConcentricRingsStructurePlacement concentricRingsStructurePlacement) {
|
|
Pair<BlockPos, Holder<Structure>> pair2 = this.getNearestGeneratedStructure(
|
|
(Set<Holder<Structure>>)entry.getValue(), level, structureManager, pos, skipKnownStructures, concentricRingsStructurePlacement
|
|
);
|
|
if (pair2 != null) {
|
|
BlockPos blockPos = pair2.getFirst();
|
|
double e = pos.distSqr(blockPos);
|
|
if (e < d) {
|
|
d = e;
|
|
pair = pair2;
|
|
}
|
|
}
|
|
} else if (structurePlacement2 instanceof RandomSpreadStructurePlacement) {
|
|
list.add(entry);
|
|
}
|
|
}
|
|
|
|
if (!list.isEmpty()) {
|
|
int i = SectionPos.blockToSectionCoord(pos.getX());
|
|
int j = SectionPos.blockToSectionCoord(pos.getZ());
|
|
|
|
for (int k = 0; k <= searchRadius; k++) {
|
|
boolean bl = false;
|
|
|
|
for (Entry<StructurePlacement, Set<Holder<Structure>>> entry2 : list) {
|
|
RandomSpreadStructurePlacement randomSpreadStructurePlacement = (RandomSpreadStructurePlacement)entry2.getKey();
|
|
Pair<BlockPos, Holder<Structure>> pair3 = getNearestGeneratedStructure(
|
|
(Set<Holder<Structure>>)entry2.getValue(),
|
|
level,
|
|
structureManager,
|
|
i,
|
|
j,
|
|
k,
|
|
skipKnownStructures,
|
|
chunkGeneratorStructureState.getLevelSeed(),
|
|
randomSpreadStructurePlacement
|
|
);
|
|
if (pair3 != null) {
|
|
bl = true;
|
|
double f = pos.distSqr(pair3.getFirst());
|
|
if (f < d) {
|
|
d = f;
|
|
pair = pair3;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bl) {
|
|
return pair;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pair;
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
private Pair<BlockPos, Holder<Structure>> getNearestGeneratedStructure(
|
|
Set<Holder<Structure>> structureHoldersSet,
|
|
ServerLevel level,
|
|
StructureManager structureManager,
|
|
BlockPos pos,
|
|
boolean skipKnownStructures,
|
|
ConcentricRingsStructurePlacement placement
|
|
) {
|
|
List<ChunkPos> list = level.getChunkSource().getGeneratorState().getRingPositionsFor(placement);
|
|
if (list == null) {
|
|
throw new IllegalStateException("Somehow tried to find structures for a placement that doesn't exist");
|
|
} else {
|
|
Pair<BlockPos, Holder<Structure>> pair = null;
|
|
double d = Double.MAX_VALUE;
|
|
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
|
|
|
for (ChunkPos chunkPos : list) {
|
|
mutableBlockPos.set(SectionPos.sectionToBlockCoord(chunkPos.x, 8), 32, SectionPos.sectionToBlockCoord(chunkPos.z, 8));
|
|
double e = mutableBlockPos.distSqr(pos);
|
|
boolean bl = pair == null || e < d;
|
|
if (bl) {
|
|
Pair<BlockPos, Holder<Structure>> pair2 = getStructureGeneratingAt(structureHoldersSet, level, structureManager, skipKnownStructures, placement, chunkPos);
|
|
if (pair2 != null) {
|
|
pair = pair2;
|
|
d = e;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pair;
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
private static Pair<BlockPos, Holder<Structure>> getNearestGeneratedStructure(
|
|
Set<Holder<Structure>> structureHoldersSet,
|
|
LevelReader level,
|
|
StructureManager structureManager,
|
|
int x,
|
|
int y,
|
|
int z,
|
|
boolean skipKnownStructures,
|
|
long seed,
|
|
RandomSpreadStructurePlacement spreadPlacement
|
|
) {
|
|
int i = spreadPlacement.spacing();
|
|
|
|
for (int j = -z; j <= z; j++) {
|
|
boolean bl = j == -z || j == z;
|
|
|
|
for (int k = -z; k <= z; k++) {
|
|
boolean bl2 = k == -z || k == z;
|
|
if (bl || bl2) {
|
|
int l = x + i * j;
|
|
int m = y + i * k;
|
|
ChunkPos chunkPos = spreadPlacement.getPotentialStructureChunk(seed, l, m);
|
|
Pair<BlockPos, Holder<Structure>> pair = getStructureGeneratingAt(
|
|
structureHoldersSet, level, structureManager, skipKnownStructures, spreadPlacement, chunkPos
|
|
);
|
|
if (pair != null) {
|
|
return pair;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
@Nullable
|
|
private static Pair<BlockPos, Holder<Structure>> getStructureGeneratingAt(
|
|
Set<Holder<Structure>> structureHoldersSet,
|
|
LevelReader level,
|
|
StructureManager structureManager,
|
|
boolean skipKnownStructures,
|
|
StructurePlacement placement,
|
|
ChunkPos chunkPos
|
|
) {
|
|
for (Holder<Structure> holder : structureHoldersSet) {
|
|
StructureCheckResult structureCheckResult = structureManager.checkStructurePresence(chunkPos, holder.value(), placement, skipKnownStructures);
|
|
if (structureCheckResult != StructureCheckResult.START_NOT_PRESENT) {
|
|
if (!skipKnownStructures && structureCheckResult == StructureCheckResult.START_PRESENT) {
|
|
return Pair.of(placement.getLocatePos(chunkPos), holder);
|
|
}
|
|
|
|
ChunkAccess chunkAccess = level.getChunk(chunkPos.x, chunkPos.z, ChunkStatus.STRUCTURE_STARTS);
|
|
StructureStart structureStart = structureManager.getStartForStructure(SectionPos.bottomOf(chunkAccess), holder.value(), chunkAccess);
|
|
if (structureStart != null && structureStart.isValid() && (!skipKnownStructures || tryAddReference(structureManager, structureStart))) {
|
|
return Pair.of(placement.getLocatePos(structureStart.getChunkPos()), holder);
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static boolean tryAddReference(StructureManager structureManager, StructureStart structureStart) {
|
|
if (structureStart.canBeReferenced()) {
|
|
structureManager.addReference(structureStart);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public void applyBiomeDecoration(WorldGenLevel level, ChunkAccess chunk, StructureManager structureManager) {
|
|
ChunkPos chunkPos = chunk.getPos();
|
|
if (!SharedConstants.debugVoidTerrain(chunkPos)) {
|
|
SectionPos sectionPos = SectionPos.of(chunkPos, level.getMinSectionY());
|
|
BlockPos blockPos = sectionPos.origin();
|
|
Registry<Structure> registry = level.registryAccess().lookupOrThrow(Registries.STRUCTURE);
|
|
Map<Integer, List<Structure>> map = (Map<Integer, List<Structure>>)registry.stream().collect(Collectors.groupingBy(structure -> structure.step().ordinal()));
|
|
List<StepFeatureData> list = (List<StepFeatureData>)this.featuresPerStep.get();
|
|
WorldgenRandom worldgenRandom = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed()));
|
|
long l = worldgenRandom.setDecorationSeed(level.getSeed(), blockPos.getX(), blockPos.getZ());
|
|
Set<Holder<Biome>> set = new ObjectArraySet<>();
|
|
ChunkPos.rangeClosed(sectionPos.chunk(), 1).forEach(chunkPosx -> {
|
|
ChunkAccess chunkAccess = level.getChunk(chunkPosx.x, chunkPosx.z);
|
|
|
|
for (LevelChunkSection levelChunkSection : chunkAccess.getSections()) {
|
|
levelChunkSection.getBiomes().getAll(set::add);
|
|
}
|
|
});
|
|
set.retainAll(this.biomeSource.possibleBiomes());
|
|
int i = list.size();
|
|
|
|
try {
|
|
Registry<PlacedFeature> registry2 = level.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE);
|
|
int j = Math.max(GenerationStep.Decoration.values().length, i);
|
|
|
|
for (int k = 0; k < j; k++) {
|
|
int m = 0;
|
|
if (structureManager.shouldGenerateStructures()) {
|
|
for (Structure structure : (List)map.getOrDefault(k, Collections.emptyList())) {
|
|
worldgenRandom.setFeatureSeed(l, m, k);
|
|
Supplier<String> supplier = () -> (String)registry.getResourceKey(structure).map(Object::toString).orElseGet(structure::toString);
|
|
|
|
try {
|
|
level.setCurrentlyGenerating(supplier);
|
|
structureManager.startsForStructure(sectionPos, structure)
|
|
.forEach(structureStart -> structureStart.placeInChunk(level, structureManager, this, worldgenRandom, getWritableArea(chunk), chunkPos));
|
|
} catch (Exception var29) {
|
|
CrashReport crashReport = CrashReport.forThrowable(var29, "Feature placement");
|
|
crashReport.addCategory("Feature").setDetail("Description", supplier::get);
|
|
throw new ReportedException(crashReport);
|
|
}
|
|
|
|
m++;
|
|
}
|
|
}
|
|
|
|
if (k < i) {
|
|
IntSet intSet = new IntArraySet();
|
|
|
|
for (Holder<Biome> holder : set) {
|
|
List<HolderSet<PlacedFeature>> list3 = ((BiomeGenerationSettings)this.generationSettingsGetter.apply(holder)).features();
|
|
if (k < list3.size()) {
|
|
HolderSet<PlacedFeature> holderSet = (HolderSet<PlacedFeature>)list3.get(k);
|
|
StepFeatureData stepFeatureData = (StepFeatureData)list.get(k);
|
|
holderSet.stream().map(Holder::value).forEach(placedFeaturex -> intSet.add(stepFeatureData.indexMapping().applyAsInt(placedFeaturex)));
|
|
}
|
|
}
|
|
|
|
int n = intSet.size();
|
|
int[] is = intSet.toIntArray();
|
|
Arrays.sort(is);
|
|
StepFeatureData stepFeatureData2 = (StepFeatureData)list.get(k);
|
|
|
|
for (int o = 0; o < n; o++) {
|
|
int p = is[o];
|
|
PlacedFeature placedFeature = (PlacedFeature)stepFeatureData2.features().get(p);
|
|
Supplier<String> supplier2 = () -> (String)registry2.getResourceKey(placedFeature).map(Object::toString).orElseGet(placedFeature::toString);
|
|
worldgenRandom.setFeatureSeed(l, p, k);
|
|
|
|
try {
|
|
level.setCurrentlyGenerating(supplier2);
|
|
placedFeature.placeWithBiomeCheck(level, this, worldgenRandom, blockPos);
|
|
} catch (Exception var30) {
|
|
CrashReport crashReport2 = CrashReport.forThrowable(var30, "Feature placement");
|
|
crashReport2.addCategory("Feature").setDetail("Description", supplier2::get);
|
|
throw new ReportedException(crashReport2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
level.setCurrentlyGenerating(null);
|
|
} catch (Exception var31) {
|
|
CrashReport crashReport3 = CrashReport.forThrowable(var31, "Biome decoration");
|
|
crashReport3.addCategory("Generation").setDetail("CenterX", chunkPos.x).setDetail("CenterZ", chunkPos.z).setDetail("Decoration Seed", l);
|
|
throw new ReportedException(crashReport3);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static BoundingBox getWritableArea(ChunkAccess chunk) {
|
|
ChunkPos chunkPos = chunk.getPos();
|
|
int i = chunkPos.getMinBlockX();
|
|
int j = chunkPos.getMinBlockZ();
|
|
LevelHeightAccessor levelHeightAccessor = chunk.getHeightAccessorForGeneration();
|
|
int k = levelHeightAccessor.getMinY() + 1;
|
|
int l = levelHeightAccessor.getMaxY();
|
|
return new BoundingBox(i, k, j, i + 15, l, j + 15);
|
|
}
|
|
|
|
public abstract void buildSurface(WorldGenRegion level, StructureManager structureManager, RandomState random, ChunkAccess chunk);
|
|
|
|
public abstract void spawnOriginalMobs(WorldGenRegion level);
|
|
|
|
public int getSpawnHeight(LevelHeightAccessor level) {
|
|
return 64;
|
|
}
|
|
|
|
public BiomeSource getBiomeSource() {
|
|
return this.biomeSource;
|
|
}
|
|
|
|
public abstract int getGenDepth();
|
|
|
|
public WeightedList<MobSpawnSettings.SpawnerData> getMobsAt(Holder<Biome> biome, StructureManager structureManager, MobCategory category, BlockPos pos) {
|
|
Map<Structure, LongSet> map = structureManager.getAllStructuresAt(pos);
|
|
|
|
for (Entry<Structure, LongSet> entry : map.entrySet()) {
|
|
Structure structure = (Structure)entry.getKey();
|
|
StructureSpawnOverride structureSpawnOverride = (StructureSpawnOverride)structure.spawnOverrides().get(category);
|
|
if (structureSpawnOverride != null) {
|
|
MutableBoolean mutableBoolean = new MutableBoolean(false);
|
|
Predicate<StructureStart> predicate = structureSpawnOverride.boundingBox() == StructureSpawnOverride.BoundingBoxType.PIECE
|
|
? structureStart -> structureManager.structureHasPieceAt(pos, structureStart)
|
|
: structureStart -> structureStart.getBoundingBox().isInside(pos);
|
|
structureManager.fillStartsForStructure(structure, (LongSet)entry.getValue(), structureStart -> {
|
|
if (mutableBoolean.isFalse() && predicate.test(structureStart)) {
|
|
mutableBoolean.setTrue();
|
|
}
|
|
});
|
|
if (mutableBoolean.isTrue()) {
|
|
return structureSpawnOverride.spawns();
|
|
}
|
|
}
|
|
}
|
|
|
|
return biome.value().getMobSettings().getMobs(category);
|
|
}
|
|
|
|
public void createStructures(
|
|
RegistryAccess registryAccess,
|
|
ChunkGeneratorStructureState structureState,
|
|
StructureManager structureManager,
|
|
ChunkAccess chunk,
|
|
StructureTemplateManager structureTemplateManager,
|
|
ResourceKey<Level> level
|
|
) {
|
|
ChunkPos chunkPos = chunk.getPos();
|
|
SectionPos sectionPos = SectionPos.bottomOf(chunk);
|
|
RandomState randomState = structureState.randomState();
|
|
structureState.possibleStructureSets()
|
|
.forEach(
|
|
holder -> {
|
|
StructurePlacement structurePlacement = ((StructureSet)holder.value()).placement();
|
|
List<StructureSelectionEntry> list = ((StructureSet)holder.value()).structures();
|
|
|
|
for (StructureSelectionEntry structureSelectionEntry : list) {
|
|
StructureStart structureStart = structureManager.getStartForStructure(sectionPos, structureSelectionEntry.structure().value(), chunk);
|
|
if (structureStart != null && structureStart.isValid()) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (structurePlacement.isStructureChunk(structureState, chunkPos.x, chunkPos.z)) {
|
|
if (list.size() == 1) {
|
|
this.tryGenerateStructure(
|
|
(StructureSelectionEntry)list.get(0),
|
|
structureManager,
|
|
registryAccess,
|
|
randomState,
|
|
structureTemplateManager,
|
|
structureState.getLevelSeed(),
|
|
chunk,
|
|
chunkPos,
|
|
sectionPos,
|
|
level
|
|
);
|
|
} else {
|
|
ArrayList<StructureSelectionEntry> arrayList = new ArrayList(list.size());
|
|
arrayList.addAll(list);
|
|
WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
|
|
worldgenRandom.setLargeFeatureSeed(structureState.getLevelSeed(), chunkPos.x, chunkPos.z);
|
|
int i = 0;
|
|
|
|
for (StructureSelectionEntry structureSelectionEntry2 : arrayList) {
|
|
i += structureSelectionEntry2.weight();
|
|
}
|
|
|
|
while (!arrayList.isEmpty()) {
|
|
int j = worldgenRandom.nextInt(i);
|
|
int k = 0;
|
|
|
|
for (StructureSelectionEntry structureSelectionEntry3 : arrayList) {
|
|
j -= structureSelectionEntry3.weight();
|
|
if (j < 0) {
|
|
break;
|
|
}
|
|
|
|
k++;
|
|
}
|
|
|
|
StructureSelectionEntry structureSelectionEntry4 = (StructureSelectionEntry)arrayList.get(k);
|
|
if (this.tryGenerateStructure(
|
|
structureSelectionEntry4,
|
|
structureManager,
|
|
registryAccess,
|
|
randomState,
|
|
structureTemplateManager,
|
|
structureState.getLevelSeed(),
|
|
chunk,
|
|
chunkPos,
|
|
sectionPos,
|
|
level
|
|
)) {
|
|
return;
|
|
}
|
|
|
|
arrayList.remove(k);
|
|
i -= structureSelectionEntry4.weight();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
);
|
|
}
|
|
|
|
private boolean tryGenerateStructure(
|
|
StructureSelectionEntry structureSelectionEntry,
|
|
StructureManager structureManager,
|
|
RegistryAccess registryAccess,
|
|
RandomState random,
|
|
StructureTemplateManager structureTemplateManager,
|
|
long seed,
|
|
ChunkAccess chunk,
|
|
ChunkPos chunkPos,
|
|
SectionPos sectionPos,
|
|
ResourceKey<Level> level
|
|
) {
|
|
Structure structure = structureSelectionEntry.structure().value();
|
|
int i = fetchReferences(structureManager, chunk, sectionPos, structure);
|
|
HolderSet<Biome> holderSet = structure.biomes();
|
|
Predicate<Holder<Biome>> predicate = holderSet::contains;
|
|
StructureStart structureStart = structure.generate(
|
|
structureSelectionEntry.structure(), level, registryAccess, this, this.biomeSource, random, structureTemplateManager, seed, chunkPos, i, chunk, predicate
|
|
);
|
|
if (structureStart.isValid()) {
|
|
structureManager.setStartForStructure(sectionPos, structure, structureStart, chunk);
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private static int fetchReferences(StructureManager structureManager, ChunkAccess chunk, SectionPos sectionPos, Structure structure) {
|
|
StructureStart structureStart = structureManager.getStartForStructure(sectionPos, structure, chunk);
|
|
return structureStart != null ? structureStart.getReferences() : 0;
|
|
}
|
|
|
|
public void createReferences(WorldGenLevel level, StructureManager structureManager, ChunkAccess chunk) {
|
|
int i = 8;
|
|
ChunkPos chunkPos = chunk.getPos();
|
|
int j = chunkPos.x;
|
|
int k = chunkPos.z;
|
|
int l = chunkPos.getMinBlockX();
|
|
int m = chunkPos.getMinBlockZ();
|
|
SectionPos sectionPos = SectionPos.bottomOf(chunk);
|
|
|
|
for (int n = j - 8; n <= j + 8; n++) {
|
|
for (int o = k - 8; o <= k + 8; o++) {
|
|
long p = ChunkPos.asLong(n, o);
|
|
|
|
for (StructureStart structureStart : level.getChunk(n, o).getAllStarts().values()) {
|
|
try {
|
|
if (structureStart.isValid() && structureStart.getBoundingBox().intersects(l, m, l + 15, m + 15)) {
|
|
structureManager.addReferenceForStructure(sectionPos, structureStart.getStructure(), p, chunk);
|
|
DebugPackets.sendStructurePacket(level, structureStart);
|
|
}
|
|
} catch (Exception var21) {
|
|
CrashReport crashReport = CrashReport.forThrowable(var21, "Generating structure reference");
|
|
CrashReportCategory crashReportCategory = crashReport.addCategory("Structure");
|
|
Optional<? extends Registry<Structure>> optional = level.registryAccess().lookup(Registries.STRUCTURE);
|
|
crashReportCategory.setDetail(
|
|
"Id", (CrashReportDetail<String>)(() -> (String)optional.map(registry -> registry.getKey(structureStart.getStructure()).toString()).orElse("UNKNOWN"))
|
|
);
|
|
crashReportCategory.setDetail(
|
|
"Name", (CrashReportDetail<String>)(() -> BuiltInRegistries.STRUCTURE_TYPE.getKey(structureStart.getStructure().type()).toString())
|
|
);
|
|
crashReportCategory.setDetail("Class", (CrashReportDetail<String>)(() -> structureStart.getStructure().getClass().getCanonicalName()));
|
|
throw new ReportedException(crashReport);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public abstract CompletableFuture<ChunkAccess> fillFromNoise(Blender blender, RandomState randomState, StructureManager structureManager, ChunkAccess chunk);
|
|
|
|
public abstract int getSeaLevel();
|
|
|
|
public abstract int getMinY();
|
|
|
|
public abstract int getBaseHeight(int x, int z, Heightmap.Types type, LevelHeightAccessor level, RandomState random);
|
|
|
|
public abstract NoiseColumn getBaseColumn(int x, int z, LevelHeightAccessor height, RandomState random);
|
|
|
|
public int getFirstFreeHeight(int x, int z, Heightmap.Types type, LevelHeightAccessor level, RandomState random) {
|
|
return this.getBaseHeight(x, z, type, level, random);
|
|
}
|
|
|
|
public int getFirstOccupiedHeight(int x, int z, Heightmap.Types types, LevelHeightAccessor level, RandomState random) {
|
|
return this.getBaseHeight(x, z, types, level, random) - 1;
|
|
}
|
|
|
|
public abstract void addDebugScreenInfo(List<String> info, RandomState random, BlockPos pos);
|
|
|
|
@Deprecated
|
|
public BiomeGenerationSettings getBiomeGenerationSettings(Holder<Biome> biome) {
|
|
return (BiomeGenerationSettings)this.generationSettingsGetter.apply(biome);
|
|
}
|
|
}
|