Improved saved world startup

- Startup holds generation admission through recovery and runtime attachment
- Prepared startup state removes the second recovery scan before generator readiness
- Recovery batches allocated chunks by region with reusable snapshot buffers
- Region decoding uses up to four workers within processor and heap limits
- Oversized regions and external chunks use bounded direct reads
- Status parsing validates chunk coordinates without constructing the full NBT tree
- Region snapshots reject changed files, allocation headers and truncated payloads
- Native zlib decompression uses jlibdeflate with a Java inflater fallback
- Chunk readers reuse region handles and decompression buffers during recovery and boundary capture
- Semantic shards reuse validated summaries with invalidation on mutation
- Semantic key validation avoids regular expression allocation
- Saved biome indexes use buffered sequential reads with reusable CRC state
- Pack inspection reads validated manifest and pack state without opening terrain indexes
- Datapack compilation reuses pack inspection within each operation
- Kernel staging avoids duplicate pack hashing when no activation is pending
- Pack copying and directory synchronization use up to four available processors
- Boundary publication batches directory synchronization around durable catalog publication
- Configured world reconciliation reuses ready runtimes after identity and saved-state checks
- Explicit world loads retain the full validation path
- Regression tests cover startup admission, recovery, malformed data, resource limits, publication and reconciliation
This commit is contained in:
Brian Neumann-Fopiano committed 2026-09-23 06:56:45 -04:00
1 parent 30c533feaf
commit 98ddb6644d
35 files changed
+3301 -197

No files matched your search

