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.
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)
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:
- Install MAID —
uv syncin the repository root - Explore the tutorial world —
packages/maid-tutorial-world/shows a complete small game with rooms, NPCs, items, and quests - Create a ContentPack — This is the entry point for all game content
- Define your components — Data structures for your game objects
- Write systems — Logic that processes entities each tick
- 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¶
- Architecture Overview — Full system design
- ECS Guide — Deep dive into entities, components, systems
- Command System — Layered command processing
- Building Commands — In-game world building