Skip to content

Concept Mapping: MUD Engines → MAID

This is a quick-reference table mapping common MUD engine concepts to their MAID equivalents. Use it as a cheat sheet while reading the engine-specific migration guides or exploring the MAID codebase.

Core Concepts

Traditional MUD Concept Evennia LDMud Ranvier MAID Equivalent
Game object DefaultObject LPC object GameEntity Entity + Components
Object type/class Typeclass Blueprint / inherit Entity class Components (composition)
Room DefaultRoom Room object Room Entity with PositionComponent + ExtendedRoomComponent
Character DefaultCharacter Player object Player Entity with PlayerComponent + HealthComponent + ...
NPC DefaultCharacter NPC object Npc Entity with NPCComponent + HealthComponent + ...
Item/Object DefaultObject Item object Item Entity with relevant components
Exit DefaultExit Exit object Exit definition Entity with ExitMetadataComponent, registered in room
Container Object with contents Container inherit N/A Entity with InventoryComponent

Architecture

Concept Evennia LDMud Ranvier MAID
Language Python 3.11+ LPC (C-like) JavaScript (Node.js) Python 3.12+
Paradigm OOP inheritance OOP inheritance OOP + Behaviors ECS (composition)
Concurrency Twisted (reactor) Single-threaded + callouts Node.js event loop asyncio (async/await)
Game loop Twisted reactor Heart beat / call_out GameLoop GameEngine tick loop
Tick rate Script intervals Heartbeat (2s default) Configurable MAID_GAME__TICK_RATE (default 4/sec)

Data & Persistence

Concept Evennia LDMud Ranvier MAID
Database Django ORM (Postgres/SQLite) Save files / binary JSON files DocumentStore (Postgres JSONB / in-memory)
Object attributes .db / .ndb Object properties Entity attributes Component fields
Persistent data obj.db.key = val save_object() JSON serialization DocumentStore + EntityPersistenceManager
Queries Django QuerySet find_object() Manager lookups EntityManager.with_components() / with_tag()
Migrations Django migrations Manual Manual Per-namespace migration framework

Commands

Concept Evennia LDMud Ranvier MAID
Command definition Command class add_action() Command class CommandDefinition + async handler
Command set CmdSet on objects Actions on objects Command bundle LayeredCommandRegistry per pack
Command resolution CmdSet merge + priority Verb matching Input events Priority-based layered resolution
Argument parsing self.args (manual) sscanf() / manual Manual @arguments / @pattern decorators
Permissions Lock strings query_*() efuns requiredRole Lock expressions: perm(admin) OR level(20)
Command context self.caller, self.obj this_player() Command args CommandContext (session, player_id, world, args)
Help system Docstring-based Manual Manual description + usage on CommandDefinition

Events & Hooks

Concept Evennia LDMud Ranvier MAID
Event system Signals / hooks add_action() + applies EventManager EventBus (pub/sub)
Object hooks at_object_creation() etc. create(), init() Event listeners EventBus.subscribe(EntityCreatedEvent, ...)
Timed events Scripts (DefaultScript) call_out() / heartbeat setTimeout System.update(delta) each tick
Cross-system comm Signals / direct calls Function calls Events await EventBus.emit()
Movement hooks at_before_move() etc. exit() / init() Room events RoomEnterEvent / RoomLeaveEvent
Combat hooks Custom attack() apply Damage events DamageDealtEvent, CombatStartEvent, EntityDeathEvent

Scripting & Logic

Concept Evennia LDMud Ranvier MAID
Timed logic DefaultScript call_out() setTimeout / game loop System with tick-based timing
Repeating logic Script with repeat=True Heartbeat setInterval System.update(delta) (runs every tick)
Game systems Contrib modules Mudlib modules Bundles System classes in ContentPack
AI/behavior Custom scripts Custom LPC Behaviors System + NPCComponent + AI providers

Plugins & Modularity

Concept Evennia LDMud Ranvier MAID
Plugin system Contribs / custom apps Mudlib packages Bundles ContentPack protocol
Plugin manifest N/A N/A manifest.yml ContentPackManifest
Dependencies Python imports #include Bundle deps get_dependencies() → pack names
Loading Django app loading Preloading BundleManager ContentPackLoader (dependency-ordered)
Hot reload @reload N/A N/A @reload pack/system/module
Command override CmdSet priority Shadowing N/A LayeredCommandRegistry priority

Networking

Concept Evennia LDMud Ranvier MAID
Protocols Telnet, SSH, Web Telnet Telnet, WebSocket Telnet (GMCP, MXP, MCCP2), WebSocket
Session ServerSession Interactive object PlayerSocket Session protocol (abstract)
Output msg() tell_object() socket.write() await session.send()
Web interface Django web client N/A Express.js REST API + React admin + player client

Administration

Concept Evennia LDMud Ranvier MAID
Admin interface Django admin Wizard commands CLI Web admin UI + @ commands + CLI
Access levels Lock system Wizard levels Roles AccessLevel enum (PLAYER → IMPLEMENTOR)
World building @create, @dig LPC coding Area files @create, @dig, @set + YAML loader
Configuration settings.py Config files ranvier.json Environment variables (MAID_ prefix)
Logging Django logging Debug log Bunyan AuditLogger + observability stack

Quick Translation Examples

Creating a Room

room = create_object(
    "typeclasses.rooms.Room",
    key="Town Square",
)
room.db.desc = "A bustling town square."
object room = clone_object("/std/room");
room->set_short("Town Square");
room->set_long("A bustling town square.");
# areas/town/rooms.yml
- id: town-square
  title: "Town Square"
  description: "A bustling town square."
room = world.create_entity()
room.add(DescriptionComponent(
    name="Town Square",
    long_desc="A bustling town square.",
))
room.add_tag("room")
world.register_room(room.id, {"name": "Town Square"})

Handling a Command

class CmdLook(Command):
    key = "look"
    def func(self):
        self.caller.msg(self.caller.location.return_appearance(self.caller))
void init() {
    add_action("do_look", "look");
}
int do_look(string arg) {
    write(this_player()->query_env()->long());
    return 1;
}
module.exports = {
  command: state => (args, player) => {
    const room = player.room;
    say(player, room.description);
  }
};
async def cmd_look(ctx: CommandContext) -> bool:
    pos = ctx.world.get_entity(ctx.player_id).try_get(PositionComponent)
    if not pos:
        return False
    room = ctx.world.get_entity(pos.room_id)
    # Room description rendering is handled by the look command in
    # maid_stdlib.commands.basic — it reads DescriptionComponent fields
    room_name = room.try_get(DescriptionComponent)
    if room_name:
        await ctx.session.send(f"\n{room_name.name}\n{room_name.long_desc or ''}")
    return True

Subscribing to Events

# In a Script or Typeclass
def at_object_receive(self, moved_obj, source_location, **kwargs):
    # Object entered this location
    pass
void init() {
    // Called when an object enters the environment
}
room.on('playerEnter', player => {
  say(player, 'Welcome!');
});
# In a System, subscribe to events in startup()
async def startup(self) -> None:
    self.events.subscribe(RoomEnterEvent, self.on_enter)

async def on_enter(self, event: RoomEnterEvent) -> None:
    entity = self.world.get_entity(event.entity_id)
    if entity and entity.has(PlayerComponent):
        # Emit a message event for the messaging system to deliver
        await self.events.emit(MessageEvent(
            sender_id=event.entity_id,
            target_ids=[event.entity_id],
            channel="system",
            message="Welcome!",
        ))

Further Reading