Skip to content

Commit

Permalink
Allow Biolith to generate biomes in datapack worlds.
Browse files Browse the repository at this point in the history
This is a large change; why we're still alpha I guess.

- Biolith can now generate biomes in datapack worlds
  • Loading branch information
gniftygnome committed Jun 13, 2023
1 parent 76dd6a9 commit a190f3b
Show file tree
Hide file tree
Showing 16 changed files with 255 additions and 101 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
plugins {
id 'fabric-loom' version '1.1.+'
id 'maven-publish'
id 'io.github.juuxel.loom-quiltflower' version '1.8.0'
id 'io.github.juuxel.loom-quiltflower' version '1.10.0'
}

archivesBaseName = project.archive_name
Expand Down
6 changes: 3 additions & 3 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ fabric_version=0.83.0+1.20
terraform_surfaces_api_version=7.0.0-beta.1

# TerraBlender so we can target it with mixins
terrablender_version=1.19.4-2.2.0.156
terrablender_version=1.20-3.0.0.163

# MixinExtras so we can mixin more kindly
mixin_extras_version=0.2.0-beta.5
Expand All @@ -41,14 +41,14 @@ default_release_type=alpha
# CurseForge Metadata
curseforge_slug=biolith
curseforge_id=852512
curseforge_game_versions=1.20, Fabric, Quilt
curseforge_game_versions=1.20, 1.20.1, Fabric, Quilt
curseforge_required_dependencies=fabric-api
curseforge_optional_dependencies=

# Modrinth Metadata
modrinth_slug=biolith
modrinth_id=iGEl6Crx
modrinth_game_versions=1.20
modrinth_game_versions=1.20, 1.20.1
modrinth_mod_loaders=fabric, quilt
modrinth_required_dependencies=fabric-api

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import com.terraformersmc.biolith.impl.Biolith;
import com.terraformersmc.biolith.impl.config.BiolithState;
import com.terraformersmc.biolith.impl.surface.SurfaceRuleCollector;
import net.minecraft.registry.CombinedDynamicRegistries;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.ServerDynamicRegistryType;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
Expand All @@ -26,8 +28,19 @@ public class BiomeCoordinator {
private static boolean serverStarted = false;
protected static DynamicRegistryManager.Immutable registryManager;

public static void setRegistryManager(CombinedDynamicRegistries<ServerDynamicRegistryType> combinedDynamicRegistries) {
// Called by biolith$earlyCaptureRegistries() in MixinMinecraftServer so we can set this really early.
registryManager = combinedDynamicRegistries.getCombinedRegistryManager();
}

public static @Nullable DynamicRegistryManager.Immutable getRegistryManager() {
return registryManager;
}

public static void handleServerStarting(MinecraftServer server) {
registryManager = server.getCombinedDynamicRegistries().getCombinedRegistryManager();
if (registryManager == null) {
registryManager = server.getCombinedDynamicRegistries().getCombinedRegistryManager();
}

if (Biolith.COMPAT_TERRABLENDER) {
registerWithTerrablender();
Expand Down Expand Up @@ -67,10 +80,6 @@ public static void handleServerStopped(MinecraftServer server) {
OVERWORLD_STATE = null;
}

public static @Nullable DynamicRegistryManager.Immutable getRegistryManager() {
return registryManager;
}

// When TerraBlender is present, it ignores our surface rules in the Overworld and Nether.
// To avoid this, we submit a duplicate registration to TerraBlender (but only once).
private static void registerWithTerrablender() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@
import com.terraformersmc.biolith.impl.Biolith;
import com.terraformersmc.biolith.impl.config.BiolithState;
import com.terraformersmc.terraform.noise.OpenSimplexNoise2;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.*;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
Expand Down Expand Up @@ -57,6 +54,19 @@ protected void serverReplaced(BiolithState state, long seed) {
seedlets[7] = (int) (seed >> 56 & 0xffL);
}

protected RegistryEntryLookup<Biome> getBiomeLookup() {
if (biomeRegistry != null) {
return biomeRegistry.getReadOnlyWrapper();
}

DynamicRegistryManager.Immutable registryManager = BiomeCoordinator.getRegistryManager();
if (registryManager == null) {
throw new IllegalStateException("BiomeSource created while RegistryManager is null!");
}

return registryManager.getWrapperOrThrow(RegistryKeys.BIOME);
}

public void addPlacement(RegistryKey<Biome> biome, MultiNoiseUtil.NoiseHypercube noisePoint) {
if (biomesInjected) {
Biolith.LOGGER.error("Biolith's BiomePlacement.addPlacement() called too late for biome: {}", biome.getValue());
Expand Down Expand Up @@ -84,6 +94,8 @@ public void addSubBiome(RegistryKey<Biome> target, RegistryKey<Biome> biome, Sub

public abstract RegistryEntry<Biome> getReplacement(int x, int y, int z, MultiNoiseUtil.NoiseValuePoint noisePoint, BiolithFittestNodes<RegistryEntry<Biome>> fittestNodes);

public abstract void writeBiomeEntries(Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryEntry<Biome>>> parameters);

public abstract void writeBiomeParameters(Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryKey<Biome>>> parameters);

// Approximation of normalizing K.jpg OpenSimplex2(F) values in [-1,1] to unbiased values in [0,1].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.mojang.datafixers.util.Pair;
import com.terraformersmc.biolith.impl.config.BiolithState;
import com.terraformersmc.terraform.noise.OpenSimplexNoise2;
import net.minecraft.registry.RegistryEntryLookup;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.world.biome.Biome;
Expand Down Expand Up @@ -107,6 +108,33 @@ private double getLocalNoise(int x, int y, int z) {
return localNoise;
}

public void writeBiomeEntries(Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryEntry<Biome>>> parameters) {
biomesInjected = true;
RegistryEntryLookup<Biome> biomeEntryGetter = getBiomeLookup();

// End biomes are merged during construction of the End Biome stream.

placementRequests.forEach(pair -> parameters.accept(pair.mapSecond(biomeEntryGetter::getOrThrow)));

// Replacement biomes are placed out-of-range so they do not generate except as replacements.
// This adds the biome to TheEndBiomeSource and BiomeSource so features and structures will place.

replacementRequests.values().stream()
.flatMap(requestSet -> requestSet.requests.stream())
.map(ReplacementRequest::biome).distinct()
.forEach(biome -> {
if (!biome.equals(VANILLA_PLACEHOLDER)) {
parameters.accept(Pair.of(OUT_OF_RANGE, biomeEntryGetter.getOrThrow(biome)));
}
});

subBiomeRequests.values().stream()
.flatMap(requestSet -> requestSet.requests.stream())
.map(SubBiomeRequest::biome).distinct()
.forEach(biome -> parameters.accept(Pair.of(OUT_OF_RANGE, biomeEntryGetter.getOrThrow(biome))));
}

// TODO: Deprecated for clean-up in the mixins -- Review and remove from all DimensionBiomePlacements?
// NOTE: biomeRegistry IS already available when biomeStream() is called to init BiomeSource.biomes.
public void writeBiomeParameters(Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryKey<Biome>>> parameters) {
assert biomeRegistry != null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.terraformersmc.biolith.impl.biome;

import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.world.dimension.DimensionType;

@SuppressWarnings("unused")
public interface InterfaceBiomeSource {
RegistryEntry<DimensionType> biolith$getDimensionType();

void biolith$setDimensionType(RegistryEntry<DimensionType> dimensionTypeEntry);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.terraformersmc.biolith.impl.biome;

import com.mojang.datafixers.util.Pair;
import net.minecraft.registry.RegistryEntryLookup;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.world.biome.Biome;
Expand Down Expand Up @@ -75,6 +76,33 @@ private double getLocalNoise(int x, int y, int z) {
return localNoise;
}

public void writeBiomeEntries(Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryEntry<Biome>>> parameters) {
biomesInjected = true;
RegistryEntryLookup<Biome> biomeEntryGetter = getBiomeLookup();

// Nether biomes are merged during construction of the Nether parameters list.

placementRequests.forEach(pair -> parameters.accept(pair.mapSecond(biomeEntryGetter::getOrThrow)));

// Replacement biomes are placed out-of-range so they do not generate except as replacements.
// This adds the biome to MultiNoiseBiomeSource and BiomeSource so features and structures will place.

replacementRequests.values().stream()
.flatMap(requestSet -> requestSet.requests.stream())
.map(ReplacementRequest::biome).distinct()
.forEach(biome -> {
if (!biome.equals(VANILLA_PLACEHOLDER)) {
parameters.accept(Pair.of(OUT_OF_RANGE, biomeEntryGetter.getOrThrow(biome)));
}
});

subBiomeRequests.values().stream()
.flatMap(requestSet -> requestSet.requests.stream())
.map(SubBiomeRequest::biome).distinct()
.forEach(biome -> parameters.accept(Pair.of(OUT_OF_RANGE, biomeEntryGetter.getOrThrow(biome))));
}

// TODO: Unused since 1.0.0-alpha.5 -- Review and remove from all DimensionBiomePlacements?
// NOTE: biomeRegistry IS already available when writeBiomeParameters() is called by MultiNoiseBiomeSourceParameterList.
public void writeBiomeParameters(Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryKey<Biome>>> parameters) {
assert biomeRegistry != null;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.terraformersmc.biolith.impl.biome;

import com.mojang.datafixers.util.Pair;
import net.minecraft.registry.RegistryEntryLookup;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.world.biome.Biome;
Expand Down Expand Up @@ -82,6 +83,33 @@ private double getLocalNoise(int x, int y, int z) {
return localNoise;
}

public void writeBiomeEntries(Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryEntry<Biome>>> parameters) {
biomesInjected = true;
RegistryEntryLookup<Biome> biomeEntryGetter = getBiomeLookup();

// Overworld biomes are added directly to the Overworld parameters list.

placementRequests.forEach(pair -> parameters.accept(pair.mapSecond(biomeEntryGetter::getOrThrow)));

// Replacement biomes are placed out-of-range so they do not generate except as replacements.
// This adds the biome to MultiNoiseBiomeSource and BiomeSource so features and structures will place.

replacementRequests.values().stream()
.flatMap(requestSet -> requestSet.requests.stream())
.map(ReplacementRequest::biome).distinct()
.forEach(biome -> {
if (!biome.equals(VANILLA_PLACEHOLDER)) {
parameters.accept(Pair.of(OUT_OF_RANGE, biomeEntryGetter.getOrThrow(biome)));
}
});

subBiomeRequests.values().stream()
.flatMap(requestSet -> requestSet.requests.stream())
.map(SubBiomeRequest::biome).distinct()
.forEach(biome -> parameters.accept(Pair.of(OUT_OF_RANGE, biomeEntryGetter.getOrThrow(biome))));
}

// TODO: Unused since 1.0.0-alpha.5 -- Review and remove from all DimensionBiomePlacements?
// NOTE: biomeRegistry is NOT yet available when writeBiomeParameters() is called by VanillaBiomeParameters.
public void writeBiomeParameters(Consumer<Pair<MultiNoiseUtil.NoiseHypercube, RegistryKey<Biome>>> parameters) {
biomesInjected = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.terraformersmc.biolith.impl.mixin;

import com.terraformersmc.biolith.impl.biome.InterfaceBiomeSource;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.world.biome.source.BiomeSource;
import net.minecraft.world.dimension.DimensionType;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;

@Mixin(BiomeSource.class)
public class MixinBiomeSource implements InterfaceBiomeSource {
private RegistryEntry<DimensionType> biolith$dimensionTypeEntry;

@Override
public @Nullable RegistryEntry<DimensionType> biolith$getDimensionType() {
return biolith$dimensionTypeEntry;
}

@Override
public void biolith$setDimensionType(RegistryEntry<DimensionType> dimensionTypeEntry) {
if (biolith$dimensionTypeEntry != null) {
throw new IllegalStateException("Dimension Type already set: " + biolith$dimensionTypeEntry);
}

biolith$dimensionTypeEntry = dimensionTypeEntry;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.terraformersmc.biolith.impl.mixin;

import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.world.dimension.DimensionOptions;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.gen.chunk.ChunkGenerator;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(DimensionOptions.class)
public class MixinDimensionOptions {
@Inject(method = "<init>", at = @At("RETURN"))
private void biolith$storeDimensionTypeToBiomeSource(RegistryEntry<DimensionType> dimensionTypeEntry, ChunkGenerator chunkGenerator, CallbackInfo ci) {
chunkGenerator.getBiomeSource().biolith$setDimensionType(dimensionTypeEntry);
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
package com.terraformersmc.biolith.impl.mixin;

import com.google.common.collect.Streams;
import com.mojang.datafixers.DataFixer;
import com.terraformersmc.biolith.impl.biome.BiomeCoordinator;
import com.terraformersmc.biolith.impl.surface.SurfaceRuleCollector;
import net.minecraft.registry.CombinedDynamicRegistries;
import net.minecraft.registry.Registry;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.ServerDynamicRegistryType;
import net.minecraft.resource.ResourcePackManager;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.SaveLoader;
import net.minecraft.server.WorldGenerationProgressListener;
import net.minecraft.server.WorldGenerationProgressListenerFactory;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.ApiServices;
import net.minecraft.world.World;
import net.minecraft.world.dimension.DimensionOptions;
import net.minecraft.world.dimension.DimensionTypes;
Expand All @@ -15,6 +23,8 @@
import net.minecraft.world.gen.chunk.NoiseChunkGenerator;
import net.minecraft.world.gen.surfacebuilder.MaterialRules;
import net.minecraft.world.level.ServerWorldProperties;
import net.minecraft.world.level.storage.LevelStorage;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
Expand All @@ -23,16 +33,32 @@
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;

import java.net.Proxy;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Stream;

@Mixin(MinecraftServer.class)
public abstract class MixinMinecraftServer {
@Shadow
@Final
private CombinedDynamicRegistries<ServerDynamicRegistryType> combinedDynamicRegistries;

@Shadow
@Final
private Map<RegistryKey<World>, ServerWorld> worlds;

@Inject(method = "<init>", at = @At(
value = "FIELD",
target = "Lnet/minecraft/server/MinecraftServer;combinedDynamicRegistries:Lnet/minecraft/registry/CombinedDynamicRegistries;",
opcode = Opcodes.PUTFIELD,
ordinal = 0,
shift = At.Shift.AFTER))
private void biolith$earlyCaptureRegistries(Thread serverThread, LevelStorage.Session session, ResourcePackManager dataPackManager, SaveLoader saveLoader, Proxy proxy, DataFixer dataFixer, ApiServices apiServices, WorldGenerationProgressListenerFactory worldGenerationProgressListenerFactory, CallbackInfo ci) {
// We need the registries really early in case TerraBlender calls us before the Fabric server start event.
BiomeCoordinator.setRegistryManager(combinedDynamicRegistries);
}

@Inject(method = "createWorlds", at = @At(value = "RETURN"), locals = LocalCapture.CAPTURE_FAILHARD)
private void biolith$prependSurfaceRules(WorldGenerationProgressListener worldGenerationProgressListener, CallbackInfo ci, ServerWorldProperties serverWorldProperties, boolean isDebug, Registry<DimensionOptions> dimensionOptionsRegistry) {
MaterialRules.MaterialRule[] rulesType = new MaterialRules.MaterialRule[0];
Expand Down
Loading

0 comments on commit a190f3b

Please sign in to comment.