Public Access
This commit is contained in:
1 parent
9ccec18c18
commit
d75154fa9b
15 files changed
+423
-21
No files matched your search
@@ -508,6 +508,20 @@ versions.
|
|||||||
The local `1.0.1.39-beta.1` follow-up makes the 26.3 landing search reject
|
The local `1.0.1.39-beta.1` follow-up makes the 26.3 landing search reject
|
||||||
fluid at feet and head height and prevents a submerged fallback platform.
|
fluid at feet and head height and prevents a submerged fallback platform.
|
||||||
|
|
||||||
|
The local `1.0.1.40-beta.1` slice gives pageless Descriptive Books a
|
||||||
|
seed-selected terrain and biome plan. It follows the 1.21.1 rank weights for
|
||||||
|
terrain and biome distribution, selects registered Cave/Nether biomes for Cave
|
||||||
|
Ages and overworld-source biomes for other non-Void terrains, and persists the
|
||||||
|
resolved choices. Other random Age traits remain a later port task.
|
||||||
|
|
||||||
|
The local `1.0.1.41-beta.1` slice adds the first Age-scoped feature wrapper to
|
||||||
|
the 26.3 generator. A Block page followed by Rocks now places clusters of the
|
||||||
|
chosen block in new Standard, Amplified, Island, or Flat terrain chunks. The
|
||||||
|
Age index persists that selection, and the wrapper delegates all vanilla
|
||||||
|
terrain, biome, and structure generation. Cave and Void are intentionally
|
||||||
|
excluded until their feature placement rules are ported. This is not yet the
|
||||||
|
full 1.21.1 feature grammar or instability system.
|
||||||
|
|
||||||
## Verification of this first slice
|
## Verification of this first slice
|
||||||
|
|
||||||
The 1.21.1 `compileJava` and `build` tasks passed offline after the backend
|
The 1.21.1 `compileJava` and `build` tasks passed offline after the backend
|
||||||
|
|||||||
@@ -194,6 +194,23 @@ The local `1.0.1.39-beta.1` follow-up rejects fluid at both feet and head
|
|||||||
height during safe-landing searches. It also refuses to carve the fallback
|
height during safe-landing searches. It also refuses to carve the fallback
|
||||||
platform into submerged space if no dry footing can be found.
|
platform into submerged space if no dry footing can be found.
|
||||||
|
|
||||||
|
The `1.0.1.40-beta.1` port slice seeds terrain and biome selection for
|
||||||
|
Descriptive Books with no authored symbol pages. It uses the same terrain and
|
||||||
|
biome-controller rank weights as the 1.21.1 line, freezes the resulting plan
|
||||||
|
in the Age index, and accepts the result after restart. The operator-only
|
||||||
|
`age create_random <name> <seed>` probe exercises this path. Random feature,
|
||||||
|
sky, weather, and instability choices are not yet ported; this slice covers
|
||||||
|
terrain and biomes only.
|
||||||
|
|
||||||
|
The `1.0.1.41-beta.1` slice establishes an Age-scoped decoration wrapper
|
||||||
|
for 26.3 and accepts one Block material page immediately before a Rocks feature
|
||||||
|
page. Newly generated surface chunks place small rock clusters made from that
|
||||||
|
block without modifying the shared biome registry. The selected block and
|
||||||
|
generator survive a restart. Cave and Void terrain remain unsupported for this
|
||||||
|
first surface-feature path; unsupported combinations stay in the Binder.
|
||||||
|
Random feature selection, unpaired materials, duplicate feature pages, and
|
||||||
|
instability scoring remain future port steps.
|
||||||
|
|
||||||
From the repository root:
|
From the repository root:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# Rivencraft 26.3 beta 1.0.1.41-beta.1
|
||||||
|
|
||||||
|
This beta expands Age generation on Minecraft 26.3.
|
||||||
|
|
||||||
|
- Descriptive Books with no authored symbol pages now choose a seeded random terrain and biome distribution. The chosen plan is saved with the Age and restored after restart.
|
||||||
|
- A Block material page followed by a Rocks feature page now generates small clusters of that block in newly generated surface chunks. This first feature path supports Standard, Amplified, Island, and Flat terrain; Cave and Void combinations remain unsupported.
|
||||||
|
- Expanded dedicated-server checks cover random Age plans and authored Rocks generation across restart.
|
||||||
|
|
||||||
|
The player verified the Rocks feature in game. Random feature selection, duplicate feature pages, unpaired materials, and instability scoring are not yet ported. Existing chunks are not regenerated.
|
||||||
|
|
||||||
|
This 26.3 port is still experimental and does not yet include all features of the separate Minecraft 1.21.1 version. Use a test world and keep a backup. Minecraft 1.21.1 saves are not supported by this beta.
|
||||||
|
|
||||||
|
Requires Minecraft 26.3, NeoForge 26.3.0.99 or a compatible 26.3 build, and Java 25.
|
||||||
@@ -7,4 +7,4 @@ neo_version=26.3.0.99
|
|||||||
mod_id=rivencraft
|
mod_id=rivencraft
|
||||||
mod_name=Rivencraft
|
mod_name=Rivencraft
|
||||||
mod_license=LGPL-3.0-or-later
|
mod_license=LGPL-3.0-or-later
|
||||||
mod_version=1.0.1.39-beta.1
|
mod_version=1.0.1.41-beta.1
|
||||||
@@ -24,7 +24,9 @@ final class AgeLevelIndex extends SavedData {
|
|||||||
.forGetter(index -> index.biomePlans),
|
.forGetter(index -> index.biomePlans),
|
||||||
Codec.unboundedMap(Identifier.CODEC,
|
Codec.unboundedMap(Identifier.CODEC,
|
||||||
Codec.STRING.xmap(PortAgeTerrain::valueOf, PortAgeTerrain::name))
|
Codec.STRING.xmap(PortAgeTerrain::valueOf, PortAgeTerrain::name))
|
||||||
.optionalFieldOf("terrain", Map.of()).forGetter(index -> index.terrain)
|
.optionalFieldOf("terrain", Map.of()).forGetter(index -> index.terrain),
|
||||||
|
Codec.unboundedMap(Identifier.CODEC, PortRockPlan.CODEC).optionalFieldOf("rocks", Map.of())
|
||||||
|
.forGetter(index -> index.rocks)
|
||||||
).apply(instance, AgeLevelIndex::new));
|
).apply(instance, AgeLevelIndex::new));
|
||||||
|
|
||||||
static final SavedDataType<AgeLevelIndex> TYPE = new SavedDataType<>(
|
static final SavedDataType<AgeLevelIndex> TYPE = new SavedDataType<>(
|
||||||
@@ -38,6 +40,7 @@ final class AgeLevelIndex extends SavedData {
|
|||||||
private final Map<Identifier, Identifier> biomeOverrides;
|
private final Map<Identifier, Identifier> biomeOverrides;
|
||||||
private final Map<Identifier, PortBiomePlan> biomePlans;
|
private final Map<Identifier, PortBiomePlan> biomePlans;
|
||||||
private final Map<Identifier, PortAgeTerrain> terrain;
|
private final Map<Identifier, PortAgeTerrain> terrain;
|
||||||
|
private final Map<Identifier, PortRockPlan> rocks;
|
||||||
|
|
||||||
private AgeLevelIndex() {
|
private AgeLevelIndex() {
|
||||||
this.ages = new LinkedHashMap<>();
|
this.ages = new LinkedHashMap<>();
|
||||||
@@ -45,17 +48,20 @@ final class AgeLevelIndex extends SavedData {
|
|||||||
this.biomeOverrides = new LinkedHashMap<>();
|
this.biomeOverrides = new LinkedHashMap<>();
|
||||||
this.biomePlans = new LinkedHashMap<>();
|
this.biomePlans = new LinkedHashMap<>();
|
||||||
this.terrain = new LinkedHashMap<>();
|
this.terrain = new LinkedHashMap<>();
|
||||||
|
this.rocks = new LinkedHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private AgeLevelIndex(Map<Identifier, Long> ages, List<Identifier> retired,
|
private AgeLevelIndex(Map<Identifier, Long> ages, List<Identifier> retired,
|
||||||
Map<Identifier, Identifier> biomeOverrides,
|
Map<Identifier, Identifier> biomeOverrides,
|
||||||
Map<Identifier, PortBiomePlan> biomePlans,
|
Map<Identifier, PortBiomePlan> biomePlans,
|
||||||
Map<Identifier, PortAgeTerrain> terrain) {
|
Map<Identifier, PortAgeTerrain> terrain,
|
||||||
|
Map<Identifier, PortRockPlan> rocks) {
|
||||||
this.ages = new LinkedHashMap<>(ages);
|
this.ages = new LinkedHashMap<>(ages);
|
||||||
this.retired = new LinkedHashSet<>(retired);
|
this.retired = new LinkedHashSet<>(retired);
|
||||||
this.biomeOverrides = new LinkedHashMap<>(biomeOverrides);
|
this.biomeOverrides = new LinkedHashMap<>(biomeOverrides);
|
||||||
this.biomePlans = new LinkedHashMap<>(biomePlans);
|
this.biomePlans = new LinkedHashMap<>(biomePlans);
|
||||||
this.terrain = new LinkedHashMap<>(terrain);
|
this.terrain = new LinkedHashMap<>(terrain);
|
||||||
|
this.rocks = new LinkedHashMap<>(rocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Identifier, Long> entries() {
|
Map<Identifier, Long> entries() {
|
||||||
@@ -81,13 +87,17 @@ final class AgeLevelIndex extends SavedData {
|
|||||||
return terrain.getOrDefault(id, PortAgeTerrain.STANDARD);
|
return terrain.getOrDefault(id, PortAgeTerrain.STANDARD);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(Identifier id, long seed, PortBiomePlan biomePlan, PortAgeTerrain ageTerrain) {
|
PortRockPlan rocks(Identifier id) { return rocks.get(id); }
|
||||||
|
|
||||||
|
void add(Identifier id, long seed, PortBiomePlan biomePlan, PortAgeTerrain ageTerrain,
|
||||||
|
PortRockPlan rockPlan) {
|
||||||
if (contains(id)) {
|
if (contains(id)) {
|
||||||
throw new IllegalArgumentException("Age already exists: " + id);
|
throw new IllegalArgumentException("Age already exists: " + id);
|
||||||
}
|
}
|
||||||
ages.put(id, seed);
|
ages.put(id, seed);
|
||||||
if (biomePlan != null) biomePlans.put(id, biomePlan);
|
if (biomePlan != null) biomePlans.put(id, biomePlan);
|
||||||
if (ageTerrain != PortAgeTerrain.STANDARD) terrain.put(id, ageTerrain);
|
if (ageTerrain != PortAgeTerrain.STANDARD) terrain.put(id, ageTerrain);
|
||||||
|
if (rockPlan != null) rocks.put(id, rockPlan);
|
||||||
setDirty();
|
setDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,16 +106,18 @@ final class AgeLevelIndex extends SavedData {
|
|||||||
biomeOverrides.remove(id);
|
biomeOverrides.remove(id);
|
||||||
biomePlans.remove(id);
|
biomePlans.remove(id);
|
||||||
terrain.remove(id);
|
terrain.remove(id);
|
||||||
|
rocks.remove(id);
|
||||||
retired.add(id);
|
retired.add(id);
|
||||||
setDirty();
|
setDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void restoreAfterFailedRetirement(Identifier id, long seed, PortBiomePlan biomePlan,
|
void restoreAfterFailedRetirement(Identifier id, long seed, PortBiomePlan biomePlan,
|
||||||
PortAgeTerrain ageTerrain) {
|
PortAgeTerrain ageTerrain, PortRockPlan rockPlan) {
|
||||||
if (!retired.remove(id)) throw new IllegalStateException("Age is not retired: " + id);
|
if (!retired.remove(id)) throw new IllegalStateException("Age is not retired: " + id);
|
||||||
ages.put(id, seed);
|
ages.put(id, seed);
|
||||||
if (biomePlan != null) biomePlans.put(id, biomePlan);
|
if (biomePlan != null) biomePlans.put(id, biomePlan);
|
||||||
if (ageTerrain != PortAgeTerrain.STANDARD) terrain.put(id, ageTerrain);
|
if (ageTerrain != PortAgeTerrain.STANDARD) terrain.put(id, ageTerrain);
|
||||||
|
if (rockPlan != null) rocks.put(id, rockPlan);
|
||||||
setDirty();
|
setDirty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,24 @@ public final class PortAgeCommands {
|
|||||||
: java.util.List.of(Identifier.withDefaultNamespace("plains"),
|
: java.util.List.of(Identifier.withDefaultNamespace("plains"),
|
||||||
Identifier.withDefaultNamespace("forest"))));
|
Identifier.withDefaultNamespace("forest"))));
|
||||||
})))))
|
})))))
|
||||||
|
.then(Commands.literal("create_random")
|
||||||
|
.then(Commands.argument("name", StringArgumentType.word())
|
||||||
|
.then(Commands.argument("seed", LongArgumentType.longArg())
|
||||||
|
.executes(context -> {
|
||||||
|
long seed = LongArgumentType.getLong(context, "seed");
|
||||||
|
PortRandomAgeSelection.Selection selected = PortRandomAgeSelection.select(
|
||||||
|
context.getSource().getServer(), seed);
|
||||||
|
int created = createAge(context, selected.terrain(), selected.biomes());
|
||||||
|
if (created != 0) context.getSource().sendSuccess(() -> Component.literal(
|
||||||
|
"Random Age plan: terrain=" + selected.terrain()
|
||||||
|
+ " biomes=" + selected.biomes()), false);
|
||||||
|
return created;
|
||||||
|
}))))
|
||||||
|
.then(Commands.literal("create_rocks")
|
||||||
|
.then(Commands.argument("name", StringArgumentType.word())
|
||||||
|
.then(Commands.argument("seed", LongArgumentType.longArg())
|
||||||
|
.executes(context -> createAge(context, PortAgeTerrain.FLAT,
|
||||||
|
null, new PortRockPlan(Identifier.withDefaultNamespace("gold_block")))))))
|
||||||
.then(Commands.literal("list")
|
.then(Commands.literal("list")
|
||||||
.executes(context -> {
|
.executes(context -> {
|
||||||
var ages = RuntimeAgeBackend.savedAges(context.getSource().getServer());
|
var ages = RuntimeAgeBackend.savedAges(context.getSource().getServer());
|
||||||
@@ -215,6 +233,9 @@ public final class PortAgeCommands {
|
|||||||
.then(Commands.argument("chunkOffset", IntegerArgumentType.integer(0))
|
.then(Commands.argument("chunkOffset", IntegerArgumentType.integer(0))
|
||||||
.executes(context -> sampleAge(context,
|
.executes(context -> sampleAge(context,
|
||||||
IntegerArgumentType.getInteger(context, "chunkOffset"))))))
|
IntegerArgumentType.getInteger(context, "chunkOffset"))))))
|
||||||
|
.then(Commands.literal("rocks_probe")
|
||||||
|
.then(Commands.argument("name", StringArgumentType.word())
|
||||||
|
.executes(PortAgeCommands::probeRocks)))
|
||||||
.then(Commands.literal("biome")
|
.then(Commands.literal("biome")
|
||||||
.then(Commands.argument("name", StringArgumentType.word())
|
.then(Commands.argument("name", StringArgumentType.word())
|
||||||
.then(Commands.argument("x", IntegerArgumentType.integer())
|
.then(Commands.argument("x", IntegerArgumentType.integer())
|
||||||
@@ -228,12 +249,36 @@ public final class PortAgeCommands {
|
|||||||
|
|
||||||
private static int createAge(CommandContext<CommandSourceStack> context,
|
private static int createAge(CommandContext<CommandSourceStack> context,
|
||||||
PortAgeTerrain terrain, PortBiomePlan biomePlan) {
|
PortAgeTerrain terrain, PortBiomePlan biomePlan) {
|
||||||
|
return createAge(context, terrain, biomePlan, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int probeRocks(CommandContext<CommandSourceStack> context) {
|
||||||
|
Identifier id = ageId(context);
|
||||||
|
ServerLevel level = context.getSource().getServer().getLevel(ResourceKey.create(Registries.DIMENSION, id));
|
||||||
|
if (level == null || !(level.getChunkSource().getGenerator() instanceof PortRockChunkGenerator rocks)) {
|
||||||
|
context.getSource().sendFailure(Component.literal("Age has no loaded Rocks generator: " + id));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
var material = net.minecraft.core.registries.BuiltInRegistries.BLOCK.getValue(rocks.rocks().material());
|
||||||
|
int count = 0;
|
||||||
|
for (int x = 0; x < 64; x++) for (int z = 0; z < 64; z++) {
|
||||||
|
int y = level.getHeight(Heightmap.Types.MOTION_BLOCKING, x, z) - 1;
|
||||||
|
if (level.getBlockState(new net.minecraft.core.BlockPos(x, y, z)).is(material)) count++;
|
||||||
|
}
|
||||||
|
int placed = count;
|
||||||
|
context.getSource().sendSuccess(() -> Component.literal(
|
||||||
|
"Rocks probe " + id + " material=" + rocks.rocks().material() + " count=" + placed), false);
|
||||||
|
return placed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int createAge(CommandContext<CommandSourceStack> context,
|
||||||
|
PortAgeTerrain terrain, PortBiomePlan biomePlan, PortRockPlan rockPlan) {
|
||||||
String name = StringArgumentType.getString(context, "name");
|
String name = StringArgumentType.getString(context, "name");
|
||||||
Identifier id = Identifier.fromNamespaceAndPath(Rivencraft.MOD_ID, "age/" + name);
|
Identifier id = Identifier.fromNamespaceAndPath(Rivencraft.MOD_ID, "age/" + name);
|
||||||
long seed = LongArgumentType.getLong(context, "seed");
|
long seed = LongArgumentType.getLong(context, "seed");
|
||||||
ServerLevel level;
|
ServerLevel level;
|
||||||
try {
|
try {
|
||||||
level = RuntimeAgeBackend.create(context.getSource().getServer(), id, seed, biomePlan, terrain);
|
level = RuntimeAgeBackend.create(context.getSource().getServer(), id, seed, biomePlan, terrain, rockPlan);
|
||||||
} catch (IllegalArgumentException | IllegalStateException exception) {
|
} catch (IllegalArgumentException | IllegalStateException exception) {
|
||||||
context.getSource().sendFailure(Component.literal(exception.getMessage()));
|
context.getSource().sendFailure(Component.literal(exception.getMessage()));
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -33,7 +33,9 @@ public final class PortAgeTravel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static BlockPos findLanding(ServerLevel target) {
|
private static BlockPos findLanding(ServerLevel target) {
|
||||||
if (target.getChunkSource().getGenerator() instanceof net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator noise
|
net.minecraft.world.level.chunk.ChunkGenerator generator = target.getChunkSource().getGenerator();
|
||||||
|
if (generator instanceof PortRockChunkGenerator rocks) generator = rocks.base();
|
||||||
|
if (generator instanceof net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator noise
|
||||||
&& noise.generatorSettings().value().noiseSettings().equals(target.registryAccess()
|
&& noise.generatorSettings().value().noiseSettings().equals(target.registryAccess()
|
||||||
.lookupOrThrow(Registries.NOISE_SETTINGS)
|
.lookupOrThrow(Registries.NOISE_SETTINGS)
|
||||||
.getOrThrow(net.minecraft.world.level.levelgen.NoiseGeneratorSettings.CAVES)
|
.getOrThrow(net.minecraft.world.level.levelgen.NoiseGeneratorSettings.CAVES)
|
||||||
@@ -56,7 +58,7 @@ public final class PortAgeTravel {
|
|||||||
}
|
}
|
||||||
// A Flat Age has a known surface. Use it directly before a heightmap
|
// A Flat Age has a known surface. Use it directly before a heightmap
|
||||||
// fallback can place a platform in the air above a low superflat world.
|
// fallback can place a platform in the air above a low superflat world.
|
||||||
FlatLevelGeneratorSettings flatSettings = switch (target.getChunkSource().getGenerator()) {
|
FlatLevelGeneratorSettings flatSettings = switch (generator) {
|
||||||
case FlatLevelSource flat -> flat.settings();
|
case FlatLevelSource flat -> flat.settings();
|
||||||
case PortFlatChunkGenerator flat -> flat.settings();
|
case PortFlatChunkGenerator flat -> flat.settings();
|
||||||
default -> null;
|
default -> null;
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package net.dnorberg.rivencraft.age;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import net.dnorberg.rivencraft.writing.PortRegistryPages;
|
||||||
|
import net.minecraft.core.registries.Registries;
|
||||||
|
import net.minecraft.resources.Identifier;
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
|
||||||
|
/** Seed-frozen terrain and biome choices for a book with no authored symbols. */
|
||||||
|
public final class PortRandomAgeSelection {
|
||||||
|
private PortRandomAgeSelection() {}
|
||||||
|
|
||||||
|
public record Selection(PortAgeTerrain terrain, PortBiomePlan biomes) {}
|
||||||
|
|
||||||
|
public static Selection select(MinecraftServer server, long seed) {
|
||||||
|
// Match the 1.21.1 Legacy-rank choices: Standard and Flat are common;
|
||||||
|
// the other terrain families are rarer. The sorted order is deliberate.
|
||||||
|
PortAgeTerrain terrain = weighted(new Random(seed ^ 0x5445525241494E4CL),
|
||||||
|
List.of(PortAgeTerrain.AMPLIFIED, PortAgeTerrain.CAVE, PortAgeTerrain.FLAT,
|
||||||
|
PortAgeTerrain.ISLAND, PortAgeTerrain.STANDARD, PortAgeTerrain.VOID),
|
||||||
|
new int[]{1, 1, 5, 1, 6, 1});
|
||||||
|
if (terrain == PortAgeTerrain.VOID)
|
||||||
|
return new Selection(terrain, new PortBiomePlan(PortBiomePlan.Distribution.SINGLE,
|
||||||
|
List.of(Identifier.withDefaultNamespace("the_void"))));
|
||||||
|
|
||||||
|
PortBiomePlan.Distribution distribution = weighted(new Random(seed ^ 0x524956454E435241L),
|
||||||
|
List.of(PortBiomePlan.Distribution.NATIVE, PortBiomePlan.Distribution.SINGLE,
|
||||||
|
PortBiomePlan.Distribution.MEDIUM, PortBiomePlan.Distribution.TILED,
|
||||||
|
PortBiomePlan.Distribution.GRID, PortBiomePlan.Distribution.TINY,
|
||||||
|
PortBiomePlan.Distribution.SMALL, PortBiomePlan.Distribution.LARGE,
|
||||||
|
PortBiomePlan.Distribution.HUGE),
|
||||||
|
new int[]{3, 3, 3, 1, 1, 1, 1, 1, 1});
|
||||||
|
List<Identifier> eligible = eligibleBiomes(server, terrain);
|
||||||
|
if (eligible.size() < 2 && distribution != PortBiomePlan.Distribution.NATIVE)
|
||||||
|
distribution = PortBiomePlan.Distribution.SINGLE;
|
||||||
|
// 26.3 has no native multi-cave biome source; choose a seeded Cave
|
||||||
|
// biome rather than silently using Lush Caves for every random Age.
|
||||||
|
if (terrain == PortAgeTerrain.CAVE && distribution == PortBiomePlan.Distribution.NATIVE)
|
||||||
|
distribution = PortBiomePlan.Distribution.SINGLE;
|
||||||
|
if (distribution == PortBiomePlan.Distribution.NATIVE)
|
||||||
|
return new Selection(terrain, new PortBiomePlan(distribution, List.of()));
|
||||||
|
|
||||||
|
int count = distribution == PortBiomePlan.Distribution.SINGLE ? 1 : 2;
|
||||||
|
if (count > 1) {
|
||||||
|
Random countRandom = new Random(seed ^ 0x42494F4D45434F55L);
|
||||||
|
while (count < eligible.size() && countRandom.nextInt(3) != 0) count++;
|
||||||
|
}
|
||||||
|
List<Identifier> shuffled = new ArrayList<>(eligible);
|
||||||
|
Collections.shuffle(shuffled, new Random(seed ^ 0x42494F4D45504F4FL));
|
||||||
|
return new Selection(terrain, new PortBiomePlan(distribution, shuffled.subList(0, count)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<Identifier> eligibleBiomes(MinecraftServer server, PortAgeTerrain terrain) {
|
||||||
|
if (terrain == PortAgeTerrain.CAVE) {
|
||||||
|
List<Identifier> caves = server.registryAccess().lookupOrThrow(Registries.BIOME).listElements()
|
||||||
|
.filter(PortRegistryPages::isCaveBiome)
|
||||||
|
.map(holder -> holder.key().identifier())
|
||||||
|
.sorted().toList();
|
||||||
|
return caves.isEmpty() ? List.of(Identifier.withDefaultNamespace("lush_caves")) : caves;
|
||||||
|
}
|
||||||
|
List<Identifier> surface = server.overworld().getChunkSource().getGenerator().getBiomeSource()
|
||||||
|
.possibleBiomes().stream().flatMap(holder -> holder.unwrapKey().stream())
|
||||||
|
.map(key -> key.identifier()).distinct().sorted(Comparator.naturalOrder()).toList();
|
||||||
|
return surface.isEmpty() ? List.of(Identifier.withDefaultNamespace("plains")) : surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> T weighted(Random random, List<T> choices, int[] weights) {
|
||||||
|
int total = 0;
|
||||||
|
for (int weight : weights) total += weight;
|
||||||
|
int ticket = random.nextInt(total);
|
||||||
|
for (int index = 0; index < choices.size(); index++) {
|
||||||
|
ticket -= weights[index];
|
||||||
|
if (ticket < 0) return choices.get(index);
|
||||||
|
}
|
||||||
|
throw new IllegalStateException("Random Age ticket was not consumed");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
package net.dnorberg.rivencraft.age;
|
||||||
|
|
||||||
|
import com.mojang.serialization.MapCodec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Holder;
|
||||||
|
import net.minecraft.core.HolderLookup;
|
||||||
|
import net.minecraft.core.RegistryAccess;
|
||||||
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
|
import net.minecraft.resources.Identifier;
|
||||||
|
import net.minecraft.resources.ResourceKey;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.WorldGenRegion;
|
||||||
|
import net.minecraft.util.RandomSource;
|
||||||
|
import net.minecraft.world.level.ChunkPos;
|
||||||
|
import net.minecraft.world.level.LevelHeightAccessor;
|
||||||
|
import net.minecraft.world.level.NoiseColumn;
|
||||||
|
import net.minecraft.world.level.StructureManager;
|
||||||
|
import net.minecraft.world.level.WorldGenLevel;
|
||||||
|
import net.minecraft.world.level.biome.Biome;
|
||||||
|
import net.minecraft.world.level.biome.BiomeManager;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||||
|
import net.minecraft.world.level.chunk.ChunkGeneratorStructureState;
|
||||||
|
import net.minecraft.world.level.levelgen.Heightmap;
|
||||||
|
import net.minecraft.world.level.levelgen.RandomState;
|
||||||
|
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||||
|
import net.minecraft.world.level.levelgen.densityfunction.SamplerContext;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.StructureSet;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||||
|
import com.mojang.datafixers.util.Pair;
|
||||||
|
import net.minecraft.core.HolderSet;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
|
/** Delegates 26.3 terrain generation and adds one Age-scoped Rocks decoration. */
|
||||||
|
public final class PortRockChunkGenerator extends ChunkGenerator {
|
||||||
|
public static final MapCodec<PortRockChunkGenerator> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
|
||||||
|
ChunkGenerator.CODEC.fieldOf("base").forGetter(generator -> generator.base),
|
||||||
|
PortRockPlan.CODEC.fieldOf("rocks").forGetter(generator -> generator.rocks),
|
||||||
|
com.mojang.serialization.Codec.LONG.fieldOf("age_seed").forGetter(generator -> generator.ageSeed)
|
||||||
|
).apply(instance, PortRockChunkGenerator::new));
|
||||||
|
|
||||||
|
private final ChunkGenerator base;
|
||||||
|
private final PortRockPlan rocks;
|
||||||
|
private final long ageSeed;
|
||||||
|
|
||||||
|
public PortRockChunkGenerator(ChunkGenerator base, PortRockPlan rocks, long ageSeed) {
|
||||||
|
super(base.getBiomeSource(), base::getBiomeGenerationSettings);
|
||||||
|
this.base = base;
|
||||||
|
this.rocks = rocks;
|
||||||
|
this.ageSeed = ageSeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChunkGenerator base() { return base; }
|
||||||
|
public PortRockPlan rocks() { return rocks; }
|
||||||
|
|
||||||
|
@Override protected MapCodec<? extends ChunkGenerator> codec() { return CODEC; }
|
||||||
|
@Override public void validate() { base.validate(); }
|
||||||
|
@Override public ChunkPos getOrigin(RandomState random) { return base.getOrigin(random); }
|
||||||
|
@Override public java.util.Optional<Identifier> getTypeNameForDataFixer() { return base.getTypeNameForDataFixer(); }
|
||||||
|
@Override public CompletableFuture<ChunkAccess> createBiomes(RandomState random, Blender blender,
|
||||||
|
StructureManager structures, ChunkAccess chunk) {
|
||||||
|
return base.createBiomes(random, blender, structures, chunk);
|
||||||
|
}
|
||||||
|
@Override public ChunkGeneratorStructureState createState(HolderLookup<StructureSet> sets,
|
||||||
|
RandomState random, long seed) {
|
||||||
|
return base.createState(sets, random, seed);
|
||||||
|
}
|
||||||
|
@Override public void createStructures(RegistryAccess registries, ChunkGeneratorStructureState state,
|
||||||
|
StructureManager structures, ChunkAccess chunk,
|
||||||
|
StructureTemplateManager templates, ResourceKey<Level> dimension) {
|
||||||
|
base.createStructures(registries, state, structures, chunk, templates, dimension);
|
||||||
|
}
|
||||||
|
@Override public void createReferences(WorldGenLevel level, StructureManager structures, ChunkAccess chunk) {
|
||||||
|
base.createReferences(level, structures, chunk);
|
||||||
|
}
|
||||||
|
@Override public Pair<BlockPos, Holder<Structure>> findNearestMapStructure(ServerLevel level,
|
||||||
|
HolderSet<Structure> targets, BlockPos origin, int radius, boolean skipKnown) {
|
||||||
|
return base.findNearestMapStructure(level, targets, origin, radius, skipKnown);
|
||||||
|
}
|
||||||
|
@Override public void spawnOriginalMobs(WorldGenRegion level) { base.spawnOriginalMobs(level); }
|
||||||
|
@Override public int getSpawnHeight(LevelHeightAccessor height) { return base.getSpawnHeight(height); }
|
||||||
|
@Override public int getGenDepth() { return base.getGenDepth(); }
|
||||||
|
@Override public int getSeaLevel() { return base.getSeaLevel(); }
|
||||||
|
@Override public int getMinY() { return base.getMinY(); }
|
||||||
|
@Override public CompletableFuture<ChunkAccess> buildTerrain(ChunkAccess chunk, Blender blender,
|
||||||
|
RandomState random, StructureManager structures, BiomeManager biomes,
|
||||||
|
WorldGenRegion carverBiomeRegion, Set<Holder<Biome>> possibleBiomes) {
|
||||||
|
return base.buildTerrain(chunk, blender, random, structures, biomes, carverBiomeRegion, possibleBiomes);
|
||||||
|
}
|
||||||
|
@Override public int getBaseHeight(int x, int z, Heightmap.Types type,
|
||||||
|
LevelHeightAccessor height, RandomState random) {
|
||||||
|
return base.getBaseHeight(x, z, type, height, random);
|
||||||
|
}
|
||||||
|
@Override public NoiseColumn getBaseColumn(int x, int z, LevelHeightAccessor height, RandomState random) {
|
||||||
|
return base.getBaseColumn(x, z, height, random);
|
||||||
|
}
|
||||||
|
@Override public void addDebugScreenInfo(List<String> info, RandomState random,
|
||||||
|
BlockPos pos, SamplerContext context) {
|
||||||
|
base.addDebugScreenInfo(info, random, pos, context);
|
||||||
|
}
|
||||||
|
@Override public void refreshFeaturesPerStep() {
|
||||||
|
base.refreshFeaturesPerStep();
|
||||||
|
super.refreshFeaturesPerStep();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void applyBiomeDecoration(WorldGenLevel level, ChunkAccess chunk,
|
||||||
|
StructureManager structures) {
|
||||||
|
base.applyBiomeDecoration(level, chunk, structures);
|
||||||
|
ChunkPos chunkPos = chunk.getPos();
|
||||||
|
long seed = ageSeed ^ (long) chunkPos.x() * 341873128712L ^ (long) chunkPos.z() * 132897987541L;
|
||||||
|
RandomSource random = RandomSource.create(seed);
|
||||||
|
if (random.nextInt(5) != 0) return;
|
||||||
|
BlockState material = BuiltInRegistries.BLOCK.getValue(rocks.material()).defaultBlockState();
|
||||||
|
int centerX = chunkPos.getMinBlockX() + 2 + random.nextInt(12);
|
||||||
|
int centerZ = chunkPos.getMinBlockZ() + 2 + random.nextInt(12);
|
||||||
|
for (int dx = -1; dx <= 1; dx++) for (int dz = -1; dz <= 1; dz++) {
|
||||||
|
if (random.nextInt(4) == 0) continue;
|
||||||
|
int x = centerX + dx;
|
||||||
|
int z = centerZ + dz;
|
||||||
|
int y = level.getHeight(Heightmap.Types.WORLD_SURFACE_WG, x, z);
|
||||||
|
BlockPos place = new BlockPos(x, y, z);
|
||||||
|
if (level.getBlockState(place).isAir()
|
||||||
|
&& level.getFluidState(place).isEmpty()
|
||||||
|
&& level.getBlockState(place.below()).isFaceSturdy(level, place.below(), net.minecraft.core.Direction.UP))
|
||||||
|
level.setBlock(place, material, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package net.dnorberg.rivencraft.age;
|
||||||
|
|
||||||
|
import com.mojang.serialization.Codec;
|
||||||
|
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||||
|
import net.minecraft.resources.Identifier;
|
||||||
|
|
||||||
|
/** First frozen authored feature: one Rocks page with an explicit block material. */
|
||||||
|
public record PortRockPlan(Identifier material) {
|
||||||
|
public static final Codec<PortRockPlan> CODEC = RecordCodecBuilder.create(instance -> instance.group(
|
||||||
|
Identifier.CODEC.fieldOf("material").forGetter(PortRockPlan::material)
|
||||||
|
).apply(instance, PortRockPlan::new));
|
||||||
|
}
|
||||||
@@ -57,7 +57,7 @@ public final class RuntimeAgeBackend {
|
|||||||
for (Map.Entry<Identifier, Long> age : index.entries().entrySet()) {
|
for (Map.Entry<Identifier, Long> age : index.entries().entrySet()) {
|
||||||
try {
|
try {
|
||||||
load(server, age.getKey(), age.getValue(), index.biomePlan(age.getKey()),
|
load(server, age.getKey(), age.getValue(), index.biomePlan(age.getKey()),
|
||||||
index.terrain(age.getKey()));
|
index.terrain(age.getKey()), index.rocks(age.getKey()));
|
||||||
} catch (RuntimeException exception) {
|
} catch (RuntimeException exception) {
|
||||||
Rivencraft.LOGGER.error("Could not restore Age {}. Its saved index remains intact.", age.getKey(), exception);
|
Rivencraft.LOGGER.error("Could not restore Age {}. Its saved index remains intact.", age.getKey(), exception);
|
||||||
}
|
}
|
||||||
@@ -75,12 +75,17 @@ public final class RuntimeAgeBackend {
|
|||||||
|
|
||||||
public static ServerLevel create(MinecraftServer server, Identifier id, long seed,
|
public static ServerLevel create(MinecraftServer server, Identifier id, long seed,
|
||||||
PortBiomePlan biomePlan, PortAgeTerrain terrain) {
|
PortBiomePlan biomePlan, PortAgeTerrain terrain) {
|
||||||
|
return create(server, id, seed, biomePlan, terrain, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ServerLevel create(MinecraftServer server, Identifier id, long seed,
|
||||||
|
PortBiomePlan biomePlan, PortAgeTerrain terrain, PortRockPlan rocks) {
|
||||||
checkServerThread(server);
|
checkServerThread(server);
|
||||||
ageName(id);
|
ageName(id);
|
||||||
AgeLevelIndex index = index(server);
|
AgeLevelIndex index = index(server);
|
||||||
if (index.contains(id)) throw new IllegalArgumentException("Age already exists: " + id);
|
if (index.contains(id)) throw new IllegalArgumentException("Age already exists: " + id);
|
||||||
ServerLevel level = load(server, id, seed, biomePlan, terrain);
|
ServerLevel level = load(server, id, seed, biomePlan, terrain, rocks);
|
||||||
index.add(id, seed, biomePlan, terrain);
|
index.add(id, seed, biomePlan, terrain, rocks);
|
||||||
// A newly bound book may be used immediately. Persist its Age identity
|
// A newly bound book may be used immediately. Persist its Age identity
|
||||||
// before publishing the catalog so a later crash cannot leave that
|
// before publishing the catalog so a later crash cannot leave that
|
||||||
// book pointing at a world omitted from the on-disk index.
|
// book pointing at a world omitted from the on-disk index.
|
||||||
@@ -100,7 +105,8 @@ public final class RuntimeAgeBackend {
|
|||||||
public static ServerLevel reload(MinecraftServer server, Identifier id) {
|
public static ServerLevel reload(MinecraftServer server, Identifier id) {
|
||||||
checkServerThread(server);
|
checkServerThread(server);
|
||||||
long seed = requireSeed(server, id);
|
long seed = requireSeed(server, id);
|
||||||
ServerLevel level = load(server, id, seed, index(server).biomePlan(id), index(server).terrain(id));
|
ServerLevel level = load(server, id, seed, index(server).biomePlan(id), index(server).terrain(id),
|
||||||
|
index(server).rocks(id));
|
||||||
AgeCatalogSync.onCatalogChanged(server);
|
AgeCatalogSync.onCatalogChanged(server);
|
||||||
return level;
|
return level;
|
||||||
}
|
}
|
||||||
@@ -112,18 +118,19 @@ public final class RuntimeAgeBackend {
|
|||||||
long previousSeed = requireSeed(server, id);
|
long previousSeed = requireSeed(server, id);
|
||||||
PortBiomePlan biomePlan = index.biomePlan(id);
|
PortBiomePlan biomePlan = index.biomePlan(id);
|
||||||
PortAgeTerrain terrain = index.terrain(id);
|
PortAgeTerrain terrain = index.terrain(id);
|
||||||
|
PortRockPlan rocks = index.rocks(id);
|
||||||
if (previousSeed == seed) return;
|
if (previousSeed == seed) return;
|
||||||
boolean loaded = server.getLevel(dimensionKey(id)) != null;
|
boolean loaded = server.getLevel(dimensionKey(id)) != null;
|
||||||
if (loaded) unloadLoaded(server, id);
|
if (loaded) unloadLoaded(server, id);
|
||||||
try {
|
try {
|
||||||
if (loaded) load(server, id, seed, biomePlan, terrain);
|
if (loaded) load(server, id, seed, biomePlan, terrain, rocks);
|
||||||
index.reseed(id, seed);
|
index.reseed(id, seed);
|
||||||
server.overworld().getDataStorage().saveAndJoin();
|
server.overworld().getDataStorage().saveAndJoin();
|
||||||
if (loaded) AgeCatalogSync.onCatalogChanged(server);
|
if (loaded) AgeCatalogSync.onCatalogChanged(server);
|
||||||
} catch (RuntimeException exception) {
|
} catch (RuntimeException exception) {
|
||||||
if (loaded) {
|
if (loaded) {
|
||||||
try {
|
try {
|
||||||
load(server, id, previousSeed, biomePlan, terrain);
|
load(server, id, previousSeed, biomePlan, terrain, rocks);
|
||||||
AgeCatalogSync.onCatalogChanged(server);
|
AgeCatalogSync.onCatalogChanged(server);
|
||||||
} catch (RuntimeException rollbackFailure) {
|
} catch (RuntimeException rollbackFailure) {
|
||||||
exception.addSuppressed(rollbackFailure);
|
exception.addSuppressed(rollbackFailure);
|
||||||
@@ -141,6 +148,7 @@ public final class RuntimeAgeBackend {
|
|||||||
long seed = requireSeed(server, id);
|
long seed = requireSeed(server, id);
|
||||||
PortBiomePlan biomePlan = index.biomePlan(id);
|
PortBiomePlan biomePlan = index.biomePlan(id);
|
||||||
PortAgeTerrain terrain = index.terrain(id);
|
PortAgeTerrain terrain = index.terrain(id);
|
||||||
|
PortRockPlan rocks = index.rocks(id);
|
||||||
boolean loaded = server.getLevel(dimensionKey(id)) != null;
|
boolean loaded = server.getLevel(dimensionKey(id)) != null;
|
||||||
ArchivePaths paths = archivePaths(server, name);
|
ArchivePaths paths = archivePaths(server, name);
|
||||||
if (loaded) unloadLoaded(server, id);
|
if (loaded) unloadLoaded(server, id);
|
||||||
@@ -159,14 +167,14 @@ public final class RuntimeAgeBackend {
|
|||||||
}
|
}
|
||||||
} catch (IOException | RuntimeException exception) {
|
} catch (IOException | RuntimeException exception) {
|
||||||
try {
|
try {
|
||||||
index.restoreAfterFailedRetirement(id, seed, biomePlan, terrain);
|
index.restoreAfterFailedRetirement(id, seed, biomePlan, terrain, rocks);
|
||||||
server.overworld().getDataStorage().saveAndJoin();
|
server.overworld().getDataStorage().saveAndJoin();
|
||||||
} catch (RuntimeException rollbackFailure) {
|
} catch (RuntimeException rollbackFailure) {
|
||||||
exception.addSuppressed(rollbackFailure);
|
exception.addSuppressed(rollbackFailure);
|
||||||
}
|
}
|
||||||
if (loaded) {
|
if (loaded) {
|
||||||
try {
|
try {
|
||||||
load(server, id, seed, biomePlan, terrain);
|
load(server, id, seed, biomePlan, terrain, rocks);
|
||||||
} catch (RuntimeException rollbackFailure) {
|
} catch (RuntimeException rollbackFailure) {
|
||||||
exception.addSuppressed(rollbackFailure);
|
exception.addSuppressed(rollbackFailure);
|
||||||
}
|
}
|
||||||
@@ -282,7 +290,7 @@ public final class RuntimeAgeBackend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static ServerLevel load(MinecraftServer server, Identifier id, long seed,
|
private static ServerLevel load(MinecraftServer server, Identifier id, long seed,
|
||||||
PortBiomePlan biomePlan, PortAgeTerrain terrain) {
|
PortBiomePlan biomePlan, PortAgeTerrain terrain, PortRockPlan rocks) {
|
||||||
ResourceKey<Level> key = ResourceKey.create(Registries.DIMENSION, id);
|
ResourceKey<Level> key = ResourceKey.create(Registries.DIMENSION, id);
|
||||||
if (server.getLevel(key) != null) throw new IllegalArgumentException("Level already loaded: " + id);
|
if (server.getLevel(key) != null) throw new IllegalArgumentException("Level already loaded: " + id);
|
||||||
|
|
||||||
@@ -353,6 +361,7 @@ public final class RuntimeAgeBackend {
|
|||||||
};
|
};
|
||||||
generator = new NoiseBasedChunkGenerator(source, settings);
|
generator = new NoiseBasedChunkGenerator(source, settings);
|
||||||
}
|
}
|
||||||
|
if (rocks != null) generator = new PortRockChunkGenerator(generator, rocks, seed);
|
||||||
LevelStem stem = new LevelStem(overworldStem.type(), generator, OptionalLong.of(seed));
|
LevelStem stem = new LevelStem(overworldStem.type(), generator, OptionalLong.of(seed));
|
||||||
long biomeZoomSeed = BiomeManager.obfuscateSeed(server.getWorldGenSettings().options().seed());
|
long biomeZoomSeed = BiomeManager.obfuscateSeed(server.getWorldGenSettings().options().seed());
|
||||||
ServerLevel level = new ServerLevel(server, server.executor, server.storageSource,
|
ServerLevel level = new ServerLevel(server, server.executor, server.storageSource,
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package net.dnorberg.rivencraft.network;
|
|||||||
|
|
||||||
import net.dnorberg.rivencraft.Rivencraft;
|
import net.dnorberg.rivencraft.Rivencraft;
|
||||||
import net.dnorberg.rivencraft.age.PortAgeTravel;
|
import net.dnorberg.rivencraft.age.PortAgeTravel;
|
||||||
|
import net.dnorberg.rivencraft.age.PortAgeTerrain;
|
||||||
import net.dnorberg.rivencraft.age.RuntimeAgeBackend;
|
import net.dnorberg.rivencraft.age.RuntimeAgeBackend;
|
||||||
|
import net.dnorberg.rivencraft.age.PortRandomAgeSelection;
|
||||||
import net.dnorberg.rivencraft.item.LinkBookTarget;
|
import net.dnorberg.rivencraft.item.LinkBookTarget;
|
||||||
import net.dnorberg.rivencraft.item.LinkProperties;
|
import net.dnorberg.rivencraft.item.LinkProperties;
|
||||||
import net.dnorberg.rivencraft.item.LinkingBookItem;
|
import net.dnorberg.rivencraft.item.LinkingBookItem;
|
||||||
@@ -125,8 +127,15 @@ public final class PortBookNetworking {
|
|||||||
id = Identifier.fromNamespaceAndPath(Rivencraft.MOD_ID, "age/" + name);
|
id = Identifier.fromNamespaceAndPath(Rivencraft.MOD_ID, "age/" + name);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
RuntimeAgeBackend.create(server, id, player.getRandom().nextLong(), biomePlan,
|
long seed = player.getRandom().nextLong();
|
||||||
PortAuthoredWriting.terrain(pages));
|
PortAgeTerrain terrain = PortAuthoredWriting.terrain(pages);
|
||||||
|
if (pages.isEmpty()) {
|
||||||
|
PortRandomAgeSelection.Selection random = PortRandomAgeSelection.select(server, seed);
|
||||||
|
terrain = random.terrain();
|
||||||
|
biomePlan = random.biomes();
|
||||||
|
}
|
||||||
|
RuntimeAgeBackend.create(server, id, seed, biomePlan, terrain,
|
||||||
|
PortAuthoredWriting.rockPlan(pages));
|
||||||
} catch (IllegalArgumentException | IllegalStateException exception) {
|
} catch (IllegalArgumentException | IllegalStateException exception) {
|
||||||
player.sendOverlayMessage(Component.literal("Age creation failed: " + exception.getMessage()));
|
player.sendOverlayMessage(Component.literal("Age creation failed: " + exception.getMessage()));
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package net.dnorberg.rivencraft.registry;
|
|||||||
import com.mojang.serialization.MapCodec;
|
import com.mojang.serialization.MapCodec;
|
||||||
import net.dnorberg.rivencraft.Rivencraft;
|
import net.dnorberg.rivencraft.Rivencraft;
|
||||||
import net.dnorberg.rivencraft.age.PortFlatChunkGenerator;
|
import net.dnorberg.rivencraft.age.PortFlatChunkGenerator;
|
||||||
|
import net.dnorberg.rivencraft.age.PortRockChunkGenerator;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||||
import net.neoforged.bus.api.IEventBus;
|
import net.neoforged.bus.api.IEventBus;
|
||||||
@@ -15,6 +16,7 @@ public final class PortChunkGenerators {
|
|||||||
|
|
||||||
static {
|
static {
|
||||||
GENERATORS.register("age_flat", () -> PortFlatChunkGenerator.CODEC);
|
GENERATORS.register("age_flat", () -> PortFlatChunkGenerator.CODEC);
|
||||||
|
GENERATORS.register("age_rocks", () -> PortRockChunkGenerator.CODEC);
|
||||||
}
|
}
|
||||||
|
|
||||||
private PortChunkGenerators() {}
|
private PortChunkGenerators() {}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import java.util.List;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import net.dnorberg.rivencraft.age.PortBiomePlan;
|
import net.dnorberg.rivencraft.age.PortBiomePlan;
|
||||||
import net.dnorberg.rivencraft.age.PortAgeTerrain;
|
import net.dnorberg.rivencraft.age.PortAgeTerrain;
|
||||||
|
import net.dnorberg.rivencraft.age.PortRockPlan;
|
||||||
import net.dnorberg.rivencraft.Rivencraft;
|
import net.dnorberg.rivencraft.Rivencraft;
|
||||||
import net.minecraft.core.HolderLookup;
|
import net.minecraft.core.HolderLookup;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
@@ -25,6 +26,8 @@ public final class PortAuthoredWriting {
|
|||||||
Rivencraft.MOD_ID, "terrain/void");
|
Rivencraft.MOD_ID, "terrain/void");
|
||||||
private static final Identifier CAVE_TERRAIN = Identifier.fromNamespaceAndPath(
|
private static final Identifier CAVE_TERRAIN = Identifier.fromNamespaceAndPath(
|
||||||
Rivencraft.MOD_ID, "terrain/cave");
|
Rivencraft.MOD_ID, "terrain/cave");
|
||||||
|
private static final Identifier ROCKS = Identifier.fromNamespaceAndPath(
|
||||||
|
Rivencraft.MOD_ID, "feature/rocks");
|
||||||
|
|
||||||
private PortAuthoredWriting() {}
|
private PortAuthoredWriting() {}
|
||||||
|
|
||||||
@@ -33,11 +36,19 @@ public final class PortAuthoredWriting {
|
|||||||
boolean terrain = false;
|
boolean terrain = false;
|
||||||
int biomes = 0;
|
int biomes = 0;
|
||||||
int controllers = 0;
|
int controllers = 0;
|
||||||
|
boolean material = false;
|
||||||
|
boolean rocks = false;
|
||||||
for (Identifier page : pages) {
|
for (Identifier page : pages) {
|
||||||
if (isTerrain(page) && !terrain) {
|
if (isTerrain(page) && !terrain) {
|
||||||
terrain = true;
|
terrain = true;
|
||||||
} else if (controller(page) != null && controllers++ == 0) {
|
} else if (controller(page) != null && controllers++ == 0) {
|
||||||
// An explicit controller needs an authored pool.
|
// An explicit controller needs an authored pool.
|
||||||
|
} else if (isBlockPage(page) && !material && !rocks
|
||||||
|
&& PortSymbolPages.find(page) != null
|
||||||
|
&& PortSymbolPages.find(page).category().equals("rivencraft:block")) {
|
||||||
|
material = true;
|
||||||
|
} else if (page.equals(ROCKS) && material && !rocks) {
|
||||||
|
rocks = true;
|
||||||
} else if (page.getNamespace().equals(Rivencraft.MOD_ID)
|
} else if (page.getNamespace().equals(Rivencraft.MOD_ID)
|
||||||
&& page.getPath().startsWith("biome/") && biomes < MAX_AUTHORED_BIOMES
|
&& page.getPath().startsWith("biome/") && biomes < MAX_AUTHORED_BIOMES
|
||||||
&& PortSymbolPages.find(page) != null
|
&& PortSymbolPages.find(page) != null
|
||||||
@@ -46,6 +57,7 @@ public final class PortAuthoredWriting {
|
|||||||
biomes++;
|
biomes++;
|
||||||
} else return false;
|
} else return false;
|
||||||
}
|
}
|
||||||
|
if (material != rocks || rocks && (pages.contains(CAVE_TERRAIN) || pages.contains(VOID_TERRAIN))) return false;
|
||||||
if (controllers != 0) {
|
if (controllers != 0) {
|
||||||
PortBiomePlan.Distribution distribution = pages.stream().map(PortAuthoredWriting::controller)
|
PortBiomePlan.Distribution distribution = pages.stream().map(PortAuthoredWriting::controller)
|
||||||
.filter(value -> value != null).findFirst().orElseThrow();
|
.filter(value -> value != null).findFirst().orElseThrow();
|
||||||
@@ -65,6 +77,10 @@ public final class PortAuthoredWriting {
|
|||||||
|| page.equals(VOID_TERRAIN) || page.equals(CAVE_TERRAIN);
|
|| page.equals(VOID_TERRAIN) || page.equals(CAVE_TERRAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean isBlockPage(Identifier page) {
|
||||||
|
return page.getNamespace().equals(Rivencraft.MOD_ID) && page.getPath().startsWith("block/");
|
||||||
|
}
|
||||||
|
|
||||||
private static PortBiomePlan.Distribution controller(Identifier page) {
|
private static PortBiomePlan.Distribution controller(Identifier page) {
|
||||||
if (!page.getNamespace().equals(Rivencraft.MOD_ID)) return null;
|
if (!page.getNamespace().equals(Rivencraft.MOD_ID)) return null;
|
||||||
return switch (page.getPath()) {
|
return switch (page.getPath()) {
|
||||||
@@ -91,6 +107,13 @@ public final class PortAuthoredWriting {
|
|||||||
return PortAgeTerrain.STANDARD;
|
return PortAgeTerrain.STANDARD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static PortRockPlan rockPlan(List<Identifier> pages) {
|
||||||
|
if (!supports(pages)) throw new IllegalArgumentException("Unsupported authored Age pages");
|
||||||
|
return pages.contains(ROCKS) ? new PortRockPlan(pages.stream()
|
||||||
|
.filter(PortAuthoredWriting::isBlockPage).map(PortRegistryPages::blockId)
|
||||||
|
.findFirst().orElseThrow()) : null;
|
||||||
|
}
|
||||||
|
|
||||||
/** Resolves all authored biomes from the active registry before creating a level. */
|
/** Resolves all authored biomes from the active registry before creating a level. */
|
||||||
public static PortBiomePlan biomePlan(List<Identifier> pages, HolderLookup.Provider registries) {
|
public static PortBiomePlan biomePlan(List<Identifier> pages, HolderLookup.Provider registries) {
|
||||||
if (!supports(pages)) throw new IllegalArgumentException(
|
if (!supports(pages)) throw new IllegalArgumentException(
|
||||||
@@ -99,7 +122,7 @@ public final class PortAuthoredWriting {
|
|||||||
PortBiomePlan.Distribution distribution = null;
|
PortBiomePlan.Distribution distribution = null;
|
||||||
boolean caveTerrain = pages.contains(CAVE_TERRAIN);
|
boolean caveTerrain = pages.contains(CAVE_TERRAIN);
|
||||||
for (Identifier page : pages) {
|
for (Identifier page : pages) {
|
||||||
if (isTerrain(page)) continue;
|
if (isTerrain(page) || page.equals(ROCKS) || isBlockPage(page)) continue;
|
||||||
PortBiomePlan.Distribution selected = controller(page);
|
PortBiomePlan.Distribution selected = controller(page);
|
||||||
if (selected != null) { distribution = selected; continue; }
|
if (selected != null) { distribution = selected; continue; }
|
||||||
Identifier biome = PortRegistryPages.biomeId(page);
|
Identifier biome = PortRegistryPages.biomeId(page);
|
||||||
|
|||||||
@@ -75,6 +75,8 @@ send_and_wait 'rivencraft_port age create smoke_cave 112233 cave' 'Created riven
|
|||||||
send_and_wait 'rivencraft_port age create_multi smoke_flat_multi 24681 flat' 'Created rivencraft:age/smoke_flat_multi with seed 24681 terrain=FLAT'
|
send_and_wait 'rivencraft_port age create_multi smoke_flat_multi 24681 flat' 'Created rivencraft:age/smoke_flat_multi with seed 24681 terrain=FLAT'
|
||||||
send_and_wait 'rivencraft_port age create_multi smoke_void_multi 13580 void' 'Created rivencraft:age/smoke_void_multi with seed 13580 terrain=VOID'
|
send_and_wait 'rivencraft_port age create_multi smoke_void_multi 13580 void' 'Created rivencraft:age/smoke_void_multi with seed 13580 terrain=VOID'
|
||||||
send_and_wait 'rivencraft_port age create_multi smoke_cave_multi 112234 cave' 'Created rivencraft:age/smoke_cave_multi with seed 112234 terrain=CAVE'
|
send_and_wait 'rivencraft_port age create_multi smoke_cave_multi 112234 cave' 'Created rivencraft:age/smoke_cave_multi with seed 112234 terrain=CAVE'
|
||||||
|
send_and_wait 'rivencraft_port age create_random smoke_random 889900' 'Random Age plan: terrain='
|
||||||
|
send_and_wait 'rivencraft_port age create_rocks smoke_rocks 445566' 'Created rivencraft:age/smoke_rocks with seed 445566 terrain=FLAT'
|
||||||
# The index must reach disk while the server is still running. A graceful
|
# The index must reach disk while the server is still running. A graceful
|
||||||
# shutdown would hide the interrupted-save regression this checks.
|
# shutdown would hide the interrupted-save regression this checks.
|
||||||
index_file="$server_dir/world/dimensions/minecraft/overworld/data/rivencraft/runtime_ages.dat"
|
index_file="$server_dir/world/dimensions/minecraft/overworld/data/rivencraft/runtime_ages.dat"
|
||||||
@@ -90,6 +92,8 @@ gzip -dc "$index_file" | strings | grep -Fq 'rivencraft:age/smoke_cave'
|
|||||||
gzip -dc "$index_file" | strings | grep -Fq 'rivencraft:age/smoke_flat_multi'
|
gzip -dc "$index_file" | strings | grep -Fq 'rivencraft:age/smoke_flat_multi'
|
||||||
gzip -dc "$index_file" | strings | grep -Fq 'rivencraft:age/smoke_void_multi'
|
gzip -dc "$index_file" | strings | grep -Fq 'rivencraft:age/smoke_void_multi'
|
||||||
gzip -dc "$index_file" | strings | grep -Fq 'rivencraft:age/smoke_cave_multi'
|
gzip -dc "$index_file" | strings | grep -Fq 'rivencraft:age/smoke_cave_multi'
|
||||||
|
gzip -dc "$index_file" | strings | grep -Fq 'rivencraft:age/smoke_random'
|
||||||
|
gzip -dc "$index_file" | strings | grep -Fq 'rivencraft:age/smoke_rocks'
|
||||||
send_and_wait 'rivencraft_port age sample smoke_alpha' 'rivencraft:age/smoke_alpha seed=123456789 chunkOffset=0 terrain='
|
send_and_wait 'rivencraft_port age sample smoke_alpha' 'rivencraft:age/smoke_alpha seed=123456789 chunkOffset=0 terrain='
|
||||||
send_and_wait 'rivencraft_port age sample smoke_beta' 'rivencraft:age/smoke_beta seed=987654321 chunkOffset=0 terrain='
|
send_and_wait 'rivencraft_port age sample smoke_beta' 'rivencraft:age/smoke_beta seed=987654321 chunkOffset=0 terrain='
|
||||||
send_and_wait 'rivencraft_port age sample smoke_flat' 'rivencraft:age/smoke_flat seed=24680 chunkOffset=0 terrain='
|
send_and_wait 'rivencraft_port age sample smoke_flat' 'rivencraft:age/smoke_flat seed=24680 chunkOffset=0 terrain='
|
||||||
@@ -98,12 +102,21 @@ send_and_wait 'rivencraft_port age sample smoke_cave' 'rivencraft:age/smoke_cave
|
|||||||
send_and_wait 'rivencraft_port age sample smoke_flat_multi' 'rivencraft:age/smoke_flat_multi seed=24681 chunkOffset=0 terrain='
|
send_and_wait 'rivencraft_port age sample smoke_flat_multi' 'rivencraft:age/smoke_flat_multi seed=24681 chunkOffset=0 terrain='
|
||||||
send_and_wait 'rivencraft_port age sample smoke_void_multi' 'rivencraft:age/smoke_void_multi seed=13580 chunkOffset=0 terrain='
|
send_and_wait 'rivencraft_port age sample smoke_void_multi' 'rivencraft:age/smoke_void_multi seed=13580 chunkOffset=0 terrain='
|
||||||
send_and_wait 'rivencraft_port age sample smoke_cave_multi' 'rivencraft:age/smoke_cave_multi seed=112234 chunkOffset=0 terrain='
|
send_and_wait 'rivencraft_port age sample smoke_cave_multi' 'rivencraft:age/smoke_cave_multi seed=112234 chunkOffset=0 terrain='
|
||||||
|
send_and_wait 'rivencraft_port age sample smoke_random' 'rivencraft:age/smoke_random seed=889900 chunkOffset=0 terrain='
|
||||||
|
send_and_wait 'rivencraft_port age sample smoke_rocks' 'rivencraft:age/smoke_rocks seed=445566 chunkOffset=0 terrain='
|
||||||
send_and_wait 'execute in rivencraft:age/smoke_flat run forceload add 0 0' 'Marked chunk [0, 0] in rivencraft:age/smoke_flat to be force loaded'
|
send_and_wait 'execute in rivencraft:age/smoke_flat run forceload add 0 0' 'Marked chunk [0, 0] in rivencraft:age/smoke_flat to be force loaded'
|
||||||
send_and_wait 'execute in rivencraft:age/smoke_void run forceload add 0 0' 'Marked chunk [0, 0] in rivencraft:age/smoke_void to be force loaded'
|
send_and_wait 'execute in rivencraft:age/smoke_void run forceload add 0 0' 'Marked chunk [0, 0] in rivencraft:age/smoke_void to be force loaded'
|
||||||
send_and_wait 'execute in rivencraft:age/smoke_flat_multi run forceload add 0 0' 'Marked chunk [0, 0] in rivencraft:age/smoke_flat_multi to be force loaded'
|
send_and_wait 'execute in rivencraft:age/smoke_flat_multi run forceload add 0 0' 'Marked chunk [0, 0] in rivencraft:age/smoke_flat_multi to be force loaded'
|
||||||
send_and_wait 'execute in rivencraft:age/smoke_void_multi run forceload add 0 0' 'Marked chunk [0, 0] in rivencraft:age/smoke_void_multi to be force loaded'
|
send_and_wait 'execute in rivencraft:age/smoke_void_multi run forceload add 0 0' 'Marked chunk [0, 0] in rivencraft:age/smoke_void_multi to be force loaded'
|
||||||
send_and_wait 'execute in rivencraft:age/smoke_cave run forceload add 0 0' 'Marked chunk [0, 0] in rivencraft:age/smoke_cave to be force loaded'
|
send_and_wait 'execute in rivencraft:age/smoke_cave run forceload add 0 0' 'Marked chunk [0, 0] in rivencraft:age/smoke_cave to be force loaded'
|
||||||
send_and_wait 'execute in rivencraft:age/smoke_cave_multi run forceload add 0 0' 'Marked chunk [0, 0] in rivencraft:age/smoke_cave_multi to be force loaded'
|
send_and_wait 'execute in rivencraft:age/smoke_cave_multi run forceload add 0 0' 'Marked chunk [0, 0] in rivencraft:age/smoke_cave_multi to be force loaded'
|
||||||
|
send_and_wait 'execute in rivencraft:age/smoke_rocks run forceload add 0 0' 'Marked chunk [0, 0] in rivencraft:age/smoke_rocks to be force loaded'
|
||||||
|
send_and_wait 'rivencraft_port age rocks_probe smoke_rocks' 'Rocks probe rivencraft:age/smoke_rocks material=minecraft:gold_block count='
|
||||||
|
rocks_before=$(grep -F 'Rocks probe rivencraft:age/smoke_rocks material=minecraft:gold_block count=' "$log_file" | tail -1 | sed 's/.*count=//')
|
||||||
|
[[ "$rocks_before" =~ ^[1-9][0-9]*$ ]] || {
|
||||||
|
printf 'Authored Rocks placed no gold blocks: %s\n' "$rocks_before" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
send_and_wait 'execute in rivencraft:age/smoke_flat if block 0 0 0 minecraft:bedrock run say Flat bedrock starts at Y0' 'Flat bedrock starts at Y0'
|
send_and_wait 'execute in rivencraft:age/smoke_flat if block 0 0 0 minecraft:bedrock run say Flat bedrock starts at Y0' 'Flat bedrock starts at Y0'
|
||||||
send_and_wait 'execute in rivencraft:age/smoke_flat if block 0 3 0 minecraft:grass_block run say Flat surface is Y3' 'Flat surface is Y3'
|
send_and_wait 'execute in rivencraft:age/smoke_flat if block 0 3 0 minecraft:grass_block run say Flat surface is Y3' 'Flat surface is Y3'
|
||||||
send_and_wait 'execute in rivencraft:age/smoke_void if block 0 0 0 minecraft:air run say Void remains empty' 'Void remains empty'
|
send_and_wait 'execute in rivencraft:age/smoke_void if block 0 0 0 minecraft:air run say Void remains empty' 'Void remains empty'
|
||||||
@@ -119,6 +132,7 @@ alpha_before=$(grep -F 'rivencraft:age/smoke_alpha seed=123456789 chunkOffset=0
|
|||||||
beta_before=$(grep -F 'rivencraft:age/smoke_beta seed=987654321 chunkOffset=0 terrain=' "$log_file" | tail -1 | sed 's/.*terrain=//')
|
beta_before=$(grep -F 'rivencraft:age/smoke_beta seed=987654321 chunkOffset=0 terrain=' "$log_file" | tail -1 | sed 's/.*terrain=//')
|
||||||
flat_before=$(grep -F 'rivencraft:age/smoke_flat seed=24680 chunkOffset=0 terrain=' "$log_file" | tail -1 | sed 's/.*terrain=//')
|
flat_before=$(grep -F 'rivencraft:age/smoke_flat seed=24680 chunkOffset=0 terrain=' "$log_file" | tail -1 | sed 's/.*terrain=//')
|
||||||
void_before=$(grep -F 'rivencraft:age/smoke_void seed=13579 chunkOffset=0 terrain=' "$log_file" | tail -1 | sed 's/.*terrain=//')
|
void_before=$(grep -F 'rivencraft:age/smoke_void seed=13579 chunkOffset=0 terrain=' "$log_file" | tail -1 | sed 's/.*terrain=//')
|
||||||
|
random_before=$(grep -F 'rivencraft:age/smoke_random seed=889900 chunkOffset=0 terrain=' "$log_file" | tail -1 | sed 's/.*terrain=//')
|
||||||
if [[ "$alpha_before" == "$beta_before" ]]; then
|
if [[ "$alpha_before" == "$beta_before" ]]; then
|
||||||
printf 'Different Age seeds produced the same terrain probe: %s\n' "$alpha_before" >&2
|
printf 'Different Age seeds produced the same terrain probe: %s\n' "$alpha_before" >&2
|
||||||
exit 1
|
exit 1
|
||||||
@@ -139,6 +153,10 @@ send_and_wait 'rivencraft_port age sample smoke_cave' 'rivencraft:age/smoke_cave
|
|||||||
send_and_wait 'rivencraft_port age sample smoke_flat_multi' 'rivencraft:age/smoke_flat_multi seed=24681 chunkOffset=0 terrain='
|
send_and_wait 'rivencraft_port age sample smoke_flat_multi' 'rivencraft:age/smoke_flat_multi seed=24681 chunkOffset=0 terrain='
|
||||||
send_and_wait 'rivencraft_port age sample smoke_void_multi' 'rivencraft:age/smoke_void_multi seed=13580 chunkOffset=0 terrain='
|
send_and_wait 'rivencraft_port age sample smoke_void_multi' 'rivencraft:age/smoke_void_multi seed=13580 chunkOffset=0 terrain='
|
||||||
send_and_wait 'rivencraft_port age sample smoke_cave_multi' 'rivencraft:age/smoke_cave_multi seed=112234 chunkOffset=0 terrain='
|
send_and_wait 'rivencraft_port age sample smoke_cave_multi' 'rivencraft:age/smoke_cave_multi seed=112234 chunkOffset=0 terrain='
|
||||||
|
send_and_wait 'rivencraft_port age sample smoke_random' 'rivencraft:age/smoke_random seed=889900 chunkOffset=0 terrain='
|
||||||
|
send_and_wait 'rivencraft_port age sample smoke_rocks' 'rivencraft:age/smoke_rocks seed=445566 chunkOffset=0 terrain='
|
||||||
|
send_and_wait 'rivencraft_port age rocks_probe smoke_rocks' 'Rocks probe rivencraft:age/smoke_rocks material=minecraft:gold_block count='
|
||||||
|
rocks_after=$(grep -F 'Rocks probe rivencraft:age/smoke_rocks material=minecraft:gold_block count=' "$log_file" | tail -1 | sed 's/.*count=//')
|
||||||
send_and_wait 'rivencraft_port age biome smoke_flat_multi 384 128' 'rivencraft:age/smoke_flat_multi biome at 384,128=minecraft:forest'
|
send_and_wait 'rivencraft_port age biome smoke_flat_multi 384 128' 'rivencraft:age/smoke_flat_multi biome at 384,128=minecraft:forest'
|
||||||
send_and_wait 'rivencraft_port age biome smoke_void_multi 384 128' 'rivencraft:age/smoke_void_multi biome at 384,128=minecraft:forest'
|
send_and_wait 'rivencraft_port age biome smoke_void_multi 384 128' 'rivencraft:age/smoke_void_multi biome at 384,128=minecraft:forest'
|
||||||
send_and_wait 'rivencraft_port age biome smoke_cave_multi 384 128' 'rivencraft:age/smoke_cave_multi biome at 384,128=minecraft:dripstone_caves'
|
send_and_wait 'rivencraft_port age biome smoke_cave_multi 384 128' 'rivencraft:age/smoke_cave_multi biome at 384,128=minecraft:dripstone_caves'
|
||||||
@@ -146,6 +164,7 @@ alpha_after=$(grep -F 'rivencraft:age/smoke_alpha seed=123456789 chunkOffset=0 t
|
|||||||
beta_after=$(grep -F 'rivencraft:age/smoke_beta seed=987654321 chunkOffset=0 terrain=' "$log_file" | tail -1 | sed 's/.*terrain=//')
|
beta_after=$(grep -F 'rivencraft:age/smoke_beta seed=987654321 chunkOffset=0 terrain=' "$log_file" | tail -1 | sed 's/.*terrain=//')
|
||||||
flat_after=$(grep -F 'rivencraft:age/smoke_flat seed=24680 chunkOffset=0 terrain=' "$log_file" | tail -1 | sed 's/.*terrain=//')
|
flat_after=$(grep -F 'rivencraft:age/smoke_flat seed=24680 chunkOffset=0 terrain=' "$log_file" | tail -1 | sed 's/.*terrain=//')
|
||||||
void_after=$(grep -F 'rivencraft:age/smoke_void seed=13579 chunkOffset=0 terrain=' "$log_file" | tail -1 | sed 's/.*terrain=//')
|
void_after=$(grep -F 'rivencraft:age/smoke_void seed=13579 chunkOffset=0 terrain=' "$log_file" | tail -1 | sed 's/.*terrain=//')
|
||||||
|
random_after=$(grep -F 'rivencraft:age/smoke_random seed=889900 chunkOffset=0 terrain=' "$log_file" | tail -1 | sed 's/.*terrain=//')
|
||||||
[[ "$alpha_before" == "$alpha_after" && "$beta_before" == "$beta_after" ]] || {
|
[[ "$alpha_before" == "$alpha_after" && "$beta_before" == "$beta_after" ]] || {
|
||||||
printf 'Age terrain changed across restart: %s/%s -> %s/%s\n' \
|
printf 'Age terrain changed across restart: %s/%s -> %s/%s\n' \
|
||||||
"$alpha_before" "$beta_before" "$alpha_after" "$beta_after" >&2
|
"$alpha_before" "$beta_before" "$alpha_after" "$beta_after" >&2
|
||||||
@@ -156,5 +175,15 @@ void_after=$(grep -F 'rivencraft:age/smoke_void seed=13579 chunkOffset=0 terrain
|
|||||||
"$flat_before" "$void_before" "$flat_after" "$void_after" >&2
|
"$flat_before" "$void_before" "$flat_after" "$void_after" >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
[[ "$random_before" == "$random_after" ]] || {
|
||||||
|
printf 'Random Age terrain changed across restart: %s -> %s\n' \
|
||||||
|
"$random_before" "$random_after" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
[[ "$rocks_before" == "$rocks_after" ]] || {
|
||||||
|
printf 'Authored Rocks changed across restart: %s -> %s\n' \
|
||||||
|
"$rocks_before" "$rocks_after" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
stop_server
|
stop_server
|
||||||
printf '26.3 dedicated-server smoke passed: Standard, Flat, Void and Cave Ages persisted across restart.\n'
|
printf '26.3 dedicated-server smoke passed: Standard, Flat, Void, Cave, random and authored Rocks Ages persisted across restart.\n'
|
||||||
Reference in new issue
Block a user