[Change] BCLBiome Data is managed in a Datapack-Aware registry now

This commit is contained in:
Frank 2022-07-07 18:53:08 +02:00
parent 94c08e79b2
commit 32e7ffec69
29 changed files with 910 additions and 145 deletions

View file

@ -8,7 +8,7 @@ minecraft_version=1.19
loader_version=0.14.8
fabric_version=0.57.0+1.19
# Mod Properties
mod_version=2.0.10
mod_version=2.0.11
maven_group=org.betterx.bclib
archives_base_name=bclib
# Dependencies

View file

@ -8,6 +8,7 @@ import org.betterx.bclib.api.v2.generator.GeneratorOptions;
import org.betterx.bclib.api.v2.levelgen.LevelGenEvents;
import org.betterx.bclib.api.v2.levelgen.biomes.BCLBiome;
import org.betterx.bclib.api.v2.levelgen.biomes.BCLBiomeBuilder;
import org.betterx.bclib.api.v2.levelgen.biomes.BCLBiomeRegistry;
import org.betterx.bclib.api.v2.levelgen.biomes.BiomeAPI;
import org.betterx.bclib.api.v2.levelgen.structures.TemplatePiece;
import org.betterx.bclib.api.v2.levelgen.surface.rules.Conditions;
@ -42,6 +43,7 @@ public class BCLib implements ModInitializer {
public void onInitialize() {
LevelGenEvents.register();
BlockPredicates.ensureStaticInitialization();
BCLBiomeRegistry.ensureStaticallyLoaded();
BaseRegistry.register();
GeneratorOptions.init();
BaseBlockEntities.register();

View file

@ -6,6 +6,7 @@ import org.betterx.bclib.interfaces.NoiseGeneratorSettingsProvider;
import org.betterx.bclib.mixin.common.ChunkGeneratorAccessor;
import org.betterx.worlds.together.WorldsTogether;
import org.betterx.worlds.together.biomesource.MergeableBiomeSource;
import org.betterx.worlds.together.biomesource.ReloadableBiomeSource;
import org.betterx.worlds.together.chunkgenerator.EnforceableChunkGenerator;
import org.betterx.worlds.together.chunkgenerator.InjectableSurfaceRules;
import org.betterx.worlds.together.chunkgenerator.RestorableBiomeSource;
@ -110,6 +111,8 @@ public class BCLChunkGenerator extends NoiseBasedChunkGenerator implements Resto
if (this instanceof ChunkGeneratorAccessor acc) {
if (initialBiomeSource instanceof MergeableBiomeSource bs) {
acc.bcl_setBiomeSource(bs.mergeWithBiomeSource(getBiomeSource()));
} else if (initialBiomeSource instanceof ReloadableBiomeSource bs) {
bs.reloadBiomes();
}
rebuildFeaturesPerStep(getBiomeSource());

View file

@ -3,10 +3,12 @@ package org.betterx.bclib.api.v2.generator;
import org.betterx.bclib.BCLib;
import org.betterx.bclib.api.v2.generator.config.BCLEndBiomeSourceConfig;
import org.betterx.bclib.api.v2.levelgen.biomes.BCLBiome;
import org.betterx.bclib.api.v2.levelgen.biomes.BCLBiomeRegistry;
import org.betterx.bclib.api.v2.levelgen.biomes.BiomeAPI;
import org.betterx.bclib.config.Configs;
import org.betterx.bclib.interfaces.BiomeMap;
import org.betterx.worlds.together.biomesource.BiomeSourceWithConfig;
import org.betterx.worlds.together.biomesource.ReloadableBiomeSource;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
@ -32,7 +34,7 @@ import java.util.Set;
import java.util.function.BiFunction;
import org.jetbrains.annotations.NotNull;
public class BCLibEndBiomeSource extends BCLBiomeSource implements BiomeSourceWithConfig<BCLibEndBiomeSource, BCLEndBiomeSourceConfig> {
public class BCLibEndBiomeSource extends BCLBiomeSource implements BiomeSourceWithConfig<BCLibEndBiomeSource, BCLEndBiomeSourceConfig>, ReloadableBiomeSource {
public static Codec<BCLibEndBiomeSource> CODEC
= RecordCodecBuilder.create((instance) -> instance.group(
RegistryOps
@ -61,7 +63,7 @@ public class BCLibEndBiomeSource extends BCLBiomeSource implements BiomeSourceWi
private BiomeMap mapCenter;
private BiomeMap mapBarrens;
private final BiomePicker endLandBiomePicker;
private BiomePicker endLandBiomePicker;
private BiomePicker endVoidBiomePicker;
private BiomePicker endCenterBiomePicker;
private BiomePicker endBarrensBiomePicker;
@ -94,13 +96,25 @@ public class BCLibEndBiomeSource extends BCLBiomeSource implements BiomeSourceWi
) {
super(biomeRegistry, list, seed);
this.config = config;
rebuildBiomePickers();
this.endLandFunction = GeneratorOptions.getEndLandFunction();
this.pos = new Point();
if (initMaps) {
initMap(seed);
}
}
@NotNull
private void rebuildBiomePickers() {
var includeMap = Configs.BIOMES_CONFIG.getBiomeIncludeMap();
var excludeList = Configs.BIOMES_CONFIG.getExcludeMatching(BiomeAPI.BiomeType.END);
endLandBiomePicker = new BiomePicker(biomeRegistry);
endVoidBiomePicker = new BiomePicker(biomeRegistry);
endCenterBiomePicker = new BiomePicker(biomeRegistry);
endBarrensBiomePicker = new BiomePicker(biomeRegistry);
this.endLandBiomePicker = new BiomePicker(biomeRegistry);
this.endVoidBiomePicker = new BiomePicker(biomeRegistry);
this.endCenterBiomePicker = new BiomePicker(biomeRegistry);
this.endBarrensBiomePicker = new BiomePicker(biomeRegistry);
Map<BiomeAPI.BiomeType, BiomePicker> pickerMap = new HashMap<>();
pickerMap.put(BiomeAPI.BiomeType.END_LAND, endLandBiomePicker);
pickerMap.put(BiomeAPI.BiomeType.END_VOID, endVoidBiomePicker);
@ -126,7 +140,7 @@ public class BCLibEndBiomeSource extends BCLBiomeSource implements BiomeSourceWi
}
if (bclBiome != null || bclBiome != BiomeAPI.EMPTY_BIOME) {
if (bclBiome != null || bclBiome != BCLBiomeRegistry.EMPTY_BIOME) {
if (bclBiome.getParentBiome() == null) {
//ignore small islands when void biomes are disabled
if (!config.withVoidBiomes) {
@ -168,7 +182,6 @@ public class BCLibEndBiomeSource extends BCLBiomeSource implements BiomeSourceWi
}
}
}
});
endLandBiomePicker.rebuild();
@ -194,20 +207,15 @@ public class BCLibEndBiomeSource extends BCLBiomeSource implements BiomeSourceWi
endCenterBiomePicker = endLandBiomePicker;
}
}
this.endLandFunction = GeneratorOptions.getEndLandFunction();
this.pos = new Point();
if (initMaps) {
initMap(seed);
}
}
protected BCLBiomeSource cloneForDatapack(Set<Holder<Biome>> datapackBiomes) {
datapackBiomes.addAll(getBclBiomes(this.biomeRegistry));
return new BCLibEndBiomeSource(
this.biomeRegistry,
datapackBiomes.stream().toList(),
datapackBiomes.stream()
.filter(b -> b.unwrapKey().orElse(null) != BCLBiomeRegistry.EMPTY_BIOME.getBiomeKey())
.toList(),
this.currentSeed,
this.config,
true
@ -363,4 +371,10 @@ public class BCLibEndBiomeSource extends BCLBiomeSource implements BiomeSourceWi
this.config = newConfig;
this.initMap(currentSeed);
}
@Override
public void reloadBiomes() {
rebuildBiomePickers();
this.initMap(currentSeed);
}
}

View file

@ -4,11 +4,13 @@ import org.betterx.bclib.BCLib;
import org.betterx.bclib.api.v2.generator.config.BCLNetherBiomeSourceConfig;
import org.betterx.bclib.api.v2.generator.map.MapStack;
import org.betterx.bclib.api.v2.levelgen.biomes.BCLBiome;
import org.betterx.bclib.api.v2.levelgen.biomes.BCLBiomeRegistry;
import org.betterx.bclib.api.v2.levelgen.biomes.BiomeAPI;
import org.betterx.bclib.config.Configs;
import org.betterx.bclib.interfaces.BiomeMap;
import org.betterx.bclib.util.TriFunction;
import org.betterx.worlds.together.biomesource.BiomeSourceWithConfig;
import org.betterx.worlds.together.biomesource.ReloadableBiomeSource;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
@ -26,7 +28,7 @@ import net.fabricmc.fabric.api.biome.v1.NetherBiomes;
import java.util.List;
import java.util.Set;
public class BCLibNetherBiomeSource extends BCLBiomeSource implements BiomeSourceWithConfig<BCLibNetherBiomeSource, BCLNetherBiomeSourceConfig> {
public class BCLibNetherBiomeSource extends BCLBiomeSource implements BiomeSourceWithConfig<BCLibNetherBiomeSource, BCLNetherBiomeSourceConfig>, ReloadableBiomeSource {
public static final Codec<BCLibNetherBiomeSource> CODEC = RecordCodecBuilder
.create(instance -> instance
.group(
@ -49,7 +51,7 @@ public class BCLibNetherBiomeSource extends BCLBiomeSource implements BiomeSourc
.apply(instance, instance.stable(BCLibNetherBiomeSource::new))
);
private BiomeMap biomeMap;
private final BiomePicker biomePicker;
private BiomePicker biomePicker;
private BCLNetherBiomeSourceConfig config;
public BCLibNetherBiomeSource(Registry<Biome> biomeRegistry, BCLNetherBiomeSourceConfig config) {
@ -78,6 +80,13 @@ public class BCLibNetherBiomeSource extends BCLBiomeSource implements BiomeSourc
) {
super(biomeRegistry, list, seed);
this.config = config;
rebuildBiomePicker();
if (initMaps) {
initMap(seed);
}
}
private void rebuildBiomePicker() {
biomePicker = new BiomePicker(biomeRegistry);
this.possibleBiomes().forEach(biome -> {
@ -87,12 +96,13 @@ public class BCLibNetherBiomeSource extends BCLBiomeSource implements BiomeSourc
return;
}
if (!BiomeAPI.hasBiome(biomeID)) {
BCLBiome bclBiome = new BCLBiome(biomeID, biome.value());
biomePicker.addBiome(bclBiome);
} else {
BCLBiome bclBiome = BiomeAPI.getBiome(biomeID);
if (bclBiome != BiomeAPI.EMPTY_BIOME) {
if (bclBiome != BCLBiomeRegistry.EMPTY_BIOME) {
if (bclBiome.getParentBiome() == null) {
biomePicker.addBiome(bclBiome);
}
@ -101,9 +111,6 @@ public class BCLibNetherBiomeSource extends BCLBiomeSource implements BiomeSourc
});
biomePicker.rebuild();
if (initMaps) {
initMap(seed);
}
}
protected BCLBiomeSource cloneForDatapack(Set<Holder<Biome>> datapackBiomes) {
@ -213,4 +220,10 @@ public class BCLibNetherBiomeSource extends BCLBiomeSource implements BiomeSourc
this.config = newConfig;
initMap(currentSeed);
}
@Override
public void reloadBiomes() {
rebuildBiomePicker();
initMap(currentSeed);
}
}

View file

@ -1,7 +1,7 @@
package org.betterx.bclib.api.v2.generator;
import org.betterx.bclib.api.v2.levelgen.biomes.BCLBiome;
import org.betterx.bclib.api.v2.levelgen.biomes.BiomeAPI;
import org.betterx.bclib.api.v2.levelgen.biomes.BCLBiomeRegistry;
import org.betterx.bclib.util.WeighTree;
import org.betterx.bclib.util.WeightedList;
@ -37,7 +37,7 @@ public class BiomePicker {
.map(h -> h.unwrapKey())
.filter(o -> o.isPresent())
.map(o -> o.get().location().toString()).toList() : null;
this.fallbackBiome = create(BiomeAPI.EMPTY_BIOME);
this.fallbackBiome = create(BCLBiomeRegistry.EMPTY_BIOME);
}
private boolean isAllowed(BCLBiome b) {
@ -83,7 +83,7 @@ public class BiomePicker {
//no Biome, make sure we add at least one, otherwise bad things will happen
if (list.isEmpty()) {
list.add(create(BiomeAPI.EMPTY_BIOME), 1);
list.add(create(BCLBiomeRegistry.EMPTY_BIOME), 1);
}
@ -161,4 +161,13 @@ public class BiomePicker {
'}';
}
}
@Override
public String toString() {
return "BiomePicker{" +
"biomes=" + biomes.size() + " (" + all.size() + ")" +
", biomeRegistry=" + biomeRegistry +
", type=" + super.toString() +
'}';
}
}

View file

@ -2,39 +2,179 @@ package org.betterx.bclib.api.v2.levelgen.biomes;
import org.betterx.bclib.util.WeightedList;
import com.mojang.datafixers.Products;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.Registry;
import net.minecraft.data.BuiltinRegistries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.util.KeyDispatchDataCodec;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.Climate;
import net.minecraft.world.level.levelgen.WorldgenRandom;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.jetbrains.annotations.Nullable;
public class BCLBiome extends BCLBiomeSettings {
private final Set<TagKey<Biome>> biomeTags = Sets.newHashSet();
private final WeightedList<BCLBiome> subbiomes = new WeightedList<>();
public class BCLBiome extends BCLBiomeSettings implements BiomeData {
public static final Codec<BCLBiome> CODEC = RecordCodecBuilder.create(instance -> codecWithSettings(instance).apply(
instance,
BCLBiome::new
));
public static final KeyDispatchDataCodec<BCLBiome> KEY_CODEC = KeyDispatchDataCodec.of(CODEC);
public KeyDispatchDataCodec<? extends BCLBiome> codec() {
return KEY_CODEC;
}
private static class CodecAttributes<T extends BCLBiome> {
public RecordCodecBuilder<T, Float> t0 = Codec.FLOAT.fieldOf("terrainHeight")
.orElse(0.1f)
.forGetter((T o1) -> o1.terrainHeight);
public RecordCodecBuilder<T, Float> t1 = Codec.FLOAT.fieldOf("fogDensity")
.orElse(1.0f)
.forGetter((T o1) -> o1.fogDensity);
public RecordCodecBuilder<T, Float> t2 = Codec.FLOAT.fieldOf("genChance")
.orElse(1.0f)
.forGetter((T o1) -> o1.genChance);
public RecordCodecBuilder<T, Integer> t3 = Codec.INT.fieldOf("edgeSize")
.orElse(0)
.forGetter((T o1) -> o1.edgeSize);
public RecordCodecBuilder<T, Boolean> t4 = Codec.BOOL.fieldOf("vertical")
.orElse(false)
.forGetter((T o1) -> o1.vertical);
public RecordCodecBuilder<T, Optional<ResourceLocation>> t5 =
ResourceLocation.CODEC
.optionalFieldOf("edge")
.orElse(Optional.empty())
.forGetter((T o1) -> o1.edge == null
? Optional.empty()
: Optional.of(o1.edge.biomeID));
public RecordCodecBuilder<T, ResourceLocation> t6 =
ResourceLocation.CODEC.fieldOf("biome")
.forGetter((T o) -> ((BCLBiome) o).biomeID);
public RecordCodecBuilder<T, Optional<List<Climate.ParameterPoint>>> t7 =
Climate.ParameterPoint.CODEC.listOf()
.optionalFieldOf("parameter_points")
.orElse(Optional.of(List.of()))
.forGetter((T o) ->
o.parameterPoints == null || o.parameterPoints.isEmpty()
? Optional.empty()
: Optional.of(o.parameterPoints));
public RecordCodecBuilder<T, Optional<ResourceLocation>> t8 =
ResourceLocation.CODEC.optionalFieldOf("parent")
.orElse(Optional.empty())
.forGetter(
(T o1) ->
((BCLBiome) o1).biomeParent == null
? Optional.empty()
: Optional.of(
((BCLBiome) o1).biomeParent.biomeID));
public RecordCodecBuilder<T, Optional<WeightedList<ResourceLocation>>> t9 =
WeightedList.listCodec(
ResourceLocation.CODEC,
"biomes",
"biome"
)
.optionalFieldOf("sub_biomes")
.forGetter(
(T o) -> {
if (o.subbiomes == null
|| o.subbiomes.isEmpty()
|| (o.subbiomes.size() == 1 && o.subbiomes.contains(
o))) {
return Optional.empty();
}
return Optional.of(
o.subbiomes.map(
b -> b.biomeID));
});
public RecordCodecBuilder<T, Optional<String>> t10 =
Codec.STRING.optionalFieldOf("intended_for")
.orElse(Optional.of(BiomeAPI.BiomeType.NONE.getName()))
.forGetter((T o) ->
((BCLBiome) o).intendedType == null
? Optional.empty()
: Optional.of(((BCLBiome) o).intendedType.getName()));
}
public static <T extends BCLBiome, P12> Products.P12<RecordCodecBuilder.Mu<T>, Float, Float, Float, Integer, Boolean, Optional<ResourceLocation>, ResourceLocation, Optional<List<Climate.ParameterPoint>>, Optional<ResourceLocation>, Optional<WeightedList<ResourceLocation>>, Optional<String>, P12> codecWithSettings(
RecordCodecBuilder.Instance<T> instance,
final RecordCodecBuilder<T, P12> p12
) {
CodecAttributes<T> a = new CodecAttributes<>();
return instance.group(a.t0, a.t1, a.t2, a.t3, a.t4, a.t5, a.t6, a.t7, a.t8, a.t9, a.t10, p12);
}
public static <T extends BCLBiome, P12, P13> Products.P13<RecordCodecBuilder.Mu<T>, Float, Float, Float, Integer, Boolean, Optional<ResourceLocation>, ResourceLocation, Optional<List<Climate.ParameterPoint>>, Optional<ResourceLocation>, Optional<WeightedList<ResourceLocation>>, Optional<String>, P12, P13> codecWithSettings(
RecordCodecBuilder.Instance<T> instance,
final RecordCodecBuilder<T, P12> p12,
final RecordCodecBuilder<T, P13> p13
) {
CodecAttributes<T> a = new CodecAttributes<>();
return instance.group(a.t0, a.t1, a.t2, a.t3, a.t4, a.t5, a.t6, a.t7, a.t8, a.t9, a.t10, p12, p13);
}
public static <T extends BCLBiome> Products.P11<RecordCodecBuilder.Mu<T>, Float, Float, Float, Integer, Boolean, Optional<ResourceLocation>, ResourceLocation, Optional<List<Climate.ParameterPoint>>, Optional<ResourceLocation>, Optional<WeightedList<ResourceLocation>>, Optional<String>> codecWithSettings(
RecordCodecBuilder.Instance<T> instance
) {
CodecAttributes<T> a = new CodecAttributes<>();
return instance.group(a.t0, a.t1, a.t2, a.t3, a.t4, a.t5, a.t6, a.t7, a.t8, a.t9, a.t10);
}
protected final WeightedList<BCLBiome> subbiomes = new WeightedList<>();
private final Map<String, Object> customData = Maps.newHashMap();
private final ResourceLocation biomeID;
private final ResourceKey<Biome> biomeKey;
final Biome biomeToRegister;
private final List<Climate.ParameterPoint> parameterPoints = Lists.newArrayList();
protected final List<Climate.ParameterPoint> parameterPoints = Lists.newArrayList();
private BCLBiome biomeParent;
private BiomeAPI.BiomeType intendedType = BiomeAPI.BiomeType.NONE;
protected BCLBiome(
float terrainHeight,
float fogDensity,
float genChance,
int edgeSize,
boolean vertical,
Optional<ResourceLocation> edge,
ResourceLocation biomeID,
Optional<List<Climate.ParameterPoint>> parameterPoints,
Optional<ResourceLocation> biomeParent,
Optional<WeightedList<ResourceLocation>> subbiomes,
Optional<String> intendedType
) {
super(terrainHeight, fogDensity, genChance, edgeSize, vertical, edge.map(BiomeAPI::getBiome).orElse(null));
biomeToRegister = null;
this.biomeID = biomeID;
this.biomeKey = ResourceKey.create(Registry.BIOME_REGISTRY, biomeID);
if (subbiomes.isEmpty() || subbiomes.get().size() == 0) {
this.subbiomes.add(this, 1);
} else {
this.subbiomes.addAll(subbiomes.get().map(BiomeAPI::getBiome));
}
this.biomeParent = biomeParent.map(BiomeAPI::getBiome).orElse(null);
if (parameterPoints.isPresent()) this.parameterPoints.addAll(parameterPoints.get());
this.setIntendedType(intendedType.map(t -> BiomeAPI.BiomeType.create(t)).orElse(BiomeAPI.BiomeType.NONE));
}
/**
* Create wrapper for existing biome using its {@link ResourceLocation} identifier.
*
@ -81,6 +221,7 @@ public class BCLBiome extends BCLBiomeSettings {
* @param biomeID Teh ResoureLocation for this Biome
*/
@Deprecated(forRemoval = true)
//this constructor should become package private and not get removed
public BCLBiome(ResourceLocation biomeID, Biome biomeToRegister) {
this(biomeID, biomeToRegister, null);
}
@ -124,6 +265,25 @@ public class BCLBiome extends BCLBiomeSettings {
}
}
/**
* Changes the intended Type for this Biome
*
* @param type the new type
* @return the same instance
*/
protected BCLBiome setIntendedType(BiomeAPI.BiomeType type) {
return _setIntendedType(type);
}
BCLBiome _setIntendedType(BiomeAPI.BiomeType type) {
this.intendedType = type;
return this;
}
BiomeAPI.BiomeType getIntendedType() {
return this.intendedType;
}
/**
* Get current biome edge.
*
@ -250,6 +410,10 @@ public class BCLBiome extends BCLBiomeSettings {
return biomeKey;
}
public ResourceKey<BCLBiome> getBCLBiomeKey() {
return ResourceKey.create(BCLBiomeRegistry.BCL_BIOMES_REGISTRY, biomeID);
}
/**
* For internal use from BiomeAPI only
*/
@ -291,6 +455,7 @@ public class BCLBiome extends BCLBiomeSettings {
* @param obj any data to add.
* @return same {@link BCLBiome}.
*/
@Deprecated(forRemoval = true)
public BCLBiome addCustomData(String name, Object obj) {
customData.put(name, obj);
return this;
@ -302,6 +467,7 @@ public class BCLBiome extends BCLBiomeSettings {
* @param data a {@link Map} with custom data.
* @return same {@link BCLBiome}.
*/
@Deprecated(forRemoval = true)
public BCLBiome addCustomData(Map<String, Object> data) {
customData.putAll(data);
return this;

View file

@ -54,6 +54,7 @@ public class BCLBiomeBuilder {
public interface BiomeSupplier<T> extends TriFunction<ResourceLocation, Biome, BCLBiomeSettings, T> {
}
private static final BCLBiomeBuilder INSTANCE = new BCLBiomeBuilder();
private static final SurfaceRules.ConditionSource SURFACE_NOISE = SurfaceRules.noiseCondition(
Noises.SOUL_SAND_LAYER,
@ -709,6 +710,64 @@ public class BCLBiomeBuilder {
return this;
}
/**
* Changes the type for the Biome. The intended Type defines in which Dimension a
* Biome is allowed to spawn. Currently each Biome can only spawn in one dimension
*
* @param type The intended type
* @return same {@link BCLBiomeBuilder} instance.
*/
public BCLBiomeBuilder intendedType(BiomeAPI.BiomeType type) {
this.biomeType = type;
return this;
}
/**
* Changes the intended type for the Biome to an EndLand Biome
*
* @return same {@link BCLBiomeBuilder} instance.
*/
public BCLBiomeBuilder endLandBiome() {
return intendedType(BiomeAPI.BiomeType.BCL_END_LAND);
}
/**
* Changes the intended type for the Biome to an EndVoid (aka small islands) Biome
*
* @return same {@link BCLBiomeBuilder} instance.
*/
public BCLBiomeBuilder endVoidBiome() {
return intendedType(BiomeAPI.BiomeType.BCL_END_VOID);
}
/**
* Changes the intended type for the Biome to an Endbarrens Biome
*
* @return same {@link BCLBiomeBuilder} instance.
*/
public BCLBiomeBuilder endBarrensBiome() {
return intendedType(BiomeAPI.BiomeType.BCL_END_BARRENS);
}
/**
* Changes the intended type for the Biome to an End Center Island Biome
*
* @return same {@link BCLBiomeBuilder} instance.
*/
public BCLBiomeBuilder endCenterBiome() {
return intendedType(BiomeAPI.BiomeType.BCL_END_CENTER);
}
/**
* Changes the intended type for the Biome to a Nether Biome
*
* @return same {@link BCLBiomeBuilder} instance.
*/
public BCLBiomeBuilder netherBiome() {
return intendedType(BiomeAPI.BiomeType.BCL_NETHER);
}
public BCLBiomeBuilder tag(TagKey<Biome>... tag) {
for (TagKey<Biome> t : tag) {
tags.add(t);
@ -850,6 +909,8 @@ public class BCLBiomeBuilder {
//res.setSurface(surfaceRule);
SurfaceRuleRegistry.registerRule(biomeID, surfaceRule, biomeID);
res.addClimateParameters(parameters);
if (biomeType != null)
res._setIntendedType(biomeType);
//carvers.forEach(cfg -> BiomeAPI.addBiomeCarver(biome, cfg.second, cfg.first));

View file

@ -0,0 +1,145 @@
package org.betterx.bclib.api.v2.levelgen.biomes;
import org.betterx.bclib.BCLib;
import org.betterx.worlds.together.WorldsTogether;
import org.betterx.worlds.together.world.event.WorldBootstrap;
import com.mojang.serialization.Codec;
import com.mojang.serialization.Lifecycle;
import net.minecraft.core.Holder;
import net.minecraft.core.MappedRegistry;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.data.BuiltinRegistries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.KeyDispatchDataCodec;
import net.minecraft.world.level.biome.Biomes;
import java.util.Optional;
import java.util.stream.Stream;
import org.jetbrains.annotations.ApiStatus;
public class BCLBiomeRegistry {
public static final ResourceKey<Registry<BCLBiome>> BCL_BIOMES_REGISTRY =
createRegistryKey(WorldsTogether.makeID("worldgen/betterx/biome"));
public static final ResourceKey<Registry<Codec<? extends BCLBiome>>> BCL_BIOME_CODEC_REGISTRY =
createRegistryKey(WorldsTogether.makeID("worldgen/betterx/biome_codec"));
public static Registry<Codec<? extends BCLBiome>> BIOME_CODECS = Registry.registerSimple(
BCL_BIOME_CODEC_REGISTRY,
BCLBiomeRegistry::bootstrapCodecs
);
public static Registry<BCLBiome> BUILTIN_BCL_BIOMES = new MappedRegistry<>(
BCL_BIOMES_REGISTRY,
Lifecycle.stable(), null
);
/**
* Empty biome used as default value if requested biome doesn't exist or linked. Shouldn't be registered anywhere to prevent bugs.
* Have {@code Biomes.THE_VOID} as the reference biome.
**/
public static final BCLBiome EMPTY_BIOME = new BCLBiome(Biomes.THE_VOID.location());
public static Codec<? extends BCLBiome> registerBiomeCodec(
ResourceLocation location,
KeyDispatchDataCodec<? extends BCLBiome> codec
) {
Registry.register(BIOME_CODECS, location, codec.codec());
return codec.codec();
}
public static ResourceKey<BCLBiome> register(BCLBiome biome) {
Registry.register(BUILTIN_BCL_BIOMES, biome.getBCLBiomeKey(), biome);
return biome.getBCLBiomeKey();
}
private static <T> ResourceKey<Registry<T>> createRegistryKey(ResourceLocation location) {
return ResourceKey.createRegistryKey(location);
}
private static Codec<? extends BCLBiome> bootstrapCodecs(Registry<Codec<? extends BCLBiome>> registry) {
return Registry.register(registry, BCLib.makeID("biome"), BCLBiome.KEY_CODEC.codec());
}
@ApiStatus.Internal
public static Holder<BCLBiome> bootstrap(Registry<BCLBiome> registry) {
BuiltinRegistries.register(registry, BiomeAPI.SMALL_END_ISLANDS.getBCLBiomeKey(), BiomeAPI.SMALL_END_ISLANDS);
BuiltinRegistries.register(registry, BiomeAPI.END_BARRENS.getBCLBiomeKey(), BiomeAPI.END_BARRENS);
BuiltinRegistries.register(registry, BiomeAPI.END_HIGHLANDS.getBCLBiomeKey(), BiomeAPI.END_HIGHLANDS);
BuiltinRegistries.register(registry, BiomeAPI.END_MIDLANDS.getBCLBiomeKey(), BiomeAPI.END_MIDLANDS);
BuiltinRegistries.register(registry, BiomeAPI.THE_END.getBCLBiomeKey(), BiomeAPI.THE_END);
BuiltinRegistries.register(
registry,
BiomeAPI.BASALT_DELTAS_BIOME.getBCLBiomeKey(),
BiomeAPI.BASALT_DELTAS_BIOME
);
BuiltinRegistries.register(
registry,
BiomeAPI.SOUL_SAND_VALLEY_BIOME.getBCLBiomeKey(),
BiomeAPI.SOUL_SAND_VALLEY_BIOME
);
BuiltinRegistries.register(
registry,
BiomeAPI.WARPED_FOREST_BIOME.getBCLBiomeKey(),
BiomeAPI.WARPED_FOREST_BIOME
);
BuiltinRegistries.register(
registry,
BiomeAPI.CRIMSON_FOREST_BIOME.getBCLBiomeKey(),
BiomeAPI.CRIMSON_FOREST_BIOME
);
BuiltinRegistries.register(
registry,
BiomeAPI.NETHER_WASTES_BIOME.getBCLBiomeKey(),
BiomeAPI.NETHER_WASTES_BIOME
);
return BuiltinRegistries.register(registry, EMPTY_BIOME.getBCLBiomeKey(), EMPTY_BIOME);
}
public static BCLBiome get(ResourceLocation loc) {
return get(WorldBootstrap.getLastRegistryAccessOrElseBuiltin(), loc);
}
public static BCLBiome get(RegistryAccess access, ResourceLocation loc) {
return getBclBiomesRegistry(access).get(loc);
}
public static BCLBiome getOrElseEmpty(ResourceLocation loc) {
return getOrElseEmpty(WorldBootstrap.getLastRegistryAccessOrElseBuiltin(), loc);
}
public static BCLBiome getOrElseEmpty(RegistryAccess access, ResourceLocation loc) {
BCLBiome res = get(access, loc);
if (res == null) return EMPTY_BIOME;
return res;
}
public static Stream<ResourceKey<BCLBiome>> getAll(BiomeAPI.BiomeType dim) {
return getAll(WorldBootstrap.getLastRegistryAccessOrElseBuiltin(), dim);
}
public static Stream<ResourceKey<BCLBiome>> getAll(RegistryAccess access, BiomeAPI.BiomeType dim) {
return getBclBiomesRegistry(access)
.entrySet()
.stream()
.filter(e -> e.getValue().getIntendedType().is(BiomeAPI.BiomeType.END))
.map(e -> e.getKey());
}
private static Registry<BCLBiome> getBclBiomesRegistry(RegistryAccess access) {
if (access != null) {
return ((Optional<Registry<BCLBiome>>) access
.registry(BCLBiomeRegistry.BCL_BIOMES_REGISTRY))
.orElse(BUILTIN_BCL_BIOMES);
} else {
return BUILTIN_BCL_BIOMES;
}
}
public static void ensureStaticallyLoaded() {
}
}

View file

@ -103,6 +103,22 @@ public class BCLBiomeSettings {
}
}
BCLBiomeSettings(
float terrainHeight,
float fogDensity,
float genChance,
int edgeSize,
boolean vertical,
BCLBiome edge
) {
this.terrainHeight = terrainHeight;
this.fogDensity = fogDensity;
this.genChance = genChance;
this.edgeSize = edgeSize;
this.vertical = vertical;
this.edge = edge;
}
protected BCLBiomeSettings() {
this.terrainHeight = 0.1F;
this.fogDensity = 1.0F;

View file

@ -8,8 +8,10 @@ import org.betterx.bclib.mixin.common.MobSpawnSettingsAccessor;
import org.betterx.bclib.util.CollectionsUtil;
import org.betterx.worlds.together.tag.v3.CommonBiomeTags;
import org.betterx.worlds.together.tag.v3.TagManager;
import org.betterx.worlds.together.world.event.WorldBootstrap;
import net.minecraft.client.Minecraft;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderSet;
@ -38,53 +40,142 @@ import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.placement.PlacedFeature;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.biome.v1.NetherBiomes;
import net.fabricmc.fabric.api.biome.v1.TheEndBiomes;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class BiomeAPI {
public static class BiomeType {
public static final Codec<BiomeType> DIRECT_CODEC = RecordCodecBuilder.create(instance -> instance
.group(
Codec.STRING.fieldOf("name")
.orElse("undefined")
.forGetter(o -> o.name)
).apply(instance, BiomeType::create));
public static final Codec<BiomeType> CODEC = RecordCodecBuilder.create(instance -> instance
.group(
Codec.STRING.fieldOf("name")
.orElse("undefined")
.forGetter(o -> o.name),
Codec.STRING.fieldOf("parent")
.orElse("none")
.forGetter(o -> o.parentOrNull == null ? "none" : o.parentOrNull.name)
).apply(instance, BiomeType::create));
private static final Map<String, BiomeType> KNOWN_TYPES = new HashMap<>();
public static final BiomeType NONE = new BiomeType("NONE");
public static final BiomeType OVERWORLD = new BiomeType("OVERWORLD");
public static final BiomeType NETHER = new BiomeType("NETHER");
public static final BiomeType BCL_NETHER = new BiomeType("BCL_NETHER", NETHER);
public static final BiomeType BCL_NETHER = new BiomeType("BCL_NETHER", NETHER, (biome, ignored) -> {
ResourceKey<Biome> key = biome.getBiomeKey();
if (!biome.isEdgeBiome()) {
biome.forEachClimateParameter(p -> NetherBiomes.addNetherBiome(key, p));
}
});
public static final BiomeType END = new BiomeType("END");
public static final BiomeType END_IGNORE = new BiomeType("END_IGNORE", END);
public static final BiomeType END_LAND = new BiomeType("END_LAND", END);
public static final BiomeType END_VOID = new BiomeType("END_VOID", END);
public static final BiomeType END_CENTER = new BiomeType("END_CENTER", END);
public static final BiomeType END_BARRENS = new BiomeType("END_BARRENS", END);
public static final BiomeType BCL_END_LAND = new BiomeType("BCL_END_LAND", END_LAND);
public static final BiomeType BCL_END_VOID = new BiomeType("BCL_END_VOID", END_VOID);
public static final BiomeType BCL_END_CENTER = new BiomeType("BCL_END_CENTER", END_CENTER);
public static final BiomeType BCL_END_BARRENS = new BiomeType("BCL_END_BARRENS", END_BARRENS);
public static final BiomeType BCL_END_LAND = new BiomeType("BCL_END_LAND", END_LAND, (biome, ignored) -> {
float weight = biome.getGenChance();
ResourceKey<Biome> key = biome.getBiomeKey();
if (biome.isEdgeBiome()) {
ResourceKey<Biome> parentKey = biome.getParentBiome().getBiomeKey();
TheEndBiomes.addMidlandsBiome(parentKey, key, weight);
} else {
TheEndBiomes.addHighlandsBiome(key, weight);
}
});
public static final BiomeType BCL_END_VOID = new BiomeType("BCL_END_VOID", END_VOID, (biome, ignored) -> {
float weight = biome.getGenChance();
ResourceKey<Biome> key = biome.getBiomeKey();
if (!biome.isEdgeBiome()) {
TheEndBiomes.addSmallIslandsBiome(key, weight);
}
});
public static final BiomeType BCL_END_CENTER = new BiomeType("BCL_END_CENTER", END_CENTER, (biome, ignored) -> {
float weight = biome.getGenChance();
ResourceKey<Biome> key = biome.getBiomeKey();
if (!biome.isEdgeBiome()) {
TheEndBiomes.addMainIslandBiome(key, weight);
}
});
public static final BiomeType BCL_END_BARRENS = new BiomeType(
"BCL_END_BARRENS",
END_BARRENS,
(biome, highlandBiome) -> {
float weight = biome.getGenChance();
ResourceKey<Biome> key = biome.getBiomeKey();
if (!biome.isEdgeBiome()) {
ResourceKey<Biome> parentKey = highlandBiome.getBiomeKey();
TheEndBiomes.addBarrensBiome(parentKey, key, weight);
}
}
);
static final Map<ResourceLocation, BiomeType> BIOME_TYPE_MAP = Maps.newHashMap();
public final BiomeType parentOrNull;
private final String name;
@FunctionalInterface
interface ExtraRegisterTaks {
void register(@NotNull BCLBiome biome, @Nullable BCLBiome parent);
}
final ExtraRegisterTaks extraRegisterTask;
private static BiomeType create(String name, String parentOrNull) {
BiomeType known = KNOWN_TYPES.get(name);
BiomeType parent = parentOrNull == null || "none".equals(parentOrNull)
? null
: KNOWN_TYPES.get(parentOrNull);
if (known != null) {
if (known.parentOrNull != parent) {
BCLib.LOGGER.warning("BiomeType " + name + " was deserialized with parent " + parent + " but already has " + known.parentOrNull);
}
return known;
}
return new BiomeType(name, parent);
}
static BiomeType create(String name) {
BiomeType known = KNOWN_TYPES.get(name);
if (known != null) {
return known;
}
return NONE;
}
public BiomeType(String name) {
this(name, null);
}
public BiomeType(String name, BiomeType parentOrNull) {
this(name, parentOrNull, (b, a) -> {
});
}
public BiomeType(String name, BiomeType parentOrNull, ExtraRegisterTaks extraRegisterTask) {
this.parentOrNull = parentOrNull;
this.name = name;
this.extraRegisterTask = extraRegisterTask;
KNOWN_TYPES.put(name, this);
}
public boolean is(BiomeType d) {
@ -108,10 +199,10 @@ public class BiomeAPI {
/**
* Empty biome used as default value if requested biome doesn't exist or linked. Shouldn't be registered anywhere to prevent bugs.
* Have {@code Biomes.THE_VOID} as the reference biome.
*
* @deprecated use {@link BCLBiomeRegistry#EMPTY_BIOME} instead
*/
public static final BCLBiome EMPTY_BIOME = new BCLBiome(Biomes.THE_VOID.location());
private static final Map<ResourceLocation, BCLBiome> ID_MAP = Maps.newHashMap();
public static final BCLBiome EMPTY_BIOME = BCLBiomeRegistry.EMPTY_BIOME;
public static final BCLBiome NETHER_WASTES_BIOME = InternalBiomeAPI.wrapNativeBiome(
Biomes.NETHER_WASTES,
@ -166,6 +257,16 @@ public class BiomeAPI {
InternalBiomeAPI.OTHER_END_VOID
);
/**
* Register {@link BCLBiome} instance and its {@link Biome} if necessary.
*
* @param bclbiome {@link BCLBiome}
* @return {@link BCLBiome}
*/
public static BCLBiome registerBiome(BCLBiome bclbiome) {
return registerBiome(bclbiome, BuiltinRegistries.BIOME);
}
/**
* Register {@link BCLBiome} instance and its {@link Biome} if necessary.
*
@ -173,6 +274,7 @@ public class BiomeAPI {
* @param dim The Dimension fo rthis Biome
* @return {@link BCLBiome}
*/
@Deprecated(forRemoval = true)
public static BCLBiome registerBiome(BCLBiome bclbiome, BiomeType dim) {
return registerBiome(bclbiome, dim, BuiltinRegistries.BIOME);
}
@ -184,15 +286,27 @@ public class BiomeAPI {
* @param dim The Dimension fo rthis Biome
* @return {@link BCLBiome}
*/
@Deprecated(forRemoval = true)
static BCLBiome registerBiome(BCLBiome bclbiome, BiomeType dim, Registry<Biome> registryOrNull) {
bclbiome._setIntendedType(dim);
return registerBiome(bclbiome, registryOrNull);
}
/**
* Register {@link BCLBiome} instance and its {@link Biome} if necessary.
*
* @param bclbiome {@link BCLBiome}
* @return {@link BCLBiome}
*/
static BCLBiome registerBiome(BCLBiome bclbiome, Registry<Biome> registryOrNull) {
BiomeType dim = bclbiome.getIntendedType();
if (registryOrNull != null
&& bclbiome.biomeToRegister != null
&& registryOrNull.get(bclbiome.getID()) == null) {
Registry.register(registryOrNull, bclbiome.getBiomeKey(), bclbiome.biomeToRegister);
}
ID_MAP.put(bclbiome.getID(), bclbiome);
BiomeType.BIOME_TYPE_MAP.put(bclbiome.getID(), dim);
BCLBiomeRegistry.register(bclbiome);
}
if (dim != null && dim.is(BiomeType.NETHER)) {
TagManager.BIOMES.add(BiomeTags.IS_NETHER, bclbiome.getBiomeKey());
@ -211,7 +325,7 @@ public class BiomeAPI {
return registerSubBiome(
parent,
subBiome,
BiomeType.BIOME_TYPE_MAP.getOrDefault(parent.getID(), BiomeType.NONE)
parent.getIntendedType()
);
}
@ -220,7 +334,7 @@ public class BiomeAPI {
parent,
subBiome,
genChance,
BiomeType.BIOME_TYPE_MAP.getOrDefault(parent.getID(), BiomeType.NONE)
parent.getIntendedType()
);
}
@ -248,14 +362,14 @@ public class BiomeAPI {
float weight = biome.getGenChance();
ResourceKey<Biome> key = biome.getBiomeKey();
if (biome.allowFabricRegistration()) {
if (biome.isEdgeBiome()) {
ResourceKey<Biome> parentKey = biome.getParentBiome().getBiomeKey();
TheEndBiomes.addMidlandsBiome(parentKey, key, weight);
} else {
TheEndBiomes.addHighlandsBiome(key, weight);
}
}
return biome;
}
@ -272,7 +386,7 @@ public class BiomeAPI {
float weight = biome.getGenChance();
ResourceKey<Biome> key = biome.getBiomeKey();
if (biome.allowFabricRegistration()) {
if (!biome.isEdgeBiome()) {
TheEndBiomes.addSmallIslandsBiome(key, weight);
}
return biome;
@ -291,7 +405,7 @@ public class BiomeAPI {
float weight = biome.getGenChance();
ResourceKey<Biome> key = biome.getBiomeKey();
if (biome.allowFabricRegistration()) {
if (!biome.isEdgeBiome()) {
TheEndBiomes.addMainIslandBiome(key, weight);
}
return biome;
@ -310,7 +424,7 @@ public class BiomeAPI {
float weight = biome.getGenChance();
ResourceKey<Biome> key = biome.getBiomeKey();
if (biome.allowFabricRegistration()) {
if (!biome.isEdgeBiome()) {
ResourceKey<Biome> parentKey = highlandBiome.getBiomeKey();
TheEndBiomes.addBarrensBiome(parentKey, key, weight);
}
@ -318,17 +432,20 @@ public class BiomeAPI {
}
public static BCLBiome registerEndBiome(Holder<Biome> biome) {
BCLBiome bclBiome = new BCLBiome(biome.value(), null);
/**
* Register {@link BCLBiome} instance and its {@link Biome} if necessary.
* After that biome will be added to BCLib Nether Biome Generator and into Fabric Biome API.
*
* @param bclBiome {@link BCLBiome}
* @return {@link BCLBiome}
*/
public static BCLBiome registerNetherBiome(BCLBiome bclBiome) {
registerBiome(bclBiome, BiomeType.BCL_NETHER);
registerBiome(bclBiome, BiomeType.END);
return bclBiome;
ResourceKey<Biome> key = bclBiome.getBiomeKey();
if (!bclBiome.isEdgeBiome()) {
bclBiome.forEachClimateParameter(p -> NetherBiomes.addNetherBiome(key, p));
}
public static BCLBiome registerCenterBiome(Holder<Biome> biome) {
BCLBiome bclBiome = new BCLBiome(biome.value(), null);
registerBiome(bclBiome, BiomeType.END_CENTER);
return bclBiome;
}
@ -342,9 +459,13 @@ public class BiomeAPI {
@Deprecated(forRemoval = true)
public static BCLBiome getFromBiome(Holder<Biome> biome) {
if (InternalBiomeAPI.biomeRegistry == null) {
return EMPTY_BIOME;
return BCLBiomeRegistry.EMPTY_BIOME;
}
return ID_MAP.getOrDefault(biome.unwrapKey().orElseThrow().location(), EMPTY_BIOME);
return BCLBiomeRegistry
.getOrElseEmpty(
InternalBiomeAPI.registryAccess,
biome.unwrapKey().orElseThrow().location()
);
}
/**
@ -353,15 +474,15 @@ public class BiomeAPI {
* @param biome - {@link Biome} from client world.
* @return {@link BCLBiome} or {@code BiomeAPI.EMPTY_BIOME}.
*/
@Environment(EnvType.CLIENT)
public static BCLBiome getRenderBiome(Biome biome) {
BCLBiome endBiome = InternalBiomeAPI.CLIENT.get(biome);
if (endBiome == null) {
Minecraft minecraft = Minecraft.getInstance();
ResourceLocation id = minecraft.level.registryAccess()
ResourceLocation id = WorldBootstrap.getLastRegistryAccessOrElseBuiltin()
.registryOrThrow(Registry.BIOME_REGISTRY)
.getKey(biome);
endBiome = id == null ? EMPTY_BIOME : ID_MAP.getOrDefault(id, EMPTY_BIOME);
endBiome = id == null
? BCLBiomeRegistry.EMPTY_BIOME
: BCLBiomeRegistry.getOrElseEmpty(id);
InternalBiomeAPI.CLIENT.put(biome, endBiome);
}
return endBiome;
@ -401,7 +522,7 @@ public class BiomeAPI {
if (id == null) {
BCLib.LOGGER.error("Unable to get ID for " + biome + ". Falling back to empty Biome...");
id = EMPTY_BIOME.getID();
id = BCLBiomeRegistry.EMPTY_BIOME.getID();
}
return id;
@ -414,11 +535,10 @@ public class BiomeAPI {
* @return biome {@link ResourceLocation}.
*/
public static ResourceLocation getBiomeID(Holder<Biome> biome) {
var oKey = biome.unwrapKey();
if (oKey.isPresent()) {
return oKey.get().location();
}
return null;
return biome
.unwrapKey()
.map(h -> h.location())
.orElse(null);
}
public static ResourceKey getBiomeKey(Holder<Biome> biome) {
@ -462,7 +582,8 @@ public class BiomeAPI {
* @return {@link BCLBiome} or {@code BiomeAPI.EMPTY_BIOME}.
*/
public static BCLBiome getBiome(ResourceLocation biomeID) {
return ID_MAP.getOrDefault(biomeID, EMPTY_BIOME);
if (biomeID == null) return BCLBiomeRegistry.EMPTY_BIOME;
return BCLBiomeRegistry.getOrElseEmpty(biomeID);
}
/**
@ -492,7 +613,7 @@ public class BiomeAPI {
* @return {@code true} if biome exists in API registry and {@code false} if not.
*/
public static boolean hasBiome(ResourceLocation biomeID) {
return ID_MAP.containsKey(biomeID);
return BCLBiomeRegistry.get(biomeID) != null;
}
public static Holder<Biome> getFromRegistry(ResourceLocation biomeID) {
@ -537,12 +658,9 @@ public class BiomeAPI {
}
public static boolean wasRegisteredAs(ResourceLocation biomeID, BiomeType dim) {
if (BiomeType.BIOME_TYPE_MAP.containsKey(biomeID) && BiomeType.BIOME_TYPE_MAP.get(biomeID).is(dim)) return true;
BCLBiome biome = getBiome(biomeID);
if (biome != null && biome != BiomeAPI.EMPTY_BIOME && biome.getParentBiome() != null) {
return wasRegisteredAs(biome.getParentBiome().getID(), dim);
}
if (BCLBiomeRegistry.EMPTY_BIOME.getID().equals(biomeID))
return false;
return BCLBiomeRegistry.getOrElseEmpty(biomeID).getIntendedType().is(dim);
}
public static boolean wasRegisteredAsNetherBiome(ResourceLocation biomeID) {
@ -883,22 +1001,6 @@ public class BiomeAPI {
return features.get(index).stream().collect(Collectors.toList());
}
/**
* Register {@link BCLBiome} instance and its {@link Biome} if necessary.
* After that biome will be added to BCLib Nether Biome Generator and into Fabric Biome API.
*
* @param bclBiome {@link BCLBiome}
* @return {@link BCLBiome}
*/
public static BCLBiome registerNetherBiome(BCLBiome bclBiome) {
registerBiome(bclBiome, BiomeType.BCL_NETHER);
ResourceKey<Biome> key = bclBiome.getBiomeKey();
if (bclBiome.allowFabricRegistration()) {
bclBiome.forEachClimateParameter(p -> NetherBiomes.addNetherBiome(key, p));
}
return bclBiome;
}
/**
* Register {@link BCLBiome} instance and its {@link Biome} if necessary.
@ -962,4 +1064,22 @@ public class BiomeAPI {
InternalBiomeAPI.OTHER_END_VOID
);
}
@Deprecated(forRemoval = true)
public static BCLBiome registerEndBiome(Holder<Biome> biome) {
BCLBiome bclBiome = new BCLBiome(biome.value(), null);
registerBiome(bclBiome, BiomeType.END);
return bclBiome;
}
@Deprecated(forRemoval = true)
public static BCLBiome registerCenterBiome(Holder<Biome> biome) {
BCLBiome bclBiome = new BCLBiome(biome.value(), null);
registerBiome(bclBiome, BiomeType.END_CENTER);
return bclBiome;
}
}

View file

@ -0,0 +1,15 @@
package org.betterx.bclib.api.v2.levelgen.biomes;
import com.mojang.serialization.Codec;
import net.minecraft.util.KeyDispatchDataCodec;
import java.util.function.Function;
public interface BiomeData {
Codec<BCLBiome> CODEC = BCLBiomeRegistry
.BIOME_CODECS
.byNameCodec()
.dispatch(b -> b.codec().codec(), Function.identity());
KeyDispatchDataCodec<? extends BCLBiome> codec();
}

View file

@ -165,16 +165,10 @@ public class InternalBiomeAPI {
public static void _runBiomeTagAdders() {
for (var mod : TAG_ADDERS.entrySet()) {
Stream<ResourceLocation> s = null;
if (mod.getKey() == Level.NETHER) s = BiomeAPI.BiomeType.BIOME_TYPE_MAP.entrySet()
.stream()
.filter(e -> e.getValue()
.is(BiomeAPI.BiomeType.NETHER))
.map(e -> e.getKey());
else if (mod.getKey() == Level.END) s = BiomeAPI.BiomeType.BIOME_TYPE_MAP.entrySet()
.stream()
.filter(e -> e.getValue().is(
BiomeAPI.BiomeType.END))
.map(e -> e.getKey());
if (mod.getKey() == Level.NETHER)
s = BCLBiomeRegistry.getAll(BiomeAPI.BiomeType.NETHER).map(k -> k.location());
else if (mod.getKey() == Level.END)
s = BCLBiomeRegistry.getAll(BiomeAPI.BiomeType.END).map(k -> k.location());
if (s != null) {
s.forEach(id -> {
Holder<Biome> biomeHolder = BiomeAPI.getFromRegistry(id);
@ -326,11 +320,12 @@ public class InternalBiomeAPI {
BiomeAPI.BiomeType type
) {
BCLBiome bclBiome = BiomeAPI.getBiome(biomeKey.location());
if (bclBiome == BiomeAPI.EMPTY_BIOME) {
if (bclBiome == BCLBiomeRegistry.EMPTY_BIOME) {
bclBiome = new BCLBiome(biomeKey, setings);
bclBiome._setIntendedType(type);
}
BiomeAPI.registerBiome(bclBiome, type);
BiomeAPI.registerBiome(bclBiome);
return bclBiome;
}
@ -345,7 +340,7 @@ public class InternalBiomeAPI {
@Deprecated(forRemoval = true)
static BCLBiome wrapNativeBiome(Biome biome, float genChance, BiomeAPI.BiomeType type) {
BCLBiome bclBiome = BiomeAPI.getBiome(biome);
if (bclBiome == BiomeAPI.EMPTY_BIOME) {
if (bclBiome == BCLBiomeRegistry.EMPTY_BIOME) {
bclBiome = new BCLBiome(
biome,
genChance < 0 ? null : VanillaBiomeSettings.createVanilla().setGenChance(genChance).build()
@ -363,7 +358,7 @@ public class InternalBiomeAPI {
.event(oBiomeRegistry.get())
.register((rawId, id, biome) -> {
BCLBiome b = BiomeAPI.getBiome(id);
if (!"minecraft".equals(id.getNamespace()) && (b == null || b == BiomeAPI.EMPTY_BIOME)) {
if (!"minecraft".equals(id.getNamespace()) && (b == null || b == BCLBiomeRegistry.EMPTY_BIOME)) {
//BCLib.LOGGER.info(" #### " + rawId + ", " + biome + ", " + id);
BIOMES_TO_SORT.add(id);
}

View file

@ -1,6 +1,7 @@
package org.betterx.bclib.client.render;
import org.betterx.bclib.api.v2.levelgen.biomes.BCLBiome;
import org.betterx.bclib.api.v2.levelgen.biomes.BCLBiomeRegistry;
import org.betterx.bclib.api.v2.levelgen.biomes.BiomeAPI;
import org.betterx.bclib.config.Configs;
import org.betterx.bclib.util.BackgroundInfo;
@ -116,7 +117,7 @@ public class CustomFogRenderer {
private static boolean shouldIgnore(Level level, int x, int y, int z) {
Biome biome = level.getBiome(MUT_POS.set(x, y, z)).value();
return BiomeAPI.getRenderBiome(biome) == BiomeAPI.EMPTY_BIOME;
return BiomeAPI.getRenderBiome(biome) == BCLBiomeRegistry.EMPTY_BIOME;
}
private static float getFogDensityI(Level level, int x, int y, int z) {

View file

@ -1,9 +1,10 @@
package org.betterx.bclib.commands;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.betterx.bclib.BCLib;
import org.betterx.bclib.api.v2.levelgen.biomes.BCLBiome;
import org.betterx.bclib.api.v2.levelgen.biomes.BiomeData;
import org.betterx.bclib.blocks.BaseStairsBlock;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.serialization.Codec;
@ -27,8 +28,11 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.RuleTestType;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorList;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorType;
import net.minecraft.world.level.material.Fluid;
import org.betterx.bclib.BCLib;
import org.betterx.bclib.blocks.BaseStairsBlock;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.awt.Taskbar.Feature;
import java.io.File;
@ -120,6 +124,8 @@ public class DumpDatapack {
if (obj instanceof Structure s) {
codec[0] = s.type().codec();
} else if (obj instanceof BCLBiome s) {
codec[0] = BiomeData.CODEC;
} else if (obj instanceof StructureProcessorList s) {
codec[0] = StructureProcessorType.LIST_OBJECT_CODEC;
} else if (obj instanceof GameEvent) {

View file

@ -0,0 +1,40 @@
package org.betterx.bclib.mixin.common;
import org.betterx.bclib.api.v2.levelgen.biomes.BCLBiomeRegistry;
import com.mojang.serialization.Lifecycle;
import net.minecraft.core.MappedRegistry;
import net.minecraft.core.Registry;
import net.minecraft.core.WritableRegistry;
import net.minecraft.data.BuiltinRegistries;
import net.minecraft.resources.ResourceKey;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(BuiltinRegistries.class)
public abstract class BuiltinRegistriesMixin {
@Shadow
protected static <T, R extends WritableRegistry<T>> R internalRegister(
ResourceKey<? extends Registry<T>> resourceKey,
R writableRegistry,
BuiltinRegistries.RegistryBootstrap<T> registryBootstrap,
Lifecycle lifecycle
) {
throw new RuntimeException("Shadowed Call");
}
//this needs to be added BEFORE the WORLD_PRESET-Registry. Otherwise decoding will fail!
@Inject(method = "<clinit>", at = @At(value = "INVOKE", target = "Lnet/minecraft/data/BuiltinRegistries;registerSimple(Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/data/BuiltinRegistries$RegistryBootstrap;)Lnet/minecraft/core/Registry;", ordinal = 0))
private static void bcl_registerBuiltin(CallbackInfo ci) {
BCLBiomeRegistry.BUILTIN_BCL_BIOMES = internalRegister(
BCLBiomeRegistry.BCL_BIOMES_REGISTRY,
(MappedRegistry) BCLBiomeRegistry.BUILTIN_BCL_BIOMES,
BCLBiomeRegistry::bootstrap,
Lifecycle.stable()
);
}
}

View file

@ -0,0 +1,48 @@
package org.betterx.bclib.mixin.common;
import org.betterx.bclib.api.v2.levelgen.biomes.BCLBiomeRegistry;
import org.betterx.bclib.api.v2.levelgen.biomes.BiomeData;
import com.mojang.serialization.Codec;
import net.minecraft.core.Registry;
import net.minecraft.core.RegistryAccess;
import net.minecraft.resources.ResourceKey;
import com.google.common.collect.ImmutableMap;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import java.util.Map;
import java.util.function.Supplier;
@Mixin(RegistryAccess.class)
public interface RegistryAccessMixin {
@ModifyArg(method = "<clinit>", at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;make(Ljava/util/function/Supplier;)Ljava/lang/Object;"))
private static Supplier<ImmutableMap<ResourceKey<Registry<?>>, RegistryAccess.RegistryData<?>>> together_addRegistry(
Supplier<ImmutableMap<ResourceKey<Registry<?>>, RegistryAccess.RegistryData<?>>> supplier
) {
return () -> {
ImmutableMap.Builder<ResourceKey<Registry<?>>, RegistryAccess.RegistryData<?>> builder = ImmutableMap.builder();
//Make sure this gets added before WORLD_PRESETS
put(builder, BCLBiomeRegistry.BCL_BIOMES_REGISTRY, BiomeData.CODEC);
Map<ResourceKey<Registry<?>>, RegistryAccess.RegistryData<?>> res = supplier.get();
builder.putAll(res);
return builder.build();
};
}
@Shadow
static <E> void put(
ImmutableMap.Builder<ResourceKey<Registry<?>>, RegistryAccess.RegistryData<?>> builder,
ResourceKey<? extends Registry<E>> resourceKey,
Codec<E> codec
) {
throw new RuntimeException("Shadowed Call");
}
}

View file

@ -1,8 +1,20 @@
package org.betterx.bclib.util;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import java.util.Objects;
public class Pair<A, B> {
public static <A, B> Codec<Pair<A, B>> pairCodec(Codec<A> a, Codec<B> b, String first, String second) {
return RecordCodecBuilder.create(instance -> instance
.group(
a.fieldOf(first).forGetter(o -> o.first),
b.fieldOf(second).forGetter(o -> o.second)
)
.apply(instance, Pair::new));
}
public final A first;
public final B second;

View file

@ -1,17 +1,71 @@
package org.betterx.bclib.util;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.util.RandomSource;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.function.Consumer;
import java.util.function.Function;
public class WeightedList<T> {
private final List<Float> weights = new ArrayList<Float>();
private final List<T> values = new ArrayList<T>();
private float maxWeight;
public static <T> Codec<Pair<Float, T>> pairCodec(Codec<T> elementCodec, String fieldName) {
return Pair.pairCodec(Codec.FLOAT, elementCodec, "weight", fieldName);
}
public static <T> Codec<WeightedList<T>> listCodec(Codec<T> elementCodec, String fieldName, String elementName) {
return RecordCodecBuilder.create(instance -> instance
.group(
pairCodec(elementCodec, elementName).listOf()
.fieldOf(fieldName)
.forGetter(WeightedList::pairs)
)
.apply(instance, WeightedList::new)
);
}
private List<Pair<Float, T>> pairs() {
List<Pair<Float, T>> pairs = new ArrayList<>(weights.size());
for (int i = 0; i < weights.size(); i++) {
pairs.add(new Pair<>(weights.get(i), values.get(i)));
}
return pairs;
}
private WeightedList(List<Pair<Float, T>> pairs) {
maxWeight = 0;
for (var pair : pairs) {
maxWeight += pair.first;
weights.add(pair.first);
values.add(pair.second);
}
}
public WeightedList() {
}
public <R> WeightedList<R> map(Function<T, R> map) {
List<Pair<Float, R>> pairs = new ArrayList<>(weights.size());
for (int i = 0; i < weights.size(); i++) {
pairs.add(new Pair<>(weights.get(i), map.apply(values.get(i))));
}
return new WeightedList<>(pairs);
}
public void addAll(WeightedList<T> other) {
weights.addAll(other.weights);
values.addAll(other.values);
maxWeight += other.maxWeight;
}
/**
* Adds value with specified weight to the list
*

View file

@ -0,0 +1,5 @@
package org.betterx.worlds.together.biomesource;
public interface ReloadableBiomeSource {
void reloadBiomes();
}

View file

@ -4,6 +4,6 @@ import net.minecraft.resources.ResourceKey;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.dimension.LevelStem;
public interface RestorableBiomeSource<G extends ChunkGenerator> {
public interface RestorableBiomeSource<B extends ChunkGenerator> {
void restoreInitialBiomeSource(ResourceKey<LevelStem> dimensionKey);
}

View file

@ -2,6 +2,7 @@ package org.betterx.worlds.together.levelgen;
import org.betterx.worlds.together.WorldsTogether;
import org.betterx.worlds.together.biomesource.BiomeSourceWithConfig;
import org.betterx.worlds.together.biomesource.ReloadableBiomeSource;
import org.betterx.worlds.together.chunkgenerator.EnforceableChunkGenerator;
import org.betterx.worlds.together.world.BiomeSourceWithNoiseRelatedSettings;
import org.betterx.worlds.together.world.BiomeSourceWithSeed;
@ -141,6 +142,7 @@ public class WorldGenUtil {
) {
var dimensions = TogetherWorldPreset.loadWorldDimensions();
for (var entry : settings.dimensions().entrySet()) {
boolean didRepair = false;
ResourceKey<LevelStem> key = entry.getKey();
LevelStem loadedStem = entry.getValue();
@ -156,6 +158,7 @@ public class WorldGenUtil {
loadedChunkGenerator,
settings
);
didRepair = true;
} else if (loadedChunkGenerator.getBiomeSource() instanceof BiomeSourceWithConfig bs) {
if (referenceGenerator.getBiomeSource() instanceof BiomeSourceWithConfig refbs) {
if (!refbs.getTogetherConfig().sameConfig(bs.getTogetherConfig())) {
@ -164,6 +167,12 @@ public class WorldGenUtil {
}
}
}
if (!didRepair) {
if (loadedStem.generator().getBiomeSource() instanceof ReloadableBiomeSource reload) {
reload.reloadBiomes();
}
}
}
return settings;
}

View file

@ -0,0 +1,22 @@
package org.betterx.worlds.together.mixin.common;
import org.betterx.worlds.together.world.event.WorldBootstrap;
import net.minecraft.core.RegistryAccess;
import net.minecraft.server.WorldLoader;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
@Mixin(WorldLoader.class)
public class WorldLoaderMixin {
//this is the place a new Registry access gets first istantiated
//either when a new Datapack was added to a world on the create-screen
//or because we did start world loading
@ModifyArg(method = "load", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/ReloadableServerResources;loadResources(Lnet/minecraft/server/packs/resources/ResourceManager;Lnet/minecraft/core/RegistryAccess$Frozen;Lnet/minecraft/commands/Commands$CommandSelection;ILjava/util/concurrent/Executor;Ljava/util/concurrent/Executor;)Ljava/util/concurrent/CompletableFuture;"))
private static RegistryAccess.Frozen wt_newRegistry(RegistryAccess.Frozen frozen) {
WorldBootstrap.InGUI.registryReady(frozen);
return frozen;
}
}

View file

@ -16,7 +16,7 @@ import org.jetbrains.annotations.ApiStatus;
public class SurfaceRuleRegistry {
public static final ResourceKey<Registry<AssignedSurfaceRule>> SURFACE_RULES_REGISTRY =
createRegistryKey(WorldsTogether.makeID("worldgen/surface_rules"));
createRegistryKey(WorldsTogether.makeID("worldgen/betterx/surface_rules"));
public static final Predicate<ResourceKey<LevelStem>> NON_MANAGED_DIMENSIONS = dim -> dim != LevelStem.NETHER && dim != LevelStem.END;
public static final Predicate<ResourceKey<LevelStem>> ALL_DIMENSIONS = dim -> true;

View file

@ -154,6 +154,10 @@ public class WorldBootstrap {
}
}
public static void registryReady(RegistryAccess access) {
Helpers.onRegistryReady(access);
}
public static void setupNewWorld(
Optional<LevelStorageSource.LevelStorageAccess> levelStorageAccess,
WorldGenSettingsComponent worldGenSettingsComponent

View file

@ -11,6 +11,7 @@ accessible class net/minecraft/world/level/levelgen/SurfaceRules$SequenceRuleSou
accessible class net/minecraft/world/level/levelgen/presets/WorldPresets$Bootstrap
extendable class net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator
accessible class net/minecraft/data/BuiltinRegistries$RegistryBootstrap
accessible class net/minecraft/core/Registry$RegistryBootstrap
accessible class net/minecraft/world/level/levelgen/SurfaceRules$SequenceRuleSource
#Methods
@ -18,3 +19,4 @@ accessible method net/minecraft/client/gui/screens/worldselection/WorldGenSettin
accessible method net/minecraft/world/level/storage/loot/LootPool <init> ([Lnet/minecraft/world/level/storage/loot/entries/LootPoolEntryContainer;[Lnet/minecraft/world/level/storage/loot/predicates/LootItemCondition;[Lnet/minecraft/world/level/storage/loot/functions/LootItemFunction;Lnet/minecraft/world/level/storage/loot/providers/number/NumberProvider;Lnet/minecraft/world/level/storage/loot/providers/number/NumberProvider;)V
accessible method net/minecraft/world/entity/ai/village/poi/PoiTypes register (Lnet/minecraft/core/Registry;Lnet/minecraft/resources/ResourceKey;Ljava/util/Set;II)Lnet/minecraft/world/entity/ai/village/poi/PoiType;
accessible method net/minecraft/world/level/levelgen/SurfaceRules$SequenceRuleSource <init> (Ljava/util/List;)V
accessible method net/minecraft/core/Registry registerSimple (Lnet/minecraft/resources/ResourceKey;Lnet/minecraft/core/Registry$RegistryBootstrap;)Lnet/minecraft/core/Registry;

View file

@ -10,6 +10,7 @@
"BiomeMixin",
"BiomeSourceMixin",
"BoneMealItemMixin",
"BuiltinRegistriesMixin",
"ChunkGeneratorAccessor",
"ChunkGeneratorMixin",
"ChunkGeneratorsMixin",
@ -28,6 +29,7 @@
"PotionBrewingAccessor",
"RecipeManagerAccessor",
"RecipeManagerMixin",
"RegistryAccessMixin",
"ServerLevelMixin",
"ShovelItemAccessor",
"StructuresAccessor",

View file

@ -1,7 +1,7 @@
{
"schemaVersion": 1,
"id": "bclib",
"version": "2.0.10",
"version": "2.0.11",
"name": "BCLib",
"description": "A library for BetterX team mods",
"authors": [

View file

@ -17,6 +17,7 @@
"RegistryOpsAccessor",
"TagLoaderMixin",
"WorldGenPropertiesMixin",
"WorldLoaderMixin",
"WorldPresetAccessor",
"WorldPresetMixin",
"WorldPresetsBootstrapMixin"