Items, armor and blocks
The same idea as entities: implement an interface, register with one line.
Registration
In your client initialiser:
StrutClient.item(STAFF); // strut/models/item/staff.strutmodel by convention
StrutClient.armor(OUTFIT); // strut/models/armor/outfit.strutmodel
StrutClient.blockEntity(BED); // strut/models/block/bed.strutmodel
Each has the same three forms as StrutClient.entity: the convention, a model you supply, or a renderer you build yourself (for items, a supplier of the renderer; for block entities, a renderer factory).
| Kind | Interface | Files, by convention |
|---|---|---|
| Item | StrutItem | strut/models/item/<path>.strutmodel, textures/item/<path>.png |
| Armor | StrutItem | strut/models/armor/<path>.strutmodel, textures/armor/<path>.png |
| Block entity | StrutBlockEntity | strut/models/block/<path>.strutmodel, textures/block/<path>.png |
The path comes from the registry id of the item, or of the block entity type.
Items
public class CreaturestaffItem extends Item implements StrutItem {
public CreaturestaffItem(Settings settings) {
super(settings);
}
}
That is the whole class. Two things make it render:
StrutClient.item(...)in your client initialiser.- An item model json,
assets/<ns>/models/item/<path>.json, whose parent hands drawing to Strut:{ "parent": "builtin/entity" }
You do not register the item for animation syncing: Strut does it as items are added to the registry. (Calling registerSyncedAnimatable yourself is harmless.)
Animating an item
Items and block entities are animated on the client, so they need a controller of your own. The server cannot drive an item stack the way it drives an entity. registerControllers is where you add one:
public class CreatureBedBlockEntity extends BlockEntity implements StrutBlockEntity {
public CreatureBedBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
}
@Override
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "main",
state -> state.setAndContinue(RawAnimation.begin().thenLoop("idle"))));
}
}
The same registerControllers works on an item. To trigger an animation from the server on an item or block entity, use triggerAnim.
Override isPerspectiveAware() on an item to animate differently in the hand, the inventory and the world.
A model with alternative parts
A model that holds parts of which only some are drawn (the halves of a bed, an item that shows one of them) lists the ones it never draws on the model: public Collection<String> hiddenBones(T animatable). They are hidden, with what is under them, in what is drawn, whatever the animation or a bone override says, for blocks, items and entities alike. A part that changes (by what a stack holds) is hidden in setCustomAnimations instead, with bone.setHidden(true).
Armor
StrutClient.armor(item) renders a worn model from strut/models/armor/.... An armor item normally also calls StrutClient.item(item) for how it looks in the inventory and in the hand; the two are independent.
Blocks
A block that shows a Strut model is a block with a block entity. Make the block's own render type invisible so only the block entity's renderer draws, and register the renderer with StrutClient.blockEntity(type). A block entity class is shown above.
Changing a block's bones
A StrutBlockEntity has overrides(), the same as an entity: hide, move, rotate, scale, tint or re-texture a bone.
// on the server: every player who can see the block gets it
blockEntity.overrides().setHidden("lid", true);
blockEntity.overrides().setColor("cloth", new Color(0xFFCC4444));
// on a client: only that player's own view
blockEntity.overrides().clear("lid", OverrideProperty.HIDDEN);
The server's overrides reach a player who is already near the block when they are set, and a player who comes near later when the block loads on their side. They are not saved: a block entity that loads again starts without any, so set them from the block's own state when it loads and when that state changes. They are kept for the block's position and dropped when the block entity is removed or unloaded. See Bones for the properties.
The older way still works
An item that overrides createStrutRenderer(Consumer<StrutRenderProvider>) keeps working: Strut consults its registrations first and falls back to that override. New code should not need it.