← MegaMOO Guide

Getting Started

Create a world, start it, log in.

Creating a world

Install the engine, then ask it for a game. What you get is a directory that belongs to you — the engine is a dependency, not something you copy and edit.

$ pip install megamoo $ megamoo init mygame Created /home/you/mygame 312 verb files, a starter world, and an empty game/ package Wizard login: Wizard / winter-timber-7382 Shown once, and unique to this world -- write it down.
Where to get it

That comes from PyPI. The same build is also on the releases page as a wheel, if you would rather pin an exact version or install without reaching PyPI at all.

Requires Python 3.10 or newer, and nothing else: the engine has no third-party dependencies.

Inside mygame:

world.db # the live world — objects, properties, players verbs/ # the world's code, one file per verb game/ # your own Python, imported by verbs megamoo.toml # what to serve, and where display_screen.txt # the splash players see before the login prompt README.md # a reminder of the layout .gitignore # keeps world.db and logs out of git

The starter world it copies in contains the base object library, an out-of-character lobby, a character generator, one in-character room, and a wizard account. Those 312 verb files are yours — edit them freely. The engine keeps no copy you have to merge against, so upgrading is pip install --upgrade megamoo and nothing you wrote is touched.

Taking a fix that landed after you started

The other side of that bargain used to be that improvements to the starter never reached a world already built from it. A verb fixed upstream stayed broken in your game, forever, because nothing could tell a verb you had edited from one you had simply never touched.

megamoo upgrade answers that. It compares your world against the version it was created from and against the current starter, and sorts every verb, object and property into what it can safely bring across and what is yours:

megamoo upgrade world.db World born from 0.10.0-beta21. 277 can be updated -- untouched here, changed upstream 1 can be added -- new upstream 1 dropped upstream, untouched here Nothing was written. Re-run with --apply to make the 279 safe changes above.

It writes nothing without --apply, backs the world up before it does, and refuses outright while a server has the world open. Anything you changed that also changed upstream is reported as a conflict and left exactly as you wrote it — an upgrade that overwrote your work would have taken your world away from you, which is the one thing it must not do.

Objects know who they are

Every object carries an opaque identity from the moment it is created, so pairing survives both renumbering and renaming: rename a prototype and its fixes still reach it, and the rename is reported as yours. Worlds made before this pair by object number instead, which works until something renumbers and is refused rather than guessed at when it does.

Saying something before you take a player

Drop a newaccount_screen.txt beside display_screen.txt and new players see it before account creation asks them anything — house rules, the tone you expect, a warning. Its last line is the prompt and keeps the cursor, so you write the question yourself; only an answer beginning enter goes on, and anything else ends the connection. megamoo init writes no such file, and a world that ships none is unaffected.

What belongs in version control

verbs/ and game/ are the source of your world and belong in git. world.db is running state — it holds player data and password hashes — and megamoo init writes a .gitignore that keeps it out for you.

Starting a world

From inside your game directory:

$ megamoo --dev Server listening on 0.0.0.0:6770 API server listening on 127.0.0.1:7778

Two ports are reported. The first is where players connect. The second is a private door for tooling on your own machine, which is what lets an AI assistant work inside the running world.

You do not choose either number: the server takes the first free port from 6770 for the game and 7778 for the API, and tells you what it got. If you need a specific port — because you have told people where to connect — ask for it, and you will either get that port or a clear error:

$ megamoo --dev --port 6777

Stop a world with Ctrl-C in its terminal, or from inside the game with @shutdown.

What --dev adds

It picks the database in the current directory, switches on the tooling API with the shared token from ~/.megamoo/token, publishes a discovery file so tooling can find the running world, and turns on verb auto-reload so edits to command files land in the running game. For a public world with real players, name the database instead — megamoo world.db — the same server with none of those four.

Logging in

Two ways in. The browser client needs nothing installed — megamoo --dev starts it and prints the address:

Play in a browser: http://127.0.0.1:8888/

Or connect with any MUD client, or with telnet:

$ telnet localhost 6770

The browser client is the quickest way to see your world and the easiest thing to hand somebody else on the same machine. It is the same world either way — the two are just different doors.

Log in as Wizard, with the password megamoo init printed when it created your game. You arrive in the lobby with full authority over the world.

$ megamoo init mygame
  Created mygame
  ...
  Wizard login:  Wizard  /  winter-timber-7382
  Shown once, and unique to this world -- write it down.
That password is shown once

Every world gets its own, generated when it is created. There is no default to look up and none in the released package — a shipped password hash is a shipped secret. If you lose it before setting your own with setpass, the quickest route is a fresh megamoo init.

Making it yours

Wizard is a placeholder. That account is meant to become your character, so the first thing worth doing is renaming it:

> @rname #100 = Malifax Account #100:Wizard is now Malifax. It logs in as 'Malifax'.

The login name moves with it. From the next connection you are Malifax at the prompt and Wizard no longer works. Set your own password at the same time, with setpass <newpassword>.

Why not @name

