minecraft-src/net/minecraft/world/level/levelgen/structure/Structure.java
2025-07-04 03:15:13 +03:00

282 lines
11 KiB
Java

package net.minecraft.world.level.levelgen.structure;
import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.mojang.serialization.codecs.RecordCodecBuilder.Instance;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderSet;
import net.minecraft.core.QuartPos;
import net.minecraft.core.RegistryAccess;
import net.minecraft.core.RegistryCodecs;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.RegistryFileCodec;
import net.minecraft.resources.ResourceKey;
import net.minecraft.util.RandomSource;
import net.minecraft.util.StringRepresentable;
import net.minecraft.util.profiling.jfr.JvmProfiler;
import net.minecraft.util.profiling.jfr.callback.ProfiledDuration;
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.StructureManager;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.BiomeSource;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.levelgen.LegacyRandomSource;
import net.minecraft.world.level.levelgen.RandomState;
import net.minecraft.world.level.levelgen.WorldgenRandom;
import net.minecraft.world.level.levelgen.GenerationStep.Decoration;
import net.minecraft.world.level.levelgen.Heightmap.Types;
import net.minecraft.world.level.levelgen.structure.pieces.PiecesContainer;
import net.minecraft.world.level.levelgen.structure.pieces.StructurePiecesBuilder;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
public abstract class Structure {
public static final Codec<Structure> DIRECT_CODEC = BuiltInRegistries.STRUCTURE_TYPE.byNameCodec().dispatch(Structure::type, StructureType::codec);
public static final Codec<Holder<Structure>> CODEC = RegistryFileCodec.create(Registries.STRUCTURE, DIRECT_CODEC);
protected final Structure.StructureSettings settings;
public static <S extends Structure> RecordCodecBuilder<S, Structure.StructureSettings> settingsCodec(Instance<S> instance) {
return Structure.StructureSettings.CODEC.forGetter(structure -> structure.settings);
}
public static <S extends Structure> MapCodec<S> simpleCodec(Function<Structure.StructureSettings, S> factory) {
return RecordCodecBuilder.mapCodec(instance -> instance.group(settingsCodec(instance)).apply(instance, factory));
}
protected Structure(Structure.StructureSettings settings) {
this.settings = settings;
}
public HolderSet<Biome> biomes() {
return this.settings.biomes;
}
public Map<MobCategory, StructureSpawnOverride> spawnOverrides() {
return this.settings.spawnOverrides;
}
public Decoration step() {
return this.settings.step;
}
public TerrainAdjustment terrainAdaptation() {
return this.settings.terrainAdaptation;
}
public BoundingBox adjustBoundingBox(BoundingBox boundingBox) {
return this.terrainAdaptation() != TerrainAdjustment.NONE ? boundingBox.inflatedBy(12) : boundingBox;
}
public StructureStart generate(
Holder<Structure> structure,
ResourceKey<Level> level,
RegistryAccess registryAccess,
ChunkGenerator chunkGenerator,
BiomeSource biomeSource,
RandomState randomState,
StructureTemplateManager structureTemplateManager,
long seed,
ChunkPos chunkPos,
int references,
LevelHeightAccessor heightAccessor,
Predicate<Holder<Biome>> validBiome
) {
ProfiledDuration profiledDuration = JvmProfiler.INSTANCE.onStructureGenerate(chunkPos, level, structure);
Structure.GenerationContext generationContext = new Structure.GenerationContext(
registryAccess, chunkGenerator, biomeSource, randomState, structureTemplateManager, seed, chunkPos, heightAccessor, validBiome
);
Optional<Structure.GenerationStub> optional = this.findValidGenerationPoint(generationContext);
if (optional.isPresent()) {
StructurePiecesBuilder structurePiecesBuilder = ((Structure.GenerationStub)optional.get()).getPiecesBuilder();
StructureStart structureStart = new StructureStart(this, chunkPos, references, structurePiecesBuilder.build());
if (structureStart.isValid()) {
if (profiledDuration != null) {
profiledDuration.finish(true);
}
return structureStart;
}
}
if (profiledDuration != null) {
profiledDuration.finish(false);
}
return StructureStart.INVALID_START;
}
protected static Optional<Structure.GenerationStub> onTopOfChunkCenter(
Structure.GenerationContext context, Types heightmapTypes, Consumer<StructurePiecesBuilder> generator
) {
ChunkPos chunkPos = context.chunkPos();
int i = chunkPos.getMiddleBlockX();
int j = chunkPos.getMiddleBlockZ();
int k = context.chunkGenerator().getFirstOccupiedHeight(i, j, heightmapTypes, context.heightAccessor(), context.randomState());
return Optional.of(new Structure.GenerationStub(new BlockPos(i, k, j), generator));
}
private static boolean isValidBiome(Structure.GenerationStub stub, Structure.GenerationContext context) {
BlockPos blockPos = stub.position();
return context.validBiome
.test(
context.chunkGenerator
.getBiomeSource()
.getNoiseBiome(
QuartPos.fromBlock(blockPos.getX()), QuartPos.fromBlock(blockPos.getY()), QuartPos.fromBlock(blockPos.getZ()), context.randomState.sampler()
)
);
}
public void afterPlace(
WorldGenLevel level,
StructureManager structureManager,
ChunkGenerator chunkGenerator,
RandomSource random,
BoundingBox boundingBox,
ChunkPos chunkPos,
PiecesContainer pieces
) {
}
private static int[] getCornerHeights(Structure.GenerationContext context, int minX, int maxX, int minZ, int maxZ) {
ChunkGenerator chunkGenerator = context.chunkGenerator();
LevelHeightAccessor levelHeightAccessor = context.heightAccessor();
RandomState randomState = context.randomState();
return new int[]{
chunkGenerator.getFirstOccupiedHeight(minX, minZ, Types.WORLD_SURFACE_WG, levelHeightAccessor, randomState),
chunkGenerator.getFirstOccupiedHeight(minX, minZ + maxZ, Types.WORLD_SURFACE_WG, levelHeightAccessor, randomState),
chunkGenerator.getFirstOccupiedHeight(minX + maxX, minZ, Types.WORLD_SURFACE_WG, levelHeightAccessor, randomState),
chunkGenerator.getFirstOccupiedHeight(minX + maxX, minZ + maxZ, Types.WORLD_SURFACE_WG, levelHeightAccessor, randomState)
};
}
public static int getMeanFirstOccupiedHeight(Structure.GenerationContext context, int minX, int maxX, int minZ, int maxZ) {
int[] is = getCornerHeights(context, minX, maxX, minZ, maxZ);
return (is[0] + is[1] + is[2] + is[3]) / 4;
}
protected static int getLowestY(Structure.GenerationContext context, int maxX, int maxZ) {
ChunkPos chunkPos = context.chunkPos();
int i = chunkPos.getMinBlockX();
int j = chunkPos.getMinBlockZ();
return getLowestY(context, i, j, maxX, maxZ);
}
protected static int getLowestY(Structure.GenerationContext context, int minX, int minZ, int maxX, int maxZ) {
int[] is = getCornerHeights(context, minX, maxX, minZ, maxZ);
return Math.min(Math.min(is[0], is[1]), Math.min(is[2], is[3]));
}
@Deprecated
protected BlockPos getLowestYIn5by5BoxOffset7Blocks(Structure.GenerationContext context, Rotation rotation) {
int i = 5;
int j = 5;
if (rotation == Rotation.CLOCKWISE_90) {
i = -5;
} else if (rotation == Rotation.CLOCKWISE_180) {
i = -5;
j = -5;
} else if (rotation == Rotation.COUNTERCLOCKWISE_90) {
j = -5;
}
ChunkPos chunkPos = context.chunkPos();
int k = chunkPos.getBlockX(7);
int l = chunkPos.getBlockZ(7);
return new BlockPos(k, getLowestY(context, k, l, i, j), l);
}
protected abstract Optional<Structure.GenerationStub> findGenerationPoint(Structure.GenerationContext context);
public Optional<Structure.GenerationStub> findValidGenerationPoint(Structure.GenerationContext context) {
return this.findGenerationPoint(context).filter(generationStub -> isValidBiome(generationStub, context));
}
public abstract StructureType<?> type();
public record GenerationContext(
RegistryAccess registryAccess,
ChunkGenerator chunkGenerator,
BiomeSource biomeSource,
RandomState randomState,
StructureTemplateManager structureTemplateManager,
WorldgenRandom random,
long seed,
ChunkPos chunkPos,
LevelHeightAccessor heightAccessor,
Predicate<Holder<Biome>> validBiome
) {
public GenerationContext(
RegistryAccess registryAccess,
ChunkGenerator chunkGenerator,
BiomeSource biomeSource,
RandomState randomState,
StructureTemplateManager structureTemplateManager,
long seed,
ChunkPos chunkPos,
LevelHeightAccessor heightAccessor,
Predicate<Holder<Biome>> validBiome
) {
this(
registryAccess, chunkGenerator, biomeSource, randomState, structureTemplateManager, makeRandom(seed, chunkPos), seed, chunkPos, heightAccessor, validBiome
);
}
private static WorldgenRandom makeRandom(long seed, ChunkPos chunkPos) {
WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
worldgenRandom.setLargeFeatureSeed(seed, chunkPos.x, chunkPos.z);
return worldgenRandom;
}
}
public record GenerationStub(BlockPos position, Either<Consumer<StructurePiecesBuilder>, StructurePiecesBuilder> generator) {
public GenerationStub(BlockPos position, Consumer<StructurePiecesBuilder> generator) {
this(position, Either.left(generator));
}
public StructurePiecesBuilder getPiecesBuilder() {
return this.generator.map(consumer -> {
StructurePiecesBuilder structurePiecesBuilder = new StructurePiecesBuilder();
consumer.accept(structurePiecesBuilder);
return structurePiecesBuilder;
}, structurePiecesBuilder -> structurePiecesBuilder);
}
}
public record StructureSettings(
HolderSet<Biome> biomes, Map<MobCategory, StructureSpawnOverride> spawnOverrides, Decoration step, TerrainAdjustment terrainAdaptation
) {
static final Structure.StructureSettings DEFAULT = new Structure.StructureSettings(
HolderSet.direct(), Map.of(), Decoration.SURFACE_STRUCTURES, TerrainAdjustment.NONE
);
public static final MapCodec<Structure.StructureSettings> CODEC = RecordCodecBuilder.mapCodec(
instance -> instance.group(
RegistryCodecs.homogeneousList(Registries.BIOME).fieldOf("biomes").forGetter(Structure.StructureSettings::biomes),
Codec.simpleMap(MobCategory.CODEC, StructureSpawnOverride.CODEC, StringRepresentable.keys(MobCategory.values()))
.fieldOf("spawn_overrides")
.forGetter(Structure.StructureSettings::spawnOverrides),
Decoration.CODEC.fieldOf("step").forGetter(Structure.StructureSettings::step),
TerrainAdjustment.CODEC.optionalFieldOf("terrain_adaptation", DEFAULT.terrainAdaptation).forGetter(Structure.StructureSettings::terrainAdaptation)
)
.apply(instance, Structure.StructureSettings::new)
);
public StructureSettings(HolderSet<Biome> biomes) {
this(biomes, DEFAULT.spawnOverrides, DEFAULT.step, DEFAULT.terrainAdaptation);
}
}
}