Register world features properlyTM

- Refactored Create's config-driven worldgen to better work with the feature infrastructure of 1.16. Adresses #539
This commit is contained in:
simibubi 2021-02-07 21:27:55 +01:00
parent 01a83722cf
commit dc13ea6485
10 changed files with 370 additions and 260 deletions

View file

@ -84,6 +84,7 @@ public class Create {
AllEntityTypes.register();
AllTileEntities.register();
AllMovementBehaviours.register();
AllWorldFeatures.register();
modEventBus.addListener(Create::init);
MinecraftForge.EVENT_BUS.addListener(EventPriority.HIGH, Create::onBiomeLoad);
@ -118,6 +119,7 @@ public class Create {
event.enqueueWork(() -> {
SchematicProcessor.register();
AllWorldFeatures.registerFeatures();
});
}

View file

@ -1,90 +1,93 @@
package com.simibubi.create.foundation.worldgen;
import static net.minecraft.world.biome.Biome.Category.DESERT;
import static net.minecraft.world.biome.Biome.Category.OCEAN;
import java.util.Arrays;
import java.util.Optional;
import java.util.HashMap;
import java.util.Map;
import com.simibubi.create.AllBlocks;
import com.simibubi.create.Create;
import com.simibubi.create.content.palettes.AllPaletteBlocks;
import com.simibubi.create.foundation.utility.Lang;
import com.tterrag.registrate.util.nullness.NonNullSupplier;
import net.minecraft.block.Block;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.WorldGenRegistries;
import net.minecraft.world.biome.Biome.Category;
import net.minecraft.world.biome.Biomes;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraft.world.gen.GenerationStage;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.event.world.BiomeLoadingEvent;
import net.minecraftforge.registries.ForgeRegistries;
public enum AllWorldFeatures {
public class AllWorldFeatures {
COPPER_ORE(new CountedOreFeature(AllBlocks.COPPER_ORE, 18, 2).between(40, 86)),
COPPER_ORE_OCEAN(new CountedOreFeature(AllBlocks.COPPER_ORE, 15, 4).between(20, 55).inBiomes(OCEAN)),
static Map<String, ConfigDrivenFeatureEntry> entries = new HashMap<>();
ZINC_ORE(new CountedOreFeature(AllBlocks.ZINC_ORE, 14, 4).between(15, 70)),
ZINC_ORE_DESERT(new CountedOreFeature(AllBlocks.ZINC_ORE, 17, 5).between(10, 85).inBiomes(DESERT)),
static final ConfigDrivenFeatureEntry
LIMESTONE(new ChanceOreFeature(AllPaletteBlocks.LIMESTONE, 128, 1 / 32f).between(30, 70)),
WEATHERED_LIMESTONE(new ChanceOreFeature(AllPaletteBlocks.WEATHERED_LIMESTONE, 128, 1 / 32f).between(10, 30)),
DOLOMITE(new ChanceOreFeature(AllPaletteBlocks.DOLOMITE, 128, 1 / 64f).between(20, 70)),
GABBRO(new ChanceOreFeature(AllPaletteBlocks.GABBRO, 128, 1 / 64f).between(20, 70)),
SCORIA(new ChanceOreFeature(AllPaletteBlocks.NATURAL_SCORIA, 128, 1 / 32f).between(0, 10)),
COPPER = register("copper_ore", AllBlocks.COPPER_ORE, 18, 2).between(40, 85),
ZINC = register("zinc_ore", AllBlocks.ZINC_ORE, 14, 4).between(15, 70),
LIMESTONE = register("limestone", AllPaletteBlocks.LIMESTONE, 128, 1 / 64f).between(30, 70),
WEATHERED_LIMESTONE =
register("weathered_limestone", AllPaletteBlocks.WEATHERED_LIMESTONE, 128, 1 / 64f).between(10, 30),
DOLOMITE = register("dolomite", AllPaletteBlocks.DOLOMITE, 128, 1 / 64f).between(20, 70),
GABBRO = register("gabbro", AllPaletteBlocks.GABBRO, 128, 1 / 64f).between(20, 70),
SCORIA = register("scoria", AllPaletteBlocks.NATURAL_SCORIA, 128, 1 / 32f).between(0, 10)
;
private static ConfigDrivenFeatureEntry register(String id, NonNullSupplier<? extends Block> block, int clusterSize,
float frequency) {
ConfigDrivenFeatureEntry configDrivenFeatureEntry =
new ConfigDrivenFeatureEntry(id, block, clusterSize, frequency);
entries.put(id, configDrivenFeatureEntry);
return configDrivenFeatureEntry;
}
/**
* Increment this number if all worldgen entries should be overwritten in this
* update. Worlds from the previous version will overwrite potentially changed
* values with the new defaults.
*/
public static final int forcedUpdateVersion = 1;
public static final int forcedUpdateVersion = 2;
public IFeature feature;
AllWorldFeatures(IFeature feature) {
this.feature = feature;
this.feature.setId(Lang.asId(name()));
public static void registerFeatures() {
ForgeRegistries.FEATURES.register(ConfigDrivenOreFeature.INSTANCE);
ForgeRegistries.DECORATORS.register(ConfigDrivenDecorator.INSTANCE);
entries.entrySet()
.forEach((entry) -> {
Registry.register(WorldGenRegistries.CONFIGURED_FEATURE, Create.ID + "_" + entry.getKey(),
entry.getValue()
.getFeature());
});
}
public static void reload(BiomeLoadingEvent event) {
for (AllWorldFeatures entry : AllWorldFeatures.values()) {
entries.values()
.forEach(entry -> {
if (event.getName() == Biomes.THE_VOID.getRegistryName())
continue;
return;
if (event.getCategory() == Category.NETHER)
continue;
Optional<ConfiguredFeature<?, ?>> createFeature = entry.feature.createFeature(event);
if (!createFeature.isPresent())
continue;
event.getGeneration().feature(entry.feature.getGenerationStage(), createFeature.get());
}
// // Debug contained ore features
// for (Biome biome : ForgeRegistries.BIOMES) {
// Debug.markTemporary();
// System.out.println(biome.getRegistryName().getPath() + " has the following features:");
// for (ConfiguredFeature<?> configuredFeature : biome.getFeatures(Decoration.UNDERGROUND_ORES)) {
// IFeatureConfig config = configuredFeature.config;
// if (!(config instanceof DecoratedFeatureConfig))
// continue;
// DecoratedFeatureConfig decoConf = (DecoratedFeatureConfig) config;
// if (!(decoConf.feature.config instanceof OreFeatureConfig))
// continue;
// OreFeatureConfig oreConf = (OreFeatureConfig) decoConf.feature.config;
// System.out.println(configuredFeature.feature.getRegistryName().getPath());
// System.out.println(oreConf.state.getBlock().getRegistryName().getPath());
// System.out.println("--");
// }
// }
return;
event.getGeneration()
.feature(GenerationStage.Decoration.UNDERGROUND_ORES, entry.getFeature());
});
}
public static void fillConfig(ForgeConfigSpec.Builder builder) {
Arrays.stream(values()).forEach(entry -> {
builder.push(Lang.asId(entry.name()));
entry.feature.addToConfig(builder);
entries.values()
.forEach(entry -> {
builder.push(entry.id);
entry.addToConfig(builder);
builder.pop();
});
}
public static void register() {}
}

View file

@ -1,31 +0,0 @@
package com.simibubi.create.foundation.worldgen;
import org.apache.commons.lang3.tuple.Pair;
import com.tterrag.registrate.util.nullness.NonNullSupplier;
import net.minecraft.block.Block;
import net.minecraft.world.gen.placement.ChanceConfig;
import net.minecraft.world.gen.placement.Placement;
public class ChanceOreFeature extends OreFeature<ChanceConfig> {
private ConfigFloat clusterChance;
public ChanceOreFeature(NonNullSupplier<? extends Block> block, int clusterSize, float clusterChance) {
super(block, clusterSize);
this.clusterChance = f(clusterChance, 0, 1, "clusterChance");
}
@Override
protected boolean canGenerate() {
return super.canGenerate() && clusterChance.get() > 0;
}
@Override
protected Pair<Placement<ChanceConfig>, ChanceConfig> getPlacement() {
return Pair.of(Placement.CHANCE,
// TODO 1.16 worldgen verify this
new ChanceConfig((int) (1 / clusterChance.getF())));
}
}

View file

@ -0,0 +1,42 @@
package com.simibubi.create.foundation.worldgen;
import java.util.Random;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.gen.placement.SimplePlacement;
public class ConfigDrivenDecorator extends SimplePlacement<ConfigDrivenOreFeatureConfig> {
public static final ConfigDrivenDecorator INSTANCE = new ConfigDrivenDecorator();
public ConfigDrivenDecorator() {
super(ConfigDrivenOreFeatureConfig.CODEC);
setRegistryName("create_config_driven_decorator");
}
@Override
protected Stream<BlockPos> getPositions(Random r, ConfigDrivenOreFeatureConfig config, BlockPos pos) {
float frequency = config.getFrequency();
int floored = MathHelper.floor(frequency);
int count = floored + (r.nextFloat() < frequency - floored ? 1 : 0);
if (count == 0)
return Stream.empty();
int maxY = config.getMaxY();
int minY = config.getMinY();
return IntStream.range(0, count)
.mapToObj($ -> pos)
.map(p -> {
int i = p.getX();
int j = p.getZ();
int k = r.nextInt(maxY - minY) + minY;
return new BlockPos(i, k, j);
});
}
}

View file

@ -0,0 +1,68 @@
package com.simibubi.create.foundation.worldgen;
import java.util.Optional;
import com.simibubi.create.foundation.config.ConfigBase;
import com.tterrag.registrate.util.nullness.NonNullSupplier;
import net.minecraft.block.Block;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraft.world.gen.feature.OreFeatureConfig.FillerBlockType;
import net.minecraftforge.common.ForgeConfigSpec;
public class ConfigDrivenFeatureEntry extends ConfigBase {
public final String id;
public final NonNullSupplier<? extends Block> block;
protected ConfigInt clusterSize;
protected ConfigInt minHeight;
protected ConfigInt maxHeight;
protected ConfigFloat frequency;
Optional<ConfiguredFeature<?, ?>> feature = Optional.empty();
public ConfigDrivenFeatureEntry(String id, NonNullSupplier<? extends Block> block, int clusterSize,
float frequency) {
this.id = id;
this.block = block;
this.clusterSize = i(clusterSize, 0, "clusterSize");
this.minHeight = i(0, 0, "minHeight");
this.maxHeight = i(256, 0, "maxHeight");
this.frequency = f(frequency, 0, 512, "frequency", "Amount of clusters generated per Chunk.",
" >1 to spawn multiple.", " <1 to make it a chance.", " 0 to disable.");
}
public ConfigDrivenFeatureEntry between(int minHeight, int maxHeight) {
allValues.remove(this.minHeight);
allValues.remove(this.maxHeight);
this.minHeight = i(minHeight, 0, "minHeight");
this.maxHeight = i(maxHeight, 0, "maxHeight");
return this;
}
public ConfiguredFeature<?, ?> getFeature() {
if (!feature.isPresent())
feature = Optional.of(createFeature());
return feature.get();
}
private ConfiguredFeature<?, ?> createFeature() {
ConfigDrivenOreFeatureConfig config =
new ConfigDrivenOreFeatureConfig(FillerBlockType.BASE_STONE_OVERWORLD, block.get()
.getDefaultState(), id);
return ConfigDrivenOreFeature.INSTANCE.configure(config)
.decorate(ConfigDrivenDecorator.INSTANCE.configure(config));
}
public void addToConfig(ForgeConfigSpec.Builder builder) {
registerAll(builder);
}
@Override
public String getName() {
return id;
}
}

View file

@ -0,0 +1,141 @@
package com.simibubi.create.foundation.worldgen;
import java.util.BitSet;
import java.util.Random;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.world.ISeedReader;
import net.minecraft.world.IWorld;
import net.minecraft.world.gen.ChunkGenerator;
import net.minecraft.world.gen.Heightmap;
import net.minecraft.world.gen.feature.Feature;
public class ConfigDrivenOreFeature extends Feature<ConfigDrivenOreFeatureConfig> {
public static final ConfigDrivenOreFeature INSTANCE = new ConfigDrivenOreFeature();
public ConfigDrivenOreFeature() {
super(ConfigDrivenOreFeatureConfig.CODEC);
setRegistryName("create_config_driven_ore");
}
// From OreFeature, slight adjustments
public boolean generate(ISeedReader p_241855_1_, ChunkGenerator p_241855_2_, Random p_241855_3_,
BlockPos p_241855_4_, ConfigDrivenOreFeatureConfig p_241855_5_) {
float f = p_241855_3_.nextFloat() * (float) Math.PI;
float size = p_241855_5_.getSize();
float f1 = size / 8.0F;
int i = MathHelper.ceil((size / 16.0F * 2.0F + 1.0F) / 2.0F);
double d0 = (double) p_241855_4_.getX() + Math.sin((double) f) * (double) f1;
double d1 = (double) p_241855_4_.getX() - Math.sin((double) f) * (double) f1;
double d2 = (double) p_241855_4_.getZ() + Math.cos((double) f) * (double) f1;
double d3 = (double) p_241855_4_.getZ() - Math.cos((double) f) * (double) f1;
double d4 = (double) (p_241855_4_.getY() + p_241855_3_.nextInt(3) - 2);
double d5 = (double) (p_241855_4_.getY() + p_241855_3_.nextInt(3) - 2);
int k = p_241855_4_.getX() - MathHelper.ceil(f1) - i;
int l = p_241855_4_.getY() - 2 - i;
int i1 = p_241855_4_.getZ() - MathHelper.ceil(f1) - i;
int j1 = 2 * (MathHelper.ceil(f1) + i);
int k1 = 2 * (2 + i);
for (int l1 = k; l1 <= k + j1; ++l1) {
for (int i2 = i1; i2 <= i1 + j1; ++i2) {
if (l <= p_241855_1_.getHeight(Heightmap.Type.OCEAN_FLOOR_WG, l1, i2)) {
return this.func_207803_a(p_241855_1_, p_241855_3_, p_241855_5_, d0, d1, d2, d3, d4, d5, k, l, i1,
j1, k1);
}
}
}
return false;
}
protected boolean func_207803_a(IWorld p_207803_1_, Random p_207803_2_, ConfigDrivenOreFeatureConfig p_207803_3_,
double p_207803_4_, double p_207803_6_, double p_207803_8_, double p_207803_10_, double p_207803_12_,
double p_207803_14_, int p_207803_16_, int p_207803_17_, int p_207803_18_, int p_207803_19_, int p_207803_20_) {
int i = 0;
BitSet bitset = new BitSet(p_207803_19_ * p_207803_20_ * p_207803_19_);
BlockPos.Mutable blockpos$mutable = new BlockPos.Mutable();
int j = p_207803_3_.getSize();
double[] adouble = new double[j * 4];
for (int k = 0; k < j; ++k) {
float f = (float) k / (float) j;
double d0 = MathHelper.lerp((double) f, p_207803_4_, p_207803_6_);
double d2 = MathHelper.lerp((double) f, p_207803_12_, p_207803_14_);
double d4 = MathHelper.lerp((double) f, p_207803_8_, p_207803_10_);
double d6 = p_207803_2_.nextDouble() * (double) j / 16.0D;
double d7 = ((double) (MathHelper.sin((float) Math.PI * f) + 1.0F) * d6 + 1.0D) / 2.0D;
adouble[k * 4 + 0] = d0;
adouble[k * 4 + 1] = d2;
adouble[k * 4 + 2] = d4;
adouble[k * 4 + 3] = d7;
}
for (int i3 = 0; i3 < j - 1; ++i3) {
if (!(adouble[i3 * 4 + 3] <= 0.0D)) {
for (int k3 = i3 + 1; k3 < j; ++k3) {
if (!(adouble[k3 * 4 + 3] <= 0.0D)) {
double d12 = adouble[i3 * 4 + 0] - adouble[k3 * 4 + 0];
double d13 = adouble[i3 * 4 + 1] - adouble[k3 * 4 + 1];
double d14 = adouble[i3 * 4 + 2] - adouble[k3 * 4 + 2];
double d15 = adouble[i3 * 4 + 3] - adouble[k3 * 4 + 3];
if (d15 * d15 > d12 * d12 + d13 * d13 + d14 * d14) {
if (d15 > 0.0D) {
adouble[k3 * 4 + 3] = -1.0D;
} else {
adouble[i3 * 4 + 3] = -1.0D;
}
}
}
}
}
}
for (int j3 = 0; j3 < j; ++j3) {
double d11 = adouble[j3 * 4 + 3];
if (!(d11 < 0.0D)) {
double d1 = adouble[j3 * 4 + 0];
double d3 = adouble[j3 * 4 + 1];
double d5 = adouble[j3 * 4 + 2];
int l = Math.max(MathHelper.floor(d1 - d11), p_207803_16_);
int l3 = Math.max(MathHelper.floor(d3 - d11), p_207803_17_);
int i1 = Math.max(MathHelper.floor(d5 - d11), p_207803_18_);
int j1 = Math.max(MathHelper.floor(d1 + d11), l);
int k1 = Math.max(MathHelper.floor(d3 + d11), l3);
int l1 = Math.max(MathHelper.floor(d5 + d11), i1);
for (int i2 = l; i2 <= j1; ++i2) {
double d8 = ((double) i2 + 0.5D - d1) / d11;
if (d8 * d8 < 1.0D) {
for (int j2 = l3; j2 <= k1; ++j2) {
double d9 = ((double) j2 + 0.5D - d3) / d11;
if (d8 * d8 + d9 * d9 < 1.0D) {
for (int k2 = i1; k2 <= l1; ++k2) {
double d10 = ((double) k2 + 0.5D - d5) / d11;
if (d8 * d8 + d9 * d9 + d10 * d10 < 1.0D) {
int l2 = i2 - p_207803_16_ + (j2 - p_207803_17_) * p_207803_19_
+ (k2 - p_207803_18_) * p_207803_19_ * p_207803_20_;
if (!bitset.get(l2)) {
bitset.set(l2);
blockpos$mutable.setPos(i2, j2, k2);
if (p_207803_3_.target.test(p_207803_1_.getBlockState(blockpos$mutable),
p_207803_2_)) {
p_207803_1_.setBlockState(blockpos$mutable, p_207803_3_.state, 2);
++i;
}
}
}
}
}
}
}
}
}
}
return i > 0;
}
}

View file

@ -0,0 +1,56 @@
package com.simibubi.create.foundation.worldgen;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.block.BlockState;
import net.minecraft.world.gen.feature.IFeatureConfig;
import net.minecraft.world.gen.feature.template.RuleTest;
import net.minecraft.world.gen.placement.IPlacementConfig;
public class ConfigDrivenOreFeatureConfig implements IFeatureConfig, IPlacementConfig {
public static final Codec<ConfigDrivenOreFeatureConfig> CODEC = RecordCodecBuilder.create((p_236568_0_) -> {
return p_236568_0_.group(RuleTest.field_25012.fieldOf("target")
.forGetter((p_236570_0_) -> {
return p_236570_0_.target;
}), BlockState.CODEC.fieldOf("state")
.forGetter((p_236569_0_) -> {
return p_236569_0_.state;
}),
Codec.STRING.fieldOf("key")
.forGetter(t -> t.key))
.apply(p_236568_0_, ConfigDrivenOreFeatureConfig::new);
});
public final RuleTest target;
public final BlockState state;
public final String key;
public ConfigDrivenOreFeatureConfig(RuleTest target, BlockState state, String key) {
this.target = target;
this.state = state;
this.key = key;
}
public int getSize() {
return entry().clusterSize.get();
}
public int getMinY() {
return entry().minHeight.get();
}
public int getMaxY() {
return entry().maxHeight.get();
}
public float getFrequency() {
return entry().frequency.getF();
}
protected ConfigDrivenFeatureEntry entry() {
return AllWorldFeatures.entries.get(key);
}
}

View file

@ -1,41 +0,0 @@
package com.simibubi.create.foundation.worldgen;
import java.util.Optional;
import org.apache.commons.lang3.tuple.Pair;
import com.tterrag.registrate.util.nullness.NonNullSupplier;
import net.minecraft.block.Block;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraft.world.gen.placement.IPlacementConfig;
import net.minecraft.world.gen.placement.NoPlacementConfig;
import net.minecraft.world.gen.placement.Placement;
import net.minecraftforge.event.world.BiomeLoadingEvent;
public class CountedOreFeature extends OreFeature<NoPlacementConfig> {
private ConfigInt clusterCount;
public CountedOreFeature(NonNullSupplier<? extends Block> block, int clusterSize, int clusterCount) {
super(block, clusterSize);
this.clusterCount = i(clusterCount, 0, "clusterCount");
}
@Override
protected boolean canGenerate() {
return super.canGenerate() && clusterCount.get() > 0;
}
@Override
protected Pair<Placement<NoPlacementConfig>, NoPlacementConfig> getPlacement() {
return Pair.of(Placement.NOPE, IPlacementConfig.NO_PLACEMENT_CONFIG);
}
@Override
public Optional<ConfiguredFeature<?, ?>> createFeature(BiomeLoadingEvent biome) {
return super.createFeature(biome)
// TODO 1.16 worldgen verify this
.map(cf -> cf.repeat(clusterCount.get()));
}
}

View file

@ -1,20 +0,0 @@
package com.simibubi.create.foundation.worldgen;
import java.util.Optional;
import net.minecraft.world.gen.GenerationStage.Decoration;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.event.world.BiomeLoadingEvent;
public interface IFeature {
public void setId(String id);
public void addToConfig(ForgeConfigSpec.Builder builder);
public Optional<ConfiguredFeature<?, ?>> createFeature(BiomeLoadingEvent biome);
public Decoration getGenerationStage();
}

View file

@ -1,110 +0,0 @@
package com.simibubi.create.foundation.worldgen;
import java.util.Optional;
import org.apache.commons.lang3.tuple.Pair;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.config.ConfigBase;
import com.tterrag.registrate.util.nullness.NonNullSupplier;
import net.minecraft.block.Block;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.GenerationStage;
import net.minecraft.world.gen.GenerationStage.Decoration;
import net.minecraft.world.gen.feature.ConfiguredFeature;
import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.feature.OreFeatureConfig;
import net.minecraft.world.gen.placement.IPlacementConfig;
import net.minecraft.world.gen.placement.Placement;
import net.minecraft.world.gen.placement.TopSolidRangeConfig;
import net.minecraftforge.common.ForgeConfigSpec.Builder;
import net.minecraftforge.event.world.BiomeLoadingEvent;
public abstract class OreFeature<T extends IPlacementConfig> extends ConfigBase implements IFeature {
public String id;
protected ConfigBool enable;
protected ConfigInt clusterSize;
protected ConfigInt minHeight;
protected ConfigInt maxHeight;
private NonNullSupplier<? extends Block> block;
private Biome.Category specificCategory;
public OreFeature(NonNullSupplier<? extends Block> block, int clusterSize) {
this.block = block;
this.enable = b(true, "enable", "Whether to spawn this in your World");
this.clusterSize = i(clusterSize, 0, "clusterSize");
this.minHeight = i(0, 0, "minHeight");
this.maxHeight = i(256, 0, "maxHeight");
}
public OreFeature<T> between(int minHeight, int maxHeight) {
allValues.remove(this.minHeight);
allValues.remove(this.maxHeight);
this.minHeight = i(minHeight, 0, "minHeight");
this.maxHeight = i(maxHeight, 0, "maxHeight");
return this;
}
public OreFeature<T> inBiomes(Biome.Category category) {
specificCategory = category;
return this;
}
@Override
public void onReload() {
}
@Override
public Optional<ConfiguredFeature<?, ?>> createFeature(BiomeLoadingEvent biome) {
if (specificCategory != null && biome.getCategory() != specificCategory)
return Optional.empty();
if (!canGenerate())
return Optional.empty();
Pair<Placement<T>, T> placement = getPlacement();
ConfiguredFeature<?, ?> createdFeature = Feature.ORE
.configure(new OreFeatureConfig(OreFeatureConfig.FillerBlockType.BASE_STONE_OVERWORLD, block.get()
.getDefaultState(), clusterSize.get()))
.decorate(placement.getKey()
.configure(placement.getValue()))
.decorate(Placement.RANGE
// TODO 1.16 worldgen verify this
.configure(new TopSolidRangeConfig(minHeight.get(), 0, maxHeight.get() - minHeight.get())))
.spreadHorizontally();
return Optional.of(createdFeature);
}
@Override
public Decoration getGenerationStage() {
return GenerationStage.Decoration.UNDERGROUND_ORES;
}
protected boolean canGenerate() {
return minHeight.get() < maxHeight.get() && clusterSize.get() > 0 && enable.get()
&& !AllConfigs.COMMON.worldGen.disable.get();
}
protected abstract Pair<Placement<T>, T> getPlacement();
@Override
public void addToConfig(Builder builder) {
registerAll(builder);
}
@Override
public String getName() {
return id;
}
@Override
public void setId(String id) {
this.id = id;
}
}