BlockIngredient
Package: net.astronomy.multilib.api.ingredient
Interface describing what a pattern symbol matches against in the world. Replaces the old API's fixed "symbol → exactly one Block" mapping with a pluggable abstraction.
Interface
public interface BlockIngredient {
boolean matches(BlockState state);
Set<Block> getCandidateBlocks();
default BlockState getRenderState() { ... }
}
matches(BlockState)- the actual match test used by every matcher.getCandidateBlocks()- concrete blocks this ingredient could match, if enumerable. Used byMultiblockRegistryto index definitions by block (an ingredient that returns an empty set, like a tag or predicate, makes its definition "always checked" against every block placement instead of being indexed - see Core Concepts § Registration and lookup).getRenderState()- theBlockStatepreviews (JEI/REI/EMI 3D model, ghost overlay) should render for this ingredient. Defaults to the first candidate block's default state, ornullifgetCandidateBlocks()is empty (tags, predicates,any()) - callers must null-check.
Factory methods
BlockIngredient.of(Block block)
Matches exactly one block (SingleBlockIngredient). The common case; also what .key(char, Block) wraps automatically.
BlockIngredient.ofState(Block block)
Returns a StatePropertyIngredient.Builder:
BlockIngredient.ofState(Blocks.FURNACE)
.require(BlockStateProperties.HORIZONTAL_FACING, Direction.NORTH)
.build()
Matches one block and specific blockstate properties. getRenderState() applies every required property on top of the block's default state, so directional blocks preview correctly oriented.
BlockIngredient.tag(TagKey<Block> tag)
Matches any block in tag. getCandidateBlocks() returns an empty set (tags aren't eagerly enumerated), so definitions using only tag ingredients for their activation/core symbol are checked against every block placement rather than indexed.
BlockIngredient.anyOf(BlockIngredient... ingredients)
Matches if any of the given ingredients matches. getCandidateBlocks() is the union of the children's candidates.
BlockIngredient.predicate(Predicate<BlockState> predicate)
Arbitrary logic. getCandidateBlocks() returns an empty set - same indexing caveat as tags.
BlockIngredient.any()
Matches every BlockState unconditionally (including air). getCandidateBlocks() is empty.
BlockIngredient.ability(BlockCapability<?, Direction> capability, Block previewBlock)
Matches any block whose block entity exposes capability on at least one side, checked at match-time via level.getCapability(...) — e.g. "any block exposing energy storage", from any mod, with no shared tag convention needed. previewBlock is used only for previews (there's no single block for a capability — pick a canonical example). This is the one ingredient that overrides the context-aware matches(ServerLevel, BlockPos, BlockState) overload, since it needs a Level/BlockPos.
BlockIngredient.parse(String spec)
Parses a single block id ("minecraft:iron_block") or, with a # prefix, a block tag ("#c:storage_blocks/iron") — the same two forms the JSON datapack format takes as separate block/tag fields, collapsed into one string since that's the natural shape for a scripting call site. Delegates to of(...) or tag(...) accordingly. Backs MultiblockBuilder.key(char, String) so KubeJS scripts get the same tag/block coverage Java and JSON already have. Throws IllegalArgumentException if the id is malformed or (for a plain block id) unregistered.
Choosing an ingredient type
| Need | Use |
|---|---|
| One exact block | of(block) |
| One block, one specific facing/property | ofState(block).require(...).build() |
| Any block from a set you maintain via a tag | tag(tagKey) |
| "Any of these 3 specific blocks" | anyOf(of(a), of(b), of(c)) |
| Logic that can't be expressed declaratively | predicate(state -> ...) |
| A cell that accepts anything (including air) | any() |
| Any block exposing a NeoForge capability at runtime | ability(capability, previewBlock) |
IWallSharable
public interface IWallSharable {
WallSharingMode getDefaultWallSharingMode();
}
An optional interface a Block can implement to declare its default wall-sharing behavior. Consulted in the priority chain (see getWallSharingMode) only when a symbol's ingredient has exactly one candidate block. An extension point for your own blocks — none implement it by default. Full chain: Wall sharing.
Performance note
Definitions whose activation or core symbol ingredient returns an empty getCandidateBlocks() (tags, predicates, any()) fall back to being checked on every block placement in the world, not just placements of a specific block - see MultiblockRegistry.getCandidatesFor and the "always-checked" list in Core Concepts. Prefer of(...)/ofState(...)/anyOf(...) of enumerable blocks for activation/core symbols where possible; reserve tags/predicates for body symbols, where this cost doesn't apply.
See also
- MultiblockBuilder § Symbols
- Core Concepts
- Advanced Features § JSON/datapack definitions - the JSON schema for each ingredient type
- Multiblock States & Progress Tracking § computeDetailed -
WRONG/WRONG_STATEclassification usesmatchesBlockType(...)