@name renames an object, and an account is two things: the object, and the name in the login index that a player types at the prompt. @name changes only the first, which would leave you called by your own name while still logging in as Wizard. @rname changes both, and refuses a name that already belongs to another account rather than quietly taking the login off them.

@name is still the right verb for rooms, objects and exits — everything that nobody logs in as.

Upgrading an existing world

pip install --upgrade megamoo upgrades the engine, and that part is genuinely painless: your verbs and your world are untouched, because the engine keeps no copy of them to merge against. Restart and you are running the new engine.

The starter world is a different matter, and it is worth being plain about. A world is a copy taken the day you ran megamoo init. Later releases improve the starter — a fixed verb, a better default message, a new prototype — and none of that reaches a world that already exists. There is no megamoo upgrade. Bringing a world forward is a job you do by hand, and this is it:

# 1. See what the new starter has that your world does not. # Your world's verbs/ against the template inside the package: $ python3 -c "import moo, pathlib; print(pathlib.Path(moo.__file__).parent / 'templates/starter/verbs')" $ diff -rq <that path> verbs/ # 2. Copy in what is new or changed, and delete what the starter dropped. # Skip any file you have edited yourself -- that one is yours. # 3. Restart. Verb files are loaded from disk on the way up. $ megamoo world.db [autoreload] created #3:@alias from disk [autoreload] updated #17:lock_ from disk
Deleting a file does not delete the command

Step 3 creates and updates, and does nothing else. A command whose file you removed is still in world.db and still reachable, with no source on disk to read — the worst of both. Remove it in-game as well:

> @rmverb #17.lock

Until you do, that command survives every restart.

Those steps cover verbs, which is most of what changes. What they do not cover is world.db itself: a corrected default message on a prototype, a property added to #3, a fixed object in the player pool. Those live in the database, and there is no supported way to merge them into a world that is already running. If you want them, the honest answer today is to make a fresh world and move your own work into it.

Why this is hard, and what changed in 0.10.0-beta13

The obstacle is telling a change apart from a customisation. If your world's copy of a message differs from the new starter's, that is either something you deliberately rewrote or simply the older default — and those want opposite treatment while looking identical.

Answering it needs to know what the world started as. From beta13 a world records that: megamoo init stamps the release it copied into the database, so a future tool can compare three things — the original, yours, and the new one — instead of guessing between two. Worlds created before beta13 carry no stamp and report none, which at least lets a tool know not to guess.

Encrypting connections

Logging in sends a username and then a password, each as an ordinary line of text. On your own machine that does not matter. Once a world is reachable from anywhere else it does: anyone able to watch the traffic reads the password, and people reuse passwords.

Give the world a second, encrypted port:

$ megamoo world.db --tls-port 6771 --tls-cert cert.pem --tls-key key.pem Server listening on 0.0.0.0:6770 TLS listening on 0.0.0.0:6771

Both ports serve the same world. Which one a player is on changes nothing about the game — TLS sits underneath the connection, and everything above it is identical.

Why a second port and not the first

telnet cannot speak TLS, and telnet localhost 6770 is how most people first reach a world — it is the command on this page. Encrypting the main port would break that for everyone in order to help the people who already have a client that supports TLS. So the plain port stays, and the encrypted one is additional. Tell players which to use; MUD clients that support TLS have a checkbox for it.

A misconfiguration stops the server rather than quietly falling back to plaintext. A port with no certificate, a certificate with no port, a file that is not there, or a TLS port equal to the plain one — each of them refuses to start and says which it was. That is deliberate: the one outcome worth ruling out is a world that looks encrypted and is not.

For a certificate, certbot issues free ones from Let's Encrypt and renews them. They expire every ninety days, so renewal wants to be automatic; the server reads the files at startup, so a renewal takes effect on the next restart.

The browser client is a separate question

The web client is served over plain HTTP. If you publish it, put a TLS-terminating proxy in front — Caddy obtains and renews certificates on its own. It matters more than it looks: a browser will not open a ws:// socket from an https:// page, so serving the page over HTTPS without also terminating TLS for the socket leaves you with a client that loads and cannot connect.

Running several worlds at once

Each world is a separate process with its own file, so worlds cannot interfere with each other. Running two or three at once is normal practice: the world you care about, a scratch copy for trying things that might go badly, and perhaps last week's backup for comparison.

$ megamoo --dev world.db & # game 6770, API 7778 $ megamoo --dev scratch.db & # game 6771, API 7779 $ megamoo --dev backup.db & # game 6772, API 7780

The same command each time, with nothing to keep track of — each world takes the next free ports and reports them. Connect to whichever you want by port: telnet localhost 6771 reaches the scratch copy.

This is what makes copying worlds useful in practice. Testing a risky change does not mean backing up and restoring. It means copying world.db, running the copy alongside the original, breaking it freely, and deleting it when you are done.

Which world am I in?

With several running it is worth being deliberate. The port you connected to tells you, and so does the world's name in its own terminal. If you are working with an AI assistant, ask it to confirm which world it is pointed at before you let it change anything — "scratch" and "the real one" are one word apart.