Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

OrangeTide/boris

Open more actions menu

Repository files navigation

Boris MUD

A MUD code base in the style of the last millennium, written in the new millennium.

Table of Contents

Introduction

Cyberspace. A consensual hallucination experienced daily by billions of legitimate operators, in every nation, by children being taught mathematical concepts... A graphic representation of data abstracted from the banks of every computer in the human system. Unthinkable complexity. Lines of light ranged in the nonspace of the mind, clusters and constellations of data. Like city lights, receding. -- William Gibson

Boris MUD is a text-based virtual reality that allows multiple people to engage in roleplaying, adventuring, and story-telling.

Features

  • MTH (Mud Telopt Handler) - standardized handling of TELNET protocol.
  • LMDB back-end database for objects and user accounts.
  • Room navigation: look, go, enter, and direction aliases (north/n, south/s, etc.).
  • SSE-based web client with output buffering and multipart framing.
  • ColdFire machine programs -- objects can have ColdFire V4e programs (ELF binaries) attached that post messages to the game world via hypercalls.
  • TODO ability to host multiple independent worlds from a single server.
  • TODO On-Line Creation: interactive wizard provides menu-based building.

Build Requirements

  • GNU Make 4.2.1 (or later)
  • GCC or Clang
  • zlib development headers

Optional (for rebuilding machine programs in sdk/machine/):

  • m68k-linux-gnu-gcc cross-compiler (apt install gcc-m68k-linux-gnu on Debian/Ubuntu)

Linux

Debian / Ubuntu:

sudo apt update
sudo apt install build-essential zlib1g-dev

Building

Check out from source

git clone --recurse-submodules https://github.com/OrangeTide/boris
cd boris

Build

Build using all available CPU cores:

make -j$(nproc)

To use Clang instead of GCC:

make -j$(nproc) USE_CLANG=1

To specify a cross-compiler directly:

make -j$(nproc) CC=arm-linux-gnueabihf-gcc

Build output binaries are boris, mkpass, and muddb-tool under _out/<triplet>/bin/ (e.g. _out/x86_64-linux-gnu/bin/). Object files go to _build/<triplet>/, so cross-compiled objects and binaries don't clobber native ones. make install stages the binaries and web client into bin/ for packaging and deployment.

Install Web Client

To install the web client (to bin/www/ by default):

make install

Cross-Compiling for Raspberry Pi

64-bit (aarch64)

Enable the arm64 architecture and install the cross toolchain with libraries:

sudo dpkg --add-architecture arm64
sudo apt update
sudo apt install gcc-aarch64-linux-gnu zlib1g-dev:arm64

Note: On Ubuntu and derivatives, the default apt mirrors only carry amd64/i386 packages. arm64 and armhf packages are served from ports.ubuntu.com. If apt update fails with 404 errors for arm64, you need to add a ports source and pin your existing sources to amd64 only. Create /etc/apt/sources.list.d/arm64-cross.list:

deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble main universe
deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports noble-updates main universe

Then add [arch=amd64,i386] to each deb line in your existing /etc/apt/sources.list (or files under sources.list.d/) so they stop trying to fetch arm64 from the wrong mirror. Then run sudo apt update again.

Build with the cross-compiler:

make -j$(nproc) CC=aarch64-linux-gnu-gcc

For a fully static binary (no runtime dependencies on the Pi):

make -j$(nproc) CC=aarch64-linux-gnu-gcc LDFLAGS="-static"

32-bit (armhf)

Follow the same ports.ubuntu.com setup as above (substituting [arch=armhf] for [arch=arm64], or [arch=arm64,armhf] if you want both) if needed, then:

sudo dpkg --add-architecture armhf
sudo apt update
sudo apt install gcc-arm-linux-gnueabihf zlib1g-dev:armhf
make -j$(nproc) CC=arm-linux-gnueabihf-gcc

Copy bin/boris, bin/mkpass, boris.cfg, and the data/ directory to the Raspberry Pi to run.

Cleaning

make clean                 # Remove object files and dependency files
make distclean             # Remove objects, binaries, and installed web assets

Releases

Build a release tarball containing stripped binaries, web client, sample config, and game data. Pass all build flags in the same command -- release chains through the full build so CC, LDFLAGS, and RELEASE_ARCH must all be present:

make -j$(nproc) release                         # native x86-64
make -j$(nproc) release CC=aarch64-linux-gnu-gcc LDFLAGS="-static" RELEASE_ARCH=linux-arm64
make -j$(nproc) release CC=arm-linux-gnueabihf-gcc LDFLAGS="-static" RELEASE_ARCH=linux-arm32

Output is a file like boris-0.7-linux-arm64.tar.xz in the project root.

