A complete rewrite of tbaMUD in Rust. What a player sees is the same game: every command, every admin command, all of OLC, DG Scripts, and the on-disk file formats, down to the byte where a byte is observable. Genuine bugs in the original are fixed here and sent upstream rather than reproduced.
If you have run tbaMUD, you already know how to run this.
You need a Rust toolchain, 1.85 or newer (the tree is edition 2024). Get one from rustup.rs on either platform.
git clone https://github.com/Fiztba/RustMUD
cd RustMUD
cargo build --release
The server lands at target/release/circle.
The same, in PowerShell or a terminal of your choice:
git clone https://github.com/Fiztba/RustMUD
cd RustMUD
cargo build --release
The server lands at target\release\circle.exe. Nothing else is needed — no
Visual Studio workload beyond the C++ build tools rustup itself asks for, no
Cygwin, no WSL. Windows is a first-class target here rather than an
afterthought; copyover works on it, which it does not in the C.
./target/release/circle -q -d lib 4000
.\target\release\circle.exe -q -d lib 4000
Then point a MUD client, or plain telnet, at localhost 4000.
Port defaults to 4000 and the data directory to lib, so a bare circle does
the same thing. The server prints its boot to standard error and finishes with
Entering game loop. — that line, not a successful connection, is the honest
readiness signal, because a connection made before it is a descriptor the game
counts.
| flag | effect |
|---|---|
-d <dir> |
data directory (default lib) |
-o <file> |
write the log to a file instead of stderr |
-q |
quick boot: skip the rent check |
-m |
minimized: read text/help/index.mini, stay quiet about missing castle mobs, no rent check |
-c |
syntax-check the world files and exit |
-r |
restrict the game: no new players |
-s |
suppress special procedures |
--help |
the full list |
-c is worth knowing: it boots far enough to parse every world file and then
exits, so it is the cheap way to find out whether a builder's export or a
hand-edited zone will come up before you take the live game down for it.
-m does not load a smaller world; it loads a smaller help file and stops
complaining about a lib/ that has no King's Castle. It is for running against
a cut-down data directory, not for making the stock one lighter.
On a data directory with no players, whoever creates the first character becomes the implementor — level 34, and every skill and command with it. There is no console, no separate admin tool, and no way to grant that level from outside the game. So the order on a fresh install is: start the server, connect yourself, make your character, and only then tell anyone else the address.
If someone else gets there first, the fix is to stop the server, delete their
.plr file and the matching lib/plrfiles/index entry, and start again.
Two things: the binary, and the data directory. lib/ is the game — the
world, the text files, the configuration and every player who has ever logged
in:
lib/ |
holds |
|---|---|
world/ |
the zones: .wld .mob .obj .zon .shp .trg .qst |
text/ |
greeting, motd, imotd, help, news, credits, policies, handbook |
etc/ |
config (see below), time, the ban and invalid-name lists |
plrfiles/ |
one .plr per player, plus index |
plrobjs/ |
rent files — what each player was carrying |
plrvars/ |
per-player DG script variables |
house/ |
player house contents |
misc/ |
boards, mail, quests, ideas/bugs/typos |
The binary is disposable; lib/ is not.
All five are in-game commands, and none of them is kill. Every one of them
runs the same orderly sequence — rent files written, houses written, sockets
closed, mud time saved — so none of them loses a player:
| command | file touched | exit | also |
|---|---|---|---|
shutdown |
— | 0 | |
shutdown die |
.killscript |
0 | |
shutdown pause |
pause |
0 | |
shutdown reboot |
.fastboot |
starts itself again | skips the OLC pending-save flush |
shutdown now |
— | starts itself again | skips the OLC pending-save flush |
The one thing worth knowing before you type it: reboot and now skip
save_all, the queue of world changes builders have made and not yet written
out. Rent, houses and player files are saved either way — it is zone, mob,
object and shop edits sitting in the pending list that go. If builders are
working, give them a minute to save first, or use plain shutdown.
now is not more abrupt than reboot; the two differ only in that reboot
leaves .fastboot behind.
reboot and now bring the game back on their own: once the shutdown
sequence is done the server starts the binary on disk again, with the same
arguments it was given, so a reboot after a rebuild is running the new build.
On Linux the process is replaced in place, as the C's copyover does; on
Windows a new process is started and the old one exits. Players see the
disconnect and can log back in as soon as the world has loaded, typically a
few seconds.
The files in the table are signals for a supervising script, if you run one,
and land beside the data directory — .fastboot next to lib/, not
inside it. No autorun script ships with this, and none is needed for a
reboot; a loop only earns its keep for coming back from a crash. If you do
run one, have it stop when it sees .killscript, wait while pause exists,
and restart on any exit — exit code 52 is what you get when the server tried
to start itself again and could not.
copyover restarts the binary underneath the players without dropping them —
what it is for is picking up a new build mid-session. Sockets are handed to the
new process through copyover.dat, which sits beside the data directory rather
than inside it, and everyone sees a brief pause rather than a disconnect. It
works on Windows as well as Unix.
It is the one restart that keeps players connected, and also the one that will
strand them if the new binary does not start. Have the build in hand and
-c-checked before running it.
Two layers, and the second wins:
lib/etc/config— read at boot. Lines areTAG = value.ceditin-game, which edits the same settings and saves them back to that file, so a change made there survives a reboot.
Anything not in the file takes a built-in default, and a missing file is not an error — the server says so and carries on. Stock ships without one.
DFLT_IP is worth calling out: set it and the game binds only that address, so
DFLT_IP = 127.0.0.1 keeps it on the loopback while you set things up. Leave it
unset and it listens on every interface.
By default the log goes to standard error; -o <file> sends it to a file
instead. It is the only place a good deal of trouble is reported: script errors,
world-file complaints at boot, and every SYSERR. If you run the game
unattended, capture it.
SYSERR is worth grepping for on its own. It means the server found something
it was not willing to guess about, and most of them name the zone, room or
trigger at fault.
The per-topic files under log/ beside the data directory (log/errors,
log/help, log/godcmds, log/olc and the rest) are what the in-game file
command reads. tbaMUD's autorun script fills them by grepping the syslog when
the game exits; this server appends to them itself, as each line is logged,
so they are current while the game is up. Nothing trims them, so rotate or
truncate them yourself if they matter to you.
lib/ — all of it, and while the server is down or at least quiet. The parts
that cannot be reconstructed are plrfiles/, plrobjs/, plrvars/, house/
and misc/: those are the players. world/ and text/ are recoverable from
your builders' sources if you keep them elsewhere, and etc/config is small but
fiddly to redo by hand.
Backing up a running game is safe to attempt — nothing is corrupted by being read — but a player who saves during the copy can leave you a file from one moment and an index from another, so a quiet moment is worth waiting for.
Two things ship in the binary rather than as separate programs:
circle --rebuild-index
rebuilds lib/plrfiles/index from the .plr files and exits. The index is a
derived file: losing it does not lose a single player, but nobody can log in
until it is rebuilt, which makes this the answer to a login failure that looks
catastrophic and is not.
Three things in the index are not derivable from the player files, and the
command says so as it runs: a player marked DELETED, one marked SELFDELETE,
and an immortal marked NOWIZLIST. A rebuild brings all three back as ordinary
entries. If you had any set, re-apply them by editing the index by hand
afterwards.
circle --import-binary-pfiles <file> [--endian little|big] [--dry-run]
converts a pre-3.x CircleMUD binary player file to the ASCII pfiles this server
uses. It assumes a 32-bit CircleMUD 3.0 layout and refuses rather than guessing
if the file does not fit that shape. Run it with --dry-run first; it reports
what it would write and touches nothing.
The game listens on one TCP port and speaks telnet. Nothing else is exposed — there is no HTTP side, no admin socket, no second listener. If you are forwarding a port from a home router, that one port is all that needs to go through, and the server does not need to know its public address.
See LICENSE. RustMUD carries tbaMUD's licence, which is the 1995 CircleMUD licence, and follows tbaMUD if and when that changes.
Worth knowing where those terms come from, because the text is stricter than it looks and stricter than its own authors now intend. DikuMUD relicensed to LGPL in 2020 and CircleMUD followed; the restrictions in the file below — no charging, mandatory credit — are no longer in force upstream of tbaMUD. tbaMUD has not made that change yet, and RustMUD deliberately does not get ahead of it. So the terms here are tbaMUD's, by choice rather than necessity.