Minecraft horror mob · a terrain-aware monster that stands, sneaks and crawls to chase you through any space · one per world, enforced through the save file · Java · Forge
Architecture
The vanilla spawn engine picks the walker from the biome modifier spawn pool and asks the spawn gate, whose canSpawnHere reads the singleton world data: if a walker already exists anywhere in the save, the spawn is refused. Once alive, TheCreepyWalkerEntity probes the terrain every server tick, switches between stand, sneak and crawl, resizes its hitbox, and ships the stance flags to clients through SynchedEntityData. The entity renderer reads those flags each frame and the hierarchical model samples the matching keyframe clips, including two-step choreography for crawl-to-stand transitions.
Deployed: Mod jar → Forge client + server · Spawn rules → datapack JSON, no code reload needed
Outcomes
- 3 stances · hitbox shrinks 2.7 to 1.7 to 0.7 blocks
- 1 per world · singleton enforced through the save file
- 14 clips · hand-keyframed Blockbench animations
- 0 packets · custom netcode · all sync rides vanilla entity data
Skills demonstrated: server-client state sync · finite-state machines · hitbox + collision handling · event-driven registration · NBT world persistence · game AI goal design
Problems and solutions
One constraint drives everything: a single monster that must feel alive and be able to follow the player through any space.
█ Terrain-aware stance machine
Problem: A 2.7-block-tall monster cannot chase players into 1-block crawlspaces. Vanilla mobs simply path around low openings or get stuck at the entrance, which kills the horror premise of a thing that follows you anywhere.
Every server tick the entity probes the blocks above it and ahead of it (including one step up when facing a wall) and picks a stance. Stand, sneak and crawl each carry their own hitbox: 2.7, 1.7 and 0.7 blocks tall, applied through getDimensions and refreshDimensions.
- Rejected vanilla Pose swimming/crouching: it only covers player-shaped hitboxes and gives no control over when the switch happens
- Accepted up to 8 block-state reads per tick; a 20-tick cooldown after each switch stops stance thrashing at ledges
- Bought a mob that physically fits wherever the player can go, which no pathfinding tweak alone provides
▚ Stance sync without custom packets
Problem: Stance is decided on the server, but animations only exist on the client. Plain Java fields silently desync in multiplayer: each client would guess its own stance and play the wrong clip.
Current stance, previous stance and a numeric stance-change code all live in SynchedEntityData accessors, Minecraft's replicated entity-state channel. The client reads the change code and starts the matching transition clip, then the code resets to zero.
- Rejected a custom network channel: more code, versioning and ordering concerns for data that fits in booleans and one int
- Accepted ten synced accessors on one entity, chattier than a hand-rolled packet but replicated and race-free for free
- Bought correct animations for every observer, including players who walk into render distance mid-transition
▞ Two-step transition choreography
Problem: Snapping straight from crawl to standing looks like teleportation. Transition clips were only authored between adjacent stances, so crawl-to-stand had no animation at all.
Skipping a stance queues two clips: crawl-to-sneak plays first, a waiting flag arms the follow-up, and sneak-to-stand starts the tick the first clip's timeout hits zero. Tick-counted timeouts on the client drive the whole sequence.
- Rejected authoring dedicated skip-level clips: two extra Blockbench animations to build and maintain for a rare case
- Accepted roughly 16 ticks of transition latency before the new stance moves at full speed
- Bought smooth motion through every stance path with only four authored transition clips
▓ One walker per world, enforced twice
Problem: A horror mob loses all menace if the spawn engine produces dozens of copies. Vanilla spawn caps work per-category and per-chunk, so they cannot express 'exactly one, ever, across the whole world'.
A SavedData boolean in the world save is the source of truth: canSpawnHere refuses any spawn while it is set, and Forge join/leave events flip it. Spawn weight 100 in every overworld biome makes the single allowed spawn happen fast.
- Rejected counting loaded entities: unloaded chunks hide the walker and the count would allow a duplicate
- Accepted a whole-world flag over per-dimension bookkeeping, one walker total is the design, not a limitation
- Bought singleton behavior that survives server restarts because the flag persists as NBT
▒ Melee timed to the animation, not the tick
Problem: Vanilla MeleeAttackGoal applies damage the instant the target is in range, but the lunge animation takes several ticks to reach the target. Players get hit by a monster whose arms have not moved yet.
A custom attack goal runs a windup counter: the attacking flag flips early so the client starts the swing, and doHurtTarget only fires when the counter reaches the frame where the arms land.
- Rejected the vanilla goal plus client-side guessing: latency makes the guess wrong exactly when it matters
- Accepted a slower effective attack rate (12-tick cooldown) in exchange for readable, dodgeable swings
- Bought hits that land on the animation frame in all three stances, each with its own attack clip
▧ Scripted spawn and death, no ragdoll
Problem: The vanilla death sequence (red flash, 90-degree keel-over, instant despawn) reads as a game system, not a creature dying, and the standard pop-in spawn wastes the first encounter.
Death starts a keyframed collapse clip while the renderer suppresses the vanilla corpse rotation; the body stays 60 ticks and is unpickable meanwhile. Spawning plays a 108-tick emergence clip during which the server brain holds still.
- Rejected the default LivingEntity death handling: overriding tickDeath and setupRotations was the only way to keep the pose
- Accepted pausing AI for 5 seconds on spawn, a vulnerable window, so the emergence is never cut short
- Bought entrances and exits that stay in character, the point of a horror mod