A Hytale Tool by Ionforge Labs
Crucible
An in-game automated testing framework for Hytale server mods. Provides a test harness that runs inside a live server instance, executing test suites against the actual game engine.
Test against real game state — not mocks or simulations.
Why Crucible
Built for Mod Testing
Real Game State
Tests execute against the actual Hytale engine. Real block placement, item resolution, ECS components, and world state — not mocks or simulations.
CI-Ready
JUnit XML output compatible with GitHub Actions, Jenkins, and any CI system. Headless mode boots, runs all suites, reports, and shuts down automatically.
Zero Config
Register your test suites and Crucible handles the rest. Ephemeral world creation, chunk isolation, cleanup — all managed for you.
Execution Modes
Three Ways to Run
From interactive development to headless CI pipelines, Crucible adapts to your workflow.
Player Command
In-game chat commands
Run tests from in-game chat. Results are displayed in both chat and the server console. Perfect for interactive development.
/crucible run
/crucible run <suite>
/crucible listConsole
Server console, no clients needed
Run suites directly from the server console without any connected clients. Ideal for dedicated server environments.
crucible run
crucible run <suite>
crucible listHeadless / CI
Automated pipeline integration
Boot the server, run all tests, write JUnit XML reports, and shut down. Purpose-built for CI/CD pipelines.
java -Dcrucible.autorun=true \
-jar HytaleServer.jar- name: Run Crucible Tests
run: |
java -Dcrucible.autorun=true \
-jar HytaleServer.jar
- name: Publish Results
uses: dorny/test-reporter@v1
with:
name: Crucible
path: crucible-reports/*.xml
reporter: java-junitTestContext API
Full Engine Access
The TestContext API gives your suites direct access to world manipulation, entity management, and game event observation.
Block Operations
Place, query, and break blocks in the test world. Assert block types, states, and metadata against expected values.
ctx.placeBlock(pos, type)ctx.getBlockType(pos)ctx.breakBlock(pos)Entity Operations
Spawn NPCs and entities, manipulate positions, and verify entity state through the real ECS component system.
ctx.spawnNPC(pos, type)ctx.removeEntity(entity)ctx.getPosition(entity)Container Testing
Insert items into containers, check slot contents, and clear inventories. Test chest, hopper, and custom container logic.
ctx.insertItem(pos, item)ctx.getSlot(pos, index)ctx.clearContainer(pos)Processing Bench
Test furnaces, workbenches, and custom processing blocks. Set inputs, fuel, advance ticks, and verify outputs.
ctx.setInput(pos, item)ctx.setFuel(pos, fuel)ctx.advanceProcessing(pos, ticks)Event Capture
Observe and assert on game events fired during test execution. Capture block breaks, entity spawns, and custom mod events.
ctx.captureEvents(type)capture.assertFired()capture.assertCount(n)Async Testing
Implement AsyncTestSuite for tick-based waiting. Use CompletableFuture to test time-dependent mechanics across game ticks.
AsyncTestSuite interfacectx.waitTicks(n)CompletableFuture<TestResult>Ephemeral Test World
Crucible creates a purpose-built test world for every run. Chunk saving is disabled, player state is not persisted, and NPC spawning is suppressed. Each suite receives an isolated 64-block region, ensuring tests never interfere with each other.
Architecture
Module Structure
Clean separation between public API, engine core, and output reporters.
crucible/
Project root
Public interfaces
CrucibleAPITestSuiteAsyncTestSuiteTestContextTestResultEventCaptureEngine internals
TestRegistryTestRunnerTestWorldManagerOutput adapters
ConsoleReporterChatReporterJUnitXmlReporterPlayer & console commands
Built-in test utilities
Server plugin bootstrap
Getting Started
Up and Running in Four Steps
Add Dependency
Add Crucible as an OptionalDependency in your mod's manifest.json.
{
"OptionalDependencies": [
{ "Name": "Crucible" }
]
}Register Suites
In your mod's setup() method, register your test suites with the Crucible API.
@Override
public void setup(ModSetupContext ctx) {
CrucibleAPI crucible =
ctx.getOptionalAPI(CrucibleAPI.class);
if (crucible != null) {
crucible.register(new MyModTests());
}
}Implement TestSuite
Write your test methods using the TestContext API for world manipulation and assertions.
public class MyModTests implements TestSuite {
@Override
public String name() { return "my-mod"; }
@Override
public void run(TestContext ctx) {
ctx.placeBlock(pos, "mymod:machine");
ctx.assertEqual(
ctx.getBlockType(pos),
"mymod:machine"
);
}
}Run Tests
Execute your suites from chat, console, or CI. Results appear instantly.
/crucible run my-mod