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 javax.annotation.Nullable; import net.minecraft.CrashReport; import net.minecraft.CrashReportCategory; 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.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.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; public abstract class ChunkGenerator { public static final Codec CODEC = BuiltInRegistries.CHUNK_GENERATOR.byNameCodec().dispatchStable(ChunkGenerator::codec, Function.identity()); protected final BiomeSource biomeSource; private final Supplier> featuresPerStep; private final Function, BiomeGenerationSettings> generationSettingsGetter; public ChunkGenerator(BiomeSource p_256133_) { this(p_256133_, p_223234_ -> p_223234_.value().getGenerationSettings()); } public ChunkGenerator(BiomeSource p_255838_, Function, BiomeGenerationSettings> p_256216_) { this.biomeSource = p_255838_; this.generationSettingsGetter = p_256216_; this.featuresPerStep = Suppliers.memoize( () -> FeatureSorter.buildFeaturesPerStep(List.copyOf(p_255838_.possibleBiomes()), p_223216_ -> p_256216_.apply(p_223216_).features(), true) ); } public void validate() { this.featuresPerStep.get(); } protected abstract MapCodec codec(); public ChunkGeneratorStructureState createState(HolderLookup p_256405_, RandomState p_256101_, long p_256018_) { return ChunkGeneratorStructureState.createForNormal(p_256101_, p_256018_, this.biomeSource, p_256405_); } public Optional>> getTypeNameForDataFixer() { return BuiltInRegistries.CHUNK_GENERATOR.getResourceKey(this.codec()); } public CompletableFuture createBiomes(RandomState p_223160_, Blender p_223161_, StructureManager p_223162_, ChunkAccess p_223163_) { return CompletableFuture.supplyAsync(() -> { p_223163_.fillBiomesFromNoise(this.biomeSource, p_223160_.sampler()); return p_223163_; }, Util.backgroundExecutor().forName("init_biomes")); } public abstract void applyCarvers( WorldGenRegion p_223043_, long p_223044_, RandomState p_223045_, BiomeManager p_223046_, StructureManager p_223047_, ChunkAccess p_223048_ ); @Nullable public Pair> findNearestMapStructure( ServerLevel p_223038_, HolderSet p_223039_, BlockPos p_223040_, int p_223041_, boolean p_223042_ ) { ChunkGeneratorStructureState chunkgeneratorstructurestate = p_223038_.getChunkSource().getGeneratorState(); Map>> map = new Object2ObjectArrayMap<>(); for (Holder holder : p_223039_) { for (StructurePlacement structureplacement : chunkgeneratorstructurestate.getPlacementsForStructure(holder)) { map.computeIfAbsent(structureplacement, p_223127_ -> new ObjectArraySet<>()).add(holder); } } if (map.isEmpty()) { return null; } else { Pair> pair2 = null; double d2 = Double.MAX_VALUE; StructureManager structuremanager = p_223038_.structureManager(); List>>> list = new ArrayList<>(map.size()); for (Entry>> entry : map.entrySet()) { StructurePlacement structureplacement1 = entry.getKey(); if (structureplacement1 instanceof ConcentricRingsStructurePlacement concentricringsstructureplacement) { Pair> pair = this.getNearestGeneratedStructure( entry.getValue(), p_223038_, structuremanager, p_223040_, p_223042_, concentricringsstructureplacement ); if (pair != null) { BlockPos blockpos = pair.getFirst(); double d0 = p_223040_.distSqr(blockpos); if (d0 < d2) { d2 = d0; pair2 = pair; } } } else if (structureplacement1 instanceof RandomSpreadStructurePlacement) { list.add(entry); } } if (!list.isEmpty()) { int i = SectionPos.blockToSectionCoord(p_223040_.getX()); int j = SectionPos.blockToSectionCoord(p_223040_.getZ()); for (int k = 0; k <= p_223041_; k++) { boolean flag = false; for (Entry>> entry1 : list) { RandomSpreadStructurePlacement randomspreadstructureplacement = (RandomSpreadStructurePlacement)entry1.getKey(); Pair> pair1 = getNearestGeneratedStructure( entry1.getValue(), p_223038_, structuremanager, i, j, k, p_223042_, chunkgeneratorstructurestate.getLevelSeed(), randomspreadstructureplacement ); if (pair1 != null) { flag = true; double d1 = p_223040_.distSqr(pair1.getFirst()); if (d1 < d2) { d2 = d1; pair2 = pair1; } } } if (flag) { return pair2; } } } return pair2; } } @Nullable private Pair> getNearestGeneratedStructure( Set> p_223182_, ServerLevel p_223183_, StructureManager p_223184_, BlockPos p_223185_, boolean p_223186_, ConcentricRingsStructurePlacement p_223187_ ) { List list = p_223183_.getChunkSource().getGeneratorState().getRingPositionsFor(p_223187_); if (list == null) { throw new IllegalStateException("Somehow tried to find structures for a placement that doesn't exist"); } else { Pair> pair = null; double d0 = Double.MAX_VALUE; BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos(); for (ChunkPos chunkpos : list) { blockpos$mutableblockpos.set(SectionPos.sectionToBlockCoord(chunkpos.x, 8), 32, SectionPos.sectionToBlockCoord(chunkpos.z, 8)); double d1 = blockpos$mutableblockpos.distSqr(p_223185_); boolean flag = pair == null || d1 < d0; if (flag) { Pair> pair1 = getStructureGeneratingAt(p_223182_, p_223183_, p_223184_, p_223186_, p_223187_, chunkpos); if (pair1 != null) { pair = pair1; d0 = d1; } } } return pair; } } @Nullable private static Pair> getNearestGeneratedStructure( Set> p_223189_, LevelReader p_223190_, StructureManager p_223191_, int p_223192_, int p_223193_, int p_223194_, boolean p_223195_, long p_223196_, RandomSpreadStructurePlacement p_223197_ ) { int i = p_223197_.spacing(); for (int j = -p_223194_; j <= p_223194_; j++) { boolean flag = j == -p_223194_ || j == p_223194_; for (int k = -p_223194_; k <= p_223194_; k++) { boolean flag1 = k == -p_223194_ || k == p_223194_; if (flag || flag1) { int l = p_223192_ + i * j; int i1 = p_223193_ + i * k; ChunkPos chunkpos = p_223197_.getPotentialStructureChunk(p_223196_, l, i1); Pair> pair = getStructureGeneratingAt(p_223189_, p_223190_, p_223191_, p_223195_, p_223197_, chunkpos); if (pair != null) { return pair; } } } } return null; } @Nullable private static Pair> getStructureGeneratingAt( Set> p_223199_, LevelReader p_223200_, StructureManager p_223201_, boolean p_223202_, StructurePlacement p_223203_, ChunkPos p_223204_ ) { for (Holder holder : p_223199_) { StructureCheckResult structurecheckresult = p_223201_.checkStructurePresence(p_223204_, holder.value(), p_223203_, p_223202_); if (structurecheckresult != StructureCheckResult.START_NOT_PRESENT) { if (!p_223202_ && structurecheckresult == StructureCheckResult.START_PRESENT) { return Pair.of(p_223203_.getLocatePos(p_223204_), holder); } ChunkAccess chunkaccess = p_223200_.getChunk(p_223204_.x, p_223204_.z, ChunkStatus.STRUCTURE_STARTS); StructureStart structurestart = p_223201_.getStartForStructure(SectionPos.bottomOf(chunkaccess), holder.value(), chunkaccess); if (structurestart != null && structurestart.isValid() && (!p_223202_ || tryAddReference(p_223201_, structurestart))) { return Pair.of(p_223203_.getLocatePos(structurestart.getChunkPos()), holder); } } } return null; } private static boolean tryAddReference(StructureManager p_223060_, StructureStart p_223061_) { if (p_223061_.canBeReferenced()) { p_223060_.addReference(p_223061_); return true; } else { return false; } } public void applyBiomeDecoration(WorldGenLevel p_223087_, ChunkAccess p_223088_, StructureManager p_223089_) { ChunkPos chunkpos = p_223088_.getPos(); if (!SharedConstants.debugVoidTerrain(chunkpos)) { SectionPos sectionpos = SectionPos.of(chunkpos, p_223087_.getMinSectionY()); BlockPos blockpos = sectionpos.origin(); Registry registry = p_223087_.registryAccess().lookupOrThrow(Registries.STRUCTURE); Map> map = registry.stream().collect(Collectors.groupingBy(p_223103_ -> p_223103_.step().ordinal())); List list = this.featuresPerStep.get(); WorldgenRandom worldgenrandom = new WorldgenRandom(new XoroshiroRandomSource(RandomSupport.generateUniqueSeed())); long i = worldgenrandom.setDecorationSeed(p_223087_.getSeed(), blockpos.getX(), blockpos.getZ()); Set> set = new ObjectArraySet<>(); ChunkPos.rangeClosed(sectionpos.chunk(), 1).forEach(p_223093_ -> { ChunkAccess chunkaccess = p_223087_.getChunk(p_223093_.x, p_223093_.z); for (LevelChunkSection levelchunksection : chunkaccess.getSections()) { levelchunksection.getBiomes().getAll(set::add); } }); set.retainAll(this.biomeSource.possibleBiomes()); int j = list.size(); try { Registry registry1 = p_223087_.registryAccess().lookupOrThrow(Registries.PLACED_FEATURE); int i1 = Math.max(GenerationStep.Decoration.values().length, j); for (int k = 0; k < i1; k++) { int l = 0; if (p_223089_.shouldGenerateStructures()) { for (Structure structure : map.getOrDefault(k, Collections.emptyList())) { worldgenrandom.setFeatureSeed(i, l, k); Supplier supplier = () -> registry.getResourceKey(structure).map(Object::toString).orElseGet(structure::toString); try { p_223087_.setCurrentlyGenerating(supplier); p_223089_.startsForStructure(sectionpos, structure) .forEach(p_223086_ -> p_223086_.placeInChunk(p_223087_, p_223089_, this, worldgenrandom, getWritableArea(p_223088_), chunkpos)); } catch (Exception exception) { CrashReport crashreport1 = CrashReport.forThrowable(exception, "Feature placement"); crashreport1.addCategory("Feature").setDetail("Description", supplier::get); throw new ReportedException(crashreport1); } l++; } } if (k < j) { IntSet intset = new IntArraySet(); for (Holder holder : set) { List> list1 = this.generationSettingsGetter.apply(holder).features(); if (k < list1.size()) { HolderSet holderset = list1.get(k); FeatureSorter.StepFeatureData featuresorter$stepfeaturedata1 = list.get(k); holderset.stream() .map(Holder::value) .forEach(p_223174_ -> intset.add(featuresorter$stepfeaturedata1.indexMapping().applyAsInt(p_223174_))); } } int j1 = intset.size(); int[] aint = intset.toIntArray(); Arrays.sort(aint); FeatureSorter.StepFeatureData featuresorter$stepfeaturedata = list.get(k); for (int k1 = 0; k1 < j1; k1++) { int l1 = aint[k1]; PlacedFeature placedfeature = featuresorter$stepfeaturedata.features().get(l1); Supplier supplier1 = () -> registry1.getResourceKey(placedfeature).map(Object::toString).orElseGet(placedfeature::toString); worldgenrandom.setFeatureSeed(i, l1, k); try { p_223087_.setCurrentlyGenerating(supplier1); placedfeature.placeWithBiomeCheck(p_223087_, this, worldgenrandom, blockpos); } catch (Exception exception1) { CrashReport crashreport2 = CrashReport.forThrowable(exception1, "Feature placement"); crashreport2.addCategory("Feature").setDetail("Description", supplier1::get); throw new ReportedException(crashreport2); } } } } p_223087_.setCurrentlyGenerating(null); } catch (Exception exception2) { CrashReport crashreport = CrashReport.forThrowable(exception2, "Biome decoration"); crashreport.addCategory("Generation") .setDetail("CenterX", chunkpos.x) .setDetail("CenterZ", chunkpos.z) .setDetail("Decoration Seed", i); throw new ReportedException(crashreport); } } } private static BoundingBox getWritableArea(ChunkAccess p_187718_) { ChunkPos chunkpos = p_187718_.getPos(); int i = chunkpos.getMinBlockX(); int j = chunkpos.getMinBlockZ(); LevelHeightAccessor levelheightaccessor = p_187718_.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 p_223050_, StructureManager p_223051_, RandomState p_223052_, ChunkAccess p_223053_); public abstract void spawnOriginalMobs(WorldGenRegion p_62167_); public int getSpawnHeight(LevelHeightAccessor p_156157_) { return 64; } public BiomeSource getBiomeSource() { return this.biomeSource; } public abstract int getGenDepth(); public WeightedList getMobsAt(Holder p_223134_, StructureManager p_223135_, MobCategory p_223136_, BlockPos p_223137_) { Map map = p_223135_.getAllStructuresAt(p_223137_); for (Entry entry : map.entrySet()) { Structure structure = entry.getKey(); StructureSpawnOverride structurespawnoverride = structure.spawnOverrides().get(p_223136_); if (structurespawnoverride != null) { MutableBoolean mutableboolean = new MutableBoolean(false); Predicate predicate = structurespawnoverride.boundingBox() == StructureSpawnOverride.BoundingBoxType.PIECE ? p_223065_ -> p_223135_.structureHasPieceAt(p_223137_, p_223065_) : p_223130_ -> p_223130_.getBoundingBox().isInside(p_223137_); p_223135_.fillStartsForStructure(structure, entry.getValue(), p_223220_ -> { if (mutableboolean.isFalse() && predicate.test(p_223220_)) { mutableboolean.setTrue(); } }); if (mutableboolean.isTrue()) { return structurespawnoverride.spawns(); } } } return p_223134_.value().getMobSettings().getMobs(p_223136_); } public void createStructures( RegistryAccess p_255835_, ChunkGeneratorStructureState p_256505_, StructureManager p_255934_, ChunkAccess p_255767_, StructureTemplateManager p_255832_, ResourceKey p_377248_ ) { ChunkPos chunkpos = p_255767_.getPos(); SectionPos sectionpos = SectionPos.bottomOf(p_255767_); RandomState randomstate = p_256505_.randomState(); p_256505_.possibleStructureSets() .forEach( p_255564_ -> { StructurePlacement structureplacement = p_255564_.value().placement(); List list = p_255564_.value().structures(); for (StructureSet.StructureSelectionEntry structureset$structureselectionentry : list) { StructureStart structurestart = p_255934_.getStartForStructure(sectionpos, structureset$structureselectionentry.structure().value(), p_255767_); if (structurestart != null && structurestart.isValid()) { return; } } if (structureplacement.isStructureChunk(p_256505_, chunkpos.x, chunkpos.z)) { if (list.size() == 1) { this.tryGenerateStructure( list.get(0), p_255934_, p_255835_, randomstate, p_255832_, p_256505_.getLevelSeed(), p_255767_, chunkpos, sectionpos, p_377248_ ); } else { ArrayList arraylist = new ArrayList<>(list.size()); arraylist.addAll(list); WorldgenRandom worldgenrandom = new WorldgenRandom(new LegacyRandomSource(0L)); worldgenrandom.setLargeFeatureSeed(p_256505_.getLevelSeed(), chunkpos.x, chunkpos.z); int i = 0; for (StructureSet.StructureSelectionEntry structureset$structureselectionentry1 : arraylist) { i += structureset$structureselectionentry1.weight(); } while (!arraylist.isEmpty()) { int j = worldgenrandom.nextInt(i); int k = 0; for (StructureSet.StructureSelectionEntry structureset$structureselectionentry2 : arraylist) { j -= structureset$structureselectionentry2.weight(); if (j < 0) { break; } k++; } StructureSet.StructureSelectionEntry structureset$structureselectionentry3 = arraylist.get(k); if (this.tryGenerateStructure( structureset$structureselectionentry3, p_255934_, p_255835_, randomstate, p_255832_, p_256505_.getLevelSeed(), p_255767_, chunkpos, sectionpos, p_377248_ )) { return; } arraylist.remove(k); i -= structureset$structureselectionentry3.weight(); } } } } ); } private boolean tryGenerateStructure( StructureSet.StructureSelectionEntry p_223105_, StructureManager p_223106_, RegistryAccess p_223107_, RandomState p_223108_, StructureTemplateManager p_223109_, long p_223110_, ChunkAccess p_223111_, ChunkPos p_223112_, SectionPos p_223113_, ResourceKey p_376331_ ) { Structure structure = p_223105_.structure().value(); int i = fetchReferences(p_223106_, p_223111_, p_223113_, structure); HolderSet holderset = structure.biomes(); Predicate> predicate = holderset::contains; StructureStart structurestart = structure.generate( p_223105_.structure(), p_376331_, p_223107_, this, this.biomeSource, p_223108_, p_223109_, p_223110_, p_223112_, i, p_223111_, predicate ); if (structurestart.isValid()) { p_223106_.setStartForStructure(p_223113_, structure, structurestart, p_223111_); return true; } else { return false; } } private static int fetchReferences(StructureManager p_223055_, ChunkAccess p_223056_, SectionPos p_223057_, Structure p_223058_) { StructureStart structurestart = p_223055_.getStartForStructure(p_223057_, p_223058_, p_223056_); return structurestart != null ? structurestart.getReferences() : 0; } public void createReferences(WorldGenLevel p_223077_, StructureManager p_223078_, ChunkAccess p_223079_) { int i = 8; ChunkPos chunkpos = p_223079_.getPos(); int j = chunkpos.x; int k = chunkpos.z; int l = chunkpos.getMinBlockX(); int i1 = chunkpos.getMinBlockZ(); SectionPos sectionpos = SectionPos.bottomOf(p_223079_); for (int j1 = j - 8; j1 <= j + 8; j1++) { for (int k1 = k - 8; k1 <= k + 8; k1++) { long l1 = ChunkPos.asLong(j1, k1); for (StructureStart structurestart : p_223077_.getChunk(j1, k1).getAllStarts().values()) { try { if (structurestart.isValid() && structurestart.getBoundingBox().intersects(l, i1, l + 15, i1 + 15)) { p_223078_.addReferenceForStructure(sectionpos, structurestart.getStructure(), l1, p_223079_); DebugPackets.sendStructurePacket(p_223077_, structurestart); } } catch (Exception exception) { CrashReport crashreport = CrashReport.forThrowable(exception, "Generating structure reference"); CrashReportCategory crashreportcategory = crashreport.addCategory("Structure"); Optional> optional = p_223077_.registryAccess().lookup(Registries.STRUCTURE); crashreportcategory.setDetail( "Id", () -> optional.map(p_258977_ -> p_258977_.getKey(structurestart.getStructure()).toString()).orElse("UNKNOWN") ); crashreportcategory.setDetail("Name", () -> BuiltInRegistries.STRUCTURE_TYPE.getKey(structurestart.getStructure().type()).toString()); crashreportcategory.setDetail("Class", () -> structurestart.getStructure().getClass().getCanonicalName()); throw new ReportedException(crashreport); } } } } } public abstract CompletableFuture fillFromNoise(Blender p_223210_, RandomState p_223211_, StructureManager p_223212_, ChunkAccess p_223213_); public abstract int getSeaLevel(); public abstract int getMinY(); public abstract int getBaseHeight(int p_223032_, int p_223033_, Heightmap.Types p_223034_, LevelHeightAccessor p_223035_, RandomState p_223036_); public abstract NoiseColumn getBaseColumn(int p_223028_, int p_223029_, LevelHeightAccessor p_223030_, RandomState p_223031_); public int getFirstFreeHeight(int p_223222_, int p_223223_, Heightmap.Types p_223224_, LevelHeightAccessor p_223225_, RandomState p_223226_) { return this.getBaseHeight(p_223222_, p_223223_, p_223224_, p_223225_, p_223226_); } public int getFirstOccupiedHeight(int p_223236_, int p_223237_, Heightmap.Types p_223238_, LevelHeightAccessor p_223239_, RandomState p_223240_) { return this.getBaseHeight(p_223236_, p_223237_, p_223238_, p_223239_, p_223240_) - 1; } public abstract void addDebugScreenInfo(List p_223175_, RandomState p_223176_, BlockPos p_223177_); @Deprecated public BiomeGenerationSettings getBiomeGenerationSettings(Holder p_223132_) { return this.generationSettingsGetter.apply(p_223132_); } }