Skip to content

Geckolib Items (Geckolib4)

Tslat edited this page Apr 16, 2024 · 13 revisions

Pre-word

Because Minecraft only ever keeps one instance of any given Item, GeckoLib animations for items need to be handled slightly differently. Where objects like Entities and BlockEntities can have their animations handled directly, Items need to make sure it's matching up with its respective synced equivalent.

Ensure you're thoroughly reading the examples and descriptions on this page to determine the required differences.

Steps

Creating a GeckoLib item requires the following steps:

  1. Creating your Blockbench Model
  2. Creating your Geo Model
  3. Creating your item display json
  4. Creating your item class
  5. Creating and registering your renderer

Steps #1 and #2 will not be covered on this page, instead visit their respective links for info. This page will focus on steps #3, #4, and #5

Full Video Guides

The Item Class

Quick Summary
  1. Implement GeoItem
  2. Register as a synced animatable
  3. Override getAnimatableInstanceCache and registerControllers
  4. Instantiate a new AnimatableInstanceCache via GeckoLibUtil.createInstanceCache(this) at the top of your entity class and return it in getAnimatableInstanceCache
  5. Add any controllers you want for animations in registerControllers
  6. Reference your renderer

There are a few things needed to set up a GeckoLib item class.

The first is to implement GeoItem on your item class, and override the two methods your IDE will tell you to override. This interface is the base for animatable items in GeckoLib, and lets the various other features of the mod pick up your item as an animatable one.

Next, we'll create an instance of an AnimatableInstanceCache for our item. This stores our animatable instance so that it can be retrieved by the renderer and other outside areas.

To do this, we'll instantiate a new cache via GeckoLibUtil.createInstanceCache(this) at the top of your item class, caching it in a final variable.

Next, we'll override getAnimatableInstanceCache in our item class if it hasn't been done already, and return the factory instance we just created.

And finally, override registerControllers in your item class. This method is called when your item is first being used for animations, and is where you define your actual animation handling.

This finalises the base setup for an animatable Item. However because of the nature of items, with these steps alone you'll only be able to do animations based on the client-side state of the itemstack, which often isn't very useful.

To rectify this, we need to do a couple extra things.

Triggerable animations

GeckoLib4 adds the ability to trigger animations remotely, from the server side. This greatly simplifies the process of handling animations from the server side.

To use this, we'll need to ensure we've registered our animatable as a synchable one, by calling SingletonGeoAnimatable.registerSyncedAnimatable in the constructor of our item class. This tells GeckoLib that it should expect to need to sync either animations or data from server to client during runtime for this item.

Then, we can call triggerableAnim on the AnimationController that we want the animation to play on. This method takes the name of the trigger, and the RawAnimation that will be played when triggered.

When we're ready to play the animation, we can call triggerAnim, ensuring that we pass in the correct trigger name, and the numerical id of the stack obtained from GeoItem.getOrAssignId. This id is assigned and used internally by GeckoLib to match up animatable instances, since Items are not unique.

GeckoLib then handles the rest for us, automatically resetting the animation's state on completion. Magic!

The Renderer

To then get the item rendering, we'll need to make a renderer then register it. Due to the nature of items, this requires a little more work than is usual to register.

Creating the class

To make the renderer itself, just make a class that extends GeoItemRenderer, passing in the GeoModel instance to the constructor

Example Renderer Class

    public class ExampleItemRenderer extends GeoItemRenderer<ExampleItem> {
    	public ExampleItemRenderer() {
    		super(new ExampleItemModel());
    	}
    }

Registering the Renderer

Item renderers get registered a little differently to other renderers, and GeckoLib utilises the same system, so we need to be aware of the correct method of registration.

GeckoLib4->4.4

Registering the renderer differs slightly between Forge and Fabric, so for convenience the below section is split into two.

Forge Registration

Using IClientItemExtensions

