declare_mob reference
declare_mob reference
Declare custom mobs with AI goals — captured once at load, ticked by the Go goal selector
declare_mob registers a custom mob type. Like everything in Sulfur’s plugin API it is a
load-time capture: the declaration is stored once when the module body runs; each spawned mob
gets a fresh per-mob AI referencing the shared frozen callables. The Starlark interpreter fires
only inside a running goal’s callbacks — an idle declared mob makes zero Starlark calls per
tick.
Signature
base_type — custom means custom behavior
A declared mob is a custom behavior, not a new wire entity type: base_type resolves to an
existing 26.2 entity, and the spawned mob uses that type’s wire id, hitbox dimensions, and
attribute supplier — so an unmodified vanilla client renders it natively. An unknown base_type
is a loud load error.
Allowed values: pig, cow, sheep, chicken, skeleton, creeper, spider, zombie,
cat, witch, villager, silverfish, wolf, husk, mooshroom, rabbit, enderman,
fox, endermite, turtle, ocelot, pillager, vindicator, evoker, ravager,
iron_golem, sulfur_cube, happy_ghast.
attributes
A dict of attribute overrides applied on top of the base type’s real attribute supplier. Friendly names are aliased to registry keys; any attribute the entity’s supplier registers can be overridden:
Non-string keys and non-numeric values are load errors. An attribute the base type doesn’t have is skipped at spawn (the base type itself was validated at load).
goal(...)
Rules, all enforced at load:
flagsmust be from the closed setMOVE,LOOK,JUMP,TARGET(the exact four vanillaGoal$Flagvalues). Goals claimingTARGETare arbitrated by the independent target selector, exactly as vanilla routesHurtByTargetGoal/NearestAttackableTargetGoal.- A callback goal needs at least one of
tick/start/can_use. - A
kind=goal must supply no callbacks and norequires_update_every_tick— the Go-native goal owns the behavior and its cadence. Its declared flags must match the native goal’s own flag set. avoid_typeis required forkind = "avoid_entity"and forbidden everywhere else.
All callbacks receive the three handles (entity, world, nav) — see
Handles & capabilities. Callbacks run on fresh step-budgeted
threads with error isolation: a failing can_use counts as False, a failing tick is logged
and skipped, and the tick loop survives.
kind= — Go-native goals
Some vanilla goals are the same class for every mob (combat targeting, melee chase, sun-flee, …)
with nothing per-mob to express in a script — and re-expressing their RNG in Starlark would risk
drift. A kind= goal declares only the priority + flags and routes to the verbatim Go port. The
mob’s per-entity RNG stream is shared between native and scripted goals, so both stay in lockstep.
Available kinds include (each a 1:1 port of the named jar goal):
nearest_attackable_target, hurt_by_target, melee_attack, ranged_bow_attack,
spider_attack, leap_at_target, avoid_entity, float, climb_on_powder_snow,
creeper_swell, witch_ranged_attack, restrict_sun, flee_sun, sit, follow_owner,
owner_hurt_by, owner_hurt, angry_player_target, skeleton_target,
enderman_look_for_player, enderman_freeze_when_looked_at, enderman_take_block,
enderman_leave_block, silverfish_merge_stone, silverfish_wake_friends, cube_float,
cube_random_direction, cube_keep_on_jumping, fox_faceplant, fox_stalk, fox_pounce,
fox_seek_shelter, fox_sleep, fox_perch_search, fox_defend_trusted, fox_land_target,
fox_search_items, turtle_goto_water, turtle_go_home, turtle_travel, turtle_lay_egg,
nearest_healable_raider_target, cat_relax_on_owner, cat_lie_on_bed, cat_sit_on_block,
long_distance_patrol, iron_golem_hostile_target, pillager_crossbow_attack,
evoker_casting_spell, evoker_summon_spell, evoker_attack_spell, evoker_wololo_spell.
An unknown kind fails loudly — a hostile with a typo’d combat goal never boots silently disarmed.
Example: a minimal wandering mob
The wandermob test plugin declares a pig-based mob with a single MOVE goal that keeps it walking
east via the real Go pathfinder:
Example: a real vanilla mob (excerpt)
The bundled vanilla_cat plugin is a method-for-method port of the 26.2 jar’s
Cat.registerGoals, mixing scripted passive goals with kind= natives:
How a declared mob ticks
- Spawn builds a fresh entity from the base type (wire id, hitbox, attribute supplier), applies the declared attribute overrides, initializes health to max, and builds a fresh AI whose goals reference the shared frozen callables. Each mob’s RNG is reseeded by entity id so two mobs of the same declaration wander independently.
- Arbitration runs through the same goal selector as Go-native mobs: goals claim their control flags by priority; a scripted goal is not a bypass and not a parallel tick.
- Callbacks fire only for a running goal (
can_useis evaluated when the selector offers the goal its flags;tickonly while it holds them). Per-goal, per-mob mutable state lives in theget_state/set_statescratch. - If the declaration carries
skills, a per-mob skill runner is attached and thespawntrigger fires — see Skills.

