package net.minecraft.world.level.levelgen.structure.pools; import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Lists; import com.mojang.datafixers.util.Either; import com.mojang.serialization.Codec; import com.mojang.serialization.DataResult; import com.mojang.serialization.DynamicOps; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.function.Function; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.Vec3i; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.RandomSource; import net.minecraft.world.level.StructureManager; import net.minecraft.world.level.WorldGenLevel; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.state.properties.StructureMode; import net.minecraft.world.level.chunk.ChunkGenerator; import net.minecraft.world.level.levelgen.structure.BoundingBox; import net.minecraft.world.level.levelgen.structure.templatesystem.BlockIgnoreProcessor; import net.minecraft.world.level.levelgen.structure.templatesystem.JigsawReplacementProcessor; import net.minecraft.world.level.levelgen.structure.templatesystem.LiquidSettings; import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorType; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; public class SinglePoolElement extends StructurePoolElement { private static final Comparator HIGHEST_SELECTION_PRIORITY_FIRST = Comparator.comparingInt( StructureTemplate.JigsawBlockInfo::selectionPriority ) .reversed(); private static final Codec> TEMPLATE_CODEC = Codec.of( SinglePoolElement::encodeTemplate, ResourceLocation.CODEC.map(Either::left) ); public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group(templateCodec(), processorsCodec(), projectionCodec(), overrideLiquidSettingsCodec()).apply(instance, SinglePoolElement::new) ); protected final Either template; protected final Holder processors; protected final Optional overrideLiquidSettings; private static DataResult encodeTemplate(Either template, DynamicOps ops, T values) { Optional optional = template.left(); return optional.isEmpty() ? DataResult.error(() -> "Can not serialize a runtime pool element") : ResourceLocation.CODEC.encode((ResourceLocation)optional.get(), ops, values); } protected static RecordCodecBuilder> processorsCodec() { return StructureProcessorType.LIST_CODEC.fieldOf("processors").forGetter(singlePoolElement -> singlePoolElement.processors); } protected static RecordCodecBuilder> overrideLiquidSettingsCodec() { return LiquidSettings.CODEC.optionalFieldOf("override_liquid_settings").forGetter(singlePoolElement -> singlePoolElement.overrideLiquidSettings); } protected static RecordCodecBuilder> templateCodec() { return TEMPLATE_CODEC.fieldOf("location").forGetter(singlePoolElement -> singlePoolElement.template); } protected SinglePoolElement( Either template, Holder processors, StructureTemplatePool.Projection projection, Optional overrideLiquidSettings ) { super(projection); this.template = template; this.processors = processors; this.overrideLiquidSettings = overrideLiquidSettings; } @Override public Vec3i getSize(StructureTemplateManager structureTemplateManager, Rotation rotation) { StructureTemplate structureTemplate = this.getTemplate(structureTemplateManager); return structureTemplate.getSize(rotation); } private StructureTemplate getTemplate(StructureTemplateManager structureTemplateManager) { return this.template.map(structureTemplateManager::getOrCreate, Function.identity()); } public List getDataMarkers( StructureTemplateManager structureTemplateManager, BlockPos pos, Rotation rotation, boolean relativePosition ) { StructureTemplate structureTemplate = this.getTemplate(structureTemplateManager); List list = structureTemplate.filterBlocks( pos, new StructurePlaceSettings().setRotation(rotation), Blocks.STRUCTURE_BLOCK, relativePosition ); List list2 = Lists.newArrayList(); for (StructureTemplate.StructureBlockInfo structureBlockInfo : list) { CompoundTag compoundTag = structureBlockInfo.nbt(); if (compoundTag != null) { StructureMode structureMode = (StructureMode)compoundTag.read("mode", StructureMode.LEGACY_CODEC).orElseThrow(); if (structureMode == StructureMode.DATA) { list2.add(structureBlockInfo); } } } return list2; } @Override public List getShuffledJigsawBlocks( StructureTemplateManager structureTemplateManager, BlockPos pos, Rotation rotation, RandomSource random ) { List list = this.getTemplate(structureTemplateManager).getJigsaws(pos, rotation); Util.shuffle(list, random); sortBySelectionPriority(list); return list; } @VisibleForTesting static void sortBySelectionPriority(List structureBlockInfos) { structureBlockInfos.sort(HIGHEST_SELECTION_PRIORITY_FIRST); } @Override public BoundingBox getBoundingBox(StructureTemplateManager structureTemplateManager, BlockPos pos, Rotation rotation) { StructureTemplate structureTemplate = this.getTemplate(structureTemplateManager); return structureTemplate.getBoundingBox(new StructurePlaceSettings().setRotation(rotation), pos); } @Override public boolean place( StructureTemplateManager structureTemplateManager, WorldGenLevel level, StructureManager structureManager, ChunkGenerator generator, BlockPos offset, BlockPos pos, Rotation rotation, BoundingBox box, RandomSource random, LiquidSettings liquidSettings, boolean keepJigsaws ) { StructureTemplate structureTemplate = this.getTemplate(structureTemplateManager); StructurePlaceSettings structurePlaceSettings = this.getSettings(rotation, box, liquidSettings, keepJigsaws); if (!structureTemplate.placeInWorld(level, offset, pos, structurePlaceSettings, random, 18)) { return false; } else { for (StructureTemplate.StructureBlockInfo structureBlockInfo : StructureTemplate.processBlockInfos( level, offset, pos, structurePlaceSettings, this.getDataMarkers(structureTemplateManager, offset, rotation, false) )) { this.handleDataMarker(level, structureBlockInfo, offset, rotation, random, box); } return true; } } protected StructurePlaceSettings getSettings(Rotation rotation, BoundingBox boundingBox, LiquidSettings liquidSettings, boolean offset) { StructurePlaceSettings structurePlaceSettings = new StructurePlaceSettings(); structurePlaceSettings.setBoundingBox(boundingBox); structurePlaceSettings.setRotation(rotation); structurePlaceSettings.setKnownShape(true); structurePlaceSettings.setIgnoreEntities(false); structurePlaceSettings.addProcessor(BlockIgnoreProcessor.STRUCTURE_BLOCK); structurePlaceSettings.setFinalizeEntities(true); structurePlaceSettings.setLiquidSettings((LiquidSettings)this.overrideLiquidSettings.orElse(liquidSettings)); if (!offset) { structurePlaceSettings.addProcessor(JigsawReplacementProcessor.INSTANCE); } this.processors.value().list().forEach(structurePlaceSettings::addProcessor); this.getProjection().getProcessors().forEach(structurePlaceSettings::addProcessor); return structurePlaceSettings; } @Override public StructurePoolElementType getType() { return StructurePoolElementType.SINGLE; } public String toString() { return "Single[" + this.template + "]"; } @VisibleForTesting public ResourceLocation getTemplateLocation() { return this.template.orThrow(); } }