Fail-closed modded decoration

- A generator with a refused or stopping engine refuses imported feature settings
- Those settings no longer fall back to vanilla ones
- Decoration fails when a sealed runtime has no settled feature table
- Decoration fails when the feature table was retired before placement
- Stacked initial spawns fail when the surface spawn biome lookup is rejected
- Those spawns no longer fall back to the chunk's top biome
- Bind failures from a stopping runtime stay out of the refusal memo and banner
- The next bind after such a failure retries
This commit is contained in:
Brian Neumann-Fopiano committed 2026-09-29 07:13:23 -04:00
1 parent 0cb3417755
commit 96a400b585
8 files changed
+258 -51

No files matched your search

@@ -8,6 +8,7 @@ import java.nio.channels.ClosedChannelException;
import java.util.concurrent.CancellationException;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.function.Predicate;
/**
* Separates failures of the engine, its mantle, generation history, executors or the JVM from failures of one
@@ -22,22 +23,32 @@ public final class GenerationFailures {
}
public static boolean isEngineFailure(Throwable failure) {
if (Thread.currentThread().isInterrupted()) {
return true;
}
return isShutdownFailure(failure) || hasCause(failure, (Throwable cause) -> cause instanceof Error
|| cause instanceof WrongEngineBroException
|| cause instanceof SavedBiomeUnavailableException unavailable && unavailable.isLoading()
|| cause instanceof TimeoutException);
}
/**
* The engine failures that only mean a runtime, pool or thread is stopping. They say nothing about the world, so
* nothing may remember them against it once the next runtime is up.
*/
public static boolean isShutdownFailure(Throwable failure) {
return Thread.currentThread().isInterrupted() || hasCause(failure, (Throwable cause) ->
cause instanceof GenerationClosedException
|| cause instanceof MantleClosedException
|| cause instanceof GenerationSessionException session && session.isExpectedTeardown()
|| cause instanceof InterruptedException
|| cause instanceof InterruptedIOException
|| cause instanceof ClosedChannelException
|| cause instanceof RejectedExecutionException
|| cause instanceof CancellationException);
}
private static boolean hasCause(Throwable failure, Predicate<Throwable> match) {
Throwable current = failure;
for (int depth = 0; current != null && depth < MAXIMUM_CAUSE_DEPTH; depth++, current = current.getCause()) {
if (current instanceof Error
|| current instanceof GenerationClosedException
|| current instanceof MantleClosedException
|| current instanceof WrongEngineBroException
|| current instanceof SavedBiomeUnavailableException unavailable && unavailable.isLoading()
|| current instanceof InterruptedException
|| current instanceof InterruptedIOException
|| current instanceof ClosedChannelException
|| current instanceof RejectedExecutionException
|| current instanceof CancellationException
|| current instanceof TimeoutException) {
if (match.test(current)) {
return true;
}
}
@@ -4,6 +4,7 @@ import art.arcane.iris.world.history.SavedBiomeUnavailableException;
import art.arcane.volmlib.util.mantle.MantleClosedException;
import org.junit.Test;
import java.io.IOException;
import java.nio.channels.ClosedByInterruptException;
import java.util.concurrent.CompletionException;
import java.util.concurrent.RejectedExecutionException;
@@ -52,6 +53,33 @@ public class GenerationFailuresTest {
}
}
@Test
public void shutdownFailuresAreTheLifecycleSubsetOfEngineFailures() {
assertTrue(GenerationFailures.isShutdownFailure(new GenerationClosedException("router closed")));
assertTrue(GenerationFailures.isShutdownFailure(new MantleClosedException("Tectonic Plate is closed!")));
assertTrue(GenerationFailures.isShutdownFailure(new RejectedExecutionException("pool shut down")));
assertTrue(GenerationFailures.isShutdownFailure(new ClosedByInterruptException()));
assertTrue(GenerationFailures.isShutdownFailure(new GenerationSessionException("sealed", true)));
assertTrue(GenerationFailures.isShutdownFailure(new IllegalStateException("bind failed",
new CompletionException(new InterruptedException()))));
assertFalse(GenerationFailures.isShutdownFailure(new OutOfMemoryError()));
assertFalse(GenerationFailures.isShutdownFailure(new NoClassDefFoundError("art/arcane/Missing")));
assertFalse(GenerationFailures.isShutdownFailure(new GenerationSessionException("wrong engine")));
assertFalse(GenerationFailures.isShutdownFailure(new IllegalStateException(
"Iris generation history is unusable", new IOException("registry definition changed"))));
}
@Test
public void anyFailureOnAnInterruptedThreadIsAShutdownFailure() {
Thread.currentThread().interrupt();
try {
assertTrue(GenerationFailures.isShutdownFailure(new IllegalStateException("history unusable")));
} finally {
Thread.interrupted();
}
}
@Test
public void rethrowPropagatesEngineFailuresAndReturnsForContentFailures() {
GenerationClosedException closed = new GenerationClosedException("router closed");