From 8f692b181ca9a456bd27b7fed5fe42c094d19a46 Mon Sep 17 00:00:00 2001 From: Dustin Norberg Date: Tue, 22 Sep 2026 16:53:41 -0400 Subject: [PATCH] Apply single biome Age distribution --- docs/PORTING.md | 8 ++++++ gradle.properties | 2 +- .../rivencraft/book/AgeDefinition.java | 2 +- .../rivencraft/book/AgeDimensionService.java | 27 +++++++++++++++++-- .../rivencraft/gametest/LinkingGameTests.java | 18 ++++++++++++- 5 files changed, 52 insertions(+), 5 deletions(-) diff --git a/docs/PORTING.md b/docs/PORTING.md index b002445..23c9185 100644 --- a/docs/PORTING.md +++ b/docs/PORTING.md @@ -309,6 +309,14 @@ instead of advancing and correcting their time once per tick. This keeps the integer day time fixed while preventing the client skybox from visually bouncing between adjacent celestial positions. +Version 0.2.21.0 begins applying persisted biome-distribution traits. Native +Distribution retains Minecraft's data-pack-aware Overworld climate source. +Single Biome Distribution deterministically selects one registered biome from +the Age seed and uses it throughout compatible noise-based terrain. Flat and +Void profiles retain their profile-specific biome sources. Legacy's scaled, +tiled, and grid layouts require dedicated modern biome sources and remain the +next distribution work. + Version 0.2.14.0 restores the legacy Linking and Descriptive Book artwork for inventory items and gives each book its matching open-book texture when placed on a Book Stand. Return Linking Books created inside another Age also retain and diff --git a/gradle.properties b/gradle.properties index c87434a..80f4b6c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -35,7 +35,7 @@ mod_name=Rivencraft # The license of the mod. Review your options at https://choosealicense.com/. All Rights Reserved is the default. mod_license=LGPL-3.0-or-later # The mod version. Rivencraft uses the x.y.z.b policy documented in README.md. -mod_version=0.2.20.2 +mod_version=0.2.21.0 immersive_portals_version=6.0.7 # The group ID for the mod. It is only important when publishing as an artifact to a Maven repository. # This should match the base package used for the mod sources. diff --git a/src/main/java/net/dnorberg/rivencraft/book/AgeDefinition.java b/src/main/java/net/dnorberg/rivencraft/book/AgeDefinition.java index 5c91056..6eab9c7 100644 --- a/src/main/java/net/dnorberg/rivencraft/book/AgeDefinition.java +++ b/src/main/java/net/dnorberg/rivencraft/book/AgeDefinition.java @@ -21,7 +21,7 @@ public record AgeDefinition( public static final ResourceLocation RANDOM_VANILLA_PROFILE = ResourceLocation.fromNamespaceAndPath( Rivencraft.MOD_ID, "random_vanilla" ); - public static final int CURRENT_GENERATION_VERSION = 7; + public static final int CURRENT_GENERATION_VERSION = 8; public AgeDefinition { Objects.requireNonNull(id, "id"); diff --git a/src/main/java/net/dnorberg/rivencraft/book/AgeDimensionService.java b/src/main/java/net/dnorberg/rivencraft/book/AgeDimensionService.java index 07fb9f5..97f6351 100644 --- a/src/main/java/net/dnorberg/rivencraft/book/AgeDimensionService.java +++ b/src/main/java/net/dnorberg/rivencraft/book/AgeDimensionService.java @@ -5,6 +5,7 @@ import dev.galacticraft.dynamicdimensions.api.event.DynamicDimensionLoadCallback import java.util.Optional; import java.util.OptionalLong; import java.util.List; +import java.util.Comparator; import net.dnorberg.rivencraft.Rivencraft; import net.dnorberg.rivencraft.compat.ImmersivePortalsChunkLoaders; import net.minecraft.core.BlockPos; @@ -20,6 +21,9 @@ import net.minecraft.util.valueproviders.UniformInt; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.biome.Biomes; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeSource; +import net.minecraft.world.level.biome.FixedBiomeSource; import net.minecraft.world.level.biome.MultiNoiseBiomeSource; import net.minecraft.world.level.biome.MultiNoiseBiomeSourceParameterLists; import net.minecraft.world.level.chunk.ChunkGenerator; @@ -263,12 +267,31 @@ public final class AgeDimensionService { // island. Both use Overworld biomes and dimension environment. // random_vanilla is retained as a migration alias for existing Ages. return new NoiseBasedChunkGenerator( - MultiNoiseBiomeSource.createFromPreset( - biomeParameters.getOrThrow(MultiNoiseBiomeSourceParameterLists.OVERWORLD)), + createBiomeSource(server, age, biomeParameters), noiseSettings.getOrThrow(settingsKey) ); } + private static BiomeSource createBiomeSource( + MinecraftServer server, + AgeDefinition age, + net.minecraft.core.HolderGetter biomeParameters + ) { + if (age.traits().biomeDistribution() == AgeTraits.BiomeDistribution.SINGLE) { + List> biomes = server.registryAccess() + .lookupOrThrow(Registries.BIOME) + .listElements() + .sorted(Comparator.comparing(holder -> holder.key().location().toString())) + .toList(); + if (!biomes.isEmpty()) { + int index = Math.floorMod(Long.hashCode(age.seed() ^ 0x53494E474C454249L), biomes.size()); + return new FixedBiomeSource(biomes.get(index)); + } + } + return MultiNoiseBiomeSource.createFromPreset( + biomeParameters.getOrThrow(MultiNoiseBiomeSourceParameterLists.OVERWORLD)); + } + private static DimensionType createDimensionType(AgeDefinition age) { AgeTraits.Lighting lighting = age.traits().lighting(); boolean hasSkyLight = lighting != AgeTraits.Lighting.DARK; diff --git a/src/main/java/net/dnorberg/rivencraft/gametest/LinkingGameTests.java b/src/main/java/net/dnorberg/rivencraft/gametest/LinkingGameTests.java index 0e8210d..4e693b5 100644 --- a/src/main/java/net/dnorberg/rivencraft/gametest/LinkingGameTests.java +++ b/src/main/java/net/dnorberg/rivencraft/gametest/LinkingGameTests.java @@ -166,7 +166,16 @@ public final class LinkingGameTests { Rivencraft.MOD_ID, "gametest/" + profile.getPath() + "_" + id ), null, - AgeTraits.select(id.getMostSignificantBits()) + profile.equals(AgeGenerationProfiles.STANDARD) + ? new AgeTraits( + AgeTraits.BiomeDistribution.SINGLE, + AgeTraits.Lighting.NORMAL, + AgeTraits.Weather.NORMAL, + 1.0D, + 0.0F, + 0.0F + ) + : AgeTraits.select(id.getMostSignificantBits()) ); var level = AgeDimensionService.ensureLoaded(helper.getLevel().getServer(), definition); helper.assertTrue(level != null, "The " + profile + " profile must load a runtime dimension"); @@ -176,6 +185,13 @@ public final class LinkingGameTests { helper.assertTrue(level.dimension().location().equals(definition.dimensionId()), "The " + profile + " profile must retain its assigned dimension id"); + if (profile.equals(AgeGenerationProfiles.STANDARD)) { + helper.assertValueEqual( + level.getChunkSource().getGenerator().getBiomeSource().possibleBiomes().size(), + 1, + "Single Biome Distribution must expose exactly one biome" + ); + } if (definition.traits().lighting() == AgeTraits.Lighting.DARK) { helper.assertFalse(level.dimensionType().hasSkyLight(), "Dark Lighting Ages must suppress dimension skylight");