Deploying

Deploy a release to a remote server via SSH. Include the same build flags as you would for make release:

make -j$(nproc) deploy DEPLOY_DEST=jon@myserver.com:mud
make -j$(nproc) deploy CC=arm-linux-gnueabihf-gcc LDFLAGS="-static" RELEASE_ARCH=linux-arm32 DEPLOY_DEST=jon@myserver.com:mud

This builds a release tarball and uploads it to the remote host. The deploy script uses versioned releases with symlinks:

~/mud/
  releases/boris-0.7-linux-arm64/   # each release unpacked here
  shared/boris.cfg                  # persistent config (survives upgrades)
  shared/data/chars/                # persistent game data
  shared/data/users/
  current -> releases/...           # symlink to active release

On first deploy, boris.cfg is copied to shared/ for editing. Game data directories (chars/, users/) are symlinked from each release into shared/ so they persist across upgrades.

You can also deploy directly without the Makefile:

./scripts/deploy.sh jon@myserver.com:mud [tarball]

Local Configuration (.env)

Per-checkout settings go in a .env file (not committed to git). The Makefile reads it automatically. Copy the example to get started:

cp doc/env.example .env

Available settings:

Variable Description Example
DEPLOY_DEST Deploy target for make deploy (user@host[:dir]) jon@myserver.com:mud
RELEASE_ARCH Override release architecture label linux-arm32
CC C compiler (useful for cross-compilation) arm-linux-gnueabihf-gcc
LDFLAGS Linker flags -static

Database Tools

Boris stores game data (rooms, characters, users) in an LMDB database (data/muddb/) by default. The muddb-tool utility exports and imports this data as plain-text JSON files organized by domain. World objects can optionally be moved to a content-addressed store instead, see Object Store Backends.

Export

Dump the entire database to a directory tree:

./bin/muddb-tool export data/muddb dump/

This creates dump/<domain>/<key>.json for every object. To export a single domain:

./bin/muddb-tool export data/muddb dump/ rooms

Import

Load JSON files into the database:

./bin/muddb-tool import data/muddb dump/

To import only specific domains:

./bin/muddb-tool import data/muddb dump/ rooms chars

Initializing a New MUD

A sample/ directory ships with the source (and binary releases) containing a starter world with a handful of connected rooms. To set up a fresh database:

mkdir -p data/muddb
./bin/muddb-tool import data/muddb sample/

Then start the server normally with ./bin/boris.

File Layout

The export/import format is a flat directory hierarchy:

sample/
  rooms/
    town-square.json    # {"id":"town-square","name.short":"Town Square", ...}
    east-gates.json
    ...

Each .json file contains a single JSON object. The filename (minus .json) is the database key.

Object Store Backends

World objects (the objs domain: rooms, exits, machine programs) can be served by one of two backends, selected with database.backend in boris.cfg:

  • muddb (default): the LMDB database at data/muddb/, described above.
  • cas: a content-addressed depot (smolvfs). Every commit is an immutable snapshot, so the depot keeps a history of world states.

User accounts, characters, and invites always live in LMDB regardless of this setting, so data/muddb/ is required either way.

Configuration keys:

Setting Default Description
database.backend muddb Object store backend: muddb or cas
database.cas.path data/casdb Depot directory for the cas backend
database.cas.ref world Named snapshot reference inside the depot
database.cas.retain 0 Keep the last N snapshots; 0 keeps all history
database.cas.commit_seconds 60 Interval between automatic snapshot commits

To switch an existing MUD to the cas backend, stop the server, import the objects into a depot, and enable the backend in boris.cfg:

./bin/muddb-tool to-cas data/muddb data/casdb
database.backend	=	cas
database.cas.path	=	data/casdb
database.cas.retain	=	8

Notes for operating the cas backend:

  • Object edits are saved to the depot as they happen, but only become part of a snapshot at the next commit. Commits run every database.cas.commit_seconds and once more at clean shutdown. After a crash, the world reverts to the last committed snapshot, losing at most one commit interval of edits.
  • With database.cas.retain = 0 the depot grows without bound, because every snapshot is kept forever. Set it to a small number (for example 8) to prune old snapshots automatically; the depot then converges to a bounded size.
  • There is currently no cas-to-muddb export. Keep your LMDB data (or a JSON export of it) until you are confident in the cutover, and back up the depot as described below.

Backup and Restore

The server runs unattended on hobby hardware, so regular backups are important. Two approaches:

JSON export (portable, human-readable)

./bin/muddb-tool export data/muddb backup/

This creates one .json file per record. The export can be inspected, edited, diffed, or version-controlled. To restore from a JSON export, stop the server, then:

