Skip to content

Migrating to MAID from Other MUD Engines

Coming from another MUD engine? You're in the right place. MAID shares many goals with engines you already know — building rich, interactive text worlds — but takes a modern approach with Python 3.12+, async/await, and an Entity Component System (ECS) architecture.

Choose Your Starting Point

Pick the guide that matches your background:

Coming from Guide Key Differences
Evennia From Evennia Typeclasses → ECS, Scripts → Systems, synchronous → async
LDMud From LDMud LPC → Python, object inheritance → composition, mudlib → ContentPacks
Ranvier From Ranvier JavaScript → Python, Behaviors → Systems, BundleManager → ContentPacks

Don't see your engine? The Concept Mapping reference table covers universal MUD concepts and how they map to MAID — it's useful regardless of your background.

What Makes MAID Different

Before diving into engine-specific guides, here are the core architectural ideas that set MAID apart from traditional MUD engines:

Entity Component System (ECS)

Traditional MUD engines use object inheritance — a Sword inherits from Weapon, which inherits from Item, which inherits from Object. MAID uses composition: a sword is an Entity with components attached to it (a DescriptionComponent, an ItemComponent, a HealthComponent if it's breakable, etc.).

# MAID: Compose behavior from components
sword = world.create_entity()
sword.add(DescriptionComponent(name="Iron Sword", long_desc="A sturdy blade."))
sword.add(ItemComponent(item_type="weapon", wear_slots=["main_hand"]))
sword.add(HealthComponent(current=100, maximum=100))

This means no deep inheritance trees, no diamond inheritance problems, and easy mixing of behaviors that weren't anticipated in the original design.

→ Learn more about ECS

Async Throughout

MAID is async from top to bottom. Every I/O operation — network, database, AI provider calls — uses async/await. Systems, commands, and event handlers are all async:

async def cmd_look(ctx: CommandContext) -> bool:
    room = ctx.world.get_entity(room_id)
    # Room description is built from DescriptionComponent fields
    desc = room.try_get(DescriptionComponent)
    if desc:
        await ctx.session.send(f"\n{desc.name}\n{desc.long_desc or ''}")
    return True

Event-Driven Communication

Systems don't call each other directly. They communicate through an EventBus using publish/subscribe:

# Emit an event
await world.events.emit(DamageDealtEvent(
    source_id=attacker.id,
    target_id=target.id,
    damage=25,
    damage_type="physical",
))

# Subscribe to events
world.events.subscribe(DamageDealtEvent, self.handle_damage)

→ Learn more about Events

Content Packs (Plugins)

All game content comes from pluggable ContentPack modules. A content pack registers its systems, commands, events, components, and document schemas:

class MyGamePack(BaseContentPack):
    @property
    def manifest(self) -> ContentPackManifest:
        return ContentPackManifest(name="my-game", version="1.0.0")

    def get_systems(self, world: World) -> list[System]:
        return [CombatSystem(world), WeatherSystem(world)]

    def register_commands(self, registry: LayeredCommandRegistry) -> None:
        registry.register("attack", cmd_attack, pack_name="my-game")

→ Learn more about Content Packs

Common First Steps

Regardless of which engine you're coming from, your first steps in MAID will be:

  1. Install MAID — uv sync in the repository root
  2. Explore the tutorial world — packages/maid-tutorial-world/ shows a complete small game with rooms, NPCs, items, and quests
  3. Create a ContentPack — This is the entry point for all game content
  4. Define your components — Data structures for your game objects
  5. Write systems — Logic that processes entities each tick
  6. Register commands — Player-facing commands in the LayeredCommandRegistry

Try the playground

Use maid dev playground to experiment with MAID's ECS, events, and command patterns in a live sandbox before migrating your game.

Further Reading