On more modern versions of Forge, Forge provides a client-interfacing handler for item-specific functionalities. GeckoLib utilises this to render, so we'll need to implement this in our item class.

See below for an example implementation of this:

	@Override
	public void initializeClient(Consumer<IClientItemExtensions> consumer) {
		consumer.accept(new IClientItemExtensions() {
			private ExampleItemRenderer renderer = null;
			// Don't instantiate until ready. This prevents race conditions breaking things
			@Override public BlockEntityWithoutLevelRenderer getItemStackRenderer() {
				if (this.renderer == null)
					this.renderer = new ExampleItemRenderer();

				return renderer;
			}
		});
	}
Fabric Registration

GeckoLib implements a dynamic render retrieving system for animatable items, to allow for flexible render handling. We will use this to implement our GeoItem's renderer

To do this, we need to do a few things. First, we create and cache a new renderProvider in our item. This is done via GeoItem#makeRenderer, and MUST be stored as Supplier<Object>

private final Supplier<Object> renderProvider = GeoItem.makeRenderer(this);

Then, we override getRenderProvider, and return this provider

@Override
public Supplier<Object> getRenderProvider() {
	return this.renderProvider;
}

And finally, we override createRenderer, and give the consumer a new RenderProvider, which tells GeckoLib how to find our renderer.

@Override
public void createRenderer(Consumer<Object> consumer) {
	consumer.accept(new RenderProvider() {
		private ExampleItemRenderer renderer;

		@Override
		public BlockEntityWithoutLevelRenderer getCustomRenderer() {
			if (this.renderer == null)
				this.renderer = new ExampleItemRenderer();

			return this.renderer;
		}
	});
}

You can alternatively use Fabric's built-in item renderer registration method - though no guarantee is made for 100% compatibility with this option in the future if further functionality is required.

GeckoLib4.5+

GeckoLib implements a dynamic render retrieving system for animatable items, to allow for flexible render handling. We will use this to implement our GeoItem's renderer

To do this, we need to override createGeoRenderer, and provide the consumer a new instance of GeoRenderProvider.

@Override
public void createGeoRenderer(Consumer<GeoRenderProvider> consumer) {
	consumer.accept(new GeoRenderProvider() {
		private ExampleItemRenderer renderer;

		@Override
		public BlockEntityWithoutLevelRenderer getCustomRenderer() {
			if (this.renderer == null)
				this.renderer = new ExampleItemRenderer();

			return this.renderer;
		}
	});
}

You can alternatively use Forge/NeoForge/Fabric's built-in item renderer registration methods - though no guarantee is made for 100% compatibility with this option in the future if further functionality is required.

Example Item Class

GeckoLib4->4.4
Forge
public class ExampleItem extends Item implements GeoItem {
	private static final RawAnimation ACTIVATE_ANIM = RawAnimation.begin().thenPlay("use.activate");
	private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);

	public ExampleItem(Properties properties) {
		super(properties);

		// Register our item as server-side handled.
		// This enables both animation data syncing and server-side animation triggering
		SingletonGeoAnimatable.registerSyncedAnimatable(this);
	}

	// Utilise the existing forge hook to define our custom renderer
	@Override
	public void initializeClient(Consumer<IClientItemExtensions> consumer) {
		consumer.accept(new IClientItemExtensions() {
			private ExampleItemRenderer renderer;

			@Override
			public BlockEntityWithoutLevelRenderer getCustomRenderer() {
				if (this.renderer == null)
					this.renderer = new ExampleItemRenderer();

				return this.renderer;
			}
		});
	}

	@Override
	public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
		controllers.add(new AnimationController<>(this, "Activation", 0, state -> PlayState.STOP)
				.triggerableAnim("activate", ACTIVATE_ANIM));
				// We've marked the "activate" animation as being triggerable from the server
	}

	// Let's handle our use method so that we activate the animation when right-clicking while holding the box
	@Override
	public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
		if (level instanceof ServerLevel serverLevel)
			triggerAnim(player, GeoItem.getOrAssignId(player.getItemInHand(hand), serverLevel), "Activation", "activate");

		return super.use(level, player, hand);
	}

	@Override
	public AnimatableInstanceCache getAnimatableInstanceCache() {
		return this.cache;
	}
}
Fabric
public final class ExampleItem extends Item implements GeoItem {
	private static final RawAnimation ACTIVATE_ANIM = RawAnimation.begin().thenPlay("use.activate");
	private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);
	private final Supplier<Object> renderProvider = GeoItem.makeRenderer(this);

	public ExampleItem(Properties properties) {
		super(properties);

		// Register our item as server-side handled.
		// This enables both animation data syncing and server-side animation triggering
		SingletonGeoAnimatable.registerSyncedAnimatable(this);
	}

	// Utilise our own render hook to define our custom renderer
	@Override
	public void createRenderer(Consumer<Object> consumer) {
		consumer.accept(new RenderProvider() {
			private ExampleItemRenderer renderer;

			@Override
			public BlockEntityWithoutLevelRenderer getCustomRenderer() {
				if (this.renderer == null)
					this.renderer = new ExampleItemRenderer();

				return this.renderer;
			}
		});
	}

	@Override
	public Supplier<Object> getRenderProvider() {
		return this.renderProvider;
	}

	@Override
	public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
		controllers.add(new AnimationController<>(this, "Activation", 0, state -> PlayState.STOP)
				.triggerableAnim("activate", ACTIVATE_ANIM));
				// We've marked the "activate" animation as being triggerable from the server
	}

	// Let's handle our use method so that we activate the animation when right-clicking while holding the box
	@Override
	public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
		if (level instanceof ServerLevel serverLevel)
			triggerAnim(player, GeoItem.getOrAssignId(player.getItemInHand(hand), serverLevel), "Activation", "activate");

		return super.use(level, player, hand);
	}

	@Override
	public AnimatableInstanceCache getAnimatableInstanceCache() {
		return this.cache;
	}
}
GeckoLib4.5+
public final class ExampleItem extends Item implements GeoItem {
	private static final RawAnimation ACTIVATE_ANIM = RawAnimation.begin().thenPlay("use.activate");
	private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);

	public ExampleItem(Properties properties) {
		super(properties);

		// Register our item as server-side handled.
		// This enables both animation data syncing and server-side animation triggering
		SingletonGeoAnimatable.registerSyncedAnimatable(this);
	}

	// Utilise our own render hook to define our custom renderer
	@Override
	public void createGeoRenderer(Consumer<GeoRenderProvider> consumer) {
		consumer.accept(new GeoRenderProvider() {
			private ExampleItemRenderer renderer;

			@Override
			public BlockEntityWithoutLevelRenderer getCustomRenderer() {
				if (this.renderer == null)
					this.renderer = new ExampleItemRenderer();

				return this.renderer;
			}
		});
	}

	@Override
	public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
		controllers.add(new AnimationController<>(this, "Activation", 0, state -> PlayState.STOP)
				.triggerableAnim("activate", ACTIVATE_ANIM));
				// We've marked the "activate" animation as being triggerable from the server
	}

	// Let's handle our use method so that we activate the animation when right-clicking while holding the box
	@Override
	public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
		if (level instanceof ServerLevel serverLevel)
			triggerAnim(player, GeoItem.getOrAssignId(player.getItemInHand(hand), serverLevel), "Activation", "activate");

		return super.use(level, player, hand);
	}

	@Override
	public AnimatableInstanceCache getAnimatableInstanceCache() {
		return this.cache;
	}
}

Table of Contents

Geckolib 3
Geckolib 4

Hosted By: Cloudsmith

Package repository hosting is graciously provided by Cloudsmith.

Cloudsmith is the only fully hosted, cloud-native, universal package management solution that enables your organization to create, store and share packages in any format, to any place, with total confidence.

Clone this wiki locally