rm -rf data/muddb
mkdir -p data/muddb
./bin/muddb-tool import data/muddb backup/

LMDB file copy with mdb_copy (fast, compact)

mdb_copy creates a consistent snapshot even while the server is running. Install it from the system LMDB package (apt install lmdb-utils on Debian/Ubuntu).

mdb_copy data/muddb /path/to/backup/muddb

Add -c to compact free pages during copy:

mdb_copy -c data/muddb /path/to/backup/muddb

To restore, stop the server and replace the database directory:

rm -rf data/muddb
cp -r /path/to/backup/muddb data/muddb

CAS depot copy (cas backend only)

Both methods above cover only the LMDB database. When database.backend = cas, world objects live in the depot directory and must be backed up separately with a plain file copy:

cp -a data/casdb /path/to/backup/casdb

Depot files are immutable once written and the snapshot reference is updated by atomic rename, so copying while the server runs is safe. The only files that can disappear during a live copy are old snapshots pruned by database.cas.retain. To restore, stop the server and copy the depot back into place.

Automated daily backup (cron)

Back up with muddb-tool, keep 14 days of snapshots. Adjust MUD_DIR to match your installation:

MUD_DIR=/home/mud/boris

# daily database export, 04:00, keep 14 days
0 4 * * * d=$MUD_DIR/backups/$(date +\%F); mkdir -p "$d" && $MUD_DIR/bin/muddb-tool export $MUD_DIR/data/muddb "$d" && find $MUD_DIR/backups -maxdepth 1 -mindepth 1 -mtime +14 -exec rm -rf {} +

Or with mdb_copy for a faster binary snapshot:

MUD_DIR=/home/mud/boris

# daily LMDB snapshot, 04:00, keep 14 days
0 4 * * * d=$MUD_DIR/backups/muddb-$(date +\%F); mkdir -p "$d" && mdb_copy -c $MUD_DIR/data/muddb "$d" && find $MUD_DIR/backups -maxdepth 1 -name 'muddb-*' -mtime +14 -exec rm -rf {} +

Running the Server

Configure

Edit configuration file (boris.cfg) with your preferred MUD port (server.port, default 4444) and web port (webserver.port, default 8080).

Starting for the first time

Initialize the database with the sample world (optional but recommended):

mkdir -p data/muddb
./bin/muddb-tool import data/muddb sample/

Start the server:

./bin/boris

Login and create your account.

When running the server the web client will be hosted at http://localhost:8080 (or your configured webserver.port).

Stopping the server

Press Ctrl+C to shut down the server gracefully.

TODO: provide instructions on how to manually set administrator privileges on an account

Troubleshooting

CRITICAL:telnetserver:telnet server error: could not bind socket (Address already in use)

Another process is already listening on the configured port. Find it with:

lsof -i tcp:4444

Either stop the other process or change server.port in boris.cfg to an unused port.

ERROR:muddb:mdb_env_open(data/muddb): No such file or directory

The LMDB database directory does not exist. Create it:

mkdir -p data/muddb

Clients cannot connect (firewall blocking ports)

If ufw is active on the server, open the telnet and web ports:

sudo ufw allow 4444/tcp
sudo ufw allow 8080/tcp

Replace the port numbers if you changed server.port or webserver.port in boris.cfg. Check status with sudo ufw status.

Machine Programs

Objects can have ColdFire V4e programs attached to them. Pre-built ELF binaries ship in data/machine/ and are loaded automatically when the object enters the cache.

Source code for machine programs lives in sdk/machine/. To rebuild:

cd sdk/machine
make

This cross-compiles with m68k-linux-gnu-gcc and writes ELF files to data/machine/. The SDK header sdk/machine/include/machine_hc.h provides inline wrappers for all available hypercalls (sleep, message post, exit, etc.).

See DEV.md for the machine program architecture details.

Development

See DEV.md for architecture, build system internals, code patterns, and contributor information.

Support

Please open an issue for support.

Contributing

Please contribute using Github Flow. Create a branch, add commits, and open a pull request.

Third-Party Libraries

Boris includes the following third-party libraries:

  • tiny-AES-c -- Small portable AES128/192/256 in C. Public domain (Unlicense). Used for scrypt password encryption.
  • LMDB -- Lightning Memory-Mapped Database.
  • jsmn -- Minimalistic JSON parser.
  • MTH -- MUD Telopt Handler.

License

Copyright (c) 2008-2025, Jon Mayo
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those
of the authors and should not be interpreted as representing official policies,
either expressed or implied, of the Boris MUD project.

About

Boris MUD - a small C plugin-based MUD for RPI or deep adventuring

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages

Morty Proxy This is a proxified and sanitized view of the page, visit original site.