Getting started

A Strut entity from nothing, in five steps. The example mod id is mymod and the entity is ava; use your own.

1. Add Strut

Strut is on Modrinth, which also serves it as a Maven repository. In your build.gradle:

repositories {
	exclusiveContent {
		forRepository {
			maven {
				name = "Modrinth"
				url = "https://api.modrinth.com/maven"
			}
		}
		filter {
			includeGroup "maven.modrinth"
		}
	}
}

dependencies {
	modImplementation "maven.modrinth:strut:1.0.0"
}

The part after the last colon is the version number of the Strut release on Modrinth. In your fabric.mod.json:

"depends": {
	"strut": "*"
}

To build Strut from source instead, run ./gradlew publishToMavenLocal in its repository, add mavenLocal() to repositories and depend on com.sandymandy.strut:strut-fabric-1.21.1:1.0.0.

2. Put the files where Strut looks

Strut finds an entity's files from its registry id. For mymod:ava that is:

assets/mymod/strut/models/entity/ava.strutmodel
assets/mymod/textures/entity/ava.png
assets/mymod/strut/animations/entity/ava.strutanim

Author them in Blockbench with the Strut plugin, which reads and writes both formats directly. See Models and files for every folder and how to change the convention.

3. Write the entity

Implement StrutEntity. That is all: no animation cache field, no getTick, no registerControllers.

public class HeroEntity extends PathAwareEntity implements StrutEntity {
	public HeroEntity(EntityType<? extends HeroEntity> type, World world) {
		super(type, world);
	}
}

4. Register it, on both sides

Register the EntityType the way you always do. Then, in your client initialiser, one line:

StrutClient.entity(AVA);

That builds the model, the renderer and the registration for you. If a file does not follow the convention, see changing what differs.

5. Make it move

Animations are played by the server. Tell it which animation to play on which controller, and every client sees the same thing at the same time:

ServerAnimationStates.forEntity(entity, false)
		.play("base", "walk", entity.getWorld().getTime(), 0, 1f, LoopMode.LOOP);

"base" is a controller name of your choosing; you do not have to register it. The first time the server plays on a name, the client creates the controller. See Playing animations for seek, setRate, stop and how late joiners are handled.

Press F9 in game for the render statistics overlay. See Commands and debugging.

Where next