=============================
content loading pipeline
=============================

the content pipeline converts static TOML definitions into runtime Python objects
at startup. all loading happens once before accepting player connections — no hot-reload.

startup sequence
================

from ``server.py run_server()``, in order::

    1. init_db() → database
    2. init_game_time() → game clock
    3. World() + terrain → procedural terrain
    4. Zone (overworld) → overworld from terrain
    5. load_zones() → content zones (hub, tavern, etc) from content/zones/
    6. load_commands() → TOML commands from content/commands/
    7. load_help_topics() → help topics from content/help/
    8. register_combat_commands() → combat moves from content/combat/
    9. load_mob_templates() → mob definitions from content/mobs/
    10. load_thing_templates() → thing/container definitions from content/things/
    11. load_recipes() → crafting recipes from content/recipes/
    12. load_all_dialogues() → NPC dialogue trees from content/dialogue/

common loader pattern
=====================

every content type follows the same pattern::

    1. load_X(path) → parse single TOML file into dataclass
    2. load_Xs(directory) → iterate .toml files, call load_X each, return dict
    3. At startup: global_registry.update(load_Xs(dir))

the loader functions are synchronous. they read files, parse TOML, validate data,
and populate module-level registries. errors during load are logged but don't
crash the server (graceful degradation).

content directory structure
===========================

all content lives under ``content/``::

    content/commands/      → command definitions (name, aliases, handler or message)
    content/combat/        → combat moves (attacks, defenses, variants)
    content/things/        → thing/container templates
    content/mobs/          → mob templates with loot and schedules
    content/zones/         → zone definitions (terrain grids, portals, spawns, ambient)
    content/recipes/       → crafting recipes
    content/dialogue/      → NPC dialogue trees
    content/help/          → help topic files
    content/stories/       → z-machine game files (not TOML, loaded separately)

each subdirectory contains multiple ``.toml`` files. the loader iterates them
all and merges results into a single registry.

global registries
=================

module-level dicts, populated at startup, read-only during play::

    commands/__init__.py:  _registry dict
    combat/commands.py:    combat_moves dict
    things.py:             thing_templates dict
    mobs.py:               mob_templates dict, mobs list
    zones.py:              zone_registry dict
    crafting.py:           recipes dict
    commands/help.py:      _help_topics dict
    commands/talk.py:      dialogue_trees dict

these dicts are never mutated after startup. new content requires a server restart.

handler resolution
==================

commands and thing verbs can reference Python functions via ``"module:function"``
strings. at load time, the loader uses ``importlib`` to resolve the string into
a callable.

commands can also have inline message text instead of a handler. in that case,
the loader wraps the message in a simple async handler that sends the text.

if handler resolution fails, the loader logs a warning and continues. the command
is registered but won't work (graceful degradation).

template vs instance
====================

templates are immutable definitions stored as dataclasses. spawning creates mutable
runtime objects (``Thing``, ``Mob``, etc).

templates stay in the registry forever. instances are created and destroyed during
play. when a mob dies, the template remains — you can spawn another.

this separation keeps content definitions clean and allows multiple instances of
the same template (ten wolves from one ``wolf`` template).

validation
==========

different content types validate at different levels:

dialogue trees
--------------

validate all node references at load time. if a choice points to a nonexistent node,
``load_dialogue()`` raises ``ValueError`` and the server won't start.

combat moves
------------

validate ``countered_by`` references. if a move references a nonexistent counter,
the loader logs a warning but continues. the move is still usable, just without
that counter.

zone portals
------------

parse ``"zone_name:x,y"`` target format at load time. if the format is invalid,
the portal won't work. if the zone doesn't exist yet (load order), it's fine —
the portal stores the string and runtime lookup happens when someone uses it.

code
====

startup sequence::

    src/mudlib/server.py           (run_server function)

loaders::

    src/mudlib/commands/__init__.py       (load_command, load_commands)
    src/mudlib/combat/commands.py         (load_combat_move, register_combat_commands)
    src/mudlib/things.py                  (load_thing_templates)
    src/mudlib/mobs.py                    (load_mob_template, load_mob_templates)
    src/mudlib/zones.py                   (load_zone, load_zones)
    src/mudlib/crafting.py                (load_recipes)
    src/mudlib/commands/help.py           (load_help_topic, load_help_topics)
    src/mudlib/commands/talk.py           (load_dialogue, load_all_dialogues)

content directories::

    content/commands/
    content/combat/
    content/things/
    content/mobs/
    content/zones/
    content/recipes/
    content/dialogue/
    content/help/
    content/stories/
