minecraft-src/net/minecraft/world/level/levelgen/structure/placement/StructurePlacement.java
2025-07-04 01:41:11 +03:00

179 lines
7.6 KiB
Java

package net.minecraft.world.level.levelgen.structure.placement;
import com.mojang.datafixers.Products.P5;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.mojang.serialization.codecs.RecordCodecBuilder.Instance;
import com.mojang.serialization.codecs.RecordCodecBuilder.Mu;
import java.util.Optional;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.Vec3i;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.RegistryFileCodec;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
import net.minecraft.world.level.levelgen.LegacyRandomSource;
import net.minecraft.world.level.levelgen.WorldgenRandom;
import net.minecraft.world.level.levelgen.structure.StructureSet;
public abstract class StructurePlacement {
public static final Codec<StructurePlacement> CODEC = BuiltInRegistries.STRUCTURE_PLACEMENT
.byNameCodec()
.dispatch(StructurePlacement::type, StructurePlacementType::codec);
private static final int HIGHLY_ARBITRARY_RANDOM_SALT = 10387320;
private final Vec3i locateOffset;
private final StructurePlacement.FrequencyReductionMethod frequencyReductionMethod;
private final float frequency;
private final int salt;
private final Optional<StructurePlacement.ExclusionZone> exclusionZone;
protected static <S extends StructurePlacement> P5<Mu<S>, Vec3i, StructurePlacement.FrequencyReductionMethod, Float, Integer, Optional<StructurePlacement.ExclusionZone>> placementCodec(
Instance<S> instance
) {
return instance.group(
Vec3i.offsetCodec(16).optionalFieldOf("locate_offset", Vec3i.ZERO).forGetter(StructurePlacement::locateOffset),
StructurePlacement.FrequencyReductionMethod.CODEC
.optionalFieldOf("frequency_reduction_method", StructurePlacement.FrequencyReductionMethod.DEFAULT)
.forGetter(StructurePlacement::frequencyReductionMethod),
Codec.floatRange(0.0F, 1.0F).optionalFieldOf("frequency", 1.0F).forGetter(StructurePlacement::frequency),
ExtraCodecs.NON_NEGATIVE_INT.fieldOf("salt").forGetter(StructurePlacement::salt),
StructurePlacement.ExclusionZone.CODEC.optionalFieldOf("exclusion_zone").forGetter(StructurePlacement::exclusionZone)
);
}
protected StructurePlacement(
Vec3i locateOffset,
StructurePlacement.FrequencyReductionMethod frequencyReductionMethod,
float frequency,
int salt,
Optional<StructurePlacement.ExclusionZone> exclusionZone
) {
this.locateOffset = locateOffset;
this.frequencyReductionMethod = frequencyReductionMethod;
this.frequency = frequency;
this.salt = salt;
this.exclusionZone = exclusionZone;
}
protected Vec3i locateOffset() {
return this.locateOffset;
}
protected StructurePlacement.FrequencyReductionMethod frequencyReductionMethod() {
return this.frequencyReductionMethod;
}
protected float frequency() {
return this.frequency;
}
protected int salt() {
return this.salt;
}
protected Optional<StructurePlacement.ExclusionZone> exclusionZone() {
return this.exclusionZone;
}
public boolean isStructureChunk(ChunkGeneratorStructureState structureState, int x, int z) {
return this.isPlacementChunk(structureState, x, z)
&& this.applyAdditionalChunkRestrictions(x, z, structureState.getLevelSeed())
&& this.applyInteractionsWithOtherStructures(structureState, x, z);
}
public boolean applyAdditionalChunkRestrictions(int regionX, int regionZ, long levelSeed) {
return !(this.frequency < 1.0F) || this.frequencyReductionMethod.shouldGenerate(levelSeed, this.salt, regionX, regionZ, this.frequency);
}
public boolean applyInteractionsWithOtherStructures(ChunkGeneratorStructureState structureState, int x, int z) {
return !this.exclusionZone.isPresent() || !((StructurePlacement.ExclusionZone)this.exclusionZone.get()).isPlacementForbidden(structureState, x, z);
}
protected abstract boolean isPlacementChunk(ChunkGeneratorStructureState structureState, int x, int z);
public BlockPos getLocatePos(ChunkPos chunkPos) {
return new BlockPos(chunkPos.getMinBlockX(), 0, chunkPos.getMinBlockZ()).offset(this.locateOffset());
}
public abstract StructurePlacementType<?> type();
private static boolean probabilityReducer(long levelSeed, int regionX, int regionZ, int salt, float probability) {
WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
worldgenRandom.setLargeFeatureWithSalt(levelSeed, regionX, regionZ, salt);
return worldgenRandom.nextFloat() < probability;
}
private static boolean legacyProbabilityReducerWithDouble(long baseSeed, int salt, int chunkX, int chunkZ, float probability) {
WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
worldgenRandom.setLargeFeatureSeed(baseSeed, chunkX, chunkZ);
return worldgenRandom.nextDouble() < probability;
}
private static boolean legacyArbitrarySaltProbabilityReducer(long levelSeed, int salt, int regionX, int regionZ, float probability) {
WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
worldgenRandom.setLargeFeatureWithSalt(levelSeed, regionX, regionZ, 10387320);
return worldgenRandom.nextFloat() < probability;
}
private static boolean legacyPillagerOutpostReducer(long levelSeed, int salt, int regionX, int regionZ, float probability) {
int i = regionX >> 4;
int j = regionZ >> 4;
WorldgenRandom worldgenRandom = new WorldgenRandom(new LegacyRandomSource(0L));
worldgenRandom.setSeed(i ^ j << 4 ^ levelSeed);
worldgenRandom.nextInt();
return worldgenRandom.nextInt((int)(1.0F / probability)) == 0;
}
@Deprecated
public record ExclusionZone(Holder<StructureSet> otherSet, int chunkCount) {
public static final Codec<StructurePlacement.ExclusionZone> CODEC = RecordCodecBuilder.create(
instance -> instance.group(
RegistryFileCodec.create(Registries.STRUCTURE_SET, StructureSet.DIRECT_CODEC, false)
.fieldOf("other_set")
.forGetter(StructurePlacement.ExclusionZone::otherSet),
Codec.intRange(1, 16).fieldOf("chunk_count").forGetter(StructurePlacement.ExclusionZone::chunkCount)
)
.apply(instance, StructurePlacement.ExclusionZone::new)
);
boolean isPlacementForbidden(ChunkGeneratorStructureState structureState, int x, int z) {
return structureState.hasStructureChunkInRange(this.otherSet, x, z, this.chunkCount);
}
}
@FunctionalInterface
public interface FrequencyReducer {
boolean shouldGenerate(long l, int i, int j, int k, float f);
}
public static enum FrequencyReductionMethod implements StringRepresentable {
DEFAULT("default", StructurePlacement::probabilityReducer),
LEGACY_TYPE_1("legacy_type_1", StructurePlacement::legacyPillagerOutpostReducer),
LEGACY_TYPE_2("legacy_type_2", StructurePlacement::legacyArbitrarySaltProbabilityReducer),
LEGACY_TYPE_3("legacy_type_3", StructurePlacement::legacyProbabilityReducerWithDouble);
public static final Codec<StructurePlacement.FrequencyReductionMethod> CODEC = StringRepresentable.fromEnum(
StructurePlacement.FrequencyReductionMethod::values
);
private final String name;
private final StructurePlacement.FrequencyReducer reducer;
private FrequencyReductionMethod(final String name, final StructurePlacement.FrequencyReducer reducer) {
this.name = name;
this.reducer = reducer;
}
public boolean shouldGenerate(long levelSeed, int salt, int regionX, int regionZ, float probability) {
return this.reducer.shouldGenerate(levelSeed, salt, regionX, regionZ, probability);
}
@Override
public String getSerializedName() {
return this.name;
}
}
}