Coding an entity
In this section, we’ll walk through creating a custom Capybara entity, just like the one in the Bestium Example plugin.
Choose the right base class
Section titled “Choose the right base class”Minecraft has a deep and confusing entity class hierarchy, and picking the correct base class can be challenging.
Unlike traditional approaches that force you to extend a concrete class and work around AI structures, Bestium gives you full control. You can design your entity from scratch, almost like Mojang does internally.
The real power of Bestium lies in choice: you’re free to select the base class that best fits your entity’s behavior. The ideal way to determine this is to:
- Decompile a similar vanilla entity and check which class it extends.
- Open
net.minecraft.world.entity.Entity
in your IDE and explore the class hierarchy.
Here are some frequently used abstract or base classes you might consider extending:
Animal
for passive creatures like cows, axolotls or capybarasMonster
for hostile mobs like zombies or skeletonsAbstractVillager
for villagers and their variantsAbstractGolem
for golems like iron golems or snow golemsAbstractFish
for fish entities like pufferfish or tadpolesAbstractSchoolingFish
for schooling fish like cod or salmonAgeableWaterCreature
for water creatues with baby forms like dolphins or squids
Create the entity class
Section titled “Create the entity class”Your class must
- Extend the appropriate base class
- Implement all abstract methods
- Provide a public super-matching constructor
Your initial code should look something like this:
package com.example.myplugin
import net.minecraft.server.level.ServerLevelimport net.minecraft.world.entity.AgeableMobimport net.minecraft.world.entity.EntityTypeimport net.minecraft.world.entity.animal.Animalimport net.minecraft.world.item.ItemStackimport net.minecraft.world.level.Level
class Capybara(entityType: EntityType<out Capybara>, level: Level) : Animal(entityType, level) { override fun isFood(stack: ItemStack): Boolean { TODO("Not yet implemented") }
override fun getBreedOffspring( serverLevel: ServerLevel, otherParent: AgeableMob ): AgeableMob? { TODO("Not yet implemented") }}
package com.example.myplugin;
import net.minecraft.server.level.ServerLevel;import net.minecraft.world.entity.AgeableMob;import net.minecraft.world.entity.EntityType;import net.minecraft.world.entity.animal.Animal;import net.minecraft.world.item.ItemStack;import net.minecraft.world.level.Level;
public class Capybara extends Animal { protected Capybara(final EntityType<? extends Capybara> entityType, final Level level) { super(entityType, level); }
@Override public boolean isFood(final ItemStack stack) { return false; }
@Override public AgeableMob getBreedOffspring(final ServerLevel serverLevel, final AgeableMob otherParent) { return null; }}
Add default attributes
Section titled “Add default attributes”If your entity extends LivingEntity
it needs a set of default attributes, like health, speed, or attack damage, to function properly in the game.
These are defined in a static method, typically named createAttributes()
. Some attributes are required (like max health and movement speed),
you will usually find out, because the server will crash 🙂.
Use the appropriate base method provided by your parent for your entity type:
createAnimalAttributes()
for animalscreateMonsterAttributes()
for monsters
etc.
companion object { fun createAttributes() = createAnimalAttributes() .add(Attributes.MAX_HEALTH, 10.0) .add(Attributes.MOVEMENT_SPEED, .25)}
public static AttributeSupplier.Builder createAttributes() { return createAnimalAttributes() .add(Attributes.MAX_HEALTH, 10) .add(Attributes.MOVEMENT_SPEED, 0.3);}
Add goals
Section titled “Add goals”Goals power your entity’s AI, how it moves, reacts, or interacts with the world.
To add goals, override the registerGoals()
method in your custom entity. Use:
goalSelector.addGoal(PRIORITY, GOAL)
goalSelector.addGoal(PRIORITY, GOAL);
PRIORITY
: The importance of this goal over others, lower numbers run firstGOAL
: An instance of an existing goal (check thenet.minecraft.world.entity.ai.goal
package) or your own custom goal
Here’s an example for our capybara:
override fun registerGoals() { goalSelector.addGoal(0, FloatGoal(this)) goalSelector.addGoal(1, PanicGoal(this, 1.25)) goalSelector.addGoal(3, BreedGoal(this, 1.0)) goalSelector.addGoal(4, TemptGoal(this, 1.2, ::isFood, false)) goalSelector.addGoal(5, FollowParentGoal(this, 1.1)) goalSelector.addGoal(6, WaterAvoidingRandomStrollGoal(this, 1.0)) goalSelector.addGoal(7, LookAtPlayerGoal(this, Player::class.java, 6.0F)) goalSelector.addGoal(8, RandomLookAroundGoal(this))}
@Overrideprotected void registerGoals() { goalSelector.addGoal(0, new FloatGoal(this)); goalSelector.addGoal(1, new PanicGoal(this, 1.25)); goalSelector.addGoal(3, new BreedGoal(this, 1.0)); goalSelector.addGoal(4, new TemptGoal(this, 1.2, this::isFood, false)); goalSelector.addGoal(5, new FollowParentGoal(this, 1.1)); goalSelector.addGoal(6, new WaterAvoidingRandomStrollGoal(this, 1.0)); goalSelector.addGoal(7, new LookAtPlayerGoal(this, Player.class, 6.0F)); goalSelector.addGoal(8, new RandomLookAroundGoal(this));}
Finishing touches
Section titled “Finishing touches”Now, complete your entity class by implementing the required methods:
isFood(ItemStack)
defines what items your entity considers food.getBreedOffspring()
handles breeding behavior and returns the baby entity.
Your finished class will look something like this:
package com.example.myplugin
import net.minecraft.server.level.ServerLevelimport net.minecraft.world.entity.AgeableMobimport net.minecraft.world.entity.EntitySpawnReasonimport net.minecraft.world.entity.EntityTypeimport net.minecraft.world.entity.ai.attributes.AttributeSupplierimport net.minecraft.world.entity.ai.attributes.Attributesimport net.minecraft.world.entity.ai.goal.*import net.minecraft.world.entity.animal.Animalimport net.minecraft.world.entity.player.Playerimport net.minecraft.world.item.ItemStackimport net.minecraft.world.item.Itemsimport net.minecraft.world.level.Level
open class Capybara(entityType: EntityType<out Capybara>, level: Level) : Animal(entityType, level) { companion object { fun createAttributes(): AttributeSupplier.Builder { return createAnimalAttributes() .add(Attributes.MAX_HEALTH, 10.0) .add(Attributes.MOVEMENT_SPEED, 0.3) } }
override fun registerGoals() { goalSelector.addGoal(0, FloatGoal(this)) goalSelector.addGoal(1, PanicGoal(this, 1.25)) goalSelector.addGoal(2, FollowParentGoal(this, 1.1)) goalSelector.addGoal(3, WaterAvoidingRandomStrollGoal(this, 1.0)) goalSelector.addGoal(4, LookAtPlayerGoal(this, Player::class.java, 6.0f)) goalSelector.addGoal(5, RandomLookAroundGoal(this)) }
override fun isFood(itemStack: ItemStack) = when (itemStack.item) { Items.SEAGRASS, Items.MELON_SLICE -> true else -> false }
override fun getBreedOffspring(level: ServerLevel, otherParent: AgeableMob): AgeableMob? { return type.create(level, EntitySpawnReason.BREEDING) as Capybara? }}
package com.example.myplugin;
import net.minecraft.server.level.ServerLevel;import net.minecraft.world.entity.AgeableMob;import net.minecraft.world.entity.EntitySpawnReason;import net.minecraft.world.entity.EntityType;import net.minecraft.world.entity.ai.attributes.AttributeSupplier;import net.minecraft.world.entity.ai.attributes.Attributes;import net.minecraft.world.entity.ai.goal.*;import net.minecraft.world.entity.animal.Animal;import net.minecraft.world.entity.player.Player;import net.minecraft.world.item.Item;import net.minecraft.world.item.ItemStack;import net.minecraft.world.item.Items;import net.minecraft.world.level.Level;
public class Capybara extends Animal { public static AttributeSupplier.Builder createAttributes() { return createAnimalAttributes() .add(Attributes.MAX_HEALTH, 10) .add(Attributes.MOVEMENT_SPEED, 0.3); }
@Override protected void registerGoals() { goalSelector.addGoal(0, new FloatGoal(this)); goalSelector.addGoal(1, new PanicGoal(this, 1.25)); goalSelector.addGoal(2, new FollowParentGoal(this, 1.1)); goalSelector.addGoal(3, new WaterAvoidingRandomStrollGoal(this, 1.0)); goalSelector.addGoal(4, new LookAtPlayerGoal(this, Player.class, 6.0f)); goalSelector.addGoal(5, new RandomLookAroundGoal(this)); }
protected Capybara(EntityType<? extends Capybara> entityType, Level level) { super(entityType, level); }
@Override public boolean isFood(ItemStack itemStack) { Item item = itemStack.getItem(); return item == Items.SEAGRASS || item == Items.MELON_SLICE; }
@Override public AgeableMob getBreedOffspring(ServerLevel level, AgeableMob otherParent) { return (Capybara) getType().create(level, EntitySpawnReason.BREEDING); }}