Skip to content

Model Variant Selection (Multiplexer) Reference

Interfaces, configuration, and call reference. For the mechanics see multiplexer.md.

Multiplexer is a stateless variant selector: a set of Variant nodes plus guarded transition edges. It picks the current variant (such as a block's appearance/model) from the context. The definition itself holds no runtime state — one block type shares one definition, with one MuxState per instance.

Multiplexer is a code definition (Java DSL), not JSON. FSM is the data-driven JSON one (see fsm.md).

When to use Multiplexer and when to use FSM

Multiplexer FSM
Driving mechanism Context snapshot selects the variant (ctx.property(...)) Layered state machine + tick advance + actions
State None (definition); one MuxState per instance One complete StateMachine per instance
Suited to Switches, gears, redstone states, model variant switching Animation timing, attack chains, locomotion
Definition location Java code data/<ns>/state_machines/*.json

Use Multiplexer for a simple "powered / unpowered model swap"; use FSM when you need state duration, actions, and multi-layer blending.

Core API (lib.kasuga.rendering.models.uml.dynamic.multiplexer)

// 1. Definition (shared, one per type)
Multiplexer<MyContext, MyVariant> def = Multiplexer.define(MyVariant::new, mux -> {
    MyVariant off = mux.variant("off", v -> { /* variant config: model/material... */ });
    MyVariant on  = mux.variant("on",  v -> { });
    mux.transition(off, on, t -> t
            .when(ctx -> ctx.property("powered").equals("true"))
            .crossFade(0.2f));
    mux.transition(on, off, t -> t.when(ctx -> ctx.property("powered").equals("false")));
    mux.initial(off);
});

// 2. One state per instance
MuxState<MyVariant> state = def.newState();

// 3. Advance each tick (advance the cross-fade + evaluate guards; a guard hit switches)
def.advance(state, context, dt);

// The currently selected variant (during a transition it is the target variant)
MyVariant current = state.current();

Builder methods

Method Description
variant(id, config) Defines a variant and returns a typed handle
transition(from, to, config) Adds a transition edge; TransitionBuilder:
↳ .when(Predicate<C>) Guard; empty = always true
↳ .crossFade(seconds) Cross-fade duration in seconds; 0 = switch immediately
↳ .onSwitch(Consumer<MuxState>) Callback on switch
initial(variant) Initial variant

Runtime

Method Description
newState() Creates a per-instance MuxState (starting at initial)
advance(state, ctx, dt) Advances the cross-fade + evaluates guards, switching on a hit; call every tick
select(ctx, state) Read-only: which variant would be selected now (does not change state)
state.current() Current variant
state.inTransition() / state.to() In transition / target variant (during the cross-fade)

Minecraft side

The MC implementation uses McContext (wrapping block states/redstone and so on into a context snapshot) + McVariant (model/material variant) + McMultiplexer; the core Multiplexer does not depend on Minecraft. A block type creates one McMultiplexer at registration, each BE calls newState(), and in #onDataChanged or tick it calls advance(state, ctx, dt) and feeds state.current() to rendering to select the variant.

Composition with FSM

Multiplexer selects "which model variant to use", FSM drives "the animation timing of that model". The two compose: Mux selects the variant → the ModelInstance for that variant mounts an FSM PoseDriver to run animation.