Claude-skill-registry add-custom-mob
Use when adding new mobs or entities. Covers entity class, model, renderer, client registration, spawn config, loot tables.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/add-custom-mob" ~/.claude/skills/majiayu000-claude-skill-registry-add-custom-mob && rm -rf "$T"
skills/data/add-custom-mob/SKILL.mdAdd Custom Mob to ChronoDawn
Purpose: Complete guide for adding a new mob entity to ChronoDawn with full multi-version and multi-loader support.
Quick Checklist
When adding a new mob, complete ALL of the following:
Java Classes (per module: 1.20.1, 1.21.1, 1.21.2, 1.21.4)
Note: 1.21.3 uses 1.21.2 modules (no separate directory)
- Entity class (
)entities/mobs/MobNameEntity.java - Model class (
)client/model/MobNameModel.java - Renderer class (
)client/renderer/mobs/MobNameRenderer.java - RenderState class (
) - 1.21.2 and 1.21.4 onlyclient/renderer/mobs/MobNameRenderState.java
Registration (per version)
-
- Entity type registrationModEntities.java -
- Spawn egg registration (see custom-mob-spawn-egg skill)ModItems.java
Platform-Specific Registration (CRITICAL - Common source of crashes)
- Fabric
- Attribute & spawn placement registrationChronoDawnFabric.java - Fabric
- Import statements for Model and RendererChronoDawnClientFabric.java - Fabric
- Model layer registration (ChronoDawnClientFabric.java
)EntityModelLayerRegistry.registerModelLayer - Fabric
- Renderer registration (ChronoDawnClientFabric.java
)EntityRendererRegistry.register - NeoForge
- Attribute & spawn placement registrationChronoDawnNeoForge.java - NeoForge
- Model layer & renderer registrationChronoDawnClientNeoForge.java - NeoForge
- Spawn egg color handlerChronoDawnClientNeoForge.java
Resource Files (per version)
- Texture file (
)textures/entity/mobs/mob_name.png - Loot table (
orloot_table/entities/mob_name.json
for 1.20.1)loot_tables/ - Spawn egg model (
)models/item/mob_name_spawn_egg.json - Spawn egg item definition (
) - 1.21.4 onlyitems/mob_name_spawn_egg.json - Language files (
,lang/en_us.json
)lang/ja_jp.json - Biome spawn configuration (if replacing vanilla mob)
Vanilla Mob Reference Parameters
When creating a mob based on a vanilla mob, use these reference values:
| Base Mob | sized(w, h) | Health | Attack | Speed | Other Attributes |
|---|---|---|---|---|---|
| Iron Golem | 1.4f, 2.7f | 100 | 7.5-21 | 0.25 | Knockback Resistance 1.0 |
| Enderman | 0.6f, 2.9f | 40 | 7 | 0.3 | Follow Range 64 |
| Zombie | 0.6f, 1.95f | 20 | 3 | 0.23 | - |
| Spider | 1.4f, 0.9f | 16 | 2 | 0.3 | Wall climbing |
| Skeleton | 0.6f, 1.99f | 20 | - | 0.25 | Bow attack |
| Chicken | 0.4f, 0.7f | 4 | - | 0.25 | - |
| Pig | 0.9f, 0.9f | 10 | - | 0.25 | - |
| Rabbit | 0.4f, 0.5f | 3 | - | 0.3 | - |
Tip: For hostile versions of friendly mobs, consider increasing Health and adding Attack damage.
1. Entity Class
Create in
common-{version}/src/main/java/com/chronodawn/entities/mobs/
1.21.2 Template
package com.chronodawn.entities.mobs; 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.monster.Monster; import net.minecraft.world.level.Level; public class MobNameEntity extends Monster { public MobNameEntity(EntityType<? extends MobNameEntity> entityType, Level level) { super(entityType, level); } public static AttributeSupplier.Builder createAttributes() { return Monster.createMonsterAttributes() .add(Attributes.MAX_HEALTH, 20.0D) .add(Attributes.ATTACK_DAMAGE, 3.0D) .add(Attributes.MOVEMENT_SPEED, 0.3D) .add(Attributes.FOLLOW_RANGE, 35.0D); } @Override protected void registerGoals() { // Add AI goals here } // 1.21.2: Use EntitySpawnReason, SynchedEntityData.Builder // Example for synched data: // @Override // protected void defineSynchedData(SynchedEntityData.Builder builder) { // super.defineSynchedData(builder); // builder.define(DATA_FLAGS, (byte)0); // } }
1.21.1 Template
Key differences from 1.21.2:
- Use
instead ofMobSpawnTypeEntitySpawnReason
pattern same as 1.21.2SynchedEntityData.Builder
1.20.1 Template
Key differences:
- Use
instead ofMobSpawnTypeEntitySpawnReason
has no parameters, usedefineSynchedData()
directlythis.entityData.define()
@Override protected void defineSynchedData() { super.defineSynchedData(); this.entityData.define(DATA_FLAGS, (byte)0); }
2. Model Class
Create in
common-{version}/src/main/java/com/chronodawn/client/model/
1.21.2 Template (RenderState pattern)
package com.chronodawn.client.model; import com.chronodawn.client.renderer.mobs.MobNameRenderState; import net.minecraft.client.model.EntityModel; import net.minecraft.client.model.geom.ModelPart; import net.minecraft.client.model.geom.PartPose; import net.minecraft.client.model.geom.builders.*; public class MobNameModel extends EntityModel<MobNameRenderState> { private final ModelPart body; public MobNameModel(ModelPart root) { super(root); this.body = root.getChild("body"); } public static LayerDefinition createBodyLayer() { MeshDefinition meshdefinition = new MeshDefinition(); PartDefinition partdefinition = meshdefinition.getRoot(); partdefinition.addOrReplaceChild("body", CubeListBuilder.create() .texOffs(0, 0) .addBox(-4.0F, -8.0F, -4.0F, 8.0F, 8.0F, 8.0F), PartPose.offset(0.0F, 24.0F, 0.0F)); return LayerDefinition.create(meshdefinition, 64, 32); } @Override public void setupAnim(MobNameRenderState state) { super.setupAnim(state); // Animation logic using state.walkAnimationPos, state.walkAnimationSpeed } }
1.21.1 / 1.20.1 Template (Entity pattern)
package com.chronodawn.client.model; import com.chronodawn.entities.mobs.MobNameEntity; import net.minecraft.client.model.EntityModel; import net.minecraft.client.model.geom.ModelPart; // ... other imports public class MobNameModel extends EntityModel<MobNameEntity> { public MobNameModel(ModelPart root) { // 1.21.1/1.20.1: No super(root) call this.body = root.getChild("body"); } // Same createBodyLayer() as 1.21.2 @Override public void setupAnim(MobNameEntity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { // Animation logic } // 1.21.1: renderToBuffer with int color @Override public void renderToBuffer(PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay, int color) { body.render(poseStack, buffer, packedLight, packedOverlay, color); } // 1.20.1: renderToBuffer with float RGBA @Override public void renderToBuffer(PoseStack poseStack, VertexConsumer buffer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { body.render(poseStack, buffer, packedLight, packedOverlay, red, green, blue, alpha); } }
3. Renderer Class
Create in
common-{version}/src/main/java/com/chronodawn/client/renderer/mobs/
1.21.2 Template
package com.chronodawn.client.renderer.mobs; import com.chronodawn.ChronoDawn; import com.chronodawn.client.model.MobNameModel; import com.chronodawn.entities.mobs.MobNameEntity; import net.minecraft.client.renderer.entity.EntityRendererProvider; import net.minecraft.client.renderer.entity.MobRenderer; import net.minecraft.resources.ResourceLocation; public class MobNameRenderer extends MobRenderer<MobNameEntity, MobNameRenderState, MobNameModel> { private static final ResourceLocation TEXTURE = ResourceLocation.fromNamespaceAndPath(ChronoDawn.MOD_ID, "textures/entity/mobs/mob_name.png"); public MobNameRenderer(EntityRendererProvider.Context context) { super(context, new MobNameModel(context.bakeLayer(MobNameModel.LAYER_LOCATION)), 0.5f); } @Override public ResourceLocation getTextureLocation(MobNameRenderState state) { return TEXTURE; } @Override public MobNameRenderState createRenderState() { return new MobNameRenderState(); } }
1.21.1 / 1.20.1 Template
// MobRenderer has 2 type parameters: <Entity, Model> public class MobNameRenderer extends MobRenderer<MobNameEntity, MobNameModel> { @Override public ResourceLocation getTextureLocation(MobNameEntity entity) { return TEXTURE; } // No createRenderState() method needed }
4. RenderState Class (1.21.2 and 1.21.4)
Create in
common-1.21.2/ and common-1.21.4/ under src/main/java/com/chronodawn/client/renderer/mobs/
package com.chronodawn.client.renderer.mobs; import net.minecraft.client.renderer.entity.state.LivingEntityRenderState; public class MobNameRenderState extends LivingEntityRenderState { // Add custom state fields if needed for animations }
5. Entity Registration (ModEntities.java)
public static final RegistrySupplier<EntityType<MobNameEntity>> MOB_NAME = ENTITIES.register( "mob_name", () -> EntityType.Builder.of(MobNameEntity::new, MobCategory.MONSTER) .sized(0.6f, 1.8f) // width, height .clientTrackingRange(8) .updateInterval(3) .build(ResourceKey.create(Registries.ENTITY_TYPE, CompatResourceLocation.create(ChronoDawn.MOD_ID, "mob_name"))) );
1.20.1 difference: Use
.build("mob_name") instead of .build(ResourceKey...)
6. Platform-Specific Registration
Fabric - ChronoDawnFabric.java
// Attributes FabricDefaultAttributeRegistry.register(ModEntities.MOB_NAME.get(), MobNameEntity.createAttributes()); // Spawn placement (1.21.1/1.21.2) SpawnPlacements.register( ModEntities.MOB_NAME.get(), SpawnPlacementTypes.ON_GROUND, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, Monster::checkMonsterSpawnRules ); // Spawn placement (1.20.1) SpawnPlacements.register( ModEntities.MOB_NAME.get(), SpawnPlacements.Type.ON_GROUND, // Note: different enum Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, Monster::checkMonsterSpawnRules );
Fabric - ChronoDawnClientFabric.java
IMPORTANT: Don't forget to add imports at the top of the file!
// Required imports (add to import section) import com.chronodawn.client.model.MobNameModel; import com.chronodawn.client.renderer.mobs.MobNameRenderer; // In registerEntityModelLayers() method - Model layer registration EntityModelLayerRegistry.registerModelLayer(MobNameModel.LAYER_LOCATION, MobNameModel::createBodyLayer); // In registerEntityRenderers() method - Renderer registration EntityRendererRegistry.register(ModEntities.MOB_NAME.get(), MobNameRenderer::new);
Common Mistake: Forgetting either the imports OR the registrations will cause a NullPointerException crash when spawning the entity.
NeoForge - ChronoDawnNeoForge.java
// In onEntityAttributeCreation event event.put(ModEntities.MOB_NAME.get(), MobNameEntity.createAttributes().build()); // In onSpawnPlacementRegister event event.register( ModEntities.MOB_NAME.get(), SpawnPlacementTypes.ON_GROUND, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, Monster::checkMonsterSpawnRules, SpawnPlacementRegisterEvent.Operation.AND );
NeoForge - ChronoDawnClientNeoForge.java
// In onRegisterEntityRenderers event event.registerEntityRenderer(ModEntities.MOB_NAME.get(), MobNameRenderer::new); // In onRegisterLayerDefinitions event event.registerLayerDefinition(MobNameModel.LAYER_LOCATION, MobNameModel::createBodyLayer);
7. Resource Files
Loot Table
1.21.1 / 1.21.2 / 1.21.3 (
data/chronodawn/loot_table/entities/mob_name.json):
{ "type": "minecraft:entity", "pools": [ { "rolls": 1, "entries": [ { "type": "minecraft:item", "name": "minecraft:bone", "functions": [ { "function": "minecraft:set_count", "count": { "min": 0, "max": 2, "type": "minecraft:uniform" } }, { "function": "minecraft:enchanted_count_increase", "enchantment": "minecraft:looting", "count": { "type": "minecraft:uniform", "min": 0, "max": 1 } } ] } ] } ] }
1.20.1 (
data/chronodawn/loot_tables/entities/mob_name.json):
- Directory is
(plural)loot_tables - Use
instead of"function": "minecraft:looting_enchant"enchanted_count_increase
Language Files
"entity.chronodawn.mob_name": "Mob Name", "item.chronodawn.mob_name_spawn_egg": "Mob Name Spawn Egg"
Biome Spawn Configuration
To replace a vanilla mob in ChronoDawn biomes, edit
worldgen/biome/chronodawn_*.json:
{ "spawners": { "monster": [ { "type": "chronodawn:mob_name", "weight": 100, "minCount": 4, "maxCount": 4 } ] } }
8. Spawn Egg
IMPORTANT: See the
custom-mob-spawn-egg skill for complete spawn egg implementation checklist.
Quick reminder:
- Register in
ModItems.java - Add to
andinitializeSpawnEggs()populateCreativeTab() - Create
withmodels/item/mob_name_spawn_egg.json"parent": "item/template_spawn_egg" - Add to NeoForge color handler in
ChronoDawnClientNeoForge.java - 1.21.4 only: Create
with tints array (colors in decimal)items/mob_name_spawn_egg.json
Version API Differences Summary
| Feature | 1.20.1 | 1.21.1 | 1.21.2/1.21.3 | 1.21.4 |
|---|---|---|---|---|
| Spawn type enum | | | | |
| SynchedEntityData | no params | | | |
| EntityModel type param | | | | |
| MobRenderer type params | 2 () | 2 () | 3 () | 3 () |
| renderToBuffer color | | | N/A (handled by RenderState) | N/A (handled by RenderState) |
| EntityType.Builder.build() | | | | |
| SpawnPlacements.Type | | | | |
| Loot table directory | | | | |
| Looting enchant function | | | | |
| RenderState class | Not needed | Not needed | Required | Required |
| Damage handling | | | | |
| Y coord minimum | | | | |
| Spawn egg item def | Not needed | Not needed | Not needed | with tints |
Note: 1.21.3 is a hotfix release that shares modules with 1.21.2. No separate code changes are needed.
Common Issues
CRITICAL: The most common mistakes are missing Fabric client registrations. Always verify both model layer AND renderer are registered in
ChronoDawnClientFabric.java.
| Issue | Cause | Solution |
|---|---|---|
| Entity not spawning | Missing attribute registration | Add to Fabric/NeoForge attribute events |
| Model not rendering | Missing model layer registration | Add in ChronoDawnClientFabric.java |
| Crash on entity spawn | Wrong API for version | Check version differences table above |
| NullPointerException on entityRenderer | Missing Fabric renderer registration | Add in ChronoDawnClientFabric.java |
| Entity invisible | Missing renderer registration | Add to EntityRendererRegistry in BOTH Fabric and NeoForge client classes |
| Spawn egg crash | Missing spawn egg color handler | See skill |
| Loot table error (1.21.2) | Using old function | Use with |