I have always been fascinated with MUDs, also known as Multi-User Dungeons. They are the World of Warcraft before there was a world to craft war in. It's an interesting mesh between computers and Dungeons and Dragons. Imagination is an important factor, and from my experience, they are a great place for beginning programmers to learn how to code. I first learned how to code in C by programming for a few MUDs.
Compiling your first MUD can be a scary experience, though. Many of the codebases are pre-y2k, thus making some of the processes obsolete. For this demonstration, I will be using the EnvyMUD source code. It is widely used enough that many people can relate to it, and being directly related to DIKUMUD, the processes will be very similar for other DIKU related subservers. The first step will be to download the MUD source code. You can get it from the MUDBytes codebase repository here. The version of Envy I will be using is Envy 2.0 on a current Ubuntu server.
After downloading the Envy source code, you will need to un-tar and un-gzip it:
tar -xvzf envy-20.tar.gz
Next, navigate to the src
directory:
cd ./Envy20/src/
Now the fun starts. Run the make
command and see what happens:
$ make gcc -c -O -Wall -g act_comm.c gcc -c -O -Wall -g act_info.c gcc -c -O -Wall -g act_move.c act_move.c: In function 'move_char': act_move.c:230: warning: suggest explicit braces to avoid ambiguous 'else' gcc -c -O -Wall -g act_obj.c gcc -c -O -Wall -g act_wiz.c act_wiz.c: In function 'do_sset': act_wiz.c:2296: warning: suggest explicit braces to avoid ambiguous 'else' gcc -c -O -Wall -g comm.c comm.c:162: error: conflicting types for 'accept' /usr/include/sys/socket.h:214: note: previous declaration of 'accept' was here comm.c:163: error: conflicting types for 'bind' /usr/include/sys/socket.h:115: note: previous declaration of 'bind' was here comm.c:165: error: conflicting types for 'getpeername' /usr/include/sys/socket.h:133: note: previous declaration of 'getpeername' was here comm.c:166: error: conflicting types for 'getsockname' /usr/include/sys/socket.h:119: note: previous declaration of 'getsockname' was here comm.c:169: error: conflicting types for 'read' /usr/include/bits/unistd.h:35: note: previous definition of 'read' was here comm.c:173: error: conflicting types for 'write' /usr/include/unistd.h:363: note: previous declaration of 'write' was here comm.c: In function 'init_socket': comm.c:440: warning: ignoring return value of 'system', declared with attribute warn_unused_result comm.c:490: warning: ignoring return value of 'system', declared with attribute warn_unused_result comm.c: In function 'nanny': comm.c:1829: warning: comparison with string literal results in unspecified behavior make: *** [comm.o] Error 1
Fixing Prototypes
These errors are caused by prototype functions that are no longer needed. To fix this, open up comm.c
in your favorite text editor and navigate to line 161. There, you will comment out the following lines:
$ #if defined( linux ) // int accept args( ( int s, struct sockaddr *addr, int *addrlen ) ); // int bind args( ( int s, struct sockaddr *name, int namelen ) ); int close args( ( int fd ) ); // int getpeername args( ( int s, struct sockaddr *name, int *namelen ) ); // int getsockname args( ( int s, struct sockaddr *name, int *namelen ) ); // int gettimeofday args( ( struct timeval *tp, struct timezone *tzp ) ); int listen args( ( int s, int backlog ) ); int read args( ( int fd, char *buf, int nbyte ) ); int select args( ( int width, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout ) ); int socket args( ( int domain, int type, int protocol ) ); int write args( ( int fd, char *buf, int nbyte ) ); #endif
Get Crypty With It
Now, you could re-run the make
command, but there's a good chance it will still error out because the crypt
library hasn't been properly setup or linked. I won't walk you through how to install libcrypt
in your operating system of choice (probably something like apt install libcrypt-dev
, but YMMV), but when it is installed, you have to pull in the headers and make sure it is linked in the compiler.
The first thing we need to do is open up both act_info.c
and comm.c
and add the following line at the top of the file, just before the #include "merc.h"
directive:
#include <crypt.h>
Next, we need to add the -lcrypt
flag to the envy
target in the Makefile
, like so:
envy: $(O_FILES) rm -f envy $(CC) $(L_FLAGS) -o envy $(O_FILES) -lcrypt
Bring It All Together
If we did everything above right, then run make
again and you should get a successful build:
$ make gcc -c -O -Wall -g comm.c comm.c: In function 'init_socket': comm.c:440: warning: ignoring return value of 'system', declared with attribute warn_unused_result comm.c:490: warning: ignoring return value of 'system', declared with attribute warn_unused_result comm.c: In function 'new_descriptor': comm.c:870: warning: pointer targets in passing argument 3 of 'getsockname' differ in signedness /usr/include/sys/socket.h:119: note: expected 'socklen_t * __restrict__' but argument is of type 'int *' comm.c:871: warning: pointer targets in passing argument 3 of 'accept' differ in signedness /usr/include/sys/socket.h:214: note: expected 'socklen_t * __restrict__' but argument is of type 'int *' comm.c:914: warning: pointer targets in passing argument 3 of 'getpeername' differ in signedness /usr/include/sys/socket.h:133: note: expected 'socklen_t * __restrict__' but argument is of type 'int *' comm.c: In function 'nanny': comm.c:1829: warning: comparison with string literal results in unspecified behavior gcc -c -O -Wall -g const.c gcc -c -O -Wall -g db.c gcc -c -O -Wall -g fight.c gcc -c -O -Wall -g handler.c gcc -c -O -Wall -g interp.c interp.c:436:50: warning: trigraph ??' ignored, use -trigraphs to enable interp.c:437:50: warning: trigraph ??' ignored, use -trigraphs to enable interp.c:438:55: warning: trigraph ??' ignored, use -trigraphs to enable interp.c:439:56: warning: trigraph ??' ignored, use -trigraphs to enable interp.c:440:56: warning: trigraph ??' ignored, use -trigraphs to enable gcc -c -O -Wall -g magic.c gcc -c -O -Wall -g save.c save.c: In function 'load_char_obj': save.c:406: warning: ignoring return value of 'system', declared with attribute warn_unused_result gcc -c -O -Wall -g special.c gcc -c -O -Wall -g update.c rm -f envy gcc -O -lcrypt -o envy act_comm.o act_info.o act_move.o act_obj.o act_wiz.o comm.o const.o db.o fight.o handler.o interp.o magic.o save.o special.o update.o
Now, we need to start the game up. If you are on a modern Ubuntu version like I am, run the following:
nohup ./startupSH &
That's it! You have just started your first MUD on port 4000! To log into your game from the localhost, just type the following:
telnet localhost 4000
Congratulations! You are officially an implementor!
--
If you like this post or one of my projects, you can buy me a coffee, or send me a note. I'd love to hear from you!