mudlib landscape - research into the MUD engine ecosystem this document synthesizes research into existing MUD engines, their architectures, and their design decisions. useful reference when designing our own mudlib. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SECTION 1: the mud family tree ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ dikumud (1990) -------------- the combat MUD ancestor. C, room-based, zone files, tick-based. spawned the entire diku family: merc, rom, circle/tbamud, smaug. binary/text zone files define the world. NPCs are templates that get instantiated via "resets". main loop is tick-based (1/4 second typical). zones have 6 sections: zone declaration, mobiles, objects, rooms, resets, DIL scripts. major descendants: - merc 2.0 was a total rewrite improving code quality, dropping binary formats - circlemud stayed closer to original diku, cleaner but fewer features - smaug was the biggest architectural divergence - reintroduced complexity merc stripped out - tbamud is the modern circle continuation lpc/lpmud (1989) ---------------- the big innovation: driver + mudlib separation. driver is a VM for the LPC language. mudlib is the game framework written in LPC. this means game logic is hot-reloadable interpreted code while the engine is compiled C/C++. drivers: - fluffos (active, mudos fork, C++) - ldmud (C, long history) - dgd (independent implementation) mudlibs: - dead souls (beginner-friendly, batteries-included) - lima (clean modern) - discworld (the famous one) - morgengrauen (oldest german lpmud, since 1992) - realms-mud (rich combat/crafting) - cdlib/genesis (historically influential) LPC is C-like, OOP. blueprint objects serve as templates, clones are instances. objects inherit from parents forming hierarchies. code lives in .c files compiled by the driver at runtime. in-world programming: builders write LPC code that gets compiled by the driver without restart. this is the core appeal of lpc muds. moo/lambdamoo ------------- everything-is-an-object. the entire world is a persistent object database. objects have properties (data) and verbs (methods/commands). verbs are the core interaction - "put ball in box" parses into a verb call with direct object, preposition, indirect object. in-world programming via @program command - attach MOO code to object verbs. persistence: entire DB in RAM, periodic checkpoints to disk. fork statement creates background tasks for async work. modern forks: - toaststunt: multiple inheritance, HTTP, JSON, threading for sqlite/sort/etc - stunt: adds map datatype, RESTful interface mush/mux (tinymud family) ------------------------- objects with attributes (key-value), flags (booleans), locks (permission expressions). two kinds of code: - hardcoded (C, needs restart) - softcoded (mushcode, stored in attributes, hot-reloadable) three parsers: command parser, function parser, locks parser. building with @dig, @create, @link. modern engines -------------- evennia (python/twisted/django) see section 2 for deep dive. most framework-like of modern engines. ranvier (node.js) bundle system where nearly everything is modular. bundles package commands, areas, items, NPCs, channels, behaviors. network layer is swappable. unopinionated core. exventure (elixir) uses erlang/otp process model. kalevala framework underneath. web client, telnet, clustering, prometheus metrics, cross-game gossip chat. coffeemud (java) most feature-complete MUD codebase. supports everything: MSP, MXP, GMCP, OLC, built-in web+mail server. d20-style combat with extensive modifier system. compiled engine + scripted logic pattern: - gomud (go) - dragon-mud (go+lua) - ataxia (rust+lua) taleweave-ai (python) AI NPCs via LLMs, discord integration, stable diffusion visuals. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SECTION 2: evennia deep dive ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ most relevant reference since we're also python. architecture ------------ dual-process: portal (networking) + server (game logic), communicate via AMP protocol. portal handles all protocols: telnet, SSH, SSL, websocket, IRC, discord, grapevine. server runs game logic, django ORM for persistence. twisted-based async, event-driven (not tick-based by default). scripts use ExtendedLoopingCall for timed tasks (tickers). object model ------------ TypedObject base -> ObjectDB (django model) -> typeclasses (DefaultObject, DefaultRoom, DefaultCharacter, DefaultExit) rooms are objects with location=None. exits are objects with db_destination. they dynamically create CmdExit commands. typing "north" runs the exit's command which checks locks and traverses. contents cached via ContentsHandler. attributes: arbitrary pickled data via obj.db.attrname, stored in DB. tags: lightweight string labels with categories, fast for filtering. locks: function-based access control strings like "cmd:perm(Builder) AND attr(canEdit)". command system -------------- commands define: key, aliases, locks, arg_regex, parse(), func(). processing pipeline: gather cmdsets from session/account/character/room/exits -> merge -> match -> execute: at_pre_cmd -> parse -> func -> at_post_cmd cmdset merging uses set theory operations with priorities: - union (default) - intersect - replace - remove priorities: exits=10, account=0, room objects=lower. this is how game states work: push a "fishing" cmdset that overrides "throw", pop it when done fishing. push "dark room" cmdset that replaces default commands. stack them. this is genuinely novel - set-theoretic operations on command availability. session handling ---------------- portal creates PortalSession per connection. server creates ServerSession, links to Account and Character. MULTISESSION_MODE: 0 = exclusive 1 = multiple chars 2 = multiple sessions per char 3 = full multi building -------- @create, @dig, @link, @open, @desc, @set, @tag, @lock, @typeclass, @spawn no in-game python execution in base evennia. prototypes: dict-based object templates with inheritance via prototype_parent. spawn system creates objects from prototypes. persistence ----------- django ORM with any supported DB (sqlite default, postgres for production). what's unique about evennia ---------------------------- - cmdset merging system is genuinely novel - typeclass system lets you change object behavior by swapping its class at runtime - multi-protocol portal means same game is playable via telnet, web, ssh simultaneously - django integration means you get admin panel, REST API, web views for free - most "framework-like" of all MUD engines - it's a toolkit, not a game ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SECTION 3: how they all solve the same problems ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ world representation -------------------- room graph (most common) rooms as nodes, exits as edges, directed graph. diku, lpc, evennia, ranvier all use this. advantage: clear sight lines (same room = visible) disadvantage: no consistent coordinate system grid-based n*m orthogonal grid, each cell ~8 neighbors. used for wilderness areas in some lpc muds. consistent scale but less flexible. continuous/coordinate entities at exact 2D/3D coordinates, descriptions generated from proximity. modern trend. advantages: seamless movement, true spatial relationships challenge: rendering continuous space as text OUR APPROACH (from dreambook) 2D toroidal tile grid with terrain types. viewport centered on player. hybrid - overworld is grid, interiors can be rooms. this is relatively uncommon in the MUD world. command parsing --------------- all MUDs: text in -> parse command name -> extract arguments -> dispatch to handler -> execute -> text out simple split on first space, command + args string complex (moo) parse verb + direct object + preposition + indirect object mush three separate parsers for commands, functions, and locks evennia fuzzy matching against merged cmdset, disambiguation for ambiguous matches ranvier modular command definitions in bundles persistence ----------- flat files diku zone files, moo database dumps. human-readable, version-controllable. ORM/SQL evennia (django), some modern engines. structured, queryable, scalable. in-memory + checkpoint moo model. fast but needs enough RAM. sqlite good for embedded single-server muds. ACID compliant, single file. OUR APPROACH world definitions in yaml/toml files (version controlled), runtime state in memory, player data in sqlite. game loop --------- tick-based (diku) fixed interval (1/4 sec), process all queues each tick. synchronous progression. simple, deterministic. event-driven (evennia, ranvier, exventure) no central loop, react to events. tickers optional for periodic tasks. better for async, scales with concurrency. turn-based advance only on player input. good for single-player IF, awkward for multiplayer. OUR APPROACH (from dreambook) tick-based (10 ticks/sec), drain input queues each tick. timing-based combat needs a real clock. combat ------ diku-style automatic attacks on timer ("pulse_violence" ~3 sec), player can enter special moves. hit/miss/damage calculated from stats, armor, weapon, random rolls. no built-in combat most frameworks (evennia, ranvier) provide none - you build it. coffeemud most complete combat system. d20-style with extensive modifier system. OUR APPROACH (from dreambook) timing-based with telegraph/response windows. PL as health AND damage multiplier. stamina as action economy. diminishing returns on grinding. communication ------------- universal pattern: channels with audience types. say (room) tell (private) yell (area) chat (global) emote (room action) ranvier's architecture: channel = name + audience type + formatter functions. inter-mud: gossip protocol, IMC2, I3. in-world creation/coding ------------------------ lpc write LPC code, driver compiles it live. most powerful but requires learning a language. moo @program attaches code to object verbs. everything modifiable from inside. mush softcode in attributes. simpler language than lpc/moo but capable. evennia no in-game coding. prototypes and building commands only. spawn system for templates. ranvier bundles written externally, loaded on startup. OUR APPROACH (from dreambook) world-building DSL for puzzles/triggers/state machines, writable in in-MUD editor. NOT a full programming language but powerful enough for IF-style content. ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SECTION 4: protocols ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ telnet is the base. extensions negotiate via telnet option subnegotiation. essential --------- NAWS (RFC 1073) terminal window size. critical for viewport sizing. GMCP generic mud communication protocol. JSON over telnet. the modern standard for structured data (hp bars, inventory, room info). MSDP mud server data protocol. typeless variables, arrays, tables. alternative to gmcp. MCCP2/3 compression. zlib over telnet. useful ------ MTTS terminal type standard. client capabilities negotiation. MNES new environment standard. supplements mtts. MSSP server status protocol. for MUD crawlers/listings. MSLP clickable links in terminal. content ------- MXP markup for enhanced text (zuggsoft). MSP sound protocol (zuggsoft). MCMP modernized sound via gmcp (mudlet). MCP moo client protocol. our telnetlib3 already handles ------------------------------- GMCP, MSDP, NAWS, CHARSET, MTTS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SECTION 5: clients and what they support ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ top clients by protocol support -------------------------------- mudlet gmcp, mssp, mcmp, msp, atcp, msdp, mxp, mmp. lua scripting. the dominant modern client. tintin++ gmcp, mccp, mccp3, msdp, mslp, mssp, mtts, mmcp, naws, mnes. terminal-based. blightmud tls, gmcp, msdp, mccp2. terminal-based, rust. mushclient mxp, mccp, mmcp, mtts. windows only. cmud mxp, msp, mcp, mccp, atcp. commercial, windows. axmud mxp, gmcp, msdp, mnes, mtts. perl/gtk3. web clients ----------- mudportal mccp, mxp, msdp, gmcp, atcp, mtts. proxy + web client. grapevine mud listing with web client. xterm.js approach terminal emulator in browser pointing at telnet (our dreambook mentions this). ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SECTION 6: what's missing from our INDEX.txt ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ repos worth adding for study: https://github.com/dworkin/dgd DGD driver - independent LPC driver, not derived from lpmud https://github.com/EmpireMUD/EmpireMUD-2.0-Beta EmpireMUD - persistent world map, circlemud derivative https://github.com/wowpin/dumserver DUM/dumserver - modern python MU* engine https://github.com/irmen/Tale Tale - python MUD/IF framework by irmen https://github.com/fuzzball-muck/fuzzball Fuzzball MUCK - tinymuck server https://github.com/necanthrope/HellCore HellCore - lambdamoo fork https://github.com/luciensadi/AwakeMUD AwakeMUD - community fork, C++ https://github.com/mainframecity/fluxspace fluxspace - elixir MUD engine https://github.com/maldorne/mudos MudOS - historical fork https://github.com/cotillion/cd-gamedriver CD MUD driver - alternative LPC driver https://github.com/DikuMUDOmnibus DikuMUD Omnibus - 100+ diku-related projects https://github.com/mudhistoricalsociety MUD Historical Society - preservation ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ SECTION 7: key takeaways for our design ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ observations relevant to our mudlib: 1. driver/mudlib separation ---------------------------- the driver/mudlib split (lpc model) is the most influential architectural decision in MUD history. we get a version of this with python: the engine is the "driver", game content/logic is the "mudlib". keeping this separation clean matters. 2. composable command sets -------------------------- command sets that compose (evennia's cmdset merging) are a genuinely good idea for session modes. our mode stack (normal/combat/editor/IF) maps directly to pushing/popping cmdsets. 3. room graphs vs grids ----------------------- everyone uses room graphs. our tile grid is unusual. the dreambook's hybrid (grid overworld + room interiors) is the right call - it gives us the spatial consistency of a grid with the narrative flexibility of rooms. 4. in-world creation -------------------- in-world creation is the MOO dream. we don't need a full programming language, but the DSL for IF/puzzles needs to be powerful enough that people can create real content from a telnet client. 5. persistence strategy ----------------------- world in files + player state in sqlite is actually the cleanest approach. most modern engines do something similar. don't fight the pattern. 6. protocol investment ---------------------- gmcp is the protocol to invest in. it's what mudlet and tintin++ both support well, and it's how we'll send structured data (maps, gauges, inventory) to smart clients. 7. tick-based for combat ------------------------ tick-based is right for timing-based combat. event-driven is cleaner for everything else. hybrid is fine - main loop ticks, but non-combat systems can be event-driven. 8. prototype/spawn pattern -------------------------- the prototype/spawn pattern (evennia, diku zone resets) is how everyone handles mob/item templates. define once, instantiate many.