package net.minecraft.world.level.levelgen.structure.structures; import com.google.common.annotations.VisibleForTesting; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import java.util.List; import java.util.Optional; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.WorldGenerationContext; import net.minecraft.world.level.levelgen.heightproviders.HeightProvider; import net.minecraft.world.level.levelgen.structure.Structure; import net.minecraft.world.level.levelgen.structure.StructureType; import net.minecraft.world.level.levelgen.structure.Structure.GenerationContext; import net.minecraft.world.level.levelgen.structure.Structure.GenerationStub; import net.minecraft.world.level.levelgen.structure.Structure.StructureSettings; import net.minecraft.world.level.levelgen.structure.pools.DimensionPadding; import net.minecraft.world.level.levelgen.structure.pools.JigsawPlacement; import net.minecraft.world.level.levelgen.structure.pools.StructureTemplatePool; import net.minecraft.world.level.levelgen.structure.pools.alias.PoolAliasBinding; import net.minecraft.world.level.levelgen.structure.pools.alias.PoolAliasLookup; import net.minecraft.world.level.levelgen.structure.templatesystem.LiquidSettings; public final class JigsawStructure extends Structure { public static final DimensionPadding DEFAULT_DIMENSION_PADDING = DimensionPadding.ZERO; public static final LiquidSettings DEFAULT_LIQUID_SETTINGS = LiquidSettings.APPLY_WATERLOGGING; public static final int MAX_TOTAL_STRUCTURE_RANGE = 128; public static final int MIN_DEPTH = 0; public static final int MAX_DEPTH = 20; public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group( settingsCodec(instance), StructureTemplatePool.CODEC.fieldOf("start_pool").forGetter(jigsawStructure -> jigsawStructure.startPool), ResourceLocation.CODEC.optionalFieldOf("start_jigsaw_name").forGetter(jigsawStructure -> jigsawStructure.startJigsawName), Codec.intRange(0, 20).fieldOf("size").forGetter(jigsawStructure -> jigsawStructure.maxDepth), HeightProvider.CODEC.fieldOf("start_height").forGetter(jigsawStructure -> jigsawStructure.startHeight), Codec.BOOL.fieldOf("use_expansion_hack").forGetter(jigsawStructure -> jigsawStructure.useExpansionHack), Heightmap.Types.CODEC.optionalFieldOf("project_start_to_heightmap").forGetter(jigsawStructure -> jigsawStructure.projectStartToHeightmap), Codec.intRange(1, 128).fieldOf("max_distance_from_center").forGetter(jigsawStructure -> jigsawStructure.maxDistanceFromCenter), Codec.list(PoolAliasBinding.CODEC).optionalFieldOf("pool_aliases", List.of()).forGetter(jigsawStructure -> jigsawStructure.poolAliases), DimensionPadding.CODEC.optionalFieldOf("dimension_padding", DEFAULT_DIMENSION_PADDING).forGetter(jigsawStructure -> jigsawStructure.dimensionPadding), LiquidSettings.CODEC.optionalFieldOf("liquid_settings", DEFAULT_LIQUID_SETTINGS).forGetter(jigsawStructure -> jigsawStructure.liquidSettings) ) .apply(instance, JigsawStructure::new) ) .validate(JigsawStructure::verifyRange); private final Holder startPool; private final Optional startJigsawName; private final int maxDepth; private final HeightProvider startHeight; private final boolean useExpansionHack; private final Optional projectStartToHeightmap; private final int maxDistanceFromCenter; private final List poolAliases; private final DimensionPadding dimensionPadding; private final LiquidSettings liquidSettings; private static DataResult verifyRange(JigsawStructure structure) { int i = switch (structure.terrainAdaptation()) { case NONE -> 0; case BURY, BEARD_THIN, BEARD_BOX, ENCAPSULATE -> 12; }; return structure.maxDistanceFromCenter + i > 128 ? DataResult.error(() -> "Structure size including terrain adaptation must not exceed 128") : DataResult.success(structure); } public JigsawStructure( StructureSettings settings, Holder startPool, Optional startJigsawName, int maxDepth, HeightProvider startHeight, boolean useExpansionHack, Optional projectStartToHeightmap, int maxDistanceFromCenter, List poolAliases, DimensionPadding dimensionPadding, LiquidSettings liquidSettings ) { super(settings); this.startPool = startPool; this.startJigsawName = startJigsawName; this.maxDepth = maxDepth; this.startHeight = startHeight; this.useExpansionHack = useExpansionHack; this.projectStartToHeightmap = projectStartToHeightmap; this.maxDistanceFromCenter = maxDistanceFromCenter; this.poolAliases = poolAliases; this.dimensionPadding = dimensionPadding; this.liquidSettings = liquidSettings; } public JigsawStructure( StructureSettings settings, Holder startPool, int maxDepth, HeightProvider startHeight, boolean useExpansionHack, Heightmap.Types projectStartToHeightmap ) { this( settings, startPool, Optional.empty(), maxDepth, startHeight, useExpansionHack, Optional.of(projectStartToHeightmap), 80, List.of(), DEFAULT_DIMENSION_PADDING, DEFAULT_LIQUID_SETTINGS ); } public JigsawStructure(StructureSettings settings, Holder startPool, int maxDepth, HeightProvider startHeight, boolean useExpansionHack) { this( settings, startPool, Optional.empty(), maxDepth, startHeight, useExpansionHack, Optional.empty(), 80, List.of(), DEFAULT_DIMENSION_PADDING, DEFAULT_LIQUID_SETTINGS ); } @Override public Optional findGenerationPoint(GenerationContext context) { ChunkPos chunkPos = context.chunkPos(); int i = this.startHeight.sample(context.random(), new WorldGenerationContext(context.chunkGenerator(), context.heightAccessor())); BlockPos blockPos = new BlockPos(chunkPos.getMinBlockX(), i, chunkPos.getMinBlockZ()); return JigsawPlacement.addPieces( context, this.startPool, this.startJigsawName, this.maxDepth, blockPos, this.useExpansionHack, this.projectStartToHeightmap, this.maxDistanceFromCenter, PoolAliasLookup.create(this.poolAliases, blockPos, context.seed()), this.dimensionPadding, this.liquidSettings ); } @Override public StructureType type() { return StructureType.JIGSAW; } @VisibleForTesting public Holder getStartPool() { return this.startPool; } @VisibleForTesting public List getPoolAliases() { return this.poolAliases; } }