Merc with Merc
The Merc 2.2 codebase, last updated in 1993, predates modern C standards and POSIX specifications. This guide provides concrete steps to compile Merc on contemporary Linux distributions using GCC, addressing specific incompatibilities in type systems, socket APIs, and linker behavior that prevent successful compilation with default modern toolchain settings.

Verify GCC Toolchain Compatibility
Merc 2.2 predates C99 and relies on K&R C conventions. Modern GCC defaults to C17 with strict warnings that treat legacy patterns as errors. Verify your GCC supports the -std=gnu89 flag to enable pre-standard C behavior required for Merc's implicit function declarations and loose typing.
gcc --version
gcc -std=gnu89 -E - < /dev/null > /dev/null 2>&1 && echo "GNU89 support confirmed" || echo "Compiler too old or incompatible"⚠ Common Pitfalls
- •GCC 14+ defaults may reject implicit int declarations
- •Clang handles const correctness differently than GCC and requires separate flag syntax
Obtain Merc 2.2 Source Distribution
Download the canonical Merc 2.2 release from the MUDBytes repository at https://www.mudbytes.net. Verify the archive contains the complete src/ directory including merc.h, comm.c, and the area/ directory template. Do not use Merc derivatives that modify the base socket handling or area file formats.
wget https://www.mudbytes.net/files/merc-2.2.tar.gz
tar -tzf merc-2.2.tar.gz | grep -E 'src/(merc\.h|comm\.c|Makefile)$' || echo "Archive missing critical files"⚠ Common Pitfalls
- •Avoid Merc 1.0 derivatives with incompatible area formats
- •Ensure you have the complete src/ directory including merc.h - partial archives cause cryptic linker errors
Patch String Constness Violations
Merc assigns string literals to char* variables in do_function tables and cmd_table initializations. Modern compilers treat string literals as const char*, triggering warnings treated as errors with -Wall. Modify declarations in merc.h and merc.c to use const char* for command tables and skill names.
// In merc.h, change:
extern char *const cmd_line[];
// To:
extern const char *cmd_line[];
// In tables.c, change:
char *const cmd_line[] = { ... };
// To:
const char *cmd_line[] = { ... };⚠ Common Pitfalls
- •Do not cast away const with (char*) as this causes segfaults on modern memory protection
- •Focus on do_function tables and cmd_table initializations where strings are defined as static data
Fix Implicit Function Declarations
Merc omits headers for exit(), getpid(), and socket functions. Add #include <unistd.h>, <stdlib.h>, <signal.h>, and <crypt.h> to merc.h. The crypt() function requires explicit header inclusion on modern glibc versions, and signal handling requires sigaction structures defined in signal.h.
// Add to merc.h after #include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <crypt.h>
#include <string.h> // For memset on sockaddr structures⚠ Common Pitfalls
- •Missing crypt.h causes linker errors for crypt() function on Linux
- •signal.h is required for sigaction on modern POSIX - Merc uses old signal() API that is deprecated
Update Socket API for IPv4 Compatibility
The sockaddr_in initialization in comm.c uses deprecated syntax. Modify the init_socket() function to explicitly cast address structures and ensure sa_family_t is set to AF_INET. Modern compilers require explicit type casting between struct sockaddr and struct sockaddr_in to avoid strict aliasing warnings.
// In comm.c, modify bind() call:
struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = INADDR_ANY;
if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) < 0) {
perror("bind");
exit(1);
}⚠ Common Pitfalls
- •SO_REUSEADDR must be set before bind() or port binding fails silently
- •IPv6 support requires significant refactoring beyond this guide's scope - stick to IPv4 for Merc 2.2
Configure Makefile Compiler Flags
Replace CFLAGS in src/Makefile to suppress anachronism warnings while maintaining function. Remove -Wall and -Werror. Add -std=gnu89 -Wno-implicit -Wno-return-mismatch -Wno-write-strings -Wno-format-security to allow Merc's printf-style logging with variable arguments to compile without modification.
# Replace existing CFLAGS line with:
CFLAGS = -g -std=gnu89 -Wno-implicit -Wno-return-mismatch \
-Wno-write-strings -Wno-format-security -DCRYPT
# Add to LIBS line:
LIBS = -lcrypt -lm⚠ Common Pitfalls
- •-Werror will prevent compilation despite working code
- •Omitting -g prevents debugging with GDB when crashes occur during runtime testing
Link Crypt and Math Libraries
The linker requires explicit -lcrypt for password hashing and -lm for math functions used in dice() and number_fuzzy() calculations. Modern linkers treat these as separate shared objects that must be explicitly linked, unlike historical systems where they were implicit.
# In src/Makefile, ensure LIBS line includes:
LIBS = -lcrypt -lm
# And ensure the link command uses $(LIBS):
$(CC) $(LDFLAGS) -o merc $(OBJ) $(LIBS)⚠ Common Pitfalls
- •Order matters: -lcrypt must follow object files in the link command on modern binutils
- •Static linking (-static) requires libcrypt.a presence - install libc6-dev package if missing
Execute Build Sequence
Run make clean to remove precompiled objects from incompatible architectures or previous failed attempts, then execute make. Monitor output for undefined reference errors indicating missing header patches or library link failures.
cd src
make clean
make 2>&1 | tee build.log
# Check for errors:
grep -i error build.log || echo "Build successful"⚠ Common Pitfalls
- •Parallel make (-j) can obscure error origins in legacy Makefiles with complex dependencies
- •Clean builds are mandatory when switching GCC versions to avoid object file ABI incompatibilities
Validate Binary Dependencies
Use ldd to verify dynamic linking against libcrypt.so and libm.so. Check that the merc binary is not linked against incompatible shared objects from previous build attempts. Verify the binary architecture matches your system (32-bit vs 64-bit).
ldd ./merc | grep -E '(crypt|m)'
file ./merc
# Expected output: ELF 64-bit LSB executable or similar matching your arch⚠ Common Pitfalls
- •Missing libcrypt.so.1 requires libc6-dev package on Debian/Ubuntu
- •32-bit binaries on 64-bit systems need gcc-multilib and libc6-dev-i386 packages
Initial Runtime Smoke Test
Execute ./merc 4000 from the src directory. Verify syslog output in ../log/ for successful bind() on port 4000. Connect with telnet localhost 4000 to test the login prompt. Check for immediate crashes indicating missing area files or permission errors.
./merc 4000 &
sleep 2
tail ../log/merc.log
# In another terminal:
telnet localhost 4000⚠ Common Pitfalls
- •Running as non-root requires port >1024 or setcap configuration for privileged ports
- •Missing area files cause immediate crash on startup - verify ../area/ directory presence and file permissions
What you built
Merc 2.2 now compiles and executes on modern Linux systems with these compatibility adjustments. While this preserves the historical codebase, consider migrating to ROM 2.4 or SmaugFUSS for active development, as they include modern POSIX compliance, IPv6 support, and active maintenance that eliminates these compilation barriers. Archive this patched Merc version for historical preservation while using contemporary derivatives for production MUDs.