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 MAIDuv sync in the repository root
  2. Explore the tutorial worldpackages/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