The Single-Tick Batch Pattern
The most reliable way to test mod APIs is to do everything in a singleexecuteOnWorld() call. This runs your spawn, query, and assert code on the world thread in the same tick.
Why this works: Hytale’s entity system requires all entity operations to happen on the world’s tick thread. Entities in dedicated test worlds may not survive across ticks because Hytale’s Spawning plugin removes NPCs without spawn markers.
Why not use waitTicks? If your mod processes entities synchronously during spawning (e.g., your mod’s spawn logic finishes all setup before returning), everything you need is available immediately. No ticks needed.
When You Need Multiple Ticks
Some mod features use game systems that run once per tick (health scaling, model changes, ability setup). These changes won’t be visible until the next tick processes them. The problem: Entities may not survive ticks in dedicated test worlds. Solutions (in order of preference):- Use your mod’s query API if it reads from internal state rather than entity components. Many mods store data in their own Maps or fields that are set immediately during spawn.
-
Test what’s available right away. If
spawnElite()assigns a tier immediately but health scaling happens on the next tick, test the tier (works) and accept that health scaling can’t be verified in this test. - Use @FlowTest with executeOnWorld() if your entities DO survive ticks. This depends on your world setup and whether Hytale’s Spawning plugin manages your entities.
Testing Custom Events
If your mod fires events through Hytale’sEventRegistry, use HRTK’s ctx.captureEvent():
ModEventBus class), HRTK’s captureEvent() won’t see those events. Instead, register your own listener:
Config Manipulation in Tests
Most mods have configurable behavior. Use @BeforeAll to set known config values, and @AfterAll to restore them:Choosing the Right Annotation
| What you’re testing | Annotation | Why |
|---|---|---|
| Pure logic (no world) | @HytaleTest | No world needed, fastest |
| Entity spawn + immediate query | @WorldTest | Runs on world thread, full entity access |
| Multi-tick behavior (waitTicks) | @FlowTest | Can wait for ticks between steps |
| Performance measurement | @Benchmark | Controlled warmup + timed iterations |
waitTicks(). If you call waitTicks() from @WorldTest, you’ll get a clear error telling you to switch to @FlowTest.
Known Limitations
- Component changes are deferred. Components added through the command buffer are not visible until the next tick processes them. If your mod adds components during spawn, they won’t be queryable in the same tick via
store.getComponent(). Use your mod’s own query API or accept this limitation. - Entities may not survive ticks. Hytale’s Spawning plugin can remove NPCs from test worlds. The single-tick batch pattern avoids this entirely.
- Custom event buses aren’t captured by HRTK. Register your own listener if your mod doesn’t use Hytale’s EventRegistry.