Skip to content(if available)orjump to list(if available)

How Copyover MUD Servers Worked

How Copyover MUD Servers Worked

46 comments

·February 11, 2025

teddyh

Doing exec() to a new process but keeping the file descriptors relies on none of the file descriptors having the CLOEXEC (close-on-exec) flag. This flag is, IIUC, considered best practice for all file descriptors now, and in Python, all file descriptors are now by default created “non-inheritable” (i.e. with the CLOEXEC flag). A working model would instead be to create one inheritable file descriptor, do the fork() (and exec the new process in the child), and then sendmsg() all the normal file descriptors to the new process.

ydant

This is a fun blast from the past. One of my first real programming experiences was working as a (the) programmer on a MUD and I remember learning about this technique somewhere (I think just reading about how forking worked) and then figuring out how to implement it on our MUD.

It felt absolutely magical to be able to hot deploy changes without kicking everyone off of the server - but felt even more magical to have gotten it to work.

The MUD community was a fun early introduction to open source. People (many of them probably "kids" like I was at the time) sharing various patches and features. It felt so cool to release something and have other people use it and provide feedback. Like the author says - at some point the MUD itself became a lot less interesting than the programming.

jonmarkgo

MUDs were how I really learned to code (in C) and how I fell in love with programming and online communities, almost 25 years ago now.

ZeWaka

> The MUD community was a fun early introduction to open source.

They still are these days. There's plenty around, and there's even more MUD-likes (GUI) that are open-source and played/developed by hundreds & thousands.

borlak

The way my mud did it, and I believe most muds (considering I took my implementation from another popular codebase), was not by forking. Sockets are just files, so all we did was save everything including players, execl() another instance of the MUD, and exit. Same port wasn't an issue with SO_REUSEADDR.

On boot as it's loading players, their file descriptor ID was saved, so it loads it up and resumes talking to the socket. It also loads the ID of the server socket descriptor. No need to send information to a new process as it just loads the previous game state.

If you're curious: https://github.com/borlak/acmud/blob/7c2442dfccfc28364fc399a...

And: https://github.com/borlak/acmud/blob/7c2442dfccfc28364fc399a...

jonmarkgo

I actually run a MUD that stills uses this copyover method (primarily C code) for major code changes and migrations. We have about 100 players online at any given time! We've actually built in some more modern automatic copyover triggers and hooks related to recovering from crashes, capturing backtraces, performing database migrations, and sending/receiving systemd signals (among a lot of other modernization upgrades).

_jackdk_

Very cool - I thought it would be a forgotten technique by now. Which MUD, and have you written up the details anywhere? I'm very interested to know what a modern version of this looks like, particularly with the systemd integration.

jonmarkgo

https://www.legendsofthejedi.com/

It originated (more than 20 years ago) as a fork of SWReality, which is itself a fork of SMAUG, which comes from DIKU, and so on and so on.

We (myself and a lot of other awesome volunteer coders over the years) have made some pretty major modifications to the codebase. It all runs on PostgreSQL now (instead of flat text files with a custom parser), it has a built-in Lua scripting interpreter for extensions and in-game interactions, it has deep integrations to our Discord community, it has sidecar web services, and much much more...

maxwelljoslyn

Where can we find your MUD?

jonmarkgo

Posted earlier in the thread!

draculero

is still online? I would like to play to remember my good old days!

mmastrac

This is a pretty interesting approach to replacing your own executable, though it's really akin to a spicy spawn where you (the parent) exec rather than they (the child) exec. Not to minimize the coolness of this approach -- it's certainly a good way to do it, and doubly so on older Unix.

These days you could probably spawn your child process, test-boot it and then mmap bits of it back into your process and then unmap your old code pages once you're certain it boots. Or given how cheap cycles are, test-boot it and then throw that away and spawn the new executable if you're happy with the results.

null

[deleted]

jisnsm

Irssi has a command that does what I assume is something similar to this to upgrade in place without disconnecting https://irssi.org/documentation/help/upgrade/

skulk

No need for past tense! The most active MUD today (AFAIK, 250 logged in at most times) uses a version of this to automatically reboot every 2 days.

somenameforme

Wow. You made me check. I can't believe Medievia finally 'died'. I mean it's still alive, but on life support at the minimum.

BugsJustFindMe

Which one, Aardwolf?

cdr

I played on a fantastic MUD as a kid. It was based on the code for some other MUD, and the admins were unhappy about some things, so they decided to write their own MUD engine from scratch. They got as far as establishing a network connection to a client - all network code written in scratch from C - before they burned themselves out and quit, which pretty much killed the MUD too.

cheschire

There’s a lot to learn here. Many developers fall into this trap halfway through their careers. Spolsky would probably call these folks you’re talking about architect astronauts.

Nuzzerino

As someone who has (for several years) developed, supported, and operated my own dialect of a multiplayer game server engine (which includes an embedded physics engine), that sounds absolutely brutal. It took several years before I saw my first user and it was after I thought my project was long dead. Then it started catching on. You probably won’t see results if you expect more than that.

Hopefully they gained some appreciation for how much work goes into these.

fragmede

As the adage goes, you can make a game, or you can make a game engine. Choose one.

em-bee

this is why languages such as LPC were developed. LPMuds allowed developers to code rooms and objects while the game is running and simply reload them whenever they wanted without admin intervention. if loading failed then the old version was kept and you'd just fix your code and try again. it was incredibly robust and powerful. for a player to get access to new versions of objects or rooms they had to drop the objects and pick them up again or reenter the room so that the object references could be updated to the new versions.

even better, LPC has been rewritten into pike, a general purpose programming language that retains the same capability. in the roxen webserver i can write and reload modules at runtime. this works efficiently because the lifetime of any object instance is limited to each http request. when a module is reloaded http requests already in progress are not affected, only new ones.

in the object storage server open-sTeam also written in pike a more advanced method was developed using proxy objects that can update object references and thus allow the updating of code without breaking the references. i am still using that to host my own websites.

to this day i have yet to find any other language with this power. smalltalk can do it and i believe lisp too, but that's it.

_jackdk_

Thanks for bringing this up. I actually had a section on LPMuds in an earlier draft, but took it out because I didn't think I could do it justice. I also wanted to focus more on the Unix stuff. My friends and I tended to play DIKU derivatives, so I was never intimately familiar with LPMuds' capabilities.

I hear that class/skill variety was often greater on LPMuds because it was so much easier to code up custom behaviour. Do you know of any especially good articles on LPMuds class (in the OO sense)/object hierarchies and the software architecture of mudlibs?

tito

I looked up my favorite mud awhile back. Just this post of other people looking for it for over 20 years!

https://groups.google.com/g/alt.mud/c/Hn5EOqDfcyg

CobrastanJorji

Maybe a dumb question here. Why is a child process needed? Can the parent process not open a pipe, write its own state into the pipe, and then call exec, allowing the new binary to read from the pipe? Will exec destroy the pipe if there is no child process?

lmz

Pipes will block (eventually) if written to without a reader.