Public Access
This commit is contained in:
1 parent
4886f8bb7d
commit
a7986001f1
8 files changed
+167
-2
No files matched your search
@@ -340,6 +340,11 @@ Age-seeded biomes in Legacy's diagonal checkerboard layout. Each cell is one
|
||||
16-by-16-block chunk, and the built-in serializable biome source reconstructs
|
||||
the same layout whenever the Age reloads.
|
||||
|
||||
Version 0.2.24.0 applies Tiny Biome Distribution using three deterministic
|
||||
Age-seeded biomes and softly bounded seeded regions. Tiny begins at Legacy's
|
||||
smallest 16-block nominal scale; the serializable biome source retains its
|
||||
seed, selected biomes, and scale across Age reloads.
|
||||
|
||||
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
|
||||
|
||||
+1
-1
@@ -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.23.0
|
||||
mod_version=0.2.24.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.
|
||||
|
||||
@@ -7,6 +7,7 @@ import net.dnorberg.rivencraft.registry.ModItems;
|
||||
import net.dnorberg.rivencraft.registry.ModSounds;
|
||||
import net.dnorberg.rivencraft.registry.ModBlocks;
|
||||
import net.dnorberg.rivencraft.registry.ModBlockEntities;
|
||||
import net.dnorberg.rivencraft.registry.ModBiomeSources;
|
||||
import net.dnorberg.rivencraft.event.BookStandConversionHandler;
|
||||
import net.dnorberg.rivencraft.network.ModNetworking;
|
||||
import net.dnorberg.rivencraft.config.RivencraftConfig;
|
||||
@@ -31,6 +32,7 @@ public final class Rivencraft {
|
||||
modContainer.registerConfig(ModConfig.Type.CLIENT, RivencraftClientConfig.SPEC);
|
||||
modEventBus.addListener(ModNetworking::register);
|
||||
ModDataComponents.register(modEventBus);
|
||||
ModBiomeSources.register(modEventBus);
|
||||
ModBlocks.register(modEventBus);
|
||||
ModBlockEntities.register(modEventBus);
|
||||
ModItems.register(modEventBus);
|
||||
|
||||
@@ -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 = 11;
|
||||
public static final int CURRENT_GENERATION_VERSION = 12;
|
||||
|
||||
public AgeDefinition {
|
||||
Objects.requireNonNull(id, "id");
|
||||
|
||||
@@ -29,6 +29,7 @@ import net.minecraft.world.level.biome.CheckerboardColumnBiomeSource;
|
||||
import net.minecraft.world.level.biome.FixedBiomeSource;
|
||||
import net.minecraft.world.level.biome.MultiNoiseBiomeSource;
|
||||
import net.minecraft.world.level.biome.MultiNoiseBiomeSourceParameterLists;
|
||||
import net.dnorberg.rivencraft.world.ScaledBiomeSource;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.dimension.BuiltinDimensionTypes;
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
@@ -307,6 +308,15 @@ public final class AgeDimensionService {
|
||||
: TILED_BIOME_SCALE
|
||||
);
|
||||
}
|
||||
if (distribution == AgeTraits.BiomeDistribution.TINY && biomes.size() >= 3) {
|
||||
List<net.minecraft.core.Holder.Reference<Biome>> selected = new java.util.ArrayList<>(biomes);
|
||||
Collections.shuffle(selected, new java.util.Random(age.seed() ^ 0x54494E5942494F4DL));
|
||||
return new ScaledBiomeSource(
|
||||
HolderSet.direct(List.of(selected.get(0), selected.get(1), selected.get(2))),
|
||||
age.seed(),
|
||||
0
|
||||
);
|
||||
}
|
||||
return MultiNoiseBiomeSource.createFromPreset(
|
||||
biomeParameters.getOrThrow(MultiNoiseBiomeSourceParameterLists.OVERWORLD));
|
||||
}
|
||||
|
||||
@@ -183,6 +183,15 @@ public final class LinkingGameTests {
|
||||
selectedTraits.celestialAngle(),
|
||||
selectedTraits.celestialPhase()
|
||||
)
|
||||
: profile.equals(AgeGenerationProfiles.CAVE)
|
||||
? new AgeTraits(
|
||||
AgeTraits.BiomeDistribution.TINY,
|
||||
selectedTraits.lighting(),
|
||||
selectedTraits.weather(),
|
||||
selectedTraits.celestialPeriod(),
|
||||
selectedTraits.celestialAngle(),
|
||||
selectedTraits.celestialPhase()
|
||||
)
|
||||
: selectedTraits;
|
||||
AgeDefinition definition = new AgeDefinition(
|
||||
id,
|
||||
@@ -245,6 +254,30 @@ public final class LinkingGameTests {
|
||||
helper.assertTrue(origin.equals(diagonalChunk),
|
||||
"Diagonal Grid chunks must repeat the biome");
|
||||
}
|
||||
if (profile.equals(AgeGenerationProfiles.CAVE)) {
|
||||
var biomeSource = level.getChunkSource().getGenerator().getBiomeSource();
|
||||
helper.assertTrue(biomeSource instanceof net.dnorberg.rivencraft.world.ScaledBiomeSource,
|
||||
"Tiny Biome Distribution must use the serializable scaled biome source");
|
||||
helper.assertValueEqual(
|
||||
biomeSource.possibleBiomes().size(),
|
||||
3,
|
||||
"Tiny Biome Distribution must expose exactly three seeded biomes"
|
||||
);
|
||||
java.util.Set<net.minecraft.core.Holder<net.minecraft.world.level.biome.Biome>> sampled =
|
||||
new java.util.HashSet<>();
|
||||
for (int x = -32; x <= 32; x += 4) {
|
||||
for (int z = -32; z <= 32; z += 4) {
|
||||
sampled.add(biomeSource.getNoiseBiome(x, 0, z, null));
|
||||
}
|
||||
}
|
||||
helper.assertTrue(sampled.size() >= 2,
|
||||
"Tiny Biome Distribution must form multiple seeded regions near spawn");
|
||||
helper.assertValueEqual(
|
||||
biomeSource.getNoiseBiome(7, 0, -5, null),
|
||||
biomeSource.getNoiseBiome(7, 0, -5, null),
|
||||
"Tiny biome sampling must be deterministic"
|
||||
);
|
||||
}
|
||||
if (definition.traits().lighting() == AgeTraits.Lighting.DARK) {
|
||||
helper.assertFalse(level.dimensionType().hasSkyLight(),
|
||||
"Dark Lighting Ages must suppress dimension skylight");
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.dnorberg.rivencraft.registry;
|
||||
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import net.dnorberg.rivencraft.Rivencraft;
|
||||
import net.dnorberg.rivencraft.world.ScaledBiomeSource;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.neoforged.bus.api.IEventBus;
|
||||
import net.neoforged.neoforge.registries.DeferredRegister;
|
||||
|
||||
/** Serializable biome-source types used by generated Ages. */
|
||||
public final class ModBiomeSources {
|
||||
private static final DeferredRegister<MapCodec<? extends BiomeSource>> BIOME_SOURCES =
|
||||
DeferredRegister.create(Registries.BIOME_SOURCE, Rivencraft.MOD_ID);
|
||||
|
||||
static {
|
||||
BIOME_SOURCES.register("scaled", () -> ScaledBiomeSource.CODEC);
|
||||
}
|
||||
|
||||
private ModBiomeSources() {
|
||||
}
|
||||
|
||||
public static void register(IEventBus modEventBus) {
|
||||
BIOME_SOURCES.register(modEventBus);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package net.dnorberg.rivencraft.world;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.MapCodec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import java.util.stream.Stream;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeSource;
|
||||
import net.minecraft.world.level.biome.Climate;
|
||||
|
||||
/** Seeded, softly bounded biome regions at the five Mystcraft distribution scales. */
|
||||
public final class ScaledBiomeSource extends BiomeSource {
|
||||
public static final MapCodec<ScaledBiomeSource> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||
Biome.LIST_CODEC.fieldOf("biomes").forGetter(source -> source.biomes),
|
||||
Codec.LONG.fieldOf("seed").forGetter(source -> source.seed),
|
||||
Codec.intRange(0, 4).fieldOf("scale").forGetter(source -> source.scale)
|
||||
).apply(instance, ScaledBiomeSource::new));
|
||||
|
||||
private final HolderSet<Biome> biomes;
|
||||
private final long seed;
|
||||
private final int scale;
|
||||
private final int cellWidth;
|
||||
|
||||
public ScaledBiomeSource(HolderSet<Biome> biomes, long seed, int scale) {
|
||||
if (biomes.size() < 3) {
|
||||
throw new IllegalArgumentException("Scaled biome distributions require at least three biomes");
|
||||
}
|
||||
this.biomes = biomes;
|
||||
this.seed = seed;
|
||||
this.scale = scale;
|
||||
// BiomeSource coordinates are quart positions. Legacy applies two zooms
|
||||
// before the selected scale, yielding 16-block Tiny regions that double
|
||||
// with every subsequent distribution size.
|
||||
this.cellWidth = 4 << scale;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MapCodec<? extends BiomeSource> codec() {
|
||||
return CODEC;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Stream<Holder<Biome>> collectPossibleBiomes() {
|
||||
return biomes.stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Holder<Biome> getNoiseBiome(int x, int y, int z, Climate.Sampler sampler) {
|
||||
int baseX = Math.floorDiv(x, cellWidth);
|
||||
int baseZ = Math.floorDiv(z, cellWidth);
|
||||
int closestX = baseX;
|
||||
int closestZ = baseZ;
|
||||
double closestDistance = Double.POSITIVE_INFINITY;
|
||||
|
||||
for (int cellX = baseX - 1; cellX <= baseX + 1; cellX++) {
|
||||
for (int cellZ = baseZ - 1; cellZ <= baseZ + 1; cellZ++) {
|
||||
long cellSeed = mix(seed, cellX, cellZ);
|
||||
double centerX = (cellX + 0.5D + jitter(cellSeed) * 0.8D) * cellWidth;
|
||||
double centerZ = (cellZ + 0.5D + jitter(mix64(cellSeed)) * 0.8D) * cellWidth;
|
||||
double deltaX = x - centerX;
|
||||
double deltaZ = z - centerZ;
|
||||
double distance = deltaX * deltaX + deltaZ * deltaZ;
|
||||
if (distance < closestDistance) {
|
||||
closestDistance = distance;
|
||||
closestX = cellX;
|
||||
closestZ = cellZ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return biomes.get(Math.floorMod((int)mix(seed ^ 0x42494F4D45534CL, closestX, closestZ), biomes.size()));
|
||||
}
|
||||
|
||||
private static double jitter(long value) {
|
||||
return ((value >>> 11) * 0x1.0p-53) - 0.5D;
|
||||
}
|
||||
|
||||
private static long mix(long seed, int x, int z) {
|
||||
return mix64(seed ^ (long)x * 0x9E3779B97F4A7C15L ^ (long)z * 0xC2B2AE3D27D4EB4FL);
|
||||
}
|
||||
|
||||
private static long mix64(long value) {
|
||||
value = (value ^ value >>> 30) * 0xBF58476D1CE4E5B9L;
|
||||
value = (value ^ value >>> 27) * 0x94D049BB133111EBL;
|
||||
return value ^ value >>> 31;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user