Expanded terrain generation tooling

- Standalone Minecraft 26.3 generation uses native registries, biome planning and structure planning with bounded concurrency
- Offline export publishes fresh TERRAIN checkpoints for native server completion
- Checkpoints retain mantle data, immutable generation history and captured server configuration
- Output publication uses staged directories, durability checks and atomic moves
- Offline distribution tasks provide an installed launcher and reusable native fixtures
- Dimension settings control terrain, biome-bound and cave-density sampling with unchanged defaults
- Pack validation and schemas enforce the supported sampling intervals
- Biome layer caches isolate data, engine and seed identities
- Floating biome palettes keep stable thickness patterns across generation orders
- Hydrology batches terrain samples within the owning generation runtime
- Hydrology planners share bounded basis caches and reuse raster geometry and distance samples
- Demand planning admits new tiles as active plans finish
- Volumetric height queries avoid unnecessary density evaluation and bound cached anchors
- Cave generation reuses chunk-scoped inputs and semantic capture
- Object replay indexes mutations by destination and reuses resolved records
- Source-plan caches coordinate concurrent builds and reduce overlay allocation
- Prerequisite carving reads preserve the underlying hydrology before object placement
- Tree material decoding preserves stored native block identities
- Engine shutdown drains generation outside the lifecycle lock and coordinates runtime retirement
- Pregeneration sizes temporary worker boosts from processor and heap capacity
- Pregeneration restores native worker counts after drain and failure paths
- Native structure queries retain per-origin coordination without broad window locks
- Saved biome writes drain pending batches under their write stripe
- Boundary column lookups avoid repeated voxel decoding
- Modded registry consumers use explicit VolmLib providers and version-specific native source overlays
- Regression tests cover generation parity, cache isolation, concurrency, shutdown, persistence and offline checkpoints
This commit is contained in:
Brian Neumann-Fopiano
2026-09-23 07:00:55 -04:00
parent 98ddb6644d
commit ca9f28c9b0
142 changed files with 11159 additions and 773 deletions
@@ -0,0 +1,41 @@
package art.arcane.iris.probe;
import art.arcane.iris.generation.terrain.IrisDimension;
import art.arcane.iris.pack.datapack.IDataFixer;
import art.arcane.iris.pack.loading.IrisData;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.iris.spi.PlatformGenerationRegistry;
import art.arcane.iris.spi.PlatformRegistries;
import art.arcane.iris.world.history.GenerationEpoch;
import art.arcane.iris.world.history.GenerationEpochContractFactory;
import art.arcane.iris.world.history.GenerationHistory;
import art.arcane.iris.world.history.GenerationPackFingerprint;
import art.arcane.iris.world.history.GenerationRegistryContract;
import art.arcane.iris.world.history.GenerationRegistryContractFactory;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
final class HeadlessGenerationHistorySession {
private HeadlessGenerationHistorySession() {
}
static GenerationHistory create(RealPackProbeSupport.HistoryRequest request, IDataFixer fixer) throws IOException {
IrisData data = request.data();
IrisDimension dimension = request.dimension();
PlatformRegistries registries = IrisPlatforms.get().registries();
PlatformGenerationRegistry generationRegistry = registries.generationRegistry();
Path pack = data.getDataFolder().toPath();
String fingerprint = GenerationPackFingerprint.compute(pack, GenerationPackFingerprint.CURRENT_VERSION);
GenerationRegistryContract registryContract = GenerationRegistryContractFactory.create(
data, dimension, fingerprint, fixer, registries, generationRegistry);
String dimensionTypeKey = generationRegistry.dimensionTypeResourceKey(
data.getDataFolder().getName(), dimension.getLoadKey(), dimension.getDimensionTypeKey());
GenerationEpoch.DimensionContract dimensionContract = GenerationEpochContractFactory.create(
dimension, dimension.getLoadKey(), dimensionTypeKey);
Files.createDirectories(request.worldRoot());
return GenerationHistory.create(request.worldRoot(), pack, fingerprint, request.seed(),
dimensionContract, registryContract);
}
}
@@ -18,21 +18,33 @@ import art.arcane.iris.generation.mantle.MantleComponent;
import art.arcane.iris.generation.mantle.MantleObjectComponent;
import art.arcane.iris.generation.terrain.IrisDimension;
import art.arcane.iris.world.IrisWorld;
import art.arcane.iris.world.history.GenerationHistory;
import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter;
import art.arcane.iris.world.history.IrisBoundarySignatureSampler;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.iris.spi.IrisPlatform;
import art.arcane.iris.spi.IrisServices;
import art.arcane.volmlib.nativelib.terrain.NativeBiome;
import art.arcane.volmlib.nativelib.terrain.NativeBlockState;
import art.arcane.volmlib.util.hunk.Hunk;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitOption;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.EnumSet;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.stream.Stream;
@@ -43,6 +55,15 @@ final class RealPackProbeSupport {
}
static Workspace openWorkspace(File packSource, String dimensionKey, String logPrefix) throws Exception {
return openWorkspace(new WorkspaceOptions(packSource, dimensionKey, logPrefix, null,
Path.of(System.getProperty("java.io.tmpdir"))));
}
static Workspace openWorkspace(WorkspaceOptions options) throws Exception {
File packSource = options.packSource();
String dimensionKey = options.dimensionKey();
String logPrefix = options.logPrefix();
RuntimeBindings bindings = options.bindings();
if (packSource == null || !packSource.isDirectory()) {
throw new IllegalArgumentException("Pack folder not found: "
+ (packSource == null ? "null" : packSource.getAbsolutePath()));
@@ -51,12 +72,12 @@ final class RealPackProbeSupport {
|| dimensionKey.chars().anyMatch(Character::isWhitespace)) {
throw new IllegalArgumentException("Dimension key must be non-blank and contain no whitespace.");
}
File workRoot = Files.createTempDirectory("iris-real-pack-probe-").toFile();
File workRoot = Files.createTempDirectory(options.parent(), "iris-real-pack-probe-").toFile();
try {
File pack = clonePack(packSource, workRoot);
configureRuntime(new File(workRoot, "platform-validation"), false);
configureRuntime(new File(workRoot, "platform-validation"), false, bindings);
validatePack(pack, logPrefix);
return new Workspace(workRoot, pack, dimensionKey, logPrefix, false);
return new Workspace(workRoot, pack, dimensionKey, logPrefix, false, bindings);
} catch (Throwable failure) {
Throwable cleanupFailure = deleteWorkRoot(workRoot);
if (cleanupFailure != null) {
@@ -79,8 +100,8 @@ final class RealPackProbeSupport {
}
File workRoot = Files.createTempDirectory("iris-generated-chunk-probe-").toFile();
try {
configureRuntime(new File(workRoot, "platform-validation"), hydrologyGeneratedVerification);
return new Workspace(workRoot, pack, dimensionKey, logPrefix, hydrologyGeneratedVerification);
configureRuntime(new File(workRoot, "platform-validation"), hydrologyGeneratedVerification, null);
return new Workspace(workRoot, pack, dimensionKey, logPrefix, hydrologyGeneratedVerification, null);
} catch (Throwable failure) {
Throwable cleanupFailure = deleteWorkRoot(workRoot);
if (cleanupFailure != null) {
@@ -190,12 +211,20 @@ final class RealPackProbeSupport {
}
}
record WorkspaceOptions(File packSource, String dimensionKey, String logPrefix,
RuntimeBindings bindings, Path parent) {
WorkspaceOptions {
Objects.requireNonNull(parent, "parent");
}
}
static final class Workspace implements AutoCloseable {
private final File workRoot;
private final File pack;
private final String dimensionKey;
private final String logPrefix;
private final boolean hydrologyGeneratedVerification;
private final RuntimeBindings bindings;
private int sessionSequence;
private boolean sessionOpen;
private boolean closed;
@@ -205,13 +234,15 @@ final class RealPackProbeSupport {
File pack,
String dimensionKey,
String logPrefix,
boolean hydrologyGeneratedVerification
boolean hydrologyGeneratedVerification,
RuntimeBindings bindings
) {
this.workRoot = workRoot;
this.pack = pack;
this.dimensionKey = dimensionKey;
this.logPrefix = logPrefix;
this.hydrologyGeneratedVerification = hydrologyGeneratedVerification;
this.bindings = bindings;
}
EngineSession openEngine(long seed, boolean studio, String runLabel) throws Exception {
@@ -228,13 +259,28 @@ final class RealPackProbeSupport {
IrisData data = null;
Engine engine = null;
try {
configureRuntime(platformRoot, hydrologyGeneratedVerification);
configureRuntime(platformRoot, hydrologyGeneratedVerification, bindings);
data = IrisData.openRuntime(pack);
IrisDimension dimension = data.getDimensionLoader().load(dimensionKey);
if (dimension == null) {
throw new IllegalStateException("Dimension '" + dimensionKey
+ "' did not load from " + pack.getAbsolutePath());
}
if (bindings != null) {
bindings.prepare(data, dimension);
}
GenerationHistory history = bindings == null ? null
: Objects.requireNonNull(bindings.createHistory(
new HistoryRequest(worldRoot.toPath(), data, dimension, seed)), "Native generation history");
if (history != null) {
data.close();
data = IrisData.openRuntime(history.activePackRoot().toFile());
data.bindGenerationRegistryContract(history.activeEpoch().registryContract());
dimension = data.getDimensionLoader().load(dimensionKey);
if (dimension == null) {
throw new IOException("Immutable generation pack does not contain dimension " + dimensionKey);
}
}
IrisWorld world = IrisWorld.builder()
.platformIdentity("iris:probe")
.name("probe")
@@ -244,13 +290,30 @@ final class RealPackProbeSupport {
.maxHeight(dimension.getMaxHeight())
.build();
long started = System.nanoTime();
engine = new IrisEngine(
new EngineTarget(world, dimension, data),
studio ? IrisEngine.InitializationMode.STUDIO : IrisEngine.InitializationMode.RUNTIME
);
EngineTarget target = new EngineTarget(world, dimension, data);
IrisEngine.InitializationMode mode = studio
? IrisEngine.InitializationMode.STUDIO : IrisEngine.InitializationMode.RUNTIME;
if (history == null) {
engine = new IrisEngine(target, mode);
} else {
IrisEngine created = new IrisEngine(target, mode,
history.paths().activationMantleRoot(history.activeActivation().activationId()),
history.activeEpoch().kernelVersion(), null);
engine = created;
GenerationHistoryRuntimeRouter router = GenerationHistoryRuntimeRouter.attach(
created, history, IrisBoundarySignatureSampler.INSTANCE);
router.preloadActiveRuntimes();
}
long readyNanos = System.nanoTime() - started;
List<Throwable> initializationReports = settleAndDrain();
printReports(logPrefix, runLabel + " engine-init reports", initializationReports);
if (bindings != null && !initializationReports.isEmpty()) {
IllegalStateException failure = new IllegalStateException("Native engine initialization reported failures");
for (Throwable report : initializationReports) {
failure.addSuppressed(report);
}
throw failure;
}
return new EngineSession(this, data, engine, readyNanos);
} catch (Throwable failure) {
Throwable cleanupFailure = closeResources(engine, data);
@@ -276,9 +339,20 @@ final class RealPackProbeSupport {
throw new IllegalStateException("Cannot close a probe workspace with an open engine session.");
}
closed = true;
Throwable failure = null;
if (bindings != null) {
try {
bindings.close();
} catch (Throwable bindingFailure) {
failure = bindingFailure;
}
}
IrisPlatforms.unbind();
StubPlatform.errorSink(null);
Throwable failure = deleteWorkRoot(workRoot);
Throwable cleanupFailure = deleteWorkRoot(workRoot);
if (cleanupFailure != null) {
failure = appendFailure(failure, cleanupFailure);
}
if (failure != null) {
throwAsException(failure);
}
@@ -294,6 +368,7 @@ final class RealPackProbeSupport {
private final IrisData data;
private final Engine engine;
private final long readyNanos;
private Path retainedWorld;
private boolean closed;
private EngineSession(Workspace workspace, IrisData data, Engine engine, long readyNanos) {
@@ -311,6 +386,13 @@ final class RealPackProbeSupport {
return readyNanos;
}
void retainWorld(Path destination) {
if (closed || retainedWorld != null || Files.exists(destination)) {
throw new IllegalStateException("World retention requires an open session and unused destination");
}
retainedWorld = destination;
}
@Override
public void close() throws Exception {
if (closed) {
@@ -331,6 +413,10 @@ final class RealPackProbeSupport {
if (failure != null) {
throwAsException(failure);
}
if (retainedWorld != null) {
Files.move(engine.getTarget().getWorld().worldFolder().toPath(), retainedWorld,
StandardCopyOption.ATOMIC_MOVE);
}
}
}
@@ -408,7 +494,7 @@ final class RealPackProbeSupport {
}
}
private static void configureRuntime(File platformRoot, boolean hydrologyGeneratedVerification) {
private static void configureRuntime(File platformRoot, boolean hydrologyGeneratedVerification, RuntimeBindings bindings) {
REPORTED.clear();
StubPlatform.bindGenerationStateHandlers();
StubPlatform.verbose(false);
@@ -417,9 +503,21 @@ final class RealPackProbeSupport {
IrisServices.register(EngineWorldManagerProvider.class,
(EngineWorldManagerProvider) (Engine engine) -> new InertWorldManager());
IrisServices.register(EngineEffectsProvider.class, (EngineEffectsProvider) InertEffects::new);
IrisServices.register(EnginePlatformHooks.class, new InertPlatformHooks(hydrologyGeneratedVerification));
IrisServices.register(EnginePlatformHooks.class, bindings == null
? new InertPlatformHooks(hydrologyGeneratedVerification) : bindings);
IrisPlatforms.unbind();
IrisPlatforms.bind(new StubPlatform(platformRoot));
IrisPlatforms.bind(bindings == null ? new StubPlatform(platformRoot) : bindings.create(platformRoot));
}
interface RuntimeBindings extends AutoCloseable, EnginePlatformHooks {
IrisPlatform create(File platformRoot);
void prepare(IrisData data, IrisDimension dimension);
GenerationHistory createHistory(HistoryRequest request) throws IOException;
}
record HistoryRequest(Path worldRoot, IrisData data, IrisDimension dimension, long seed) {
}
private static void validatePack(File pack, String logPrefix) {
@@ -438,18 +536,29 @@ final class RealPackProbeSupport {
private static File clonePack(File source, File workRoot) throws Exception {
File destination = new File(workRoot, "pack");
Process clone = new ProcessBuilder("cp", "-Rc", source.getAbsolutePath(), destination.getAbsolutePath())
.inheritIO()
.start();
if (clone.waitFor() != 0) {
deleteRecursively(destination.toPath());
Process copy = new ProcessBuilder("cp", "-R", source.getAbsolutePath(), destination.getAbsolutePath())
.inheritIO()
.start();
if (copy.waitFor() != 0) {
throw new IllegalStateException("Failed to copy pack to " + destination.getAbsolutePath());
if (System.getProperty("os.name").startsWith("Mac")) {
Process clone = new ProcessBuilder("/bin/cp", "-Rc", source.getAbsolutePath(), destination.getAbsolutePath())
.inheritIO().start();
if (clone.waitFor() == 0) {
return destination;
}
deleteRecursively(destination.toPath());
}
Path sourcePath = source.toPath();
Files.walkFileTree(sourcePath, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
new SimpleFileVisitor<>() {
@Override
public FileVisitResult preVisitDirectory(Path directory, BasicFileAttributes attributes) throws IOException {
Files.createDirectory(destination.toPath().resolve(sourcePath.relativize(directory)));
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) throws IOException {
Files.copy(file, destination.toPath().resolve(sourcePath.relativize(file)), StandardCopyOption.COPY_ATTRIBUTES);
return FileVisitResult.CONTINUE;
}
});
return destination;
}