Javelin's Guide for PennMUSH Gods
by Alan Schwartz
Javelin@Belgariad and elsewhere
Paul@DuneMUSH and Dune II
Note: This document is available as both a single document (suitable for
printing) and a multi-part document
(more appropriate to hypertext).
These multiple views are
automatically generated with a Perl script called multiview,
by James "Eric" Tilton, 3/29/94 (jtilton@willamette.edu),
heavily modified by me. This document is text-browser friendly.
Copyright (c) 1994-1998, Alan Schwartz (alansz@pennmush.org / Javelin).
This document may be distributed freely in whole or part
so long as none of its text is changed, no profit is made from the
distribution, and appropriate attribution
is given to indicate the source of the text. While every effort
is made to ensure accuracy, the author can not be responsible for
the results of applying the ideas herein.
The current edition of this document is available online at
http://www.pennmush.org/~alansz/guide.html.
Here are the diffs since the last
revision of the Guide, and here's the guide's log
of changes.
You can search the Guide.
Please leave your comments about this Guide!
This document is an attempt to set down the fruits of my
experience as a PennMUSH God, site admin, and source code hack. It
should be particularly useful to the new PennMUSH God who's having
trouble or wants to get a handle on where to start hacking in new
features.
Documentation is never a solo project. I am indebted first and foremost
to Amberyl (aka Lydia Leong) not only for maintaining, updating, and
generally rewriting the PennMUSH code into the form I received it in, not
only for teaching me a great deal about how to build, run, hack, and
debug a MUSH, not only for writing an excellent MUSH user and coder
manual herself, but for that first roybit which got me started on the
path to MUSH administration!
I also thank all the MUSH gods who've contributed questions,
problems, suggestions, patches, and ideas to the various MUSH mailing
lists and newsgroups. As well as the players and admin of the various
MUSHes which I've been involved in as site/source hack or consultant
(Pandemonium, Mua'kaar, Gohs, Dune II, Princess Bride, Dorsai, and especially
DuneMUSH) for their patience. :)
Finally, the other past and present members of the PennMUSH
development team made PennMUSH what it is today: T. Alexander Popiel,
Ralph Melton, Thorvald Natvig.
Choosing a MUSH server
There are currently at least four actively developed MUSH server flavors:
- PennMUSH
- The subject of this guide, a memory-based MUSH server. PennMUSH
tends to suffer from creeping featurism, which may be a good or bad
thing depending on your outlook. :) A lot of the development lately
has focused on applying principles of good programming and design
to the internals.
- TinyMUSH 3.x
- A server that merged the features of TinyMUSH 2.2.4 and TinyMUX.
It has a relatively conservative development cycle, focusing more
on stability than features, historically. Typically disk-based.
- Mux2
- The continued development of TinyMUX, a MUSH server that can
be either disk- or memory-based. Like PennMUSH, tends to have lots
of interesting features, including an advanced channel system.
- Rhost
- Based on TinyMUD, but heavily developed thereafter to include
a lot of additional features and security and efficiency tweaks. This is
the only actively developed MUSH server that has a closed codebase;
you must license with the developers (for free) to use it.
Which server you should choose depends primarily on the features of
your site and secondarily on the features of the server. The big
first decision is if you want a memory-based server (PennMUSH)
or a disk-based server (the others). If you choose disk-based,
you'll get to make other choices which are not covered in this
document.
A very good list of servers that's often more up-to-date than the
one above is maintained by T. Alexander Popiel at
http://www.pennmush.org/~talek/mushinfo.html. Another is maintained by Brazil (the mux2
maintainer) at http://www.tinymux.com/links.html.
Ideally you'll want to know the following information about the
computer system on which you're going to run your MUSH:
- What is the brand and model of the hardware? For example: Sun
Sparcstation 2, DEC Decstation 3100S, etc. The "uname -a" command often
will give you this information.
- What is the name and revision level of the operating system?
For example, SunOS 4.1.3_u1, Ultrix 4.2, etc. "uname -a" gives this
information. It may also be mentioned in the banner you see when you log
into the system or in the file /etc/motd. It's useful to know if the
operating system is basically BSD (SunOS 4, Ultrix, HP-UX, Linux, NeXT) or
SysV (Solaris 2, Irix)
- How much physical memory is in the machine? For example: 8Mb (typical
of a 386/486 machine), 16Mb, 64Mb, etc. Physical memory is ram chips on
the machine's motherboard. Figuring this out can be tricky. You can
usually find this out if you reboot the machine, or can read its boot
logs, which are /usr/adm/messages or /var/adm/messages on most BSD
systems.
- How much virtual memory (swap space) is the machine configured for?
Unix systems can use disk space as virtual memory - it's slower than
physical memory, but much cheaper and more available. Virtual memory is
used when processes need to be paged (portions of their memory sent to
swap space) or swapped (the whole process sent to swap space) to free
more physical memory. The command /etc/pstat -s reports total memory
space (physical + swap) on BSD systems. Solaris uses swap -l. For
others, check the man pages for 'swap' or 'virtual'. Typically, machines
have around twice as much swap as physical memory, but this varies.
- How much disk space will the MUSH have available to it? If you're
running the game on someone else's machine (and of course you have
permission, right? Illegal MUSHes are easily detected and often result
in nasty consequences and give MU*'s a bad name), how much disk space
can your game account use without causing problems? The upper limit is
usually the size of the disk partition on which the game account lives.
The "df" command shows the size of disk partitions, and the directories
which are housed by those partitions.
- What is the typical user load of the machine, and what is important
to the users? If you own the machine and it's the workstation on your
desk that only you use, there's no problem. If the machine is used by
others however, it's important to consider what kind of other software
is likely to be run. If people use it for email, news, and
word-processing, they'll want quick keyboard response, but won't be
taking up much CPU or memory. If people use it to compile large software
packages or run statistical analyses, you'll be sharing much more CPU
and memory.
- How is the machine connected to the internet? Most university sites
don't have to worry about the bandwidth of their connections, but if
you're connected via a 14.4k SLIP line, expect some trouble. :)
The main difference between PennMUSH and TinyMUSH is where
the database is stored while the game is running. In PennMUSH, the
database is loaded entirely into memory while the game is running (text
on objects and in mail messages is compressed), and a copy of the
database is dumped onto disk periodically. In TinyMUSH, the database
resides on the disk, and as portions are needed, they are loaded into
memory and cached (kept around for a while in memory). Additional
memory is required for player connections, so the more players
the game typically has, the more memory is typically required.
Typically, this means that you want to run PennMUSH when you
have plenty of memory to spare, and TinyMUSH when memory is scarce but
you have plenty of disk space (and fast disks).
Actually, things are a bit more complicated on modern
workstations, since parts of the PennMUSH process that aren't being
used will generally be paged out of memory to disk by the operating
system. In some cases, this can be more efficient than having the
MUSH server do the paging, but generally, it results in more use
of physical memory.
Here are some typical usage statistics for an earlier
version of PennMUSH (more recent versions use a bit less memory
and disk).
The "memory" column
refers to the game's total size (SZ on ps listings). The "disk" column
refers only to the uncompressed size of the database - not the source code,
executables, backup databases, etc. (TinyMUSH entries include the
size of the .gdbm.{db,pag,dir} and the .db file).
Typically for PennMUSH, you have 2
databases: the
database from which the process was started (indb) and the database
which the process is currently using/writing (outdb). It's also possible
to use compressed databases, which save considerable disk space at the
cost of longer startup times and dumps.
Recent versions of servers:
Type OS Name # Objects Memory Disk
Penn1.7.2 Sun4.1.3u1 M*U*S*H 6700 21Mb 7Mb uncompressed
Old versions of servers:
Type OS Name # Objects Memory Disk
Penn1.6.0 Sun4.1.3u1 DuneMUSH 19000 28Mb 14Mb uncompressed
Penn Ultrix4.2 DuneII 2200 8.7Mb 3.8Mb uncompressed
Penn Linux1.0.8 Gohs 2500 5.5Mb 1.6Mb uncompressed
Tiny2.0.10 Sun4.1.3 TwoMoons 9350 5.5Mb 30Mb (1)
(1) Info from elfchief@lupine.org. TwoMoons is based on Tiny 2.0.10p6
If you still can't decide between PennMUSH and TinyMUSH, or are
fortunate enough not to care about either disk or memory space, here are
the primary differences in features between Penn and Tiny 2.x. Thanks
to T. Alexander Popiel for some of these.
What Penn has that Tiny does not:
- The Royalty flag, and @powers
- Multiple control and local master rooms via Zones
- A built-in mail system
- A built-in chat channel system
- A built-in building/topology checking system
- Object creation/modification times
- More extensive help and other documentations systems (news, rules, events,
index)
What Tiny has that Penn does not:
- The @robot command
- The @forwardlist command, which makes creating puppets
which report to multiple players a whole lot easier
- Exits within things and players
- The @list command
- Exits are inherited from parent rooms
Of course, if you use TinyMUX or AlloyMUSH, you get the Tiny 2.x
memory setup with many Penn-like features. The advantage of using
TinyMUSH 2.2, however, is that its a current standard, and being
actively developed - you get excellent support.
Compiling PennMUSH
PennMUSH up to 1.50 pl10 was developed by Amberyl.
PennMUSH since then is being maintained by Alan Schwartz
<dunemush@pennmush.org>, and developed
by Alan Schwartz, T. Alexander Popiel, and Shawn Wagner. There
are also people responsible for various ports to other operating
systems (Win32, Mac, OS/2) and groups of translators.
PennMUSH now uses the following version-numbering scheme:
1.7.2 patchlevel 30
| | | |
Code base version -----/ | | |
Major version -----------/ | |
Minor version -------------/ |
Patchlevel -----------------------------/
The patchlevel is updated when new bugs are fixed by patches.
The minor version is updated when a new version of the tar
file is posted; it may include bugfixes and new features.
The latest odd-numbered minor version is undergoing active development;
the latest even-numbered minor version is only receiving bugfixes.
The major version is updated when major internals are changed.
You can find the most recent PennMUSH code
at pennmush.org in /pub/PennMUSH/Source.
It will have a filename like "pennmush-1.7.2p3.tar.gz". Once you've
got the file, you'll need to uncompress it (using gunzip or uncompress
if the file ends in .gz or .Z, respectively),
and untar it ("tar xvf pennmush-1.7.2p3.tar"). As of PennMUSH 1.7.2,
the tar file will create a pennmush/ directory and unpack into
that.
There may also
be patches on the FTP site, which should be applied to the
code once you've uncompressed and untarred it. They have names like
1.7.2-patch04. Only apply patches that are later than the version
of the tarfile. To apply a patch, use the 'patch' command, like
this:
% patch -p0 < 1.7.2-patch04
GNU patch users may want "-b -p0" instead of "-p0".
PennMUSH releases are signed with Javelin's PGP public key, which
is also available in the ftp directory.
Older versions of the PennMUSH source code can be found in the
"oldsrc" subdirectory.
Prebuilt binaries
PennMUSH can be compiled on Windows 95/98/ME/NT/2k, MacOS 7/8/9/X,
and OS/2 as well as Unix.
But if you'd rather not compile and you have one of these operating
systems, you can get a pre-built binary that you can simply run.
You won't be able to configure some of the options that are set
at compile-time, but you'll save yourself the work of compiling.
The files below are listed as they appear in the most recent
PennMUSH releases. The PennMUSH source includes the following files:
- BUGS
- A list of known bugs in the current release. If you fix one,
let me know!
- CHANGES
- Changes in the most recent minor version of PennMUSH. Update with
each patchlevel.
- CHANGES.OLD
- Past changes
- COPYRITE
- A discussion of hdrs/copyrite.h, the terms under which you can use PennMUSH.
- Configure
- A program which automatically configures MUSH for your system and
builds you a Makefile and some other important files.
- FAQ
- Some frequently asked questions
- Makefile.SH, config_h.SH
- A template for a Makefile and config.h.
Configure uses this to build the real Makefile and config.h files.
- Patchlevel
- A file which tracks official patches to prevent them from being
applied out of order. Don't mess with it.
- README
- Compilation instructions. If you haven't read this, do it now.
- options.h.dist
- A file which contains compile-time options that you'll want to set.
Copy this to options.h, and modify that.
- src/Makefile
- The Makefile that actually builds the MUSH server.
- src/SWITCHES
- A file that contains a list of all the switches used by any MUSH
command. The program utils/mkcmds.sh uses this file to build
src/switchinc.c and hdrs/switches.h, used by the command parser.
- src/access.c and hdrs/access.h
- Code for controlling access to the MUSH
- src/announce.c
- The source code for a standalone port announcer.
You can use this code when your MUSH is down to
provide a simple message when people try to connect.
But it's a bit buggy, so consider using portmsg.c, instead,
described in the Appendix on
Programs and Scripts.
- hdrs/ansi.h
- The header file defining ANSI codes
- src/atr_tab.c and hdrs/atr_tab.h
- Attribute table stuff. This code handles the hashed
attribute table, and contains the attribute alias
table.
atr_tab.h contains the table of standard attributes. All standard MUSH
attribs are listed here. The format is described below.
- src/attrib.c and hdrs/attrib.h
- Attribute setting and getting.
The header file which lays out the actual attribute (ATTR) structure
- src/boolexp.c
- Working out boolean expressions (like locks!)
- src/bsd.c
- The tcp/ip socket interface to the MUSH. Handles player
connection to the game, low-level input/output from
players, WHO and QUIT. This is where the main() function
is and where the main input/output loop is.
- src/cmdlocal.dst
- This file is copied to src/cmdlocal.c if it doesn't exist.
If you add your own commands to PennMUSH, add them to
src/cmdlocal.c.
- src/cmds.c
- This file contains the cmd_ functions that are called when
a command called is run.
- src/command.c and hdrs/command.h
- The command parser. This file also includes the list of all the
MUSH commands, their switches, etc. The header file lays out the
structure of a command.
- src/compress.c
- A wrapper for the routines to compress and uncompress the
text of attributes.
This keeps down MUSH memory requirements. It either defines
things for no compress, or includes comp_h.c, comp_b.c, or comp_w.c
depending on the setting of the COMPRESSION_TYPE option.
- src/comp_b.c
- MUSH before 1.50pl10 used a "bigram" compression method,
which is simple and unlikely to go very wrong, but doesn't give
very good memory savings. This file contains that algorithm.
- src/comp_h.c
- MUSH after 1.50pl10 introduced
a Huffman compression method which should reduce memory
requirements but be slightly slower.
- src/comp_w.c
- Nick Gammon's word-based compression, required for Win32
machines. Unix sites can also use this method, which is more
efficient for large databases than Huffman, but is not 8-bit clean.
- src/conf.c and hdrs/conf.h
- Code to read your mush.cnf file and set up the MUSH's
configuration. The header file also defines important MUSH configuration
constants that are rarely
changed.
- src/convdb.c
- Code which does db conversions for db's which don't
contain a Powers field and the like.
- hdrs/copyrite.h
- The copyright statement
- src/cque.c
- Code to maintain the MUSH queue
- src/create.c
- Object creation code
- src/csrimalloc.c and hdrs/csrimalloc.h
- An adapted version of the CSRI malloc package, a very efficient
memory allocation system.
- src/db.c and hdrs/mushdb.h
- Code to implement the MUSH database, let you access objects,
etc.
- hdrs/dbdefs.h
- Definitions of macros about db objects
- src/destroy.c
- Object destruction code
- src/extchat.c and hdrs/extchat.h
- Source code for the extended chat system.
- src/extmail.c and hdrs/extmail.h
- The source code for the PennMUSH @mail system.
- src/flags.c and hdrs/flags.h
- Code for setting, checking, and using MUSH flags is here.
Also, the flag table and powers table are here.
Constants representing MUSH flags and powers are defined
in the header file.
- src/function.c and hdrs/function.h
- The function table and code for dealing with it.
- src/fun*.c
- The src/fun*.c files (src/funlist.c, src/funmath.c, etc.) contain
the definitions of PennMUSH functions of various types.
- src/funlocal.dst
- This file is copied to src/funlocal.c if it doesn't exist.
If you add your own functions to PennMUSH, add them to
src/funlocal.c.
- src/game.c and hdrs/game.h
- The main MUSH command loop is here.
- hdrs/getpgsiz.h
- Header file to simulate the getpagesize() system call if your
system doesn't have it. This is used only with the gmalloc package,
and in fact behaves incorrectly on at least Ultrix 4.2.
- hdrs/globals.h
- Global macros. A very small file.
- src/gmalloc.c
- The source code for the GNU malloc package, one of
the basic malloc packages. Discussed below.
- hdrs/help.h
- Header containing the structure of a help index entry.
- src/htab.c and hdrs/htab.h
- Code for implementing hash tables, taken with permission from
TinyMUSH 2.2, but since modified.
- src/ident.c and hdrs/ident.h
- Source code for the ident (RFC1143) client routines. Discussed
below.
- src/info_slave.c
- Source code for the info_slave process, a separate program which
the server can use to do DNS and ident lookups so the main server
doesn't have to wait around for these things. Discussed below.
- src/intrface.c and hdrs/intrface.h
- This file really doesn't do much more than #include
bsd.c. If more kinds of interfaces were written (or
if the port concentrator worked), this file would
include the appropriate code.
- src/local.dst
- This file is copied to src/local.c if it doesn't exist.
It contains hooks for adding your own local hacks to PennMUSH
without disrupting the distributed source code. If you want
to extend the db object structure, this is where you can do
it.
- src/lock.c and hdrs/lock.h
- Code for the extended @lock system introduced in pl13. To add
new locks, you'll modify the top of lock.c and the bottom of lock.h
- src/log.c
- Code to do logging of things to the logfiles
- src/look.c
- Code for look and examine
- src/match.c and hdrs/match.h
- Code to determine if an object matches a name, with
various constraints. This is where the MUSH figures out
if, say, there's an exit in the room called 'out'.
The header file contains the match.c prototypes; it's included
in other modules which intend to use matching functions.
- src/memcheck.c and hdrs/memcheck.h
- Code to do some simple checking on the allocation and
deallocation of memory
- src/mkindx.c
- The mkindx program, which creates indexes
for the help.txt, news.txt, and events.txt files. When building
on Win32 systems, this code is incorporated directly into the server.
- src/move.c
- Code to move objects from place to place. Commands like
@tel, enter, and moving through exits.
- src/mycrypt.c
- A wrapper file that defines the mush_crypt() function, which is
used for encrypting player passwords. Depending on the setting of
CRYPT_SYSTEM in options.h, encryption may use the standard UNIX
crypt(3) routines, SHS, or no encryption at all.
- src/mymalloc.c and hdrs/mymalloc.h
- A wrapper file that includes one of the malloc packages (nmalloc,
smalloc, gmalloc, csrimalloc, or nothing) depending on the setting
of MALLOC_PACKAGE in options.h. The header file must be included
in any source code that calls mush_malloc().
- src/nmalloc.c
- Nick Gammon's nmalloc package, for win32 systems.
- hdrs/oldattrb.h
- The old header file attrib.h, used in some db conversions
- src/parse.c and hdrs/parse.h
- The function parser
- src/player.c
- Code to handle players, including creation, password-checking,
names and aliases, etc.
- src/plyrlist.c
- This maintains the list of connected players and aliases.
- src/portmsg.c
- Code for the portmsg program, a standalone program that simply
listens for connections on a port, spits back a message, and
closes the connection. Useful when your MUSH is down. See also
src/announce.c, which does the same thing differently.
- src/predicat.c
- A bunch of standard functions to check various conditions.
One of the most important source code functions, controls(),
is here (which determines when one thing controls another!)
- src/privtab.c and hdrs/privtab.h
- Code for privilege tables, a data structure used by the MUSH.
- src/rob.c
- Code to manage MUSH money, include give and rob.
- src/rpage.c
- Code for remote-paging of other MUSHes
- src/rwho.c
- Code for reporting MUSH connection and disconnection information
to an RWHO server. Discussed below.
- src/services.c
- Code for the win32 versions to allow pennmush to run as a service.
- src/set.c
- Code for setting object attributes.
- src/shs.c and hdrs/shs.h
- Code for the SHS password encryption algorithm.
- src/smalloc.c
- The Satoria malloc package, one of the 3 standard ones.
- src/speech.c
- Code for messages, including say, pose, page, whisper,
@emit, etc. (Doesn't include @mail or @chat)
- src/strutil.c
- Utility functions for doing string comparisons and similar
kinds of things.
- src/SWITCHES
- The master list of all switches taken by any command.
- src/switchinc.c and hdrs/switches.h
- C versions of SWITCHES, automatically built during compiling.
- src/timer.c
- The game timer, which determines when a dump should take
place, when objects from the wait queue should be triggered,
etc.
- src/unparse.c
- Unparsing an object is correctly displaying its brief examine
information (flags, locks, etc.) Some routines for that are here.
- src/utils.c
- Various utility functions.
- src/version.c
- Source file which implements the @version command, reporting
build time, date, flags, and MUSH version. This file is rebuilt
at every compile.
- hdrs/version.h
- Header telling you the version of the code
- src/warnings.c and hdrs/warnings.h
- Code which implements the building warning system if USE_WARNINGS
is defined in options.h.
- src/wild.c
- Code for wildcard matching
- src/wiz.c
- Wizard functions and commands are defined here
-
-
- patches/
- If you plan to apply user-contributed PennMUSH patches, create
a subdirectory called patches and store applied patchfiles
there. The server will try to incorporate the patch names and
versions into the @version output.
-
-
- game/
- The directory for run-time game stuff, including:
- game/access.README
The access.cnf file controls which sites can access the MUSH
in a various ways. You can prevent sites from connecting, creating
players, and connecting to guests. You can also enforce registration
and automatically SUSPECT players from certain sites.
access.README explains how to create access.cnf.
Details about access.cnf can be found in the section on
Security.
- game/mushcnf.dst
- game/mushcnf.dst is a template for game/mush.cnf, the run-time
configuration file for the MUSH, discussed below.
- game/names.cnf
- A list of names which players aren't allowed to use.
Obscenities typically, and/or out-of-theme names.
- game/restart
- The MUSH startup/restart shell script. You'll want
to edit this to reflect your setup. Discussed below.
-
-
- game/data
- The directory for game databases in use
- game/data/minimal.db
- The minimal database, containing 3 objects:
#0 (Limbo, the start room),
#1 (One aka God, the SuperWizard), and
#2 (the Master Room)
-
-
- game/log
- The directory where MUSH log files are created
-
- game/save
- The directory where backup databases are usually kept
-
- game/txt
- The directory for text files used by the MUSH
- game/txt/connect.txt and game/txt/connect.html
- Text file shown when a player connects to the MUSH. The html version
is shown if a player connects using Pueblo.
- game/txt/create_reg.txt and game/txt/create_reg.html
- Text file shown when a player tries to create a
character from a site where creation is not allowed.
- game/txt/down.txt and game/txt/down.html
- Text file shown when the MUSH is set to disallow
non-admin logins and any player tries to connect
- game/txt/events.txt
- Text file containing 'events' entries. This
file is built for you from files in the game/txt/evt directory.
See game/txt/README for information.
- game/txt/full.txt and game/txt/full.html
- Text file shown when a player tries to connect to the
MUSH and the MUSH is full (at the player login limit)
- game/txt/guest.txt and game/txt/guest.html
- Text file shown to guests that log in.
- game/txt/help.txt
- Text file containing 'help' entries. This
file is built for you from files in the game/txt/hlp directory.
See game/txt/README for information.
- game/txt/motd.txt and game/txt/motd.html
- Text file shown to all players on connection.
- game/txt/news.txt
- Text file containing 'news' entries. This
file is built for you from files in the game/txt/nws directory.
See game/txt/README for information.
- game/txt/newuser.txt and game/txt/newuser.html
- Text file shown when a player successfully creates a new character
- game/txt/quit.txt and game/txt/quit.html
- Text file shown when a player quits
- game/txt/wizmotd.txt and game/txt/wizmotd.html
- Text file shown when an admin connects
The options.h file lists
the compile-time options which can be turned on or off. Whenever
you change one of these files, you should recompile the MUSH.
Options which are unclear or which deserve special mention
are discussed in this section, in order of their appearance in
options.h.
CRYPT_SYSTEM determines how player passwords are encrypted. Because
Win32 PennMUSH must use SHS encryption (CRYPT_SYSTEM 2), and because
some Unixes vary in their implementation of DES encryption, consider
using SHS encryption if you expect to need to move your database
to another site running a different operating system in the future.
Defining MEM_CHECK allows you to test for
memory leaks if you know what you're doing. When MEM_CHECK is defined,
a count is kept of each memory allocation of various types which is
incremented when more memory is allocated and decremented when memory
is freed. You can see the list of active (allocated) memory counters
by logging in as #1 and typing @stat. If an allocation from a certain
function builds up and never decreases, the function may be leaking
memory and you can examine it. Rhyanna provides
more detailed
information about how to use MEM_CHECK. Leave MEM_CHECK undefined
for a normally running MUSH, as it's more efficient without.
LOCAL_DATA adds a pointer to the db object structure from which you
can add your own extensions to the db. A boon for serious hackers.
See src/local.dst for some information.
If INFO_SLAVE is defined, a program called info_slave will
be compiled and will run alongside the MUSH server. The MUSH will
ask info_slave to do DNS and ident lookups on new player connections,
so the MUSH doesn't have to wait around for answers to these lookups.
FUNCTION_SIDE_EFFECTS are nifty, and make possible some very
queue-light (albeit often CPU-intensive) code. They also make possible
a lot of security problems. In general, if your coders aren't demanding
these, avoid them.
While the SINGLE_LOGFILE option can save some file descriptors
and possibly allow more players to log in to your MUSH, it should be
avoided in general as it makes dealing with your MUSH's error, warning,
and other output more difficult. However, if you do want to use it,
you can often separate out the individual components by using the
'grep' command and searching for NET, RPT, ERR, WIZ, or LOG.
Always read the README file for detailed instructions. Let
me say that again.
Always read the README file for detailed instructions.
Here's the quickstart version:
- Win32 users should get and install the Cygnus gnu-win32 tools.
This provides a very good unix-like environment on your win32 system,
including the free gcc compiler. Find the tools at
http://www.cygnus.com/misc/gnu-win32
and be sure you get the full development kit and follow its
installation instructions. Win32 users can build with gnu-win32 alone,
or with gnu-win32 and Microsoft Visual C++; the latter builds
an executable that can be used on systems that don't have gnu-win32.
If you want to use MSVC++, the command-line compiler "cl" should be
in your path. Run 'bash' and perform all following steps from within
a bash shell.
- Type 'sh Configure' to autobuild a Makefile.
Advanced users: If you need to force Configure to do something
(for example, add a special compiler flag automatically), you can
create a config.over file that overrides values of Configure variables
(much the way the hint/* files work). Don't expect compile help from
Javelin if you do this.
- Do one and only one of the following. Either:
- Copy options.h.dist to options.h and edit it, or
- Do a 'make update' which will walk you interactively through all
the options, or
- If you're upgrading to a new patchlevel,
copy your old options.h and dune.h to your new directory, and
then 'make update' - your old settings will be read and incorporated
into a new options.h. You'll be prompted for new options.
- Do a 'make install' to build the files and set up symbolic links.
Symbolic links don't work on win32, and info_slave.exe won't
build - follow the README instructions about this.
- Do a 'make customize' if you want to customize a directory for
your mush (very handy if you run multiple MUSHes). This requires perl.
For example, if you customize for 'mymush', a duplicate game/ subdirectory
called mymush/ will be created, containing mymush.restart and mymush.cnf
already set up correctly. The databases will be named indb.mymush.Z,
outdb.mymush.Z, and maildb.mymush.Z, and will live in mymush/data.
If you have trouble compiling, you can get help from
Javelin (dunemush@pennmush.org). Don't ask the mailing list
for help on compiling! When you email Javelin, be sure you
include complete
information about the hardware, operating system, compiler used, any
weird problems, etc.
It's a good idea to include a copy of the make
error. You can save a copy of your make output by using a command like:
make |& tee make.log (csh, tcsh, etc)
make 2>&1 | tee make.log (sh, ksh, bash, etc)
Or you can use the UNIX 'script' command, which makes a copy of everything
you type or see:
script make.log
make
(type 'exit' or ctrl-D to stop logging to the file)
Starting up
The game/ subdirectory houses the files that the MUSH uses
when it's running, including database, text files, and logs. If
you use the 'make customize' feature,
your "game" directory will have a customized name instead of "game",
and your mush.cnf, restart, and database files may have different
names as well.
The MUSH game subdirectory contains the following files and
directories:
- mushcnf.dst
- The distributed template for mush.cnf. Your mush.cnf file is built or
updated from this file when you upgrade your PennMUSH to a new version.
Don't mess with it.
- mush.cnf
- The config file for your mush (make a copy of this
using the name of your mush, e.g., dune.cnf, unless you're using
win32, in which case you must use mush.cnf as the name)
- access.README
- An explanation of how to create and use the access.cnf file.
- restart
- The restart script for starting up the MUSH
- data/
- Directory containing your game's database(s)
- save/
- Directory for old game databases
- txt/
- Directory for .txt files (help, events, news,
and various message files)
- log/
- Directory for MUSH log files
The mush.cnf file lists a set of run-time options that can be
changed without having to recompile the MUSH.
Changes to the .cnf file
can be made while the MUSH is running. You must send a signal 1 (HUP) to
the MUSH process for the changes to take effect. Use ps to find the PID#
of the MUSH process (netmush or netmud) and then kill -1 PID#.
mush.cnf includes comments about all of its options.
Read them carefully. Here are a few extra notes:
- player_start
- This is the room number where newly created players connect to.
It defaults to 0, but you will probably want to change it for
a non-social MUSH, since #0 is always a good building nexus (because
rooms which can be reached from #0 are "connected" and don't give the
owner the disconnected room message).
- master_room
- If this is defined (as the db number of the master room), then the Master Room's exits are global
throughout the MUSH, and any $commands on objects in the Master Room
are global. By default, this is room 2, which comes with minimal.db.
- max_channels, max_player_chans, chan_cost
- These refer to @chat channels under the extended chat
system. max_channels sets the maximum total number of chat
channels that can be created. max_player_chans sets the number of
channels that each mortal player can create ("0" means only admin
may create channels). If mortals can create channels, chan_cost
specifies the cost in mush coins to create a channel. Setting this
to a high number can prevent rank newbies from creating a bunch of
channels.
- dump_interval
- The time in seconds between MUSH checkpoint dumps. For reference,
3600 seconds is an hour, 7200 = 2 hours, 10800 = 3 hours, 14400 = 4 hours,
21600 = 6 hours, 28800 = 8 hours, and 43200 = 12 hours. If you don't
do dumps at least every 12 hours, you're playing with fire. If you do
any kind of development or hacking on your MUSH, dump at least every
3 hours.
- max_logins
- This is where you set the maximum number of players who can log
in, if you have LOGIN_LIMITS defined. See the section on
file descriptors and login limits
for more information.
- player_queue_limit
- Mess with the default of 100 at your risk. If this number is too low,
players won't be able to queue enough commands to make some of their
objects work. If it's too high, runaway objects may eat up too much
queue space and lag your MUSH. This makes you vulnerable to a denial
of service attack by a twink as well as to coder mistakes.
- queue_chunk
- When the MUSH is not processing input from the net, it takes some
commands off the queue and runs them. This parameter controls how many
commands are run. The higher the number, the less your queue will build
up, but the slower the game's net response will be. The default of 3
is usually fine, since by the time the queue's a serious issue, there's
often nearly constant net activity anyway. See below.
- active_queue_chunk
- When there is net activity, the MUSH still can take some commands
off the queue for processing. By default, it takes off a single command,
giving the queue the same priority as a single player. If you get a lot
of players and find your queue building up unacceptable, raising this
(start by raising it to 2) can reduce queue build-up (and therefore
queue lag) at the expense of slower command response. It's a tradeoff,
but it often pays when there's a slew of players and queued commands.
- function_recursion_limit
- Mucking with this can allow code to waste CPU by recursing (calling
itself) too many times. Encourage your coders not to write recursive
code, and keep this at the default of 50.
- function_invocation_limit
- The maximum number of functions that can be invoked in a single
command. This can be a hassle if you try to do some kind of
@dolist on all the objects in your database, but it pays to keep
it down to the default of 2500.
- log_wipe_passwd
- The @log/wipe command is a God-only command that erases
one of the MUSH logs. Because this can be used to hide a
hacker's tracks, the log_wipe_passwd in mush.cnf must be
given in the @log/wipe command. Set this password to
something other than the default value!
- rwho_*
- The values for the rwho parameters can be obtained by emailing
jds@moebius.math.okstate.edu and asking for an RWHO password, as
discussed in the README file.
- ident_timeout
- The timeout value here applies to the MUSH if info_slave
isn't being used.
- log_*
- These parameters control what the MUSH will log. log_commands and
log_huhs will both produce huge spammy logs - don't use them unless
you're trying to identify what command is causing a crash or something.
I recommend log_forces for security reasons unless you have a global
which does a lot of @forcing (like some versions of Dynamic Space).
I don't use log_walls, myself, but if you like to watch your wizards,
you could. These can also be turned on and off with @enable/@disable,
as can logins and daytime.
- player_creation
- When set to "no", the create and register commands at the
connection screen are disabled. Another option is to
to leave this as "yes" and disable creation for specific
sites (or all sites) by using access.cnf.
- daytime
- The "computationally expensive" commands are: @find, @entrances,
@mail/stats, @search, and the lsearch() function.
- room_flags, player_flags, exit_flags, thing_flags
- These very handle parameters allow you to set up flags which should
be automatically set on any object of a given type that's created on the
MUSH. Having rooms and players (and possibly things) start out no_command
is a good way to save CPU time that would be spent searching things for
$commands. Starting players enter_ok makes a lot of sense. Some MUSHes
may want to start exits transparent or players opaque or whatever.
On DuneMUSH, we had a flag called "unregistered", and players started
with this flag set, allowing us to do on-line registration.
- restrict_command
- restrict_command allows you to disable commands or
restrict their usage to certain classes of players. If
multiple restrict_commands refer to the same command, they
*all* apply. If a command is disabled (restricted to
"nobody"), you can implement it in MUSHcode as a $command
instead.
When you run the restart script, here's what it does:
- Sets some variables to use later. You must define GAMEDIR
to point to your game directory, CONF_FILE to point to your .cnf
filename, and LOG to point to where your server error log file should
be kept. If you've used 'make customize' these will be set for you.
- Scans your .cnf file and figures out the name of your incoming
and outgoing databases, and the compression scheme you use.
- The "mush" variable is set to 1 (or higher) if there's already a
MUSH running with your .cnf file,
or 0 otherwise, by using a tricky little ps/egrep thing.
If the MUSH is already running, restart reports that, and quits.
- The help.txt, news.txt, and events.txt files are re-mkindx'd by
running 'make' in the txt directory.
- If there's a PANIC.db, and it's complete (i.e., the game was able
to finish the whole panic dump), we turn it into an outdb.
- All the log files are erased. You may want to change the
script to preserve some of these.
- Tries to find a database to start up, in this order:
- If there's an outdb, indb is moved to save/indb.old, and
outdb is moved to indb.
- If not, and if there's an indb, we use that.
- If not, and if there's a save/indb.old, copy that to indb and
use that.
- Finally, if we can find minimal.db, we'll use that.
- If a reboot.db file exists, it is removed. This file is used to
preserve information when @shutdown/reboot is run, and shouldn't
exist when restarting from the shell.
- The MUSH is run.
PennMUSH uses two kinds of text files, both of which live
in the game/txt subdirectory: cached text files and indexed text files.
Cached text files
The cached text files contain various messages which are
displayed to players under different circumstances. The MUSH reads these
(typically short) files into memory when it starts up ("caching" them),
and when you change these files, you must use the @readcache command on
the MUSH to update the memory versions. These files include:
- connect.txt and connect.html
- The connection screen. This file should give the
name of the MUSH (and usually the source code version), an email address
for the God or contact person, and character connection/creation
information. It should also include the "Type WHO to see who is
currently connected" line that's in the default connect.txt - MUSH
clients like tinyfugue use this line to detect when to start displaying
text when they're doing quiet logins. If you're supporting Pueblo
clients, the connect.html file is an HTML-format connection message
for Pueblo clients.
- quit.txt and quit.html
- Shown to players when they QUIT or LOGOUT
- down.txt and down.html
- Shown to players when logins are @disabled (i.e.,
when only admin can log in). Often unused in favor of @motd/nologin,
which doesn't require site access.
- newuser.txt and newuser.html
- Shown to players when they create a new character.
- motd.txt and motd.html
- Shown to players after they've connected to their
character. Often unused in favor of @motd, which doesn't require site
access.
- guest.txt and guest.html
- Shown to Guests when they connect.
- wizmotd.txt and wizmotd.html
- Shown to admin when they connect. Often unused in
favor of @motd/wiz, which doesn't require site access.
Sending a signal 1
(SIGHUP) to the MUSH process (see .cnf file above) also causes the MUSH
to re-cache the cached .txt files, just like using @readcache. On
Unix, one does this using the command 'kill -1 <mush process id>'.
Indexed text files
The indexed text files are help.txt, news.txt, events.txt,
rules.txt, and index.txt, depending on what you've got defined in
mush.cnf.
These files are often long, so
to speed up access, the MUSH uses indexes which help it quickly find
where in the file a given topic is. Before 1.7.3p9, these indexes
were kept in index files, help.idx,
news.idx, and events.idx, built with the mkindx program (e.g.,
mkindx news.txt news.idx), and had to be rebuilt whenever you changed one
of these files. From 1.7.3p9, the indexes are kept in memory,
so there are no index files or mkindx program. They still need to
be rebuilt when you change a help file, but @readcache does that.
The help.txt, news.txt,
and events.txt files themselves should
not be edited, because they are built from files residing in the
txt/hlp, txt/nws, and txt/evt directories.
This allows you to keep your local help files separate
from those distributed with PennMUSH, and to manage them as
a set of files rather than a single large file.
Here's how it works:
-
The source files for help.txt, news.txt, and events.txt are
kept in directories called hlp, nws, and evt respectively.
-
Files in those directories which end in .<directoryname> are
considered to be part of the text. That is, files in hlp/
which end in .hlp, and files in nws/ which end in .nws
will be merged into the final help.txt, news.txt, or whatever.
-
When the MUSH is restarted, a 'make' is run in this directory.
If any file in hlp/ is newer than help.txt, it calls
compose.csh to rebuild help.txt. If you've got perl on your system,
compose.csh will also call index-files.pl to make a 'help index'
entry which lists all your help entries.
So, if you want to add your own news entries, make a file called
nws/local.nws and put 'em there. Or maybe organize it into parts:
nws/theme.nws, nws/code.nws, etc. Then run 'make' in the game/txt
directory.
The directions which follow about formatting these files
still applies to the files in the hlp/, nws/, etc. subdirectories.
The format for the indexed text files is a topic line (or set
of lines) followed by the text of the topic. Topic lines begin with "&".
The topic "& help" (usually first in each file) is shown if the player
types "help", "news", or "events" without any topic.
Topic names with themselves beging with an & denote
admin help or news topics, accessible with the ahelp or anews commands.
Here's what a piece of a news file might look like:
& rpg <-- note that both topics will work for this entry
& game
You've typed 'news rpg' or 'news game'.
This is the player information for the role-playing game...
& &rpg
You've typed 'anews rpg', and you must be an admin.
This is the admin information for the role-playing game.
Caution: Only topic lines should start with &'s. Lines of the
help entry which need to start with &'s (like programming examples of
the form: &test me=....) should have at least 1 space before the &
so that it's not the first character on the line.
Now you're ready for that first startup.
Go to the game directory and type 'restart' (or './restart' if "." isn't
in your path).
To monitor the restart, type: "tail -f log/netmush.log", which
will show you the progress of the startup. When it reaches "MUSH startup
completed" (or an error message!), hit ctrl-C to kill the tail process.
(If you got an error, see Troubleshooting, below).
In general, you can check to see if the mush is running by
using "ps -ux" (on BSD systems) or "ps -lf" (on SysV systems),
and looking for the process named "netmush" or "netmud". If you've
got INFO_SLAVE defined, there should also be a process called
"info_slave" running.
Assuming you've succeeded in getting the MUSH up and running
on your chosen port, here are some things you should do immediately:
- Log in as One (no password) and change One's password. (Many
people also rename One to God or some other appropriate name). This is
very important, as #1 is the only player which can confer wizbits on
other players, and needs to be protected. @dig a floating room for God
to live in, and @tel and @link #1 there.
- Make a wizard character for yourself. You can do it while logged into
#1 by using @pcreate name=password, @set *name=wizard. Once you've done
this, you can log out of #1 and into your wizard character. You'll need
#1 later, but it's best not to use God unless necessary.
- Create some guest characters, if you like. You can do this with
@pcreate as above, and @power *name=guest. I typically use the following
kind of code:
@dol Guest Guest2 Guest3 <etc> =
{@pcreate ##=guest; @power *##=guest; @desc *##=A guest. Be kind. }
One of your Guest characters should be either named Guest or have
an alias of Guest.
The MUSH crashes
If the MUSH actually crashes (leaving behind a file called
"core" in the game directory), see the section on MUSH crashes.
The problems addressed here result from the MUSH failing to load the db,
and exiting cleanly.
Problems loading minimal.db
Most problems loading minimal.db are caused by misconfiguring
the compression options in mush.cnf or databases with funny
end-of-line characters.
The symptoms of these problems are messages like:
Bad character 0 on object 0
Bad attribute list object 0
Problems loading your db
"Bad character on object xxx" - the bane of all MUSH Gods. If
you've had this db working before, and you haven't modified the source
code, this indicates there is some corruption of your database. To
recover, you'll want to locate the corruption by hand-editing the db.
Here are the steps:
- MAKE A BACKUP OF THE DB! Do this every time. It's very
very easy to make a mistake. I put mine in game/save,
with names like indb.(date).corrupt
- If your db is compressed, uncompress it.
- Load your db into your favorite editor. Some editors can not
handle some db's, usually because the lines of text in the db are too
long for the editor's buffer (Sun's vi can have this problem), and
sometimes because the db itself is too large (20Mb is a big file :).
GNU emacs version 19 works pretty reliably. If the problem is the size
of the file as a whole (not line length), you can use the unix 'split'
(split indb) command to split up the db into 5000-line chunks which you
can paste back together when you're done using 'cat x?? > indb.new'.
- Find the offending object. The beginnings of objects are
indicated by lines which start with !object# (for example, "!512").
The next set of lines which contain numbers represent the object's
type, flags, powers, chat channels, locks, etc.
- If you're lucky enough to know the attribute on the object that's
causing your trouble, you can find it. Attributes start with a "]" symbol
(for example: "]STARTUP"). If you just know the bad character, start
searching for that character in the object. The attribute name is followed
by some owner and flag information (separated by ^'s) and then on the
next line, the text of the attribute itself, which is a single line but
may contain literal carriage returns (^M's).
- When you find the problem character, try to fix it. The most
reliable fix tends to be deleting that attribute (the ]attrib line, and
the line following it which contains the character), but if you see
something strange (control characters besides ^M, hard returns in attributes,
etc.) you could try just fixing that and see if it helps.
- Quit the editor, saving the new db, recompress it if necessary,
and try starting up under it. Repeat as above until it works. :P
MUSH Management
God, player #1, is a very special Wizard, and a very useful tool.
God can do some things that no other Wizard can, and some commands
function differently for God. Because of these powers, it's wise to
safegaurd God's password at least as well as you would the MUSH
account's password. While the same can be said about any Wizard
password, it goes doubly for God.
Making wizards
God is the only thing in the game which can set a WIZARD bit on a
player. When you recruit Wizards for your MUSH, you'll have to log in as
God to grant them the bit. For that matter, only God can remove
a wizard bit or @nuke a wizard player, a definite safety feature.
Malloc stats
If you have MEM_CHECK defined, or are using smalloc.c with
debugging,
running @stat as God will give you a dump of
memory allocation information internal to the game. This can be
useful in debugging memory leaks and the like.
@startup for @functions
When the MUSH starts up, the @startup attribs on all objects are
triggered, in ascending order of db#. This means that God's @startup
is the first one triggered, which makes it ideal for doing @function
commands, since later @startup's may rely on your @functions being
installed.
Schmidt@Dune (and elsewhere) developed this code for #1's startup
to make automatic @function setup easier:
@STARTUP #1=@dolist lattr(#191/FN_*)=@function [after(##,FN_)]=#191,##
(In this case, object #191 had all the global functions on attribs
call FN_FOOFUNC, FN_BARFUNC, etc.)
Miscellany
- Only God can use the @poor and @allquota commands.
- Only God can use @log/wipe to erase a game log, and this additionally
requires the password given in mush.cnf.
- God may @chown a player, which should only be used if, due to some
corruption, a player ends up not owning themselves. God can also
@boot players who the game lists as not connected. Don't do it.
- God can @wipe wizard-only attributes on things. Even wizards
can not (using @wipe - they can clear them manually).
- Only God may @set, @edit, @tel, @force, or @trigger God.
- Only God can do @mail/nuke and
wipe all @mail. And only God may use the mail() function to
read other players' @mail.
- God may change a player's name (using @name)
without giving the player's password.
What's a dump?
A database dump is the process of making a copy of the MUSH's database
(which is in memory when the MUSH is running) on the machine's disk.
This is absolutely necessary when the MUSH is shut down, so that it can
be restarted in the same state it was in when it was shut down. In addition,
"checkpoint dumps" are performed by the MUSH at regular intervals
(e.g. every hour, every 3 hours, every day, or however you configure it
in mush.cnf). If the MUSH should crash, it can be restarted from a
checkpoint dump, and data loss will be restricted to changes made in the
last interval. Finally, a dump can be performed by a wizard using the
@dump command.
What's a forking dump?
In mush.cnf,
you can decide if your MUSH should fork when it dumps.
"Forking" means that the MUSH makes an identical copy of itself in memory,
and that copy runs the dump and then exits, while the original copy
continues doing MUSH things. This means that players won't even notice
when your MUSH dumps, which is nice. Forking doesn't work on Win32.
There's a downside, however. When the MUSH forks, there are basically
two MUSH processes running. That means durnig the dump, your MUSH is using
twice the CPU (roughly) and twice the memory that it normally would.
Unless your machine has a lot of memory (or your MUSH is very small),
this can cause the MUSH to swap, and lag everyone badly.
The alternative is to dump without forking. While the MUSH is dumping,
it can do nothing else in this case, so the game will pause until the
dump is complete. It gives the players a message to let them know that
there will be a pause.
What happens on a shutdown dump?
A shutdown dump is just like any other dump, but it occurs at shutdown. :)
What's a paranoid dump?
A paranoid dump is performed by a Wizard typing @dump/paranoid.
A paranoid dump differs from a normal dump in that it automatically
corrects certain kinds of database corruption, writing out a non-corrupt
database to disk. This is often useful when your database is corrupted
in such a way that normal dumps cause a crash. You can @dump/paranoid,
kill -9 the MUSH process (DON'T @shutdown, which will overwrite the paranoid
dump with a corrupt one), and then restart from the paranoid dump.
What exactly does a paranoid dump try to fix?
- Bad attribute names: Unprintable characters or spaces in names
of attributes will be replaced with a '!' character.
- Bad attribute owners: If the attribute is owned by an invalid
db#, its owner will be set to God.
- Bad attribute text: If the attribute's text contains
unprintable characters or hard newlines (\r's and \n's), they
will be replaced with '!' characters. The fixing of hard newlines
used to be important, but is actually now more of a hassle since
MUSH can now deal with \r\n's in dbs.
What's a debug dump?
A debug dump is performed by a Wizard typing @dump/debug. It's a
paranoid dump that also tries to repair some of the problems it finds
on the in-memory (running) copy of the database, as well as writing
out a clean database. It only corrects bad attribute owners like this
(but these are the most common corruptions that cause crashing on
dump).
What's a panic dump?
A panic dump occurs when the MUSH process is terminated with a signal
15 (SIGTERM). This is a pretty unusual thing, but if someone accidentally
kills your MUSH process somehow, it'll attempt to save a copy of the
database before it dies. The copy will be stored in game/data/PANIC.db,
and will be uncompressed. You can tell if the dump was successful by
examining the last line of that file. If it's ***END OF DUMP***, it
was successful. The restart script knows about panic dumps, and will
try to restart from a panic dump if it can find a successful one.
How many players can be connected to a MUSH at once? There are 3 factors
which affect the answer to this question.
The operating system
Each player connection comes in over a TCP/IP socket, and each socket
require that the operating system allocate it a "file descriptor", a
number which refers to that socket. In addition, each file which is
opened by the MUSH process (standard input, standard output, standard
error, one for each log file, one reserved for reading help/news/events
files, one to accept connections) requires a file descriptor.
Most operating systems have limits on how many file descriptors a user's
process can open. Some typical limits are 64, 128, or 256. Sometimes
there will be a 'soft limit', which is lower than the maximum, and which
you can increase (via the 'unlimit' command in the csh) up to the 'hard
limit', or system maximum. The hard limit can only be raised by the
system adminstrator recompiling the system's kernel. This is not
too bad to do under SunOS 4.1.x (NOFILE in sys/param.h),
Ultrix 4.2 and later (NOFILE in h/param.h and max_nofile
in conf/{mips,vax}/param.c), and Linux, where you
always have kernel source.
Note: SunOS 4.1.x systems have (by default) a limit of 256
descriptors, but there's a bug in the stdio code which prevents descriptors
after the first 128 from working correctly. :(
When you run out of descriptors and the MUSH tries to open another one,
it will likely either crash or hang. Since the MUSH requires about 10
descriptors for itself (6 if you define SINGLE_LOGFILE), subtract that
from the hard limit to figure out how many players you can support.
CPU/memory/network bandwidth
There's also a practical limit. The more players you have connected,
the more work the CPU has to do to service the process, the more memory
will be used (for player objects and for all those queued commands),
and the more load will be placed on your network connection. At some point,
your game will begin to lag increasingly. Once again, little can be done
unless you have a budget for better hardware.
Setting login limits
Finally, you can set a limit on how many players your MUSH will accept
in your mush.cnf file.
Usually, you just want to set your login limit at the
highest practical value, taking the above things into consideration.
So, if you've got a max of 64 file descriptors, and you don't define
SINGLE_LOGFILE, figure you can have 54 players connected, and set login_limit
accordingly. Remember, however, that your admin and players with the login
@power can connect even over your login_limit, and if they run out your
file descriptors, expect bad things to happen.
Most of the Wizard commands are covered in the Wizard's supplement
to Amberyl's MUSH Manual, but I though I'd mention a few that deserve
special notice.
- @attribute
- In PennMUSH, changes made by the @attribute command are not
preserved when the MUSH is shut down. If you always want to,
say, have a mortal_dark, wizard-only STATS attribute, have
God's @startup include the appropriate @attribute/access command.
- @command
- In addition to its ability to disable commands, @command can also
show you all the switches that a command recognizes and other
interesting info about the way it's parsed.
- @comment
- The COMMENT attrib can't be seen by mortals, so using @comment to
set comments on suspicious players is a common thing to do. To maximize
usefulness, consider a policy whereby whenever a player is set suspect,
they must be commented, and comments must include the name of the commenting
Wizard. If you let any admin set SUSPECT, consider a +suspect player=comment
command which sets the flag and the comment at the same time.
- @disable/@enable
- The options which you can enable and disable are: logins
(when disabled, only admin/login-@power can log in),
daytime (when enabled, cpu-intensive commands can't be
used), command_log (when enabled, all commands are logged),
force_log (when enabled, all @forces are logged),
huh_log (when enabled, all commands resulting in Huh? are logged),
wall_log (when enabled, all @walls and @wizwalls are logged).
- @fixdb
- This very dangerous command allows you to directly set the
contents, location, exits, and next elements of a database object.
No error-checking is performed, so be sure you know what you're doing.
To learn more about these elements, read the source code and play
around with the examine/debug command which displays values from a
db object's structure which are not normally accessible in raw form.
- @kick
- As the help says, @kick causes the immediate execution of a specified
number of commands from the queue. The real question is when to use it.
It can help if your queue is clogged with a non-infinite loop, like a large
@dolist. But what often happens is that while the game is busy executing
the @kicked commands, commands from connected players are building up,
resulting in a large queue clog even after the @kick. I use it as a last
resort, and rarely when many players are connected.
- @log
- This works just like the help says it does, and is really handy
if you want to keep track of things (like when players die by your
combat system or use certain commands). The alternative, logging such
things to some db object, has all sorts of problems as the log gets
bigger and bigger. If you use @log, classify everything you log with
some unique KEYWORD so you can get it from your logs with a simple
grep. I used to have an object which would @log the number of connected
players and admin, and how many were IC, every 30 minutes, and I'd
make histograms out of the data in the log. :)
- @pcreate
- This is how you create characters for people from sites on
registration status.
- @readcache
- Don't forget to run this command if you change the cached .txt
files and want to update them (or, you can kill -HUP the
MUSH process from the account it runs under).
- @sitelock site-pattern[= options]
- Adds the site-pattern directly to the access.cnf file with the
specified options. Wizards can use this to lock out sites. Later
uses of @sitelock override earlier ones.
- @squota
- You can @squota player=+number or -number, to
adjust their quota up or down from its current level.
- @uptime
- On some operating systems, this command's functionality is limited.
On others, the command works, but seriously lags the MUSH while it's
working. Test it once and see.
- @toad
- I'm a fan of disabling this. Wizards shouldn't nuke players, they
should boot and newpassword (and maybe sitelock), all of which are reversible.
Only God should nuke players, and God should be secure enough to do
it with the simple @nuke, not the flashy @toad (which leaves a slimy toad
you often nuke anyway). Rhyanna@Castle D'Image notes, however, that @toad
can be a convenient way for RP MUSHes to make corpses after IC deaths.
Ok, your MUSH crashed. It happens. The good news is that if you compiled
with debugging enabled (-g), you can usually figure out *where* in the code
it crashed and *why*. Even if you personally can't correct the problem,
such information is exactly what you'd want to post/mail if you were asking
for help.
In order to get debugging information, your MUSH crash must have produced
a core dump, which is a file called 'core' in your game directory. This
file is usually quite large. On some systems, you must be sure that your
MUSH restart script does an 'unlimit coredumpsize' or you won't get
complete core files.
You also need a debugger. Most systems have one of the following
debugging programs: gdb (ideal), dbx (a good second choice), sdb (ick),
adb (very ick). You (or your sysadmin) can ftp the source code for
gdb from the GNU ftp site.
Using the gdb or dbx debugger
gdb and dbx are similar, so in the following examples I'll use them
interchangably. If you'll be needing a copy of your debugging session
to mail out, use the unix 'script' command to log everything
('script buglog', do your debugger stuff, then 'exit').
To run the debugger, go into your MUSH game directory and type:
gdb netmush core
The debugger will tell you how the process died, and what function
it was executing at the time.
Getting and using a stack trace
That alone can be useful, but far more useful is a stack trace,
which shows not only the function it was executing that caused it to
crash, but which function called that fatal function (and with what
parameters), which function called that, and so forth, back up to
the very top function, main().
The command 'where' will print out a stack trace. The commands
'up' and 'down' will walk you up and down through the stack, showing
the values of parameters that are being passed. You can also use the
'p' (print) command to print the values of other variables in each
function.
Running the MUSH under gdb
If the crash occurs during the MUSH startup process (when analyzing
or loading the db, for example), and doesn't leave a core dump, you have
another option if you have gdb. You can actually run the MUSH from
within the gdb debugger by going to the game directory and doing:
gdb netmush
run mush.cnf
(According to Rhyanna, on some operating systems, typing 'handle
SIGPIPE nostop' before
running the mud will prevent the debugger from reporting SIGPIPE
signals when players connect.)
When the MUSH crashes, you'll be told how and where, and be returned
to gdb to analyze the crash. Gdb and dbx can do much more - you can
tell them to run the MUSH until they hit a certain function and then
wait for further instructions, step through code one line at a time,
etc. Read the manual pages if you need to do these things.
Trashed stacks
Some crashes, alas, overwrite the memory containing the function stack,
and produce a totally unhelpful core file. The only help I've found for this
is to cross my fingers and run the MUSH again, hoping that the next crash
will pick a different memory location to overwrite. In some cases,
'touch'ing all the MUSH source files (touch *.c) and recompiling can help,
as can using 'make clean; make', but it's mostly superstition.
One of the most pernicious problems common to MUSHes is lag, the condition
in which the MUSH feels slow in responding to player input. There are
a number of things which contribute to lag, and once you identify the
culprit, you can decide if you can improve the situation.
Network lag
Network lag is caused by difficulties in the network connection between
the player and the MUSH host machine. For example, a router on the
internet between the two might be dropping packets, or a segment of
the network might be overloaded with packets.
The characteristic property of netlag is that you won't experience it
if you're connecting to the MUSH from the MUSH machine itself. If you
can get a lagless connection by doing a 'telnet localhost <port#>',
netlag is responsible.
If your host has the ping command (most do), you can test how long
it takes a packet to travel between your host and another machine, and
try to identify how slow things are going to be. If you happen to have
the traceroute command, you can see exactly where (between which
routers) the network is lagged.
Unless you happen to be a network administrator of the problem stretch
of network (or if it's just the local connection to your machine), there's
not much you can do. If the problem is your particular net connection,
you can probably spend more money and get one with a higher bandwidth,
or reduce other things your machine does that requires the net (email,
etc.), but neither of these are really worth it for a MUSH, usually. :(
DNS and IDENT lag
Another network-related source of lag involves domain name service
lookups. When a player connects to the MUSH, the MUSH knows the
player's IP address, and queries the DNS to get the player's
hostname. This is called a "reverse hostname lookup".
Some hosts, however, have very slow nameservers. Sometimes this is
because
the nameservers are behind slow internet connections with heavy
traffic. The MUSH stops while a reverse hostname lookup is going on,
so if it takes more than a second, you will feel lag.
If you've got your MUSH configured to use IDENT lookups, the same kind
of problem applies. In addition, IDENT lookups of non-unix systems can
hang until the lookup times out.
There are a few ways you might deal with this problem:
- #define INFO_SLAVE in options.h. This creates a separate process
that handles lookups, so your MUSH won't have to. This doesn't
work well on win32 systems.
- Turn off ident or reduce the ident_timeout value in mush.cnf
- If you don't need hostnames, set use_dns to "no" in mush.cnf
and no reverse hostname lookups will be performed.
- If there are particular sites that are causing you trouble, and
if you have access to your system's hosts file (/etc/hosts on most
Unix systems), you could try making an entry for the troublesome
system in the hosts file. Many DNS setups will check the hosts file
before asking the nameserver.
CPU lag
CPU lag is caused when the MUSH machine is having to split its time
doing many tasks or tasks which require a lot of running time spent in
the CPU. If your MUSH is on a machine which has a lot of users, this
is more likely. If the users are programmers who run things like
compilers regularly, this becomes much more likely.
You can examine the CPU load in a few ways. The uptime command
will display an interesting, if non-objective statistic called
"load average", measured over the last minute, 5 minutes, and 15 minutes.
If you know what typical load average looks like, you'll be able to
recognize abnormally high load. Loads over 3, especially in the 5/15
minute entries, tend to make for a slow game.
But what's causing the load? Here you can use ps -aux (BSD) or
ps -elf (SysV) to see all the running processes and how much
CPU time they're getting at that moment. This static picture can be
deceiving, but is a good start. Read the man page for details.
If you're responsible for the load due to your own compiling and
such (or if you need to decrease the
CPU load your MUSH puts on the machine), read the man page for
the nice and renice commands, which let you tell
the system that your compilation (or MUSH process) should be nice
about using CPU, and the CPU should give it lower priority.
Disk swapping
Unix systems have a limited amount of memory in which to run their
programs. Memory is also expensive. So unix systems use a part of
the disk as "virtual memory" or "swap space". Processes send
parts of their memory that haven't been accessed in a while off
to virtual memory, a process called paging. Paging helps make
all the programs work together gracefully.
MUSHes can be pretty big programs, though, and sometimes another
program (some compilers and editors, as well as statistical
software and other such things, for example) has to be granted more
memory than can be recovered even with paging. In these situations,
the whole MUSH process may be "swapped out" to the disk, temporarily
put on hold until it can be swapped back into memory. While the
MUSH is swapped out, the game is frozen, and if it stays swapped
long enough and often enough, you experience lag.
Dealing with swapping and paging is beyond the scope of this
guide. Read the man pages for ps, vmstat, pstat,
and iostat, or pick up a book on Unix System Administration
or Performance Tuning (say, the O'Reilly Handbooks, which are great)
if you're the system administrator of your MUSH machine. If not,
purge unused objects from your database and hope.
Queue lag
Queue lag occurs when the MUSH's queue becomes clogged, and the MUSH
can no longer keep up with servicing the queue and player input.
Players get priority, so keyboard response will be good, but if they
try to use $commands, which go on the object queue, they won't get
response for some time.
@kick can be used as a temporary fix for this, though see the concerns
above in the section on Wizcommands. A more
permanent solution might be to adjust the values of queue_chunk
and (especially) active_queue_chunk in
the mush.cnf file. This will make
keyboard response slighly worse, but will usually fix the queue
clogging.
Security on a MUSH (which many feel is an oxymoron already) addresses
the needs of the game to provide four or five crucial factors:
- Stable service
- Players should be able to play the game - it should be up reliably.
- Database control
- Players should not be able to modify objects belonging to other players.
- Privacy
- Private player information (email addresses, sites, private conversations)
should be inaccessible by unprivileged players.
- Freedom from harassment
- Players should be free of harassment from other players
(gross spamming, for example.)
- A fair game
- Players should not be able to use MUSH knowledge to cheat in any
game run on the MUSH, particularly if the game is the point of the MUSH.
Unwelcome players can generally be broken down into two major categories:
crackers, who threaten stable service and database control,
and twinks, who threaten privacy, freedom from harassment, and game
fairness. A cracker seeks to destroy the integrity of the game by
making it unusable. A twink seeks to destroy the integrity of the game
by making it unenjoyable or unfair.
A cracker philosophy
Some people who try to compromise MUSH security claim that they're
doing you a service, by showing you where you MUSH needs shoring up.
As Amberyl once put it, this is akin to someone breaking into your
house (unasked, of course) to show you that your locks should be repaired.
These folks not only put your game and database at risk, not only might
become privy to sensitive information (player email addresses, etc),
but destroy the trust which generally exists within
the internet and MUSH community. It's no service.
Unfortunately, the crackers usually win, because they have a lot of
time to spend trying to compromise your security, while you and your
Wizards have more to do than just continually improve it. The goal here,
therefore, is to emphasis some easy measures that you can
take to deal with attacks on your MUSH.
A twink philosophy
While the goal in dealing with crackers is to keep them away from your
MUSH or minimize the damage they can do, twinks come in many flavors
and require individual response. Many twinks are misguided newbies who,
once set straight, become valuable players. Others merely seek to disrupt
the game, creating dozens of characters and paging regular players with
annoying messages. As Gilbert and Sullivan put it, with twinks it's often
best to "let the punishment fit the crime."
SUSPECTs
The suspect flag makes an excellent first line of defense against
suspected problem players. In addition to allowing you to tell when
a SUSPECT player connects, disconnects, or changes their name,
all actions by SUSPECT players are logged in the MUSH's command.log
file, so you can decide if they really pose a threat.
There's a script by Talek in the Appendix
which I've used to process command.log and create individual
suspect files, one per player (by db#), which can be read or searched
for keywords like "hack" or whatnot.
By the way, I think it's good policy for every MUSH to place a statement
in news ('news policy' perhaps) noting that while every attempt is made
to preserve privacy, you reserve the right to log commands executed
in order to maintain MUSH security and/or to fix bugs.
Registration and lockout
Site registration, and site lockout, are strong measures which
can be very effective in preventing twink invasions. The key to
site-based access control is the access.cnf file.
You can control the following aspects of access:
- Whether or not a site can connect to guests
- Whether or not a site can connect to non-guest characters
- Whether or not a site can create characters at the login screen
- Whether or not a site can use the "register" command at the login
screen, to request that a character be created and its password
be emailed to the player.
- Whether or not a site is suspect. Players who connect from suspect
sites are automatically set SUSPECT.
- Which of the above your Wizards can override using @sitelock,
and which are set in stone.
access.cnf (in the game directory) is a list of sites and
their access control options. When someone connects, the file is
read through in order, and the first line which matches their site
is used to determine their access options. Order of the file is thus
critical.
A line in the file looks like this:
<host-pattern> [<option> <option> ...] [# Comment]
Host patterns may contain the wildcard "*" which will match any
number of characters. Patterns can match individual users from
sites which run ident servers, but because ident relies on
trusting the server and a reasonably speedy network, it's best avoided.
Using "*" as the host pattern matches all hosts, and really only makes
sense as the last line of the file.
A host which does not match any line in the file is allowed complete
access. A host which matches a line with no options listed is
banned completely; connections will dropped before the login
screen.
There is also a special line in the file:
@sitelock
The "@sitelock" line marks where new additions to the file via the
@sitelock command will be added (if your file doesn't contain one,
one will be added at the end of the file when someone uses @sitelock).
Any lines listed above the @sitelock line can not be overriddent by
@sitelock; those listed below can. This allows you to decide which
access control rules your Wizards should be able to override.
The available options include:
- create
- allow players from this site to use the 'create' command
- !create
- don't allow players from this site to use the 'create' command
- connect
- allow players from this site to connect to non-guest characters
- !connect
- don't allow players from this site to connect to non-guest characters
- guest
- allow players from this site to connect to guests
- !guest
- don't allow players from this site to connect to guests.
- register
- enable the 'register' command at the connection screen. Players
may type 'register ' to create a new character
and have its (randomly generated) password emailed to the player. Note that
this does not disable 'create', so you'll probably want to
use this option along with the !create option.
- suspect
- cause all players connecting from this site to be set SUSPECT. Unless
you want SUSPECT guests,
you'll probably want to include !guest as well.
The @sitelock command sets the optional comment to inform
you of who performed the @sitelock and when.
access.cnf is re-read when the MUSH receives a HUP signal.
It is updated whenever someone uses @sitelock.
The file game/access.README contains examples of using access.cnf
to enforce a variety of different access control policies.
Gagging, booting, guesting, newpasswording and nuking
There are a wide variety of things you can do to discipline a
twink player. Amberyl's wiz-ethics lecture discusses many of these,
and I, like her, believe that one should "let the punishment fit the
crime" which choosing which command to use:
- The GAG flag
- Prevents the player from speaking, a handy response to spammers
and those who page obscenities.
- The Guest power
- Restricts a number of commands players can use, depending
how how you've set up the command restrictions in mush.cnf.
Often prevents any building or attribute-setting.
- The FIXED flag
- This flag prevents the player from using the
@tel and home commands. Handy if they keep @tel'ing where they're not
wanted.
- @boot
- Disconnects the player from the MUSH. Nothing, of course, prevents
them from reconnecting, unless you lock their site or install a
global @aconnect to listen for them connecting and boot them again.
- @newpassword
- Changes a player's password. This is the proper way for non-Gods
to "nuke" a player, as it doesn't actually result in any data loss,
and can be reversed if the twink makes amends. This does no good against
twinks who are just creating scratch characters they don't care about.
God can also re-name a player without knowing/changing their password,
which can have similar effects.
- @nuke
- This is how God cleans players from the database. It's also an
appropriate response (along with a sitelock) against crackers.
Coded attacks on the MUSH (privacy, denial of service)
Most twinks just want to rise to ultimate power in your game.
But some crackers are interested only in compromising your security
in order to destroy and deny the game to others.
Admin passwords must be protected. A cracker
who can log in as a Wizard can destroy your database in one command.
Players should not be given wizard-bitted objects if at all possible.
Wizard-bitted global commands should be carefully checked for security.
One important check is to try calling the command with arguments
like "[set(me,visual)]" and "\[set(me,visual)\]" - if the function results
in the wizobject becoming visual, it's not secure! (And Wizard globals
shouldn't be visual anyway, just in case.) Best is if you can run
without FUNCTION_SIDE_EFFECTS.
Be very careful of zones. My policy is that nothing owned by an admin
should ever be zoned to a ZMO which has non-admin on the elock.
Instead, it should be @chowned to a mortal builder-character and then
zoned. You can find ZMO's without elocks by logging in as a mortal
and running an @find to see what you control.
In the worst-case scenario, if you somehow lost your entire database,
you'd still be okay if you've been making backups of your database
every now and then (how often and how many depend on your disk space.)
Always have at least one working backup somewhere. And chmod a=r it, too,
so you don't accidentally overwrite it! If you really want
disaster-recovery, you should keep a database backup on another
machine as well, in case your host should crash.
If you want to encourage people to visit your MUSH and try it out,
make some Guest characters available. Guests are usually severely limited
in the commands they can use, via restrict_command directives in mush.cnf
You can have as many Guest characters
as you see fit. Each guest should be set
@power Guest. When a player logs into a Guest-@powered
character, the following happens:
- If there's no one else connected to that Guest, the player is
connected.
- Otherwise, the server searches through the database for another
Guest-powered character which isn't in use. If it finds one, the
player is connected to that Guest.
- Otherwise, the server gives up, and connects the player to the
original Guest (resulting in multiple players controlling one character)
A typical setup is to create 5 or so Guests with names like
"Guest", "Guest2", "Guest3", etc., and ask players to connect
to simply "Guest", and let the server assign them to an unused one. If
you
want to use other names for your guests, be sure that one of them has
"Guest" as an alias, as most people will try to 'connect Guest' at the
connection screen.
Whenever you log into the MUSH or the MUSH account, there are a few
things that are worth doing as part of your regular routine.
This is just about making sure that things look fairly normal.
@stat
One simple thing that's worth a minute every time you connect is to
do an @stat command to see how many objects are in the database. If you
find that it's jumped 2000 in a day, you might suspect somebody's been
mucking around. If you find a lot of garbage, somebody's been nuking
a lot of things. If you find a lot of players and know you don't have
that many active players, maybe it's time for a player purge (discussed
below.)
@uptime
If the @uptime command doesn't lag your MUSH, running it can give you
some useful information. In addition to the load average (discussed
above under lag, it reports the PID# of the
MUSH process, a bunch of memory statistics, of which the most useful
is probably Max res mem (maximum resident memory used by the game, in bytes),
a lot of i/o information, and the head of the object free list, the
object db# which will be used for the next object that's @created.
If it's #-1, the free list is empty (you have 0 garbage) and new objects
will not be using recycled space.
Keep an eye on max res mem, to get a feel for how it grows as your
MUSH does.
Player purges
After a while, you may want to remove old players and their objects
from your game. I typically use tinyfugue macros, rather than MUSHcode
to do this. First, I define a macro /nuke <player>, which nukes a player
and everything they own, providing they're not currently connected:
/def nuke = @switch hasflag(pmatch(%1),connected)=1,{think %1 is
connected!}, {@dol lsearch(%1,none,none)=@nuke ##}
The connected check prevents you from accidentally nuking yourself.
Then, I define a player purge macro like this:
/def ppurge = @dol lsearch(all,type,player)=@switch
[and(not(orflags(##,Wr)),lt(convtime(xget(##,last)),sub(convtime(time()),
2419200)))]=1, {think /nuke [name(##)] ## [lstats(##)] }
This macro searches the db for all players and lists those who aren't
admin and whose last connection was longer than 28 days ago.
Actually doing a purge, then, requires these steps:
- /log purge, to start logging output to a file called 'purge'
- /ppurge, to list players who are candidates for purging
- /log off, to stop logging
- /sh vi purge, to edit the purge file and see who'll be purged.
Delete lines in the file for players who you don't want to see purged.
What's left will be a list of /nuke lines which will purge the players
you want purged.
- /quote 'purge, to load the commands in the purge file and do the
actual purging.
- /sh rm purge, to remove the purge file
New PennMUSH patchlevels are typically released in two forms:
a complete new .tar distribution, and a patch file that can
upgrade a server's source code from one patchlevel to the next.
Both can be found on the PennMUSH ftp site
in the /pub/PennMUSH/Sources directory. In addition, patch files
are emailed to members of the PENNMUSH and PENNMUSH-ANNOUNCE
mailing lists.
It's very important to apply patches - they fix (often serious)
bugs in the server and add new functionality. An alternative is
to get the latest .tar.gz file and rebuild from that, but patches
often save you a lot of time.
Patches are intended to patch
from one patchlevel to the next. So if you're running 1.7.3p2
and you want to go to 1.7.3p9, you must apply 1.7.3-patch03, 1.7.3-patch04,
etc., up to 1.7.3-patch09, in order. Each patch should be fully
and successfully applied before doing the next.
Before applying a patch, it's wise to have a backup of your code.
Although there is a mechanism for 'reversing' a patch if it's not
to your liking, the mechanism isn't perfect, especially if the
patch fails to apply in parts (this is usually only an issue
if you've been hacking the server -- patches should apply cleanly
to an unhacked server).
There are many ways to make a backup. The easiest is probably
to just use tar or zip to make an archive file of your whole
pennmush directory, or to use cp -r to make a copy on the side.
The best ways involve using revision
control systems.
Every official pennmush patch has instructions at the top for how to apply it.
Read them.
Sometimes you must @shutdown your MUSH after applying
a patch or do other special things. Usually, the instructions are
simply:
cd pennmush
patch -p1 < the-patch-file
make
But it's important to read each patch and follow those instructions.
Sometimes you must re-rerun Configure, sometimes shutdown and
restart fully (not @shutdown/reboot), etc.
IF you receive the patch by email, you can just save the message
to a file and use that as your patchfile. The patch program
will ignore the email header junk at the beginning - it's smart.
Some people prefer to use: patch -p1 -s < the-patch-file
This version only shows you errors, not successful patches,
and makes it easier to find problems.
If a patch hunk fails, it'll leave behind a small .rej file
showing what it was trying to do. It's up to you to make these
changes by hand, which means learning
how to read patch files.
But if you haven't hacked
your server, this shouldn't happen much at all. Unix users
ignore rejected hunks in the src/switchinc.c, hdrs/funs.h,
or hdrs/cmds.h files, as these are automatically
rebuilt when you type make, and can also ignore rejected
hunks in the win32 directory (these files aren't used under Unix)
Win32 users who are compiling from source code can find a win32
version of patch on the ftp site in the Win32Binaries section.
Win32 users who use a prebuilt binary can't apply patches,
because patches change the source code.
The motd-maintainer
It's really convenient to be able to set the MUSH motd, wizmotd,
and nologinmotd from within the MUSH. But when you @shutdown, those
messages are lost. This code provides a way to preserve the motds
across shutdown. It works like this:
Instead of directly setting the motd's with @motd, Wizards should
set the motd's as attributes on this object (&MOTD, &WIZMOTD,
&NOLOGINMOTD),
and then type 'setmotd', which will post them via @motd. If the game is
shut down, the attribs will continue to store the motd's, and on restart,
the motd's will be reposted via the object's @startup code. This code
actually builds the motd out of any attribs starting with MOTD, so you can
maintain a set of motd's more easily.
Here's the important code:
@create MOTD Device
@link MOTD Device = #2
@lock/use MOTD Device = iswizard/1
@set MOTD Device = !NO_COMMAND
@set MOTD Device = SAFE
@set MOTD Device = WIZARD
&DO_SETMOTD MOTD Device=$setmotd:@tr me/U_SETMOTD=%#
&U_SETMOTD MOTD Device=@motd [iter(sort(lattr(me/MOTD*)),%r[eval(me,##)])];@wizmotd [eval(me,WIZMOTD)];@pemit %0=MOTD's set.
@STARTUP MOTD Device=@tr me/U_SETMOTD
&ISWIZARD MOTD Device=[hasflag(%#,W)]
@DESCRIBE MOTD Device=Motd is stored in attributes of the form MOTD*. Type 'setmotd' to set the motd.
&MOTD-1 MOTD Device=First motd message
&MOTD-2 MOTD Device=Second motd message
&WIZMOTD MOTD Device=Admin motd message
@tel MOTD Device = #2
The MUSHclock (autoshutdown, announcements)
Sometimes, it's useful to have a MUSH object which performs a command
at regular intervals (for example, every hour on the hour). This object
is a MUSHclock which, upon startup, figures out how long to wait until
the hour starts, and then performs its function, @wait's an hour, and
repeats. In this example, the only function it performs to to
make an announcement if there's one set in the ANNOUNCEMENT attrib:
@create MUSHclock
@set Announce = SAFE
@power MUSHclock = announce
&ANNOUNCEMENT MUSHclock = Your messages here
&ANNOUNCE MUSHclock=@switch [eval(me,announcement)]=,,{@wall [eval(me,announcement)]}
&LOOP MUSHclock = @tr me/announce; @wait 3600=@tr me/loop
@STARTUP MUSHclock=@wait [sub(3600,mod(secs(),3600))]=@tr me/loop
After uploading this object, @trigger its STARTUP attribute to start it.
While this kind of code has many uses (weather and ecology systems
often work on similar loops), two I've seen are (1) hourly announcements
via @wall, and (2) causing the MUSH to automatically @shutdown at a
specified time (9 am). The latter code is combined with an automatic
restart script on the machine that restarts the MUSH each day at 5 pm,
allowing a MUSH to operate only during non-prime hours.
The port announcer (announce.c)
MUSH is distributed with the source code for a port announcer,
a small program which listens on a port for a connection, spits out
a text file, and closes the connection. A useful thing when your
MUSH is down indefinitely and you want to keep people posted on its
status.
The distributed announce.c code, unfortunately, has a number of
problems which keep it from running correctly in many cases, and
may also cause it to crash. I use an alternative port announcer
which works just great, called portmsg.c. Read the
Appendix on
Scripts and Programs for information about how to get portmsg.c.
When you've got it,
compile the program with: cc (or gcc) -o portmsg portmsg.c
Run it with: portmsg textfile port#
You'll have to kill it by hand when you're done with it.
Hacking PennMUSH
What? There's a command that your MUSH really needs, a function not
in standard PennMUSH, a bug you've discovered? In cases like this, the
fact that you can "Use the Source, Luke" can greatly enhance your
enjoyment of MUSH, your understanding of MUSH, and your MUSH. :)
I should probably insert here the standard cautions about not adding
loads of feeping creatures ("feeps") and kludgy hacks to your MUSH, but
I won't. Feeps are fun and usually harmless, and as long as you're
happy with your code, don't let anyone tell you otherwise.
While there are no rules, then, there are a few guidelines I try to follow
when deciding what *not* to hack:
- Avoid hacks which permanently alter the db or maildb structure, unless
you know that you'll always be willing to support the code and cope with
possible future PennMUSH upgrades, or if you don't care about ever
upgrading. The sort of thing I'm talking about here is adding
a couple new variables to the db structure.
If you think your hack is of general interest, let me know and
if I agree, you'll be assigned an official db-flag for your hack,
and we'll arrange for built-in db conversion code to turn your
changes on or off (the way USE_WARNINGS works, for example).
Another good approach is to use the LOCAL_DATA #define and local.c
to implement hacks to the database.
- Avoid hacks which require the MUSH to filter all outgoing text.
Ansi and HTML filtering is
bad enough. While I've seen some beautiful commands like @wrap
(which word-wraps output to players), each time you add a filter to
all the MUSH's output, you significantly increase CPU requirements.
Leave client-functions to clients, if possible.
- Avoid hacks with very limited functionality. If you've got spare time to
hack, try to implement the @mail alias (mailng list) system that
Javelin's been stalling on, or something.
- If the hack is something that seems very useful, email the idea
and/or the source code (a context diff) to dunemush@pennmush.org,
the current PennMUSH maintainer (me). It may well appear in the next
release!
Source code control
If you hack at the PennMUSH source code, you will eventually make a
mistake, and want to go back to an earlier version of your work. Or if
you make a really good change, you may want to distribute it to others.
For this reason, you should always use some form of "source code
control" or "revision management" when hacking source. Here are some
common revision management systems:
- Backups. Make a directory under your source directory called
"oldsrc" or whatever, and put a copy of your source code into
it. When you make changes, you can recover your old files from
oldsrc, use it to produce patches, and eventually copy your new
files into oldsrc when you're sure they work. Many people keep
a "clean" source directory containing the original stock pl10
code, in case they need it. Of course, if you need to go back
more than one revision, you're in trouble unless you clutter your
disk with many many oldsrc directories. A variant of this
strategy involves storing older versions as compressed tar files.
- SCCS. SCCS (source code control system) is a more sophisticated
way to manage source code. It stores changes from version to
version in a subdirectory. You "check out" files to work on them,
and "check in" files that you've hacked. You can revert to any
revision at any time. This is good. Many major unix systems
(Ultrix, SunOS, HP-UX) come with sccs installed. Read the man
pages for info.
- RCS. RCS (revision control system) is the GNU project's free
replacement for SCCS, available from
ftp.gnu.ai.mit.edu in /pub/gnu.
The commands are different from SCCS, and some
things are easier to do. RCS is standard with Linux. There's
a front-end program by GNU called CVS, too. RCS can also be used
to ease upgrading to a new patchlevel by preserving your hacks to
the older patchlevel (details below).
- prcs. prcs (project revision control system) is
Javelin's current favorite. It's available from
http://www.xcf.berkeley.edu/~jmacd/prcs.html.
Javelin likes prcs because it thinks about entire projects (like
'PennMUSH') rather than in terms of individual files, and has
excellent support for automatically merging new PennMUSH releases
into your locally modified version.
If you choose to use SCCS, RCS, or prcs (and I can't recommend it highly
enough), discipline yourself to *always* check in code after each
revision, so that you can undo each step.
If you have multiple people hacking (especially from
different accounts on the machine), you can take advantage of the fact that
RCS and SCCS will "lock" revisions so that only the person who checked
it out can modify it and check it back in, preventing two people from
making inconsistent changes. Or use CVS or prcs, which allow (and
expect) multiple people to change things at once, and try to help
deal with possibly conflicting changes.
When you get your first pennmush distributeion, check in the entire
source directory. With prcs, that's:
prcs checkout pennmush
prcs populate
prcs checkin
Applying patches with patch
Changes and bugfixes to the MUSH code are often distributed as
"patches" or "context diffs", which are files which describe how the
source code should be changed. The program "patch" (by Larry Wall,
distributed by the GNU project, see above for ftp site) automatically
reads these files and makes the changes to your source code. If you
don't have the patch program, ask your system administrator to get it!
Typically, if you receive a patch from a mailing list or newsgroup,
you simply save it to a file, and, from within your source directory,
type:
patch -p0 < patchfile
in order to process the patch.
When patch fails - reading context diffs by hand
Here's a guide to reading diffs (and rejected parts of patches,
commonly files ending in .rej after you try to apply a patch.)
It's based on an email message by T. Alexander Popiel.
- If the diff begins with the line "Prereq: <somestring>", then
it means that in the file that follows, the string should be
present within the first few lines, or the diff should not be applied.
Patch checks for prerequisites to help insure that you're patching
the right version of the source code.
- For each file in the diff, there are two header lines, which
look like this:
*** filename1 date1
--- filename2 date2
This indicates that the diff is the difference between filename1 and
filename2, and should usually be applied to filename2.
- After the header lines, the diff will indicate which line
numbers in filename1 (the source) are to be examined, what should be changed or
deleted,
what the resulting line numbers in filename2 (the destination)
are, and what should
be changed or added:
- A '-' in the first column of the source part of the patch
indicates a line deletion.
- A '+' in the first column of the destination part of the
patch indicates a line addition.
- A set of '!'s in the first column in both the source and the
destination indicate a line-group replacement; a group of
consecutive lines in the source are replaced with a
corresponding group of lines in the destination.
Making patches with diff -c or prcs diff
But what if you want to make a patchfile of changes you've made,
to give to someone else? The "diff" program (a standard unix utility)
can produce the patchfile, given the original and revised source code
files. For example, if you revise player.c, and save the older version
as player.c.orig, you could make a patchfile like this:
diff -c player.c.orig player.c > patchfile
The "-c" switch indicates that you want a context diff, which is
more detailed than an ordinary diff and better for patches. If you're
going to publicly distribute the patch, be sure it's a context diff!
The order of the files is important: diff -c originalfile newfile.
If there's more than one source file changed, you can do this:
diff -c player.c.orig player.c > patchfile
diff -c game.c.orig game.c >> patchfile
If you use prcs, there's an even easier way to make patchfiles.
Before you check in your revisions, type (for example):
prcs diff > patchfile
prcs diff makes a diff from the last checked-in revision to the current
version of the project. If you read the man page, you'll see that it can
also make diffs between checked-in revisions, for single files, etc.
Even if you don't use prcs, you can quickly make a collection of
diffs across the whole PennMUSH source tree by using David
Cheatham's mkpatch shell script, which is
available at
ftp://ftp.pennmush.org/pub/PennMUSH/Accessories/mkpatch.
mkpatch works only for diffs of changes on distributed files.
If you add new files, you'll have to handle them manually.
#ifdef and #define
You can save yourself a lot of hassle if you're careful in how you hack
the PennMUSH code. When you decide to add new code, or change old code,
add an #define into options.h which will turn your code change on
or off. For example, if you're adding a new flag called NOMAIL,
put something like this into options.h:
/* If defined, the NOMAIL flag (a toggle on players) will be in the game,
* which prevents players from using @mail commands.
*/
#define NOMAIL_FLAG
Then, surround your additions with #ifdef NOMAIL_FLAG...#endif pairs.
For changes, use #ifdef NOMAIL_FLAG...#else...#endif. This allows you
to preserve the original PennMUSH coding, should you ever need to refer
back to it (if, for example, you're trying to apply someone else's
patch to something you've already changed), and allows you to turn on
and off your feature as necessary.
This sections contains information on how to do the most common
kinds of hacks that people want added to their MUSH: new flags, new
powers, new attributes, new functions, and new
commands.
None of this applies to PennMUSH 1.7.7p6 or later! In those
versions, God should read 'help @flag' and use that to add a flag.
This entry will be revised later, but none of it is correct for these
versions
Unbeknownst to most players and admin, the MUSH actually has 5 different
sets of flags. Technically, there is 1 set of true generic flags, which can be
set on any type of object in the database (thing, player, exit, room).
For example, the OPAQUE flag can be set on any object, though it's not
meaningful for rooms. The same
is true for CHOWN_OK, which is not meaningful for players.
The other 4 sets of "flags" are called toggles, and each set can only
be used with one of the types of object.
There are a limited number of flags/toggles which can be created in
each set, because internally, flags are represented as bits in a 32-bit
integer. Thirty-two bits means 32 possible flags in each set. Flag space
is scarce for generic flags, since most of the MUSH flags are of this
type. There's plenty of room to add new toggles, however, and in general,
if your new flag idea only applies to a single kind of object, you should
add it as a toggle.
As an example, we'll add a (silly) toggle for players called "NOMAIL"
(letter 'i') which prevents them from using the @mail commands.
Adding a flag is a 3-step process. First, you must edit flags.h
and #define a name and bit pattern for the flags. You should be able to
follow the way it's already done in the file. One hint is to try to
use bit patterns from the highest possible (0x80000000) and work downward,
since new flags added by Amberyl work their way up, and upgrading to a future
patchlevel will be more difficult if your flags overlap new ones. If you're
adding a toggle, you don't need to start all that high, since it's unlikely
that large numbers of toggles will be added to future releases, but better
safe than sorry.
In our example, we add these lines:
#ifdef NOMAIL_FLAG
#define PLAYER_NOMAIL 0x80000000
#endif
Next you must edit flags.c, and find the flag table. Usually, I just
search for a nearby toggle, like PLAYER_TERSE. The flag table is where you
list the name of the flag as it appears when you examine someone, the
one-character abbreviation, its definition, and who can set it.
Our added lines to the table looks like this:
#ifdef NOMAIL_FLAG
{ "NOMAIL", 'i', TYPE_PLAYER, PLAYER_NOMAIL, F_WIZARD, F_WIZARD },
#endif
That means that the flag will appear as "NOMAIL", abbreviated 'i',
is a toggle on players (TYPE_PLAYER - a generic flag is NOTYPE),
is defined by the PLAYER_NOMAIL #define, can only be set by
wizards, and can only be reset (turned off) by wizards.
You must add TYPE_PLAYER toggles in the section with other TYPE_PLAYER
toggles, and the same goes for other toggle types and for NOTYPE (generic)
flags - always add them in the appropriate section of this table, not
just at the end!
The valid flag-setting values are:
- F_ANY
- anybody can set this on
something they control
- F_OWNED
- you must own the object to set this
- F_INHERIT
- only inherit objects can set this
on things they control
- F_ROYAL
- royalty/wizards can set this on things they
control - note that royalty typically don't control anything more than
normal players!
- F_WIZARD
- wizards can set this on things they control,
which is basically everything
- F_GOD
- only God (#1) can set this
- F_INTERNAL
- nobody can set this - it's used internally by the game - used for flags
like MARKED
- F_DARK
- Only God can see if this flag is set or not.
- F_MDARK
- Only admin can see if this flag is set or not.
- F_ODARK
- Only admin and the owner of the object can see if this flag is set or not.
If you want to
require that only inherit wizards can set a flag, you can use
F_INHERIT | F_WIZARD.
While you're in flags.c, also edit the flag alias table further down,
which indicates what partial names and aliases can be used to refer to a
flag when @set'ing it. You might add entries that look like this:
#ifdef NOMAIL_FLAG
{ "NO_MAIL", "NOMAIL" },
{ "NOMAI", "NOMAIL" },
{ "NOMA", "NOMAIL" },
{ "NOM", "NOMAIL" },
#endif
Basically, add all the unique abbreviations of the flag's name.
Next, you need to figure out what (if anything) your flag is
supposed to affect in the code, and add in code that checks for the
presence of the flag. For example, we could check in game.c when
someone tries to use an @mail command. A common way to handle this is
to edit dbdefs.h and define a macro like:
#ifdef NOMAIL_FLAG
#define NoMail(x) (IS(x,TYPE_PLAYER,PLAYER_NOMAIL))
#endif
And then in game.c, you can deny the player the @mail commands if
NoMail(player).
When you're defining macros for toggles, define them using
the IS() macro above. When you're defining macros for generic flags,
you can define them as (Flags(x) & FLAGNAME). For example, the
definition of Visual(x) is:
#define Visual(x) (Flags(x) & VISUAL)
Finally, you need add a line in options.h to turn all your new code
on:
/* Prohibits the use of mail commands
* (This comment block is optional, but a good idea)
*/
#define NOMAIL_FLAG
That's it! Recompile and you've got a new flag.
Addendum: Adding multi-type toggles
Because there's so little room for generic flags and so much room
for toggles, you can also simulate a generic flag by defining
toggles for multiple object types with the same name. Here's how:
- As above, create the bit patterns for each toggle in flags.h.
For example, for an IC flag that applies to players, objects, and rooms,
you'd define ROOM_IC, PLAYER_IC, and THING_IC bit patterns. These
don't need to be the same bit patterns for each!
- As above, add entries to the flag table in flags.c for each
of the new toggles, in the appropriate place in the table (put the
room toggle with the other room toggles, the player toggle with the
other player toggles, etc.) Each should use the same name (e.g. "IC").
- Also in flags.c, add each of the flag table entries
to the hack_table array, which appears later in the file. By
default, hack_table looks something like this:
static FLAG hack_table[] = {
{"MONITOR", 'M', TYPE_PLAYER, PLAYER_MONITOR, F_ROYAL, F_ROYAL},
{"MONITOR", 'M', TYPE_THING, THING_LISTEN, F_ANY, F_ANY},
{"MONITOR", 'M', TYPE_ROOM, ROOM_LISTEN, F_ANY, F_ANY},
{"LISTEN_PARENT", '^', TYPE_THING, THING_INHEARIT, F_ANY, F_ANY},
{"LISTEN_PARENT", '^', TYPE_ROOM, ROOM_INHEARIT, F_ANY, F_ANY},
{"Z_TEL", 'Z', TYPE_THING, THING_Z_TEL, F_ANY, F_ANY},
{"Z_TEL", 'Z', TYPE_ROOM, ROOM_Z_TEL, F_ANY, F_ANY},
{NULL, '\0', 0, 0, 0, 0}
};
This shows you how the MONITOR, LISTEN_PARENT, and Z_TEL multi-type
toggles are entered in hack_table. Do likewise. The NULL line must
be the last entry.
- Just below the hack table in the code, you'll find a bit of code with a comment
that starts HORRIBLE HACK and looks like this:
if (!strcmp(f->name, "MONITOR") || !strcmp(f->name, "LISTEN_PARENT") ||
!strcmp(f->name, "Z_TEL")) {
You need to add a clause for your toggle in here as well.
- As above, define useful macros in dbdefs.h. For example, you might
define macros like this:
#define PlayerIC(x) (IS(x,TYPE_PLAYER,PLAYER_IC))
#define ThingIC(x) (IS(x,TYPE_THING,THING_IC))
#define RoomIC(x) (IS(x,TYPE_ROOM,ROOM_IC))
#define IC(x) (PlayerIC(x) || ThingIC(x) || RoomIC(x))
Adding powers is much like adding flags.
In flags.h, you can define the power's bit pattern, just as you would
with a flag (the powers are at the end of the file.) The same guidelines
apply, except that powers are always generic and can always only be
granted by Wizards.
In flags.c, you add the power's name and its bit pattern to
the power_table array, and any aliases to the power_alias_tab array.
And, as with flags, you can now define a macro so you
can test for your power. For powers, however, this is done in mushdb.h.
For example,
the login power's macro looks like this:
#define Can_Login(x) (Hasprivs(x) || (Powers(x) & LOGIN_ANYTIME))
which is to say that the ability to log in goes with either being a
royalty/wizard (which is what Hasprivs() tests for) or having the
LOGIN_ANYTIME power. The Wizard() macro tests for Wizardhood and God() for
Godhood, in case you need 'em.
Adding a new attribute is as easy as using the @attribute/access
command. The allowable attribute flags are described in help @set2.
Remember that you must run the @attribute/access every time the
MUSH reboots, so put it in God's @startup.
If you've already been running for a while and need to add
a new attribute, @attribute/access/retroactive will retroactively
apply the attribute permissions to any objects that already have the
attribute.
To make use of attributes in your code, you need
to know about the functions for manipulating attributes. They're all
in attrib.c, but here's a sampler:
- ATTR *atr_match(char *name)
- Given an uppercase attribute name, looks up a standard
(i.e. defined in atr_tab.h or atr_tab.c) attribute and returns a pointer
to the attribute as defined in atr_tab.h, with the flags, value, and
owner as defined in atr_tab.h.
- ATTR *atr_get(dbref obj, char *name)
- Given an object's db# (an integer) and an attribute name,
return a pointer to that attribute on that object or its parent(s).
If that attribute isn't set on the object or parent(s), the function
returns NULL.
- ATTR *atr_get_noparent(dbref obj, char *name)
- Just like atr_get, but doesn't check parents.
- int atr_add(dbref obj, char *name, char *value, dbref enactor, int flags)
- This is the basic function for setting attributes. Given an object,
an attribute name, a value for the attribute (or NULL to clear the
attribute), the db# of the thing
trying to do the setting (for control checks), and the attribute flags
to set on the attribute, it tries to set the attribute and returns
-1 if unable to, 1 if an attribute is set, and 0 if the attribute is
cleared. If you want to be sure that the attribute is set, pass
GOD as the enactor. If you don't want to set any attribute flags,
pass NOTHING as the flags.
Sometimes you'd like to have every newly created player start
with a certain attrib, the way all players start out with LASTSITE
and LAST. You can do this by editing the function create_player()
in player.c, and adding lines like these near where LASTSITE is set up:
#ifdef EXTENDED_MAIL
(void) atr_add(player, "MAILCURF", "0", GOD,
AF_LOCKED|AF_NOPROG|AF_WIZARD|AF_ODARK);
#endif
This example is from the extended mailer, and
adds the MAILCURF attribute (mail current folder) to the newly
created player, set to "0", owned by GOD, and with the appropriate
attribute flags.
An alternative would be to use an @aconnect in the player start
room to set the attribute.
Looking through the code for do_edit (and other code in
set.c) can be very educational in learning to work with attributes.
Shawn Wagner (Raevnos) rewrote the PennMUSH lock system in
1.7.5p9. Ralph Melton (Rhyanna) wrote the lock system used from
1.50pl13 to the 1.7.4 series. Instructions on how to add new lock
types in both systems are given. Shawn's rewrite is covered in the new locks section, and Ralphs's in old locks.
Adding lock types is more similar to adding a new flag
than a new function or command. As an example, we'll add a Look lock
that controls who can look at you.
First, edit lock.h to add a declaration of your new lock type. Go down
to where it says '/* Declare new lock types here */'. Add a declaration
for your new lock type like this:
extern const lock_type Look_Lock.
Then edit lock.c. At the point where it says,
'/* define new lock types here */'
Add a definition of the lock type, with the string you want to use to
identify the lock to the user. This string is used in the output from
examining the lock, and in '@lock/<string>'. For example, we'll add
the definition
const lock_type Look_Lock = "Look";
Then, add the same string to the lock_types array just below, where the
code says '/* Add new lock types */'
That's all you need to do to define a new type of lock. @lock,
examine, and so forth will all work correctly with the new lock. In
most cases, though, you'll want to define the effects of the lock,
too.
The function eval_lock() returns the value of a given lock for a
given person.
For example, to add our Look_Lock, we'll add the following
code near the place where it tests for the 'OPAQUE' flag:
if (!eval_lock(player, thing, Look_Lock)) {
notify(player, "You can't look at that.");
}
That's it. Recompile and you're done.
The interface for adding new-style locks is very similar to the old
way. The differences come from the additional features of new-style
locks, namely, lock flags.
First, edit lock.h to add a declaration of your new lock type. Go
down to where it says /* Declare new lock types here
*/. Add a declaration for your new lock type like this:
extern const lock_type Your_lock; /* Description of what the lock is for */
Then edit lock.c. At the point where it says /* define new
lock types here */, add a definition of the lock type, with the
string you want to use to identify the lock to the user. This string
is used in the output from examining the lock, and in
@lock/<string>. For example, we'll add the
definition:
const lock_type Look_Lock = "Look"
So far, this is just like the old-style locks. Now we come to the
difference. After the lock_type definitions is a table that controls
the default flags. You need to add an entry at the end of the table,
after the /* Add new lock types just before this line. */
line, but before the very last line with the NULLs and
0's. Here's the example.
/* Add new lock types just before this line. */
{ "Look", NULL, GOD, LF_PRIVATE, NULL