Guides

MUD World History with Timeline documents

Years of organic growth often leave MUDs with conflicting dates, contradictory backstories, and lore too dense for new players to navigate. This guide provides a concrete implementation path to audit existing content, establish canonical authority tiers, and build a database-driven timeline system that integrates directly with quest logic and room descriptions. By treating world history as a structured dataset rather than static text, you eliminate silent contradictions and create retention mechanisms through unlockable historical content.

Initial audit: 6-8 hours; Schema implementation: 4-6 hours; Full integration: 2-3 weeks8 steps
MUD World History with Timeline documents illustration
Placeholder illustration shown while custom artwork is being produced.
1

Inventory Existing Lore Sources and Flag Contradictions

Export all room descriptions, object descriptions, and quest dialogs to text files. Use grep or custom scripts to extract date patterns (e.g., 'Year [0-9]+', 'Age of [A-Z]'). Create a CSV with columns: source_file, line_number, raw_text, extracted_date, event_summary. Sort by extracted_date and highlight rows where the same event has different dates across sources. This becomes your contradiction backlog.

⚠ Common Pitfalls

  • Assuming help files are current
  • Ignoring player-written histories on forums
2

Define Canonical Tiers and Truth Authority

Create a CANON_TIERS.md file in your repository root. Define Tier 1 (Immutable: coded quests, server events), Tier 2 (Flexible: books, NPC rumors), Tier 3 (Apocryphal: player tales). Require builders to tag all new lore with HTML-style comments indicating the tier. Set up a pre-commit hook that rejects commits adding dates to Tier 1 without senior admin approval and a migration script path.

⚠ Common Pitfalls

  • Making everything Tier 1
  • Not documenting the tier in the source files
3

Build the Master Timeline Database Schema

Create a relational table structure to track events with support for your in-game calendar. Include an era_id table defining era boundaries and a world_events table with fields for IG dates, real-world dates, canon tiers, and quest foreign keys. Add a supersedes_event_id nullable field for retcon tracking. Index ig_date and era_id for range queries used by quest validators.

timeline_schema.sql
CREATE TABLE eras (era_id INT PRIMARY KEY, name VARCHAR(100), start_ig_date VARCHAR(50), end_ig_date VARCHAR(50));
CREATE TABLE world_events (
  event_id INT PRIMARY KEY,
  era_id INT,
  ig_date VARCHAR(50) NOT NULL,
  rw_date TIMESTAMP,
  description TEXT,
  canon_tier TINYINT CHECK (canon_tier IN (1,2,3)),
  quest_id INT,
  supersedes_event_id INT NULL,
  FOREIGN KEY (era_id) REFERENCES eras(era_id),
  FOREIGN KEY (supersedes_event_id) REFERENCES world_events(event_id)
);
CREATE INDEX idx_ig_date ON world_events(ig_date);
CREATE INDEX idx_era ON world_events(era_id);

⚠ Common Pitfalls

  • Using vague date strings without IG calendar conversion
  • Forgetting to index the era_id column
4

Map Quest Dependencies to Historical Anchors

Modify quest loading code to accept a required_event_id parameter. When a player attempts to start a quest, verify the referenced event exists in world_events and that the player's character creation date meets temporal logic requirements (e.g., born after the event unless time-travel mechanics apply). Log validation failures to identify quests referencing non-existent history.

quest_validator.c
/* In quest_giver mob prog */
struct quest_data *qst = get_quest_by_vnum(quest_vnum);
struct world_event *evt = get_event_by_id(qst->required_event_id);

if (!evt) {
  mudlog(BRF, LVL_BUILDER, TRUE, "SYSERR: Quest %d references missing event %d", 
         quest_vnum, qst->required_event_id);
  return FALSE;
}

if (ch->creation_ig_date < evt->ig_date && !HAS_ABILITY(ch, ABILITY_TIME_TRAVEL)) {
  send_to_char(ch, "That quest belongs to a later era.\r\n");
  return FALSE;
}

⚠ Common Pitfalls

  • Hard-coding dates in quest files instead of querying the timeline
  • Not handling time-travel or flashback quest variants
5

Implement Contextual Lore Delivery Systems

Replace static room descriptions with template strings that query the timeline database. Use room scripts to check event completion status and alter descriptions accordingly (e.g., 'The statue %s' becomes 'stands tall' pre-siege and 'lies in rubble' post-siege). Update object resets to load different book contents based on the current era, pulling text from the database rather than static files.

room_scripts.c
char buf[MAX_STRING_LENGTH];
const char *castle_desc = is_event_complete("siege_of_thornwall") 
  ? "The castle lies in ruins, its walls breached."
  : "The castle stands tall, banners flying.";

sprintf(buf, "The statue %s.\r\n%s", 
        is_event_complete("statue_destroyed") ? "is missing its head" : "gleams in the sun",
        castle_desc);

send_to_char(ch, buf);

⚠ Common Pitfalls

  • Static descriptions that break immersion after world-changing events
  • Overloading players with exposition instead of environmental storytelling
6

Establish Editorial Review Workflow for New Content

Create a lore-review branch protection rule in your version control. Require builders to submit a Lore Impact Statement (lore_impact.md) listing: (1) New historical events introduced, (2) Existing events referenced with IDs, (3) Proposed canon tier. The lore team runs SELECT queries against the master timeline to check for date conflicts before approving merge requests. Reject patches that introduce Tier 1 events without database migration scripts.

⚠ Common Pitfalls

  • Skipping review for 'small' updates
  • Not versioning the lore bible alongside code
7

Generate Player-Facing Era Primers and Retention Hooks

Build a web endpoint or in-game command that exports sanitized Tier 2 lore from the database based on player achievements. Structure this as unlockable content: entries remain [REDACTED] until the player completes specific quests or visits key locations. This creates retention loops as players seek to fill gaps in their personal timeline view while preventing information overload at character creation.

player_lore_state.json
{
  "player_id": 1234,
  "unlocked_eras": ["third_age"],
  "entries": [
    {
      "event_id": 42,
      "date": "Year 44 of the Third Age",
      "text": "The Shattering began...",
      "unlock_condition": "visit_crystal_spire",
      "is_unlocked": false
    }
  ]
}

⚠ Common Pitfalls

  • Dumping the entire timeline at character creation
  • Revealing Tier 3 apocrypha as fact
8

Document Retcon Protocols and Migration Scripts

When correcting contradictions, never UPDATE the master timeline silently. Create a migration script that INSERTs a new row with supersedes_event_id pointing to the old one, archives the original to an events_archive table, and posts a global in-game message announcing the historical correction. Provide affected players a compensation mechanism if quest logic broke due to the date change, preserving trust through transparency.

retcon_migration.sql
-- Retcon Protocol: Move old event to archive, create corrected version
INSERT INTO events_archive 
SELECT *, NOW() as archived_at, 'Corrected calendar drift' as reason 
FROM world_events 
WHERE event_id = 42;

INSERT INTO world_events (event_id, era_id, ig_date, description, canon_tier, supersedes_event_id)
VALUES (1001, 3, 'Year 490', 'Battle of Thornwall', 1, 42);

UPDATE quests SET required_event_id = 1001 WHERE required_event_id = 42;

⚠ Common Pitfalls

  • Silent edits that break player trust
  • Losing the original data that veteran players remember

What you built

With these systems, your MUD's history becomes a living database that evolves without contradictions. Players encounter lore that respects their personal timeline progress, builders have clear constraints for new content, and administrators can fix errors transparently. The result is a world that feels authentically ancient yet mechanically coherent, supporting long-term retention through structured discovery rather than overwhelming exposition.