Port dry Cave Ages and safe landing to 26.3 beta
Build / build (push) Canceled after 0s

This commit is contained in:
dnorberg committed 2026-11-01 01:34:14 -05:00
1 parent d9464fad76
commit 9ccec18c18
12 files changed
+153 -18

No files matched your search

@@ -78,7 +78,7 @@ public final class PortAgeCommands {
context, "terrain").toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException exception) {
context.getSource().sendFailure(Component.literal(
"Unknown terrain; use standard, amplified, island, flat, or void"));
"Unknown terrain; use standard, amplified, island, cave, flat, or void"));
return 0;
}
return createAge(context, terrain);
@@ -94,18 +94,23 @@ public final class PortAgeCommands {
context, "terrain").toUpperCase(Locale.ROOT));
} catch (IllegalArgumentException exception) {
context.getSource().sendFailure(Component.literal(
"Unknown terrain; use flat or void"));
"Unknown terrain; use cave, flat, or void"));
return 0;
}
if (terrain != PortAgeTerrain.FLAT && terrain != PortAgeTerrain.VOID) {
if (terrain != PortAgeTerrain.CAVE
&& terrain != PortAgeTerrain.FLAT
&& terrain != PortAgeTerrain.VOID) {
context.getSource().sendFailure(Component.literal(
"Multi-biome probe supports flat or void only"));
"Multi-biome probe supports cave, flat, or void only"));
return 0;
}
return createAge(context, terrain, new PortBiomePlan(
PortBiomePlan.Distribution.TILED, java.util.List.of(
Identifier.withDefaultNamespace("plains"),
Identifier.withDefaultNamespace("forest"))));
PortBiomePlan.Distribution.TILED,
terrain == PortAgeTerrain.CAVE
? java.util.List.of(Identifier.withDefaultNamespace("lush_caves"),
Identifier.withDefaultNamespace("dripstone_caves"))
: java.util.List.of(Identifier.withDefaultNamespace("plains"),
Identifier.withDefaultNamespace("forest"))));
})))))
.then(Commands.literal("list")
.executes(context -> {
@@ -5,6 +5,7 @@ public enum PortAgeTerrain {
STANDARD,
AMPLIFIED,
ISLAND,
CAVE,
FLAT,
VOID
}
@@ -33,6 +33,27 @@ public final class PortAgeTravel {
}
private static BlockPos findLanding(ServerLevel target) {
if (target.getChunkSource().getGenerator() instanceof net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator noise
&& noise.generatorSettings().value().noiseSettings().equals(target.registryAccess()
.lookupOrThrow(Registries.NOISE_SETTINGS)
.getOrThrow(net.minecraft.world.level.levelgen.NoiseGeneratorSettings.CAVES)
.value().noiseSettings())) {
// The cave roof is not a valid arrival surface. Search down from
// Legacy's Y=64 start, then construct the normal safety platform
// at that height only when no footed opening can be found.
for (int radius = 0; radius <= 16; radius++) {
for (int x = -radius; x <= radius; x++) {
for (int z = -radius; z <= radius; z++) {
if (Math.max(Math.abs(x), Math.abs(z)) != radius) continue;
for (int y = Math.min(64, target.getMaxY() - 2); y > target.getMinY(); y--) {
BlockPos foot = new BlockPos(x, y, z);
if (hasFooting(target, foot)) return foot;
}
}
}
}
return makePlatform(target, new BlockPos(0, 64, 0));
}
// 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.
FlatLevelGeneratorSettings flatSettings = switch (target.getChunkSource().getGenerator()) {
@@ -62,10 +83,21 @@ public final class PortAgeTravel {
// origin is ocean or foliage. Final book landing rules are ported later.
int y = Math.max(64, target.getHeight(Heightmap.Types.MOTION_BLOCKING, 0, 0) + 2);
if (y + 2 >= target.getMaxY()) return null;
BlockPos foot = new BlockPos(0, y, 0);
return makePlatform(target, new BlockPos(0, y, 0));
}
private static BlockPos makePlatform(ServerLevel target, BlockPos foot) {
if (foot.getY() <= target.getMinY() || foot.getY() + 2 >= target.getMaxY()) return null;
// A carved pocket inside a flooded cave immediately refills. Refuse
// that landing instead of placing the player in submerged spawn space.
if (!target.getFluidState(foot).isEmpty() || !target.getFluidState(foot.above()).isEmpty()) return null;
for (Direction direction : Direction.Plane.HORIZONTAL) {
if (!target.getFluidState(foot.relative(direction)).isEmpty()
|| !target.getFluidState(foot.above().relative(direction)).isEmpty()) return null;
}
for (int x = -1; x <= 1; x++) {
for (int z = -1; z <= 1; z++) {
target.setBlock(new BlockPos(x, y - 1, z), Rivencraft.CRYSTAL_BLOCK.get().defaultBlockState(), 3);
target.setBlock(foot.offset(x, -1, z), Rivencraft.CRYSTAL_BLOCK.get().defaultBlockState(), 3);
}
}
target.setBlock(foot, Blocks.AIR.defaultBlockState(), 3);
@@ -76,6 +108,8 @@ public final class PortAgeTravel {
private static boolean hasFooting(ServerLevel target, BlockPos foot) {
BlockPos floor = foot.below();
return target.getBlockState(floor).isFaceSturdy(target, floor, Direction.UP)
&& target.getFluidState(foot).isEmpty()
&& target.getFluidState(foot.above()).isEmpty()
&& target.getBlockState(foot).getCollisionShape(target, foot).isEmpty()
&& target.getBlockState(foot.above()).getCollisionShape(target, foot.above()).isEmpty();
}
@@ -11,6 +11,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.OptionalLong;
import net.dnorberg.rivencraft.Rivencraft;
import net.dnorberg.rivencraft.writing.PortRegistryPages;
import net.minecraft.core.registries.Registries;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderSet;
@@ -325,7 +326,19 @@ public final class RuntimeAgeBackend {
|| terrain != PortAgeTerrain.STANDARD) {
if (!(generator instanceof NoiseBasedChunkGenerator noise))
throw new IllegalStateException("Overworld generator cannot accept authored noise settings");
var source = generator.getBiomeSource();
var source = terrain == PortAgeTerrain.CAVE
? new FixedBiomeSource(server.registryAccess().lookupOrThrow(Registries.BIOME)
.getOrThrow(Biomes.LUSH_CAVES))
: generator.getBiomeSource();
if (terrain == PortAgeTerrain.CAVE && biomePlan != null) {
var biomes = server.registryAccess().lookupOrThrow(Registries.BIOME);
for (Identifier biomeId : biomePlan.biomes()) {
var holder = biomes.get(ResourceKey.create(Registries.BIOME, biomeId))
.orElseThrow(() -> new IllegalArgumentException("Unknown authored biome: " + biomeId));
if (!PortRegistryPages.isCaveBiome(holder))
throw new IllegalArgumentException("Cave terrain requires a Cave or Nether biome: " + biomeId);
}
}
if (biomePlan != null && biomePlan.distribution() != PortBiomePlan.Distribution.NATIVE) {
source = authoredBiomeSource(server, biomePlan, seed);
}
@@ -335,6 +348,7 @@ public final class RuntimeAgeBackend {
.getOrThrow(NoiseGeneratorSettings.AMPLIFIED);
case ISLAND -> server.registryAccess().lookupOrThrow(Registries.NOISE_SETTINGS)
.getOrThrow(NoiseGeneratorSettings.FLOATING_ISLANDS);
case CAVE -> dryCaveSettings(server);
case FLAT, VOID -> throw new IllegalStateException("Flat terrain should use FlatLevelSource");
};
generator = new NoiseBasedChunkGenerator(source, settings);
@@ -369,4 +383,14 @@ public final class RuntimeAgeBackend {
throw exception;
}
}
private static Holder<NoiseGeneratorSettings> dryCaveSettings(MinecraftServer server) {
NoiseGeneratorSettings caves = server.registryAccess().lookupOrThrow(Registries.NOISE_SETTINGS)
.getOrThrow(NoiseGeneratorSettings.CAVES).value();
return Holder.direct(new NoiseGeneratorSettings(
caves.noiseSettings(), caves.defaultBlock(), Blocks.AIR.defaultBlockState(),
caves.noiseRouter(), caves.materialRule(), caves.spawnTarget(),
caves.noiseSettings().minY() - 1, caves.disableMobGeneration(), Optional.empty(),
caves.useLegacyRandomSource(), caves.debugFunctions()));
}
}
@@ -23,6 +23,8 @@ public final class PortAuthoredWriting {
Rivencraft.MOD_ID, "terrain/flat");
private static final Identifier VOID_TERRAIN = Identifier.fromNamespaceAndPath(
Rivencraft.MOD_ID, "terrain/void");
private static final Identifier CAVE_TERRAIN = Identifier.fromNamespaceAndPath(
Rivencraft.MOD_ID, "terrain/cave");
private PortAuthoredWriting() {}
@@ -60,7 +62,7 @@ public final class PortAuthoredWriting {
private static boolean isTerrain(Identifier page) {
return page.equals(STANDARD_TERRAIN) || page.equals(AMPLIFIED_TERRAIN)
|| page.equals(ISLAND_TERRAIN) || page.equals(FLAT_TERRAIN)
|| page.equals(VOID_TERRAIN);
|| page.equals(VOID_TERRAIN) || page.equals(CAVE_TERRAIN);
}
private static PortBiomePlan.Distribution controller(Identifier page) {
@@ -85,6 +87,7 @@ public final class PortAuthoredWriting {
if (pages.contains(ISLAND_TERRAIN)) return PortAgeTerrain.ISLAND;
if (pages.contains(FLAT_TERRAIN)) return PortAgeTerrain.FLAT;
if (pages.contains(VOID_TERRAIN)) return PortAgeTerrain.VOID;
if (pages.contains(CAVE_TERRAIN)) return PortAgeTerrain.CAVE;
return PortAgeTerrain.STANDARD;
}
@@ -94,14 +97,17 @@ public final class PortAuthoredWriting {
"Unsupported terrain or biome-page combination in this port slice");
List<Identifier> biomes = new ArrayList<>();
PortBiomePlan.Distribution distribution = null;
boolean caveTerrain = pages.contains(CAVE_TERRAIN);
for (Identifier page : pages) {
if (isTerrain(page)) continue;
PortBiomePlan.Distribution selected = controller(page);
if (selected != null) { distribution = selected; continue; }
Identifier biome = PortRegistryPages.biomeId(page);
var registry = registries.lookupOrThrow(Registries.BIOME);
if (registry.get(ResourceKey.create(Registries.BIOME, biome)).isEmpty())
throw new IllegalArgumentException("Unknown authored biome: " + biome);
var holder = registry.get(ResourceKey.create(Registries.BIOME, biome))
.orElseThrow(() -> new IllegalArgumentException("Unknown authored biome: " + biome));
if (caveTerrain && !PortRegistryPages.isCaveBiome(holder))
throw new IllegalArgumentException("Cave terrain requires a Cave or Nether biome: " + biome);
biomes.add(biome);
}
if (distribution == PortBiomePlan.Distribution.NATIVE)
@@ -67,7 +67,7 @@ public final class PortRegistryPages {
public static List<ItemStack> biomePages(HolderLookup.Provider registries, boolean caves) {
return registries.lookupOrThrow(Registries.BIOME).listElements()
.filter(holder -> !holder.key().identifier().equals(DEFERRED_PLACEHOLDER))
.filter(holder -> isCave(holder) == caves)
.filter(holder -> isCaveBiome(holder) == caves)
.map(holder -> PageItem.createSymbolPage(biomePageId(holder.key().identifier())))
.sorted((left, right) -> left.get(Rivencraft.SYMBOL_PAGE_ID.get()).toString()
.compareTo(right.get(Rivencraft.SYMBOL_PAGE_ID.get()).toString()))
@@ -81,7 +81,7 @@ public final class PortRegistryPages {
.map(id -> PageItem.createSymbolPage(blockPageId(id))).toList();
}
private static boolean isCave(net.minecraft.core.Holder.Reference<net.minecraft.world.level.biome.Biome> holder) {
public static boolean isCaveBiome(net.minecraft.core.Holder.Reference<net.minecraft.world.level.biome.Biome> holder) {
String path = holder.key().identifier().getPath();
return holder.is(CAVE_BIOMES) || holder.is(BiomeTags.IS_NETHER)
|| path.equals("deep_dark") || path.endsWith("_caves");
@@ -0,0 +1,8 @@
{
"replace": false,
"values": [
"minecraft:lush_caves",
"minecraft:dripstone_caves",
"minecraft:deep_dark"
]
}