@@ -27,6 +27,11 @@ import art.arcane.iris.world.lifecycle.BukkitWorldConfiguration;
import art.arcane.iris.world.lifecycle.LifecycleOperationCoordinator;
import art.arcane.iris.platform.bukkit.nms.INMS;
import art.arcane.iris.generation.terrain.IrisDimension;
import art.arcane.iris.generation.runtime.IrisEngine;
import art.arcane.iris.platform.generation.BukkitChunkGenerator;
import art.arcane.iris.world.history.GenerationEpoch;
import art.arcane.iris.world.history.GenerationHistory;
import art.arcane.iris.world.history.GenerationHistoryRuntimeRouter;
import art.arcane.iris.platform.bukkit.BukkitEnvironment;
import art.arcane.iris.localization.C;
import art.arcane.iris.platform.bootstrap.ServerProperties;
@@ -182,7 +187,7 @@ public final class BukkitWorldReconciler {
});
}
private CompletableFuture<LoadResult> loadConfiguredWorld(
CompletableFuture<LoadResult> loadConfiguredWorld(
File configurationFile,
String configuredWorldName,
NamespacedKey worldKey,
@@ -196,6 +201,28 @@ public final class BukkitWorldReconciler {
return CompletableFuture.completedFuture(LoadResult.busy(worldKey, failure));
}
try {
IrisStartupValidation.requireWorldCreationReady();
Optional<World> loaded = backend.loadedWorld(worldKey);
if (loaded.isPresent() && backend.validateLoadedRuntime(
loaded.get(), worldKey, dimension, seed) == LoadedRuntimeStatus.READY) {
ReconciliationResult result = verifyLoadedWorld(worldKey, loaded.get(), true);
BukkitWorldConfiguration.Registration registration = BukkitWorldConfiguration.Registration.UNCHANGED;
if (result.succeeded()) {
try {
registration = BukkitWorldConfiguration.register(configurationFile, configuredWorldName, dimension, seed);
} catch (Throwable failure) {
lease.close();
return CompletableFuture.completedFuture(LoadResult.configurationFailure(worldKey, failure));
}
}
lease.close();
return CompletableFuture.completedFuture(new LoadResult(result, registration, false, true, null));
}
} catch (Throwable failure) {
lease.close();
return CompletableFuture.completedFuture(LoadResult.dimensionFailure(worldKey, failure));
}
return loadWithLease(configurationFile, configuredWorldName, worldKey, dimension, seed, lease);
}
@@ -434,11 +461,19 @@ public final class BukkitWorldReconciler {
boolean isIrisWorld(World world);
LoadedRuntimeStatus validateLoadedRuntime(World world, NamespacedKey worldKey, String dimension, Long seed)
throws IOException;
DimensionResolution resolveDimension(NamespacedKey worldKey);
void requireDimensionLoadable(NamespacedKey worldKey, String dimension);
}
enum LoadedRuntimeStatus {
READY,
UNAVAILABLE
}
public enum ReconciliationStatus {
LOADED,
ALREADY_LOADED,
@@ -717,6 +752,55 @@ public final class BukkitWorldReconciler {
return IrisToolbelt.isIrisWorld(world);
}
@Override
public LoadedRuntimeStatus validateLoadedRuntime(
World world, NamespacedKey worldKey, String dimension, Long seed) throws IOException {
if (!(world.getGenerator() instanceof BukkitChunkGenerator generator)
|| generator.isStudio()
|| generator.isClosing()
|| generator.getInitializationFailure() != null
|| !generator.getStartupReady().isDone()
|| generator.getStartupReady().isCompletedExceptionally()
|| !(generator.getEngine() instanceof IrisEngine engine)
|| engine.isClosed() || engine.isClosing()) {
return LoadedRuntimeStatus.UNAVAILABLE;
}
GenerationHistory history = generator.getGenerationHistory();
GenerationHistoryRuntimeRouter router = engine.getGenerationHistoryRuntimeRouter().orElse(null);
if (history == null || router == null || router.history() != history) {
return LoadedRuntimeStatus.UNAVAILABLE;
}
GenerationEpoch active = history.activeEpoch();
if (!worldKey.equals(WorldIdentity.key(world))
|| !worldKey.toString().equals(engine.getWorld().identity())
|| !dimension.equals(generator.getDimensionKey())
|| !dimension.equals(active.dimensionContract().dimensionKey())
|| !dimension.equals(engine.getDimension().getLoadKey())
|| seed != null && seed.longValue() != active.worldSeed()) {
throw new IOException("Loaded Iris runtime does not match configured world " + worldKey + ".");
}
Path expectedRoot = WorldCreatorCompat.persistentDimensionRoot(worldKey).toPath().toAbsolutePath().normalize();
File levelRoot = IrisWorldStorage.levelRoot();
Path storedRoot = IrisWorldStorage.requireFrozenDimensionRoot(
Bukkit.getWorldContainer(), levelRoot, configuredWorldName(worldKey), worldKey)
.toPath().toAbsolutePath().normalize();
if (!expectedRoot.equals(storedRoot) || !storedRoot.equals(history.paths().dimensionRoot())
|| !storedRoot.equals(engine.getWorld().worldFolder().toPath().toAbsolutePath().normalize())
|| engine.getData().isClosed()) {
throw new IOException("Loaded Iris runtime storage does not match world " + worldKey + ".");
}
GenerationHistory.PackInspection inspection = GenerationHistory.inspectPacks(storedRoot);
if (!active.equals(inspection.manifest().activeEpoch())
|| !history.activeActivation().equals(inspection.manifest().activeActivation())
|| !history.pendingActivation().equals(inspection.manifest().pendingActivation())
|| !inspection.activePackRoot().equals(engine.getData().getDataFolder().toPath().toAbsolutePath().normalize())
|| !Files.isDirectory(storedRoot.resolve("iris/generation/semantics"), LinkOption.NOFOLLOW_LINKS)
|| !Files.isRegularFile(storedRoot.resolve("iris/generation/semantics/index.isix"), LinkOption.NOFOLLOW_LINKS)) {
throw new IOException("Loaded Iris runtime history does not match saved world " + worldKey + ".");
}
return LoadedRuntimeStatus.READY;
}
@Override
public DimensionResolution resolveDimension(NamespacedKey worldKey) {
File dimensionsDirectory = new File(snapshotRoot(worldKey), "dimensions");
@@ -383,8 +383,9 @@ public final class IrisWorldGeneratorResolver {
IrisDimension dimension = null;
if (dimensionRoot != null) {
long worldSeed = requireStoredWorldSeed(configuredWorldName, dimensionRoot);
GenerationHistory history = requireGenerationHistory(dimensionRoot, id, worldSeed);
File pack = requireActivePack(history);
LoadedHistory loaded = requireGenerationHistory(dimensionRoot, id, worldSeed);
GenerationHistory history = loaded.history();
File pack = loaded.activePack();
requireSnapshotLoadable(pack);
dimension = requireHistoricalDimension(history, pack, id);
}
@@ -615,8 +616,9 @@ public final class IrisWorldGeneratorResolver {
+ worldKey + ".");
}
long worldSeed = requireStoredWorldSeed(worldName, dimensionRoot);
GenerationHistory history = requireGenerationHistory(dimensionRoot, id, worldSeed);
File snapshotRoot = requireActivePack(history);
LoadedHistory loaded = requireGenerationHistory(dimensionRoot, id, worldSeed);
GenerationHistory history = loaded.history();
File snapshotRoot = loaded.activePack();
PackValidationResult validation = validateSnapshot(snapshotRoot);
if (hasPendingExternalContent(validation, IrisServices.getOrNull(ExternalDataSVC.class))) {
return deferFrozenWorldGenerator(worldName, worldKey, dimensionRoot, snapshotRoot, id, history);
@@ -705,7 +707,7 @@ public final class IrisWorldGeneratorResolver {
}
}
private static GenerationHistory requireGenerationHistory(
private static LoadedHistory requireGenerationHistory(
File dimensionRoot,
String dimensionKey,
long worldSeed
@@ -724,7 +726,7 @@ public final class IrisWorldGeneratorResolver {
history.requireRegistryDefinitions(
GenerationRegistryContractFactory.captureRequiredDefinitions(retainedContracts)
);
return history;
return new LoadedHistory(history, history.paths().packRoot(history.activeEpoch().epochId()).toFile());
} catch (IOException failure) {
throw new IllegalStateException("Iris generation history is unusable at " + root + ".", failure);
}
@@ -825,12 +827,7 @@ public final class IrisWorldGeneratorResolver {
);
}
private static File requireActivePack(GenerationHistory history) {
try {
return history.activePackRoot().toFile();
} catch (IOException failure) {
throw new IllegalStateException("Active Iris generation pack is unusable.", failure);
}
private record LoadedHistory(GenerationHistory history, File activePack) {
}
private static long requireStoredWorldSeed(String worldName, File dimensionRoot) {