New BCLib biome API migration (WIP)
This commit is contained in:
parent
8bea55a8d2
commit
f0e9303869
13 changed files with 26 additions and 445 deletions
|
@ -6,13 +6,13 @@ minecraft_version=1.17.1
|
||||||
yarn_mappings=6
|
yarn_mappings=6
|
||||||
loader_version=0.11.6
|
loader_version=0.11.6
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version=0.11.0-pre
|
mod_version=0.12.0-pre
|
||||||
maven_group=ru.betterend
|
maven_group=ru.betterend
|
||||||
archives_base_name=better-end
|
archives_base_name=better-end
|
||||||
# Dependencies
|
# Dependencies
|
||||||
# currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api
|
# currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api
|
||||||
patchouli_version = 55-FABRIC-SNAPSHOT
|
patchouli_version = 55-FABRIC-SNAPSHOT
|
||||||
fabric_version = 0.36.1+1.17
|
fabric_version = 0.36.1+1.17
|
||||||
bclib_version = 0.3.0
|
bclib_version = 0.4.0
|
||||||
rei_version = 6.0.264-alpha
|
rei_version = 6.0.264-alpha
|
||||||
canvas_version = 1.0.+
|
canvas_version = 1.0.+
|
||||||
|
|
|
@ -3,6 +3,7 @@ package ru.betterend;
|
||||||
import net.fabricmc.api.ModInitializer;
|
import net.fabricmc.api.ModInitializer;
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import ru.bclib.api.BiomeAPI;
|
||||||
import ru.bclib.api.WorldDataAPI;
|
import ru.bclib.api.WorldDataAPI;
|
||||||
import ru.bclib.util.Logger;
|
import ru.bclib.util.Logger;
|
||||||
import ru.betterend.api.BetterEndPlugin;
|
import ru.betterend.api.BetterEndPlugin;
|
||||||
|
@ -26,8 +27,8 @@ import ru.betterend.registry.EndStructures;
|
||||||
import ru.betterend.registry.EndTags;
|
import ru.betterend.registry.EndTags;
|
||||||
import ru.betterend.util.BonemealPlants;
|
import ru.betterend.util.BonemealPlants;
|
||||||
import ru.betterend.util.LootTableUtil;
|
import ru.betterend.util.LootTableUtil;
|
||||||
import ru.betterend.world.generator.BetterEndBiomeSource;
|
|
||||||
import ru.betterend.world.generator.GeneratorOptions;
|
import ru.betterend.world.generator.GeneratorOptions;
|
||||||
|
import ru.betterend.world.generator.TerrainGenerator;
|
||||||
import ru.betterend.world.surface.SurfaceBuilders;
|
import ru.betterend.world.surface.SurfaceBuilders;
|
||||||
|
|
||||||
public class BetterEnd implements ModInitializer {
|
public class BetterEnd implements ModInitializer {
|
||||||
|
@ -44,7 +45,6 @@ public class BetterEnd implements ModInitializer {
|
||||||
EndEntities.register();
|
EndEntities.register();
|
||||||
SurfaceBuilders.register();
|
SurfaceBuilders.register();
|
||||||
EndBiomes.register();
|
EndBiomes.register();
|
||||||
BetterEndBiomeSource.register();
|
|
||||||
EndTags.register();
|
EndTags.register();
|
||||||
EndEnchantments.register();
|
EndEnchantments.register();
|
||||||
EndPotions.register();
|
EndPotions.register();
|
||||||
|
@ -62,6 +62,11 @@ public class BetterEnd implements ModInitializer {
|
||||||
Integrations.init();
|
Integrations.init();
|
||||||
DataFixer.register();
|
DataFixer.register();
|
||||||
Configs.saveConfigs();
|
Configs.saveConfigs();
|
||||||
|
|
||||||
|
if (GeneratorOptions.useNewGenerator()) {
|
||||||
|
ru.bclib.world.generator.GeneratorOptions.setFarEndBiomes(GeneratorOptions.getIslandDistBlock() > 250000L);
|
||||||
|
ru.bclib.world.generator.GeneratorOptions.setEndLandFunction((pos) -> TerrainGenerator.isLand(pos.x, pos.y));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ResourceLocation makeID(String path) {
|
public static ResourceLocation makeID(String path) {
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
package ru.betterend;
|
package ru.betterend;
|
||||||
|
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.nbt.NbtIo;
|
|
||||||
import ru.bclib.api.datafixer.DataFixerAPI;
|
import ru.bclib.api.datafixer.DataFixerAPI;
|
||||||
import ru.bclib.api.datafixer.Patch;
|
import ru.bclib.api.datafixer.Patch;
|
||||||
import ru.bclib.api.datafixer.PatchFunction;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public class DataFixer {
|
public class DataFixer {
|
||||||
public static void register() {
|
public static void register() {
|
||||||
|
|
|
@ -1,22 +1,12 @@
|
||||||
package ru.betterend.integration.byg;
|
package ru.betterend.integration.byg;
|
||||||
|
|
||||||
import net.minecraft.data.BuiltinRegistries;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
import net.minecraft.world.entity.ai.behavior.ShufflingList;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import ru.bclib.api.BiomeAPI;
|
|
||||||
import ru.bclib.api.TagAPI;
|
import ru.bclib.api.TagAPI;
|
||||||
import ru.bclib.integration.ModIntegration;
|
import ru.bclib.integration.ModIntegration;
|
||||||
import ru.bclib.world.biomes.BCLBiome;
|
|
||||||
import ru.betterend.integration.EndBiomeIntegration;
|
import ru.betterend.integration.EndBiomeIntegration;
|
||||||
import ru.betterend.integration.Integrations;
|
import ru.betterend.integration.Integrations;
|
||||||
import ru.betterend.integration.byg.biomes.BYGBiomes;
|
import ru.betterend.integration.byg.biomes.BYGBiomes;
|
||||||
import ru.betterend.integration.byg.features.BYGFeatures;
|
import ru.betterend.integration.byg.features.BYGFeatures;
|
||||||
import ru.betterend.registry.EndBiomes;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
public class BYGIntegration extends ModIntegration implements EndBiomeIntegration {
|
public class BYGIntegration extends ModIntegration implements EndBiomeIntegration {
|
||||||
public BYGIntegration() {
|
public BYGIntegration() {
|
||||||
|
@ -38,10 +28,10 @@ public class BYGIntegration extends ModIntegration implements EndBiomeIntegratio
|
||||||
public void addBiomes() {
|
public void addBiomes() {
|
||||||
BYGBiomes.addBiomes();
|
BYGBiomes.addBiomes();
|
||||||
|
|
||||||
Class<?> biomeClass = this.getClass("corgiaoc.byg.common.world.biome.BYGEndBiome");
|
//Class<?> biomeClass = this.getClass("corgiaoc.byg.common.world.biome.BYGEndBiome");
|
||||||
List<Object> biomes = this.getStaticFieldValue(biomeClass, "BYG_END_BIOMES");
|
//List<Object> biomes = this.getStaticFieldValue(biomeClass, "BYG_END_BIOMES");
|
||||||
|
|
||||||
if (biomes != null && biomeClass != null) {
|
/*if (biomes != null && biomeClass != null) {
|
||||||
biomes.forEach((obj) -> {
|
biomes.forEach((obj) -> {
|
||||||
Biome biome = this.getAndExecuteRuntime(biomeClass, obj, "getBiome");
|
Biome biome = this.getAndExecuteRuntime(biomeClass, obj, "getBiome");
|
||||||
if (biome != null) {
|
if (biome != null) {
|
||||||
|
@ -80,6 +70,6 @@ public class BYGIntegration extends ModIntegration implements EndBiomeIntegratio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +1,14 @@
|
||||||
package ru.betterend.mixin.common;
|
package ru.betterend.mixin.common;
|
||||||
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
|
||||||
import net.minecraft.world.level.dimension.DimensionType;
|
import net.minecraft.world.level.dimension.DimensionType;
|
||||||
import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
|
|
||||||
import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.injection.At;
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
import ru.betterend.world.generator.BetterEndBiomeSource;
|
|
||||||
import ru.betterend.world.generator.GeneratorOptions;
|
import ru.betterend.world.generator.GeneratorOptions;
|
||||||
|
|
||||||
@Mixin(value = DimensionType.class, priority = 100)
|
@Mixin(value = DimensionType.class, priority = 100)
|
||||||
public class DimensionTypeMixin {
|
public class DimensionTypeMixin {
|
||||||
@Inject(method = "defaultEndGenerator", at = @At("HEAD"), cancellable = true)
|
|
||||||
private static void be_replaceGenerator(Registry<Biome> biomeRegistry, Registry<NoiseGeneratorSettings> chunkGeneratorSettingsRegistry, long seed, CallbackInfoReturnable<ChunkGenerator> info) {
|
|
||||||
info.setReturnValue(new NoiseBasedChunkGenerator(
|
|
||||||
new BetterEndBiomeSource(biomeRegistry, seed),
|
|
||||||
seed,
|
|
||||||
() -> chunkGeneratorSettingsRegistry.getOrThrow(NoiseGeneratorSettings.END)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject(method = "createDragonFight", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "createDragonFight", at = @At("HEAD"), cancellable = true)
|
||||||
private void be_hasEnderDragonFight(CallbackInfoReturnable<Boolean> info) {
|
private void be_hasEnderDragonFight(CallbackInfoReturnable<Boolean> info) {
|
||||||
if (!GeneratorOptions.hasDragonFights()) {
|
if (!GeneratorOptions.hasDragonFights()) {
|
||||||
|
|
|
@ -30,7 +30,6 @@ import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
import ru.betterend.BetterEnd;
|
import ru.betterend.BetterEnd;
|
||||||
import ru.betterend.effects.EndStatusEffects;
|
|
||||||
import ru.betterend.interfaces.FallFlyingItem;
|
import ru.betterend.interfaces.FallFlyingItem;
|
||||||
import ru.betterend.interfaces.MobEffectApplier;
|
import ru.betterend.interfaces.MobEffectApplier;
|
||||||
import ru.betterend.item.CrystaliteArmor;
|
import ru.betterend.item.CrystaliteArmor;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package ru.betterend.mixin.common;
|
package ru.betterend.mixin.common;
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.server.MinecraftServer;
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
@ -48,7 +49,7 @@ public abstract class ServerLevelMixin extends Level {
|
||||||
|
|
||||||
be_lastWorld = session.getLevelId();
|
be_lastWorld = session.getLevelId();
|
||||||
ServerLevel world = ServerLevel.class.cast(this);
|
ServerLevel world = ServerLevel.class.cast(this);
|
||||||
EndBiomes.onWorldLoad(world.getSeed());
|
EndBiomes.onWorldLoad(world.getSeed(), world.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "getSharedSpawnPos", at = @At("HEAD"), cancellable = true)
|
@Inject(method = "getSharedSpawnPos", at = @At("HEAD"), cancellable = true)
|
||||||
|
|
|
@ -1,29 +1,13 @@
|
||||||
package ru.betterend.registry;
|
package ru.betterend.registry;
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import com.google.common.collect.Sets;
|
|
||||||
import com.google.gson.JsonElement;
|
|
||||||
import com.google.gson.JsonObject;
|
|
||||||
import net.fabricmc.fabric.impl.biome.InternalBiomeData;
|
|
||||||
import net.fabricmc.fabric.impl.biome.WeightedBiomePicker;
|
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.data.BuiltinRegistries;
|
|
||||||
import net.minecraft.resources.ResourceKey;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.level.biome.Biome;
|
import net.minecraft.world.level.biome.Biome;
|
||||||
import net.minecraft.world.level.biome.Biome.BiomeCategory;
|
|
||||||
import net.minecraft.world.level.biome.Biomes;
|
|
||||||
import ru.bclib.BCLib;
|
|
||||||
import ru.bclib.api.BiomeAPI;
|
import ru.bclib.api.BiomeAPI;
|
||||||
import ru.bclib.api.ModIntegrationAPI;
|
|
||||||
import ru.bclib.util.JsonFactory;
|
|
||||||
import ru.bclib.world.biomes.BCLBiome;
|
import ru.bclib.world.biomes.BCLBiome;
|
||||||
import ru.bclib.world.generator.BiomeMap;
|
import ru.bclib.world.generator.BiomeMap;
|
||||||
import ru.bclib.world.generator.BiomePicker;
|
import ru.bclib.world.generator.BiomePicker;
|
||||||
import ru.betterend.config.Configs;
|
import ru.betterend.config.Configs;
|
||||||
import ru.betterend.integration.EndBiomeIntegration;
|
|
||||||
import ru.betterend.interfaces.IBiomeList;
|
|
||||||
import ru.betterend.world.biome.EndBiome;
|
import ru.betterend.world.biome.EndBiome;
|
||||||
import ru.betterend.world.biome.air.BiomeIceStarfield;
|
import ru.betterend.world.biome.air.BiomeIceStarfield;
|
||||||
import ru.betterend.world.biome.cave.EmptyAuroraCaveBiome;
|
import ru.betterend.world.biome.cave.EmptyAuroraCaveBiome;
|
||||||
|
@ -54,32 +38,10 @@ import ru.betterend.world.biome.land.UmbrellaJungleBiome;
|
||||||
import ru.betterend.world.generator.BiomeType;
|
import ru.betterend.world.generator.BiomeType;
|
||||||
import ru.betterend.world.generator.GeneratorOptions;
|
import ru.betterend.world.generator.GeneratorOptions;
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class EndBiomes {
|
public class EndBiomes {
|
||||||
public static final Set<ResourceLocation> FABRIC_VOID = Sets.newHashSet();
|
|
||||||
private static final Set<ResourceLocation> SUBBIOMES_UNMUTABLES = Sets.newHashSet();
|
|
||||||
|
|
||||||
public static final BiomePicker LAND_BIOMES = new BiomePicker();
|
|
||||||
public static final BiomePicker VOID_BIOMES = new BiomePicker();
|
|
||||||
public static final BiomePicker CAVE_BIOMES = new BiomePicker();
|
public static final BiomePicker CAVE_BIOMES = new BiomePicker();
|
||||||
public static final List<BCLBiome> SUBBIOMES = Lists.newArrayList();
|
|
||||||
private static final JsonObject EMPTY_JSON = new JsonObject();
|
|
||||||
private static BiomeMap caveBiomeMap;
|
private static BiomeMap caveBiomeMap;
|
||||||
|
|
||||||
// Vanilla Land
|
|
||||||
public static final EndBiome END = registerBiome(Biomes.THE_END, BiomeType.LAND, 1F);
|
|
||||||
public static final EndBiome END_MIDLANDS = registerSubBiome(Biomes.END_MIDLANDS, END, 0.5F);
|
|
||||||
public static final EndBiome END_HIGHLANDS = registerSubBiome(Biomes.END_HIGHLANDS, END, 0.5F);
|
|
||||||
|
|
||||||
// Vanilla Void
|
|
||||||
public static final EndBiome END_BARRENS = registerBiome(Biomes.END_BARRENS, BiomeType.VOID, 1F);
|
|
||||||
public static final EndBiome SMALL_END_ISLANDS = registerBiome(Biomes.SMALL_END_ISLANDS, BiomeType.VOID, 1);
|
|
||||||
|
|
||||||
// Better End Land
|
// Better End Land
|
||||||
public static final EndBiome FOGGY_MUSHROOMLAND = registerBiome(new FoggyMushroomlandBiome(), BiomeType.LAND);
|
public static final EndBiome FOGGY_MUSHROOMLAND = registerBiome(new FoggyMushroomlandBiome(), BiomeType.LAND);
|
||||||
public static final EndBiome CHORUS_FOREST = registerBiome(new ChorusForestBiome(), BiomeType.LAND);
|
public static final EndBiome CHORUS_FOREST = registerBiome(new ChorusForestBiome(), BiomeType.LAND);
|
||||||
|
@ -111,184 +73,16 @@ public class EndBiomes {
|
||||||
public static final EndCaveBiome LUSH_AURORA_CAVE = registerCaveBiome(new LushAuroraCaveBiome());
|
public static final EndCaveBiome LUSH_AURORA_CAVE = registerCaveBiome(new LushAuroraCaveBiome());
|
||||||
public static final EndCaveBiome JADE_CAVE = registerCaveBiome(new JadeCaveBiome());
|
public static final EndCaveBiome JADE_CAVE = registerCaveBiome(new JadeCaveBiome());
|
||||||
|
|
||||||
public static void register() {
|
public static void register() {}
|
||||||
CAVE_BIOMES.rebuild();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void onWorldLoad(long seed) {
|
public static void onWorldLoad(long seed, Registry<Biome> registry) {
|
||||||
|
CAVE_BIOMES.getBiomes().forEach(biome -> biome.updateActualBiomes(registry));
|
||||||
|
CAVE_BIOMES.rebuild();
|
||||||
if (caveBiomeMap == null || caveBiomeMap.getSeed() != seed) {
|
if (caveBiomeMap == null || caveBiomeMap.getSeed() != seed) {
|
||||||
caveBiomeMap = new BiomeMap(seed, GeneratorOptions.getBiomeSizeCaves(), CAVE_BIOMES);
|
caveBiomeMap = new BiomeMap(seed, GeneratorOptions.getBiomeSizeCaves(), CAVE_BIOMES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void mutateRegistry(Registry<Biome> biomeRegistry) {
|
|
||||||
LAND_BIOMES.clearMutables();
|
|
||||||
VOID_BIOMES.clearMutables();
|
|
||||||
CAVE_BIOMES.clearMutables();
|
|
||||||
|
|
||||||
if (FABRIC_VOID.isEmpty()) {
|
|
||||||
loadFabricAPIBiomes();
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<String, JsonObject> configs = Maps.newHashMap();
|
|
||||||
|
|
||||||
biomeRegistry.forEach((biome) -> {
|
|
||||||
if (biome.getBiomeCategory() == BiomeCategory.THEEND) {
|
|
||||||
ResourceLocation id = biomeRegistry.getKey(biome);
|
|
||||||
if (!id.getNamespace().equals("ultra_amplified_dimension") && Configs.BIOME_CONFIG.getBoolean(
|
|
||||||
id,
|
|
||||||
"enabled",
|
|
||||||
true
|
|
||||||
)) {
|
|
||||||
if (!LAND_BIOMES.containsImmutable(id) && !VOID_BIOMES.containsImmutable(id) && !SUBBIOMES_UNMUTABLES
|
|
||||||
.contains(id)) {
|
|
||||||
JsonObject config = configs.get(id.getNamespace());
|
|
||||||
if (config == null) {
|
|
||||||
config = loadJsonConfig(id.getNamespace());
|
|
||||||
configs.put(id.getNamespace(), config);
|
|
||||||
}
|
|
||||||
float fog = 1F;
|
|
||||||
float chance = 1F;
|
|
||||||
boolean isVoid = FABRIC_VOID.contains(id);
|
|
||||||
boolean hasCaves = true;
|
|
||||||
JsonElement element = config.get(id.getPath());
|
|
||||||
if (element != null && element.isJsonObject()) {
|
|
||||||
fog = JsonFactory.getFloat(element.getAsJsonObject(), "fog_density", 1);
|
|
||||||
chance = JsonFactory.getFloat(element.getAsJsonObject(), "generation_chance", 1);
|
|
||||||
isVoid = JsonFactory.getString(element.getAsJsonObject(), "type", "land").equals("void");
|
|
||||||
hasCaves = JsonFactory.getBoolean(element.getAsJsonObject(), "has_caves", true);
|
|
||||||
}
|
|
||||||
EndBiome endBiome = new EndBiome(id, biome, fog, chance, hasCaves);
|
|
||||||
|
|
||||||
if (isVoid) {
|
|
||||||
VOID_BIOMES.addBiomeMutable(endBiome);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
LAND_BIOMES.addBiomeMutable(endBiome);
|
|
||||||
}
|
|
||||||
BiomeAPI.registerBiome(endBiome);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
ModIntegrationAPI.getIntegrations().forEach(integration -> {
|
|
||||||
if (integration instanceof EndBiomeIntegration && integration.modIsInstalled()) {
|
|
||||||
((EndBiomeIntegration) integration).addBiomes();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Configs.BIOME_CONFIG.saveChanges();
|
|
||||||
|
|
||||||
rebuildPicker(LAND_BIOMES, biomeRegistry);
|
|
||||||
rebuildPicker(VOID_BIOMES, biomeRegistry);
|
|
||||||
rebuildPicker(CAVE_BIOMES, biomeRegistry);
|
|
||||||
|
|
||||||
SUBBIOMES.forEach((endBiome) -> {
|
|
||||||
endBiome.updateActualBiomes(biomeRegistry);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void rebuildPicker(BiomePicker picker, Registry<Biome> biomeRegistry) {
|
|
||||||
picker.rebuild();
|
|
||||||
picker.getBiomes().forEach((endBiome) -> {
|
|
||||||
endBiome.updateActualBiomes(biomeRegistry);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void loadFabricAPIBiomes() {
|
|
||||||
List<ResourceKey<Biome>> biomes = Lists.newArrayList();
|
|
||||||
biomes.addAll(getBiomes(InternalBiomeData.getEndBiomesMap().get(Biomes.SMALL_END_ISLANDS)));
|
|
||||||
biomes.addAll(getBiomes(InternalBiomeData.getEndBarrensMap().get(Biomes.END_BARRENS)));
|
|
||||||
biomes.forEach((key) -> FABRIC_VOID.add(key.location()));
|
|
||||||
FABRIC_VOID.removeIf(id -> id.getNamespace().equals("endplus"));
|
|
||||||
|
|
||||||
if (BCLib.isDevEnvironment()) {
|
|
||||||
System.out.println("==================================");
|
|
||||||
System.out.println("Added void biomes from Fabric API:");
|
|
||||||
FABRIC_VOID.forEach((id) -> {
|
|
||||||
System.out.println(id);
|
|
||||||
});
|
|
||||||
System.out.println("==================================");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<ResourceKey<Biome>> getBiomes(WeightedBiomePicker picker) {
|
|
||||||
IBiomeList biomeList = (IBiomeList) (Object) picker;
|
|
||||||
return biomeList == null ? Collections.emptyList() : biomeList.getBiomes();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static JsonObject loadJsonConfig(String namespace) {
|
|
||||||
InputStream inputstream = EndBiomes.class.getResourceAsStream("/data/" + namespace + "/end_biome_properties.json");
|
|
||||||
if (inputstream != null) {
|
|
||||||
return JsonFactory.getJsonObject(inputstream);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return EMPTY_JSON;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers new {@link EndBiome} and adds it to picker, can be used to add existing mod biomes into the End.
|
|
||||||
*
|
|
||||||
* @param biome - {@link Biome} instance
|
|
||||||
* @param type - {@link BiomeType}
|
|
||||||
* @param genChance - generation chance [0.0F - Infinity]
|
|
||||||
* @return registered {@link EndBiome}
|
|
||||||
*/
|
|
||||||
public static EndBiome registerBiome(Biome biome, BiomeType type, float genChance) {
|
|
||||||
return registerBiome(biome, type, 1, genChance);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers new {@link EndBiome} and adds it to picker, can be used to add existing mod biomes into the End.
|
|
||||||
*
|
|
||||||
* @param biome - {@link Biome} instance
|
|
||||||
* @param type - {@link BiomeType}
|
|
||||||
* @param fogDensity - density of fog (def: 1F) [0.0F - Infinity]
|
|
||||||
* @param genChance - generation chance [0.0F - Infinity]
|
|
||||||
* @return registered {@link EndBiome}
|
|
||||||
*/
|
|
||||||
public static EndBiome registerBiome(Biome biome, BiomeType type, float fogDensity, float genChance) {
|
|
||||||
EndBiome endBiome = new EndBiome(BuiltinRegistries.BIOME.getKey(biome), biome, fogDensity, genChance, true);
|
|
||||||
BiomeAPI.registerBiome(endBiome);
|
|
||||||
if (Configs.BIOME_CONFIG.getBoolean(endBiome.getID(), "enabled", true)) {
|
|
||||||
addToPicker(endBiome, type);
|
|
||||||
}
|
|
||||||
return endBiome;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers new {@link EndBiome} from existed {@link Biome} and put as a sub-biome into selected parent.
|
|
||||||
*
|
|
||||||
* @param biome - {@link Biome} instance
|
|
||||||
* @param parent - {@link EndBiome} to be linked with
|
|
||||||
* @param genChance - generation chance [0.0F - Infinity]
|
|
||||||
* @return registered {@link EndBiome}
|
|
||||||
*/
|
|
||||||
public static EndBiome registerSubBiome(Biome biome, EndBiome parent, float genChance, boolean hasCaves) {
|
|
||||||
return registerSubBiome(biome, parent, 1, genChance, hasCaves);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers new {@link EndBiome} from existed {@link Biome} and put as a sub-biome into selected parent.
|
|
||||||
*
|
|
||||||
* @param biome - {@link Biome} instance
|
|
||||||
* @param parent - {@link EndBiome} to be linked with
|
|
||||||
* @param fogDensity - density of fog (def: 1F) [0.0F - Infinity]
|
|
||||||
* @param genChance - generation chance [0.0F - Infinity]
|
|
||||||
* @return registered {@link EndBiome}
|
|
||||||
*/
|
|
||||||
public static EndBiome registerSubBiome(Biome biome, EndBiome parent, float fogDensity, float genChance, boolean hasCaves) {
|
|
||||||
EndBiome endBiome = new EndBiome(BuiltinRegistries.BIOME.getKey(biome), biome, fogDensity, genChance, hasCaves);
|
|
||||||
BiomeAPI.registerBiome(endBiome);
|
|
||||||
if (Configs.BIOME_CONFIG.getBoolean(endBiome.getID(), "enabled", true)) {
|
|
||||||
BiomeAPI.registerBiome(endBiome);
|
|
||||||
parent.addSubBiome(endBiome);
|
|
||||||
SUBBIOMES.add(endBiome);
|
|
||||||
SUBBIOMES_UNMUTABLES.add(endBiome.getID());
|
|
||||||
}
|
|
||||||
return endBiome;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Put existing {@link EndBiome} as a sub-biome into selected parent.
|
* Put existing {@link EndBiome} as a sub-biome into selected parent.
|
||||||
*
|
*
|
||||||
|
@ -298,11 +92,7 @@ public class EndBiomes {
|
||||||
*/
|
*/
|
||||||
public static EndBiome registerSubBiome(EndBiome biome, EndBiome parent) {
|
public static EndBiome registerSubBiome(EndBiome biome, EndBiome parent) {
|
||||||
if (Configs.BIOME_CONFIG.getBoolean(biome.getID(), "enabled", true)) {
|
if (Configs.BIOME_CONFIG.getBoolean(biome.getID(), "enabled", true)) {
|
||||||
BiomeAPI.registerBiome(biome);
|
BiomeAPI.registerSubBiome(parent, biome);
|
||||||
parent.addSubBiome(biome);
|
|
||||||
SUBBIOMES.add(biome);
|
|
||||||
SUBBIOMES_UNMUTABLES.add(biome.getID());
|
|
||||||
BiomeAPI.addEndLandBiomeToFabricApi(biome);
|
|
||||||
}
|
}
|
||||||
return biome;
|
return biome;
|
||||||
}
|
}
|
||||||
|
@ -316,13 +106,11 @@ public class EndBiomes {
|
||||||
*/
|
*/
|
||||||
public static EndBiome registerBiome(EndBiome biome, BiomeType type) {
|
public static EndBiome registerBiome(EndBiome biome, BiomeType type) {
|
||||||
if (Configs.BIOME_CONFIG.getBoolean(biome.getID(), "enabled", true)) {
|
if (Configs.BIOME_CONFIG.getBoolean(biome.getID(), "enabled", true)) {
|
||||||
BiomeAPI.registerBiome(biome);
|
|
||||||
addToPicker(biome, type);
|
|
||||||
if (type == BiomeType.LAND) {
|
if (type == BiomeType.LAND) {
|
||||||
BiomeAPI.addEndLandBiomeToFabricApi(biome);
|
BiomeAPI.registerEndLandBiome(biome);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
BiomeAPI.addEndVoidBiomeToFabricApi(biome);
|
BiomeAPI.registerEndVoidBiome(biome);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return biome;
|
return biome;
|
||||||
|
@ -337,9 +125,6 @@ public class EndBiomes {
|
||||||
public static EndBiome registerSubBiomeIntegration(EndBiome biome) {
|
public static EndBiome registerSubBiomeIntegration(EndBiome biome) {
|
||||||
if (Configs.BIOME_CONFIG.getBoolean(biome.getID(), "enabled", true)) {
|
if (Configs.BIOME_CONFIG.getBoolean(biome.getID(), "enabled", true)) {
|
||||||
BiomeAPI.registerBiome(biome);
|
BiomeAPI.registerBiome(biome);
|
||||||
SUBBIOMES.add(biome);
|
|
||||||
SUBBIOMES_UNMUTABLES.add(biome.getID());
|
|
||||||
BiomeAPI.addEndLandBiomeToFabricApi(biome);
|
|
||||||
}
|
}
|
||||||
return biome;
|
return biome;
|
||||||
}
|
}
|
||||||
|
@ -359,23 +144,6 @@ public class EndBiomes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static EndBiome registerBiome(ResourceKey<Biome> key, BiomeType type, float genChance) {
|
|
||||||
return registerBiome(BuiltinRegistries.BIOME.get(key), type, genChance);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static EndBiome registerSubBiome(ResourceKey<Biome> key, EndBiome parent, float genChance) {
|
|
||||||
return registerSubBiome(BuiltinRegistries.BIOME.get(key), parent, genChance, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void addToPicker(EndBiome biome, BiomeType type) {
|
|
||||||
if (type == BiomeType.LAND) {
|
|
||||||
LAND_BIOMES.addBiome(biome);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
VOID_BIOMES.addBiome(biome);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static EndCaveBiome registerCaveBiome(EndCaveBiome biome) {
|
public static EndCaveBiome registerCaveBiome(EndCaveBiome biome) {
|
||||||
if (Configs.BIOME_CONFIG.getBoolean(biome.getID(), "enabled", true)) {
|
if (Configs.BIOME_CONFIG.getBoolean(biome.getID(), "enabled", true)) {
|
||||||
BiomeAPI.registerBiome(biome);
|
BiomeAPI.registerBiome(biome);
|
||||||
|
|
|
@ -352,7 +352,7 @@ public class EndFeatures {
|
||||||
|
|
||||||
BCLBiome bclbiome = BiomeAPI.getBiome(id);
|
BCLBiome bclbiome = BiomeAPI.getBiome(id);
|
||||||
boolean hasCaves = bclbiome.getCustomData("has_caves", true);
|
boolean hasCaves = bclbiome.getCustomData("has_caves", true);
|
||||||
if (hasCaves && !EndBiomes.VOID_BIOMES.containsImmutable(id)) {
|
if (hasCaves && !BiomeAPI.END_VOID_BIOME_PICKER.containsImmutable(id)) {
|
||||||
addFeature(ROUND_CAVE, features);
|
addFeature(ROUND_CAVE, features);
|
||||||
addFeature(TUNEL_CAVE, features);
|
addFeature(TUNEL_CAVE, features);
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,8 +157,7 @@ public abstract class EndCaveFeature extends DefaultFeature {
|
||||||
protected void setBiome(WorldGenLevel world, BlockPos pos, EndCaveBiome biome) {
|
protected void setBiome(WorldGenLevel world, BlockPos pos, EndCaveBiome biome) {
|
||||||
IBiomeArray array = (IBiomeArray) world.getChunk(pos).getBiomes();
|
IBiomeArray array = (IBiomeArray) world.getChunk(pos).getBiomes();
|
||||||
if (array != null) {
|
if (array != null) {
|
||||||
Biome bio = BiomeAPI.getActualBiome(biome);
|
array.be_setBiome(biome.getActualBiome(), pos);
|
||||||
array.be_setBiome(bio, pos);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +235,7 @@ public abstract class EndCaveFeature extends DefaultFeature {
|
||||||
Biome biome = world.getBiome(pos.offset(x << 4, 0, z << 4));
|
Biome biome = world.getBiome(pos.offset(x << 4, 0, z << 4));
|
||||||
BCLBiome endBiome = BiomeAPI.getFromBiome(biome);
|
BCLBiome endBiome = BiomeAPI.getFromBiome(biome);
|
||||||
boolean hasCaves = endBiome.getCustomData("has_caves", true);
|
boolean hasCaves = endBiome.getCustomData("has_caves", true);
|
||||||
if (!hasCaves && EndBiomes.LAND_BIOMES.containsImmutable(endBiome.getID())) {
|
if (!hasCaves && BiomeAPI.END_LAND_BIOME_PICKER.containsImmutable(endBiome.getID())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,158 +0,0 @@
|
||||||
package ru.betterend.world.generator;
|
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.mojang.serialization.Codec;
|
|
||||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.resources.RegistryLookupCodec;
|
|
||||||
import net.minecraft.world.level.biome.Biome;
|
|
||||||
import net.minecraft.world.level.biome.BiomeSource;
|
|
||||||
import net.minecraft.world.level.biome.Biomes;
|
|
||||||
import net.minecraft.world.level.biome.TheEndBiomeSource;
|
|
||||||
import net.minecraft.world.level.levelgen.WorldgenRandom;
|
|
||||||
import net.minecraft.world.level.levelgen.synth.SimplexNoise;
|
|
||||||
import ru.bclib.api.BiomeAPI;
|
|
||||||
import ru.bclib.world.biomes.BCLBiome;
|
|
||||||
import ru.bclib.world.generator.BiomeMap;
|
|
||||||
import ru.betterend.BetterEnd;
|
|
||||||
import ru.betterend.noise.OpenSimplexNoise;
|
|
||||||
import ru.betterend.registry.EndBiomes;
|
|
||||||
import ru.betterend.registry.EndTags;
|
|
||||||
import ru.betterend.util.FeaturesHelper;
|
|
||||||
import ru.betterend.world.biome.EndBiome;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class BetterEndBiomeSource extends BiomeSource {
|
|
||||||
public static final Codec<BetterEndBiomeSource> CODEC = RecordCodecBuilder.create((instance) -> {
|
|
||||||
return instance.group(RegistryLookupCodec.create(Registry.BIOME_REGISTRY).forGetter((theEndBiomeSource) -> {
|
|
||||||
return theEndBiomeSource.biomeRegistry;
|
|
||||||
}), Codec.LONG.fieldOf("seed").stable().forGetter((theEndBiomeSource) -> {
|
|
||||||
return theEndBiomeSource.seed;
|
|
||||||
})).apply(instance, instance.stable(BetterEndBiomeSource::new));
|
|
||||||
});
|
|
||||||
private static final OpenSimplexNoise SMALL_NOISE = new OpenSimplexNoise(8324);
|
|
||||||
private final Registry<Biome> biomeRegistry;
|
|
||||||
private final SimplexNoise noise;
|
|
||||||
private final Biome centerBiome;
|
|
||||||
private final Biome barrens;
|
|
||||||
private BiomeMap mapLand;
|
|
||||||
private BiomeMap mapVoid;
|
|
||||||
private final long seed;
|
|
||||||
|
|
||||||
public BetterEndBiomeSource(Registry<Biome> biomeRegistry, long seed) {
|
|
||||||
super(getBiomes(biomeRegistry));
|
|
||||||
|
|
||||||
this.mapLand = new BiomeMap(seed, GeneratorOptions.getBiomeSizeLand(), EndBiomes.LAND_BIOMES);
|
|
||||||
this.mapVoid = new BiomeMap(seed, GeneratorOptions.getBiomeSizeVoid(), EndBiomes.VOID_BIOMES);
|
|
||||||
this.centerBiome = biomeRegistry.getOrThrow(Biomes.THE_END);
|
|
||||||
this.barrens = biomeRegistry.getOrThrow(Biomes.END_BARRENS);
|
|
||||||
this.biomeRegistry = biomeRegistry;
|
|
||||||
this.seed = seed;
|
|
||||||
|
|
||||||
WorldgenRandom chunkRandom = new WorldgenRandom(seed);
|
|
||||||
chunkRandom.consumeCount(17292);
|
|
||||||
this.noise = new SimplexNoise(chunkRandom);
|
|
||||||
|
|
||||||
EndBiomes.mutateRegistry(biomeRegistry);
|
|
||||||
EndTags.addTerrainTags(biomeRegistry);
|
|
||||||
FeaturesHelper.addFeatures(biomeRegistry);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<Biome> getBiomes(Registry<Biome> biomeRegistry) {
|
|
||||||
List<Biome> list = Lists.newArrayList();
|
|
||||||
biomeRegistry.forEach((biome) -> {
|
|
||||||
BCLBiome bclBiome = BiomeAPI.getBiome(biomeRegistry.getKey(biome));
|
|
||||||
if (bclBiome instanceof EndBiome) {
|
|
||||||
list.add(biome);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Biome getNoiseBiome(int biomeX, int biomeY, int biomeZ) {
|
|
||||||
boolean hasVoid = !GeneratorOptions.useNewGenerator() || !GeneratorOptions.noRingVoid();
|
|
||||||
long i = (long) biomeX * (long) biomeX;
|
|
||||||
long j = (long) biomeZ * (long) biomeZ;
|
|
||||||
|
|
||||||
long dist = i + j;
|
|
||||||
if (hasVoid) {
|
|
||||||
if (dist <= 65536L) return this.centerBiome;
|
|
||||||
}
|
|
||||||
else if (dist <= 625L) {
|
|
||||||
dist += noise.getValue(i * 0.2, j * 0.2) * 10;
|
|
||||||
if (dist <= 625L) {
|
|
||||||
return this.centerBiome;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (biomeX == 0 && biomeZ == 0) {
|
|
||||||
mapLand.clearCache();
|
|
||||||
mapVoid.clearCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
BCLBiome endBiome = null;
|
|
||||||
if (GeneratorOptions.useNewGenerator()) {
|
|
||||||
if (TerrainGenerator.isLand(biomeX, biomeZ)) {
|
|
||||||
endBiome = (EndBiome) mapLand.getBiome(biomeX << 2, biomeZ << 2);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (!GeneratorOptions.noRingVoid() && dist <= 65536L) {
|
|
||||||
return barrens;
|
|
||||||
}
|
|
||||||
endBiome = mapVoid.getBiome(biomeX << 2, biomeZ << 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
float height = TheEndBiomeSource.getHeightValue(
|
|
||||||
noise,
|
|
||||||
(biomeX >> 1) + 1,
|
|
||||||
(biomeZ >> 1) + 1
|
|
||||||
) + (float) SMALL_NOISE.eval(biomeX, biomeZ) * 5;
|
|
||||||
|
|
||||||
if (height > -20F && height < -5F) {
|
|
||||||
return barrens;
|
|
||||||
}
|
|
||||||
|
|
||||||
endBiome = height < -10F ? mapVoid.getBiome(biomeX << 2, biomeZ << 2) : mapLand.getBiome(
|
|
||||||
biomeX << 2,
|
|
||||||
biomeZ << 2
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return BiomeAPI.getActualBiome(endBiome);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Biome getLandBiome(int biomeX, int biomeY, int biomeZ) {
|
|
||||||
boolean hasVoid = !GeneratorOptions.useNewGenerator() || !GeneratorOptions.noRingVoid();
|
|
||||||
long i = (long) biomeX * (long) biomeX;
|
|
||||||
long j = (long) biomeZ * (long) biomeZ;
|
|
||||||
|
|
||||||
long dist = i + j;
|
|
||||||
if (hasVoid) {
|
|
||||||
if (dist <= 65536L) return this.centerBiome;
|
|
||||||
}
|
|
||||||
else if (dist <= 625L) {
|
|
||||||
dist += noise.getValue(i * 0.2, j * 0.2) * 10;
|
|
||||||
if (dist <= 625L) {
|
|
||||||
return this.centerBiome;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return BiomeAPI.getActualBiome(mapLand.getBiome(biomeX << 2, biomeZ << 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BiomeSource withSeed(long seed) {
|
|
||||||
return new BetterEndBiomeSource(biomeRegistry, seed);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Codec<? extends BiomeSource> codec() {
|
|
||||||
return CODEC;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void register() {
|
|
||||||
Registry.register(Registry.BIOME_SOURCE, BetterEnd.makeID("better_end_biome_source"), CODEC);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -93,9 +93,6 @@ public class TerrainGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Biome getBiome(BiomeSource biomeSource, int x, int z) {
|
private static Biome getBiome(BiomeSource biomeSource, int x, int z) {
|
||||||
if (biomeSource instanceof BetterEndBiomeSource) {
|
|
||||||
return ((BetterEndBiomeSource) biomeSource).getLandBiome(x, 0, z);
|
|
||||||
}
|
|
||||||
return biomeSource.getNoiseBiome(x, 0, z);
|
return biomeSource.getNoiseBiome(x, 0, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
"fabricloader": ">=0.11.6",
|
"fabricloader": ">=0.11.6",
|
||||||
"fabric": ">=0.36.0",
|
"fabric": ">=0.36.0",
|
||||||
"minecraft": ">=1.17",
|
"minecraft": ">=1.17",
|
||||||
"bclib": ">=0.3.0"
|
"bclib": ">=0.4.0"
|
||||||
},
|
},
|
||||||
"suggests": {
|
"suggests": {
|
||||||
"byg": ">=1.1.3",
|
"byg": ">=1.1.3",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue