FAQ & Troubleshooting
My structure never forms
Work through this in order:
- Is
.build()actually called during mod setup? Registration happens when.build()runs — make sure your registration method is invoked (e.g. fromFMLCommonSetupEvent). - Did registration silently fail? An unset id or missing geometry throws
IllegalStateException(visible). But a conflicting block-level.core(id)or a violated geometry constraint (.unique/.surfaceOnly/…) only logs an error and skips registration —.build()still returns an unregistered object. Check your logs (details). - Is the layout right? Rows → Z, characters → X,
.layer(...)order → Y top-to-bottom. - A typo silently disabled a symbol? A character with no
.key(...)is treated as' '(no constraint), with no build error. Verify every layer character has a key. - Right formation mode?
WRENCH(or any mode withallowsAutomatic = false) never triggers from placement — use a wrench (formation modes). - A validator vetoing?
.validator(...)can returnValidationResult.Invalidafter a shape match. Also check no other mod cancelsMultiblockFormedEvent. - Another definition matching first? Definitions are tried by
priority(descending), JSON before Java on ties. A higher-priority match wins (getCandidatesFor). - Server-side placement? Matching runs only from
BlockEvent.EntityPlaceEventon aServerLevel. Custom placement bypassing the event won't trigger it.
It matches in the wrong orientation (or won't when rotated)
RotationMode.NONEmatches only the built orientation. For any horizontal facing useHORIZONTAL(default).- Any
.allowRotation(...)replacesRotationMode— the matcher tries only the identity plus what you declared. RotationMode.ALL(or anX/Z.allowRotation) allows tipping onto the side; if unintended, useHORIZONTAL.
Full mechanics: Rotation & Matching.
The preview doesn't follow my directional core
Declare .mainFace() on the core's BlockDefinition — a facing property alone isn't enough. See the Directional Cores Guide.
Can I match a tag or something more flexible?
Yes — BlockIngredient.tag(...), .predicate(...), .anyOf(...) (reference). Mind the always-checked caveat if used on the activation/core symbol.
Can I react to the structure breaking?
Yes — .onBroken(cb), or subscribe to MultiblockBrokenEvent for cross-mod visibility (Callbacks & Events).
Can I define structures in JSON?
Yes — Advanced Features § JSON/datapack definitions. JSON definitions reload cleanly without touching Java ones.
Blocks aren't consumed after forming
Formation never removes blocks — the structure stays as placed. For a single-block appearance, use .model(...) (Master-Dummy). To actually consume blocks, do it in your onFormed callback.
A part block vanished after forming (hitbox remains)
Expected with .model(...): part models hide on formation (hitbox/identity unchanged) except the core and .keepVisible(char...) symbols. Add interactive blocks (IO ports) to .keepVisible(...). See Master-Dummy model.
Where are working examples?
The net.astronomy.multilib.example package:
ExamplePattern— controller as core+activation, with a Master-Dummy model and callbacks.ExampleDirectionalPattern— a.mainFace()fixed-facing core.ExampleControllerBE/ExampleControllerBlock— a minimal controller pair.ExampleWrenchItem— a referenceIMultiblockWrench(MultiLib ships no wrench itself).