Restarting processes
Replace one process while preserving its workspace and verify an explicit player reconnection.
Restart a process when a test needs to exercise initialization again using files already written to
its workspace. processes.restart(name) replaces that one server or proxy and waits for readiness.
Other processes continue running.
Start and stop individual components
For an existing ScenarioEngine engine from AnvilLauncher and a wired AnvilScenario network,
prepare the environment without starting any JVMs, then choose its initial components:
import me.whereareiam.anvil.api.scenario.ScenarioContext;
try (ScenarioContext context = engine.prepare(network)) {
context.processes().start("lobby");
context.processes().start("proxy");
// Starts the remaining components and runs the scenario setup hook once.
context.start();
context.processes().stop("lobby");
var replacement = context.processes().start("lobby");
context.start(); // Restores any remaining stopped components; setup does not repeat.
}Preparation validates and provisions the complete topology, preserving its ports, routes, and forwarding configuration. Starting a component starts only that process, so a proxy may have configured routes to backends that are not running yet. Collections contain processes that have had a generation; looking up a known but never-started process reports that it has not started. Start is idempotent for a ready process. Stop retains its address, installed assets, and workspace; starting it again creates a fresh generation and reconnects its platform agent. Closing the context also releases prepared components that were never started.
ScenarioContext owns the topology from preparation through startup and finalization.
engine.start(network) performs preparation and complete startup before returning that same
kind of context. Use it for the ordinary test path; use engine.prepare(network) when your
application controls which components start first.
A failed startup finalizes the prepared context unsuccessfully. If your own actions or assertions
fail after startup, pass that outcome through context.finish(false); default close() reports
normal caller completion. Cleanup attempts every owned resource and preserves earlier lifecycle failures.
Verify a proxy restart
Use the proxy environment, with proxy as the entrypoint and
lobby as its backend. Enable rapid reconnections in the
Velocity or
BungeeCord declaration when its throttle would delay this test.
The Session and Players capabilities must be installed.
Place this helper at src/anvil/java/com/example/test/ProxyRestartChecks.java. Call
ProxyRestartChecks.verify(anvil) from a test method in the same package:
package com.example.test;
import me.whereareiam.anvil.api.scenario.ScenarioContext;
import me.whereareiam.anvil.api.type.ProcessState;
import me.whereareiam.anvil.capability.players.PlayerWatch;
import me.whereareiam.anvil.capability.players.Players;
import me.whereareiam.anvil.capability.session.Session;
import java.time.Duration;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import static org.junit.jupiter.api.Assertions.assertSame;
public final class ProxyRestartChecks {
public static void verify(ScenarioContext anvil) {
var processes = anvil.processes();
var original = processes.proxy("proxy");
var lobby = processes.server("lobby");
var alice = anvil.players().create("RestartAlice");
var session = alice.capability(Session.class);
Players proxyPlayers = original.capability(Players.class);
PlayerWatch arrivals = lobby.capability(Players.class).watch();
PlayerWatch moves = proxyPlayers.watch();
session.connect();
session.connected(Duration.ofSeconds(30));
arrivals.joined(alice);
moves.joined(alice, "lobby");
var replacement = processes.restart("proxy");
assertNotSame(original, replacement);
assertEquals(ProcessState.STOPPED, original.state());
assertEquals(ProcessState.READY, replacement.state());
assertEquals(original.address(), replacement.address());
assertEquals(original.workDirectory(), replacement.workDirectory());
assertSame(lobby, processes.server("lobby"));
session.disconnected();
session.rejoin();
session.connected(Duration.ofSeconds(30));
arrivals.joined(alice);
moves.joined(alice, "lobby");
assertSame(proxyPlayers, replacement.capability(Players.class));
assertEquals(Optional.of("lobby"), proxyPlayers.backend(alice));
}
}Run the containing test with ./gradlew anvilTest --tests 'your.package.YourTest'.
This checks replacement of the proxy, preservation of the lobby, and reconnection of the same
registered player to that lobby. The proxy's Players is the same instance before and after the
restart, so the watch moves created before it continues: the restart ended the visits the earlier
generation reported, and the second moves.joined(alice, "lobby") waits for the replacement proxy to
connect her to lobby. After that wait, backend(alice) names the same backend.
To test persistence, write application state before the restart and assert that state after rejoining. A successful reconnection alone does not prove that plugin data survived.
What remains after a restart
| Resource | Behavior |
|---|---|
| Workspace | Same directory and data already written to disk |
| Game listener | Same address and port |
| Other processes | Continue running |
| Installed assets | Remain in place; initial asset installation and cache restoration do not repeat |
| Platform configuration | Reapplied before launch |
| Agent connection | Reestablished with fresh credentials |
| Process capability instances | Retained for the logical process; agent-backed operations may be unavailable during replacement |
| Original process handle | Remains attached to the stopped process |
| Simulated player | Remains registered; its connection needs an explicit reconnect |
Use the returned handle or look up the process again for further console or state observations. Restarting a backend can disconnect or reroute a player according to the proxy's behavior; assert that outcome before continuing the journey.
Handle restart failures
Let unexpected restart failures fail the test and inspect the affected process's console log.
A ProcessException exposes its name through getProcessName().
A failed restart marks the scenario lifecycle unsuccessful even if your test catches the exception;
success-only caches are not finalized as a successful run.
For newly installed assets or cache restoration, start a new scenario. For data shared across separate runs, declare a workspace policy.