Skip to content

feat(ecs): AI system — priority goal list and goal scheduler #119

@iverly

Description

@iverly

Context

The server runtime architecture spec (section 7) defines an AI system based on priority goal lists, matching the vanilla Minecraft MobGoal pattern. The ECS foundation is in place (basalt-ecs with system scheduler, phase ordering, component access declarations), and the physics plugin demonstrates the pattern. The AI system is the next simulation layer needed to enable mob behavior.

Spec reference: docs/superpowers/specs/2026-04-15-server-runtime-architecture-design.md sections 7.1 and 7.2.

Problem

  1. There is no AI system — entities (mobs, NPCs) have no autonomous behavior.
  2. No Goal trait exists for defining reusable behavior primitives (wander, look at player, flee, attack).
  3. No GoalScheduler exists to evaluate and switch between goals by priority each AI tick.
  4. No core components (AiGoals, Health, EntityType) are registered for AI-driven entities.

Proposed approach

7.1 — Goal trait

Define a Goal trait in basalt-ecs (or a new basalt-ai crate if preferred):

trait Goal: Send + Sync {
    fn can_start(&self, entity: EntityId, ecs: &Ecs) -> bool;
    fn start(&mut self, entity: EntityId, ecs: &mut Ecs);
    fn tick(&mut self, entity: EntityId, ecs: &mut Ecs);
    fn can_continue(&self, entity: EntityId, ecs: &Ecs) -> bool;
    fn stop(&mut self, entity: EntityId, ecs: &mut Ecs);
}

7.2 — GoalScheduler

An ECS system running in the Simulate phase at every(5) (4 effective TPS at 20 TPS global):

  1. For each entity with AiGoals, iterate goals from highest to lowest priority.
  2. If a higher-priority goal can start, interrupt the current goal (stop()) and start the new one.
  3. If the current goal can no longer continue, stop it and evaluate the next candidate.
  4. Call tick() on the active goal.

Core goals (provided by basalt)

  • RandomWalkGoal — picks a random nearby position and walks toward it.
  • LookAtPlayerGoal — rotates to face the nearest player within range.
  • PanicGoal — runs in a random direction when damaged.
  • FloatGoal — prevents drowning by swimming to the surface.

Core components to add

  • AiGoalsgoals: Vec<PrioritizedGoal> (priority + boxed goal)
  • Healthcurrent: f32, max: f32
  • EntityTypetype_id: u32 (Minecraft entity type ID)

Plugin extensibility

Plugins add custom goals and reorder goal lists per mob type via registrar.system(). Survival plugins add combat, farming, breeding, etc.

Scope

  • crates/basalt-ecs/ — new components (AiGoals, Health, EntityType), Goal trait, GoalScheduler system
  • crates/basalt-server/src/game/tick.rs — register AI system in game loop
  • Possibly a new basalt-ai plugin under plugins/ for the core goals

Benefits

  • Enables autonomous mob behavior (zombies, animals, villagers)
  • Provides the foundation for all PvE gameplay
  • Plugin-extensible: server operators can customize mob AI per server type
  • Matches vanilla Minecraft patterns — familiar to plugin developers

Non-goals

  • Mob spawning rules and spawn rate configuration (separate feature)
  • Entity rendering/model system (client-side concern)
  • Combat mechanics (PvP, damage, knockback) — separate issue
  • Pathfinding algorithm itself — covered by a dedicated issue

Metadata

Metadata

Assignees

No one assigned

    Labels

    ecsRelative to basalt-ecs crateenhancementNew feature or requestserverRelative to basalt-server crate

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions