Path: uunet!wyse!mips!zaphod.mps.ohio-state.edu!usc!cs.utexas.edu!rice!uw-beaver!zephyr.ens.tek.com!tekred!saab!billr From: billr@saab.CNA.TEK.COM (Bill Randle) Newsgroups: comp.sources.games Subject: v08i080: tinymud - user-extendible multi-user adventure game, Part01/04 Message-ID: <5095@tekred.CNA.TEK.COM> Date: 20 Jan 90 03:14:49 GMT Sender: nobody@tekred.CNA.TEK.COM Lines: 2283 Approved: billr@saab.CNA.TEK.COM Submitted-by: DEEJ@MAINE.BITNET Posting-number: Volume 8, Issue 80 Archive-name: tinymud/Part01 [I haven't tried this yet. Also, there is a newsgroup alt.mud for interested parties. -br] #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'README' <<'END_OF_FILE' XThis is beta release 1.4.1 of TinyMUD version 1.4, a user-extendible Xmulti-user adventure game. X XSee the file copyright.h for information on the copyright under which XTinyMUD 1.4.1 is distributed. X X XCompiling TinyMUD: X XThe code is written in ANSI C, and is known to compile and run under XMach (a variant of 4.3BSD Unix) on a Sun-3 and uVax-2 using GCC, and Xon an RT using AT&T's CFront 1.2 C++ preprocessor with hc. It has Xalso been compiled on a Vax running a fairly generic 4.2BSD system. X XI do not expect that it will work well on non-BSD systems, although Xmost of the BSD-isms are concentrated in interface.c and in a few Xcalls here and there to bzero() and such. Please let me know if you Xmanage to port TinyMUD to another type of system. X X XPrograms contained in the distribution: X Xnetmud X X This is the actual server program; it is invoked as X X netmud source-file dump-file [port] X X If port is not specified it defaults to 4201. The initial database Xwill be read in from source-file, which must contain at least the two Xobjects in minimal.db to work well. The file small.db, which contains Xthe core of the original TinyMUD universe, may be a better place to Xstart a new universe from. The netmud process will write a checkpoint Xout to dump-file every 3600 seconds; the interval can be changed by Xsetting DUMP_INTERVAL in config.h. X X Xdump X X Usage: dump [owner] < source-file X X Pretty-prints all objects owned by owner (if omitted, by everyone). X X Xsanity-check X X Usage: sanity-check < source-file X X Performs several simple consistency checks on the specified database Xfile. Also useful for detecting surplus WIZARDs and people with Xexcess pocket change. X X Xpaths X X Usage: paths [start] < source-file X X Prints the shortest path to every room in source-file from start. XPreference is always given to paths with fewer locked exits, Xregardless of length. Paths that involve passing through an exit Xlocked to a person are assumed to be impossible. X X Xextract X X This is the program that was used to create the file small.db Xdistributed with TinyMUD. It is used to extract a part of a database Xas a connected whole. X XExamples: X X extract < source-file > output-file X X Extracts location #0, player #1, and all objects owned by player #1; X these objects are renumbered into a contiguous block starting at 0 and X are written out as a new database file that preserves all of their X inter-relationships. Some objects may become "orphaned" as a result X of having their home or location disappear; these objects are moved to X location #0. With some combinations of command line arguments it is X possible to include an object without including its owner; in this X case player #1 "inherits" the object. Exits are only extracted if X both their source and destination rooms are extracted. X X extract 2 < source-file > output-file X X Extracts location #0, object #1 and anything it owns, and object #2 X and anything it owns. X X extract 2 -5 < source-file > output-file. X X As above, but does not extract object #5 even if it is owned by #1 or X #2. X X extract 2 5991 195 -198 3391 -12 12915 < source-file > object-file X X Extracts #0, #1, #2, #195, #3391, #5991, #12915 and anything they X own, except #12 and #198. X XThe keyword "all" can be specified as an argument to include Xeverything by default, e.g.: X X extract all -198 X X Extracts everything except object #198 and anything it owns. X X extract all -198 5 X X Extracts everything except object #198 and anything it owns, but X includes object #5 even if #198 owns it. X X X Xrestart-cmu: The shell script used at CMU to restart the TinyMUD Xserver. Useful for making sure that back versions of the database are Xalways available. X X X X XI hope that you enjoy using TinyMUD. X X--Jim Aspnes, December 9th, 1989. END_OF_FILE if test 3743 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'MANIFEST' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'MANIFEST'\" else echo shar: Extracting \"'MANIFEST'\" \(1168 characters\) sed "s/^X//" >'MANIFEST' <<'END_OF_FILE' X File Name Archive # Description X----------------------------------------------------------- X MANIFEST 1 This shipping list X Makefile 4 X README 1 X config.h 4 X copyright.h 4 X create.c 3 X db.c 3 X db.h 3 X dump.c 4 X externs.h 3 X extract.c 1 X game.c 1 X help.c 4 X help.txt 4 X interface.c 1 X interface.h 3 X look.c 3 X match.c 3 X match.h 4 X minimal.db 1 X move.c 1 X paths.c 4 X player.c 4 X predicates.c 4 X restart-cmu 4 X rob.c 3 X sanity-check.c 4 X set.c 3 X small.db 2 X small.db.README 4 X speech.c 2 X stringutil.c 4 X utils.c 4 X wiz.c 3 END_OF_FILE if test 1168 -ne `wc -c <'MANIFEST'`; then echo shar: \"'MANIFEST'\" unpacked with wrong size! fi # end of 'MANIFEST' fi if test -f 'extract.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'extract.c'\" else echo shar: Extracting \"'extract.c'\" \(5605 characters\) sed "s/^X//" >'extract.c' <<'END_OF_FILE' X#include "copyright.h" X X#include X#include X X#include "db.h" X Xint include_all = 0; /* include everything unless specified */ X Xdbref included[NCARGS+1]; Xdbref excluded[NCARGS+1]; X Xdbref *trans; /* translation vector */ X X#define DEFAULT_LOCATION (0) X#define DEFAULT_OWNER (1) X X/* returns 1 if it is not excluded */ Xint not_excluded(dbref x) X{ X int i; X X /* check that it isn't excluded */ X for(i = 0; excluded[i] != NOTHING; i++) { X if(excluded[i] == x) return 0; /* always exclude specifics */ X } X X /* if it's an exit, check that its destination is ok */ X if(Typeof(x) == TYPE_EXIT) { X return isok(db[x].location); X } else { X return 1; X } X} X X/* returns 1 if it should be included in translation vector */ Xint isok(dbref x) X{ X int i; X X if(x == DEFAULT_OWNER || x == DEFAULT_LOCATION) return 1; X X for(i = 0; included[i] != NOTHING; i++) { X if(included[i] == x) return 1; /* always get specific ones */ X if(included[i] == db[x].owner) { X return not_excluded(x); X } X } X X /* not in the list, can only get it if include_all is on */ X /* or it's owned by DEFAULT_OWNER */ X return ((include_all || db[x].owner == DEFAULT_OWNER) && not_excluded(x)); X} X Xvoid build_trans(void) X{ X dbref i; X dbref val; X X if((trans = (dbref *) malloc(sizeof(dbref) * db_top)) == 0) { X abort(); X } X X val = 0; X for(i = 0; i < db_top; i++) { X if(isok(i)) { X trans[i] = val++; X } else { X trans[i] = NOTHING; X } X } X} X Xdbref translate(dbref x) X{ X if(x == NOTHING || x == HOME) { X return(x); X } else { X return(trans[x]); X } X} X Xint ok(dbref x) X{ X if(x == NOTHING || x == HOME) { X return 1; X } else { X return trans[x] != NOTHING; X } X} X Xvoid check_bad_exits(dbref x) X{ X dbref e; X X if(Typeof(x) == TYPE_ROOM && !isok(x)) { X /* mark all exits as excluded */ X DOLIST(e, db[x].exits) { X trans[e] = NOTHING; X } X } X} X Xvoid check_owner(dbref x) X{ X if(ok(x) && !ok(db[x].owner)) { X db[x].owner = DEFAULT_OWNER; X } X} X Xvoid check_location(dbref x) X{ X dbref loc; X dbref newloc; X X if(ok(x) && (Typeof(x) == TYPE_THING || Typeof(x) == TYPE_PLAYER) X && !ok(loc = db[x].location)) { X /* move it to home or DEFAULT_LOCATION */ X if(ok(db[x].exits)) { X newloc = db[x].exits; /* home */ X } else { X newloc = DEFAULT_LOCATION; X } X db[loc].contents = remove_first(db[loc].contents, x); X PUSH(x, db[newloc].contents); X db[x].location = newloc; X } X} X Xvoid check_next(dbref x) X{ X dbref next; X X if(ok(x)) { X while(!ok(next = db[x].next)) db[x].next = db[next].next; X } X} X Xvoid check_contents(dbref x) X{ X dbref c; X X if(ok(x)) { X while(!ok(c = db[x].contents)) db[x].contents = db[c].next; X } X} X X/* also updates home */ X/* MUST BE CALLED AFTER check_owner! */ Xvoid check_exits(dbref x) X{ X dbref e; X X if(ok(x) && !ok(e = db[x].exits)) { X switch(Typeof(x)) { X case TYPE_ROOM: X while(!ok(e = db[x].exits)) db[x].exits = db[e].next; X break; X case TYPE_PLAYER: X case TYPE_THING: X if(ok(db[db[x].owner].exits)) { X /* set it to owner's home */ X db[x].exits = db[db[x].owner].exits; /* home */ X } else { X /* set it to DEFAULT_LOCATION */ X db[x].exits = DEFAULT_LOCATION; /* home */ X } X break; X } X } X} X Xvoid do_write(void) X{ X dbref i; X dbref kludge; X X /* this is braindamaged */ X /* we have to rebuild the translation map */ X /* because part of it may have gotten nuked in check_bad_exits */ X for(i = 0, kludge = 0; i < db_top; i++) { X if(trans[i] != NOTHING) trans[i] = kludge++; X } X X for(i = 0; i < db_top; i++) { X if(ok(i)) { X /* translate all object pointers */ X db[i].location = translate(db[i].location); X db[i].contents = translate(db[i].contents); X db[i].exits = translate(db[i].exits); X db[i].next = translate(db[i].next); X db[i].key = translate(db[i].key); X db[i].owner = translate(db[i].owner); X X /* write it out */ X printf("#%d\n", translate(i)); X db_write_object(stdout, i); X } X } X puts("***END OF DUMP***"); X} X Xvoid main(int argc, char **argv) X{ X dbref i; X int top_in; X int top_ex; X char *arg0; X X top_in = 0; X top_ex = 0; X X /* now parse args */ X arg0 = *argv; X for(argv++, argc--; argc > 0; argv++, argc--) { X i = atol(*argv); X if(i == 0) { X if(!strcmp(*argv, "all")) { X include_all = 1; X } else { X fprintf(stderr, "%s: bogus argument %s\n", arg0, *argv); X } X } else if(i < 0) { X excluded[top_ex++] = -i; X } else { X included[top_in++] = i; X } X } X X /* Terminate */ X included[top_in++] = NOTHING; X excluded[top_ex++] = NOTHING; X X /* Load database */ X if(db_read(stdin) < 0) { X fputs("Database load failed!\n", stderr); X exit(1); X } X X fputs("Done loading database...\n", stderr); X X /* Build translation table */ X build_trans(); X fputs("Done building translation table...\n", stderr); X X /* Scan everything */ X for(i = 0; i < db_top; i++) check_bad_exits(i); X fputs("Done checking bad exits...\n", stderr); X X for(i = 0; i < db_top; i++) check_owner(i); X fputs("Done checking owners...\n", stderr); X X for(i = 0; i < db_top; i++) check_location(i); X fputs("Done checking locations...\n", stderr); X X for(i = 0; i < db_top; i++) check_next(i); X fputs("Done checking next pointers...\n", stderr); X X for(i = 0; i < db_top; i++) check_contents(i); X fputs("Done checking contents...\n", stderr); X X for(i = 0; i < db_top; i++) check_exits(i); X fputs("Done checking homes and exits...\n", stderr); X X do_write(); X fputs("Done.\n", stderr); X X exit(0); X} END_OF_FILE if test 5605 -ne `wc -c <'extract.c'`; then echo shar: \"'extract.c'\" unpacked with wrong size! fi # end of 'extract.c' fi if test -f 'game.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'game.c'\" else echo shar: Extracting \"'game.c'\" \(11786 characters\) sed "s/^X//" >'game.c' <<'END_OF_FILE' X#include "copyright.h" X X#include X#include X#include X#include X X#include "db.h" X#include "config.h" X#include "interface.h" X#include "match.h" X#include "externs.h" X X/* declarations */ Xstatic const char *dumpfile = 0; Xstatic int epoch = 0; Xstatic int alarm_triggered = 0; Xstatic int alarm_block = 0; X Xstatic void fork_and_dump(void); Xvoid dump_database(void); X Xvoid do_dump(dbref player) X{ X if(Wizard(player)) { X alarm_triggered = 1; X notify(player, "Dumping..."); X } else { X notify(player, "Sorry, you are in a no dumping zone."); X } X} X Xvoid do_shutdown(dbref player) X{ X if(Wizard(player)) { X fprintf(stderr, "SHUTDOWN: by %s(%d)\n", X getname(player), player); X fflush(stderr); X shutdown_flag = 1; X } else { X notify(player, "Your delusions of grandeur have been duly noted."); X } X} X X/* should be void, but it's defined as int */ Xstatic int alarm_handler() X{ X alarm_triggered = 1; X if(!alarm_block) { X fork_and_dump(); X } X return 0; X} X Xstatic void dump_database_internal() X{ X char tmpfile[2048]; X FILE *f; X X sprintf(tmpfile, "%s.#%d#", dumpfile, epoch - 1); X unlink(tmpfile); /* nuke our predecessor */ X X sprintf(tmpfile, "%s.#%d#", dumpfile, epoch); X X if((f = fopen(tmpfile, "w")) != NULL) { X db_write(f); X fclose(f); X if(rename(tmpfile, dumpfile) < 0) perror(dumpfile); X } else { X perror(tmpfile); X } X} X Xvoid panic(const char *message) X{ X char panicfile[2048]; X FILE *f; X int i; X X fprintf(stderr, "PANIC: %s\n", message); X X /* turn off signals */ X for(i = 0; i < NSIG; i++) { X signal(i, SIG_IGN); X } X X /* shut down interface */ X emergency_shutdown(); X X /* dump panic file */ X sprintf(panicfile, "%s.PANIC", dumpfile); X if((f = fopen(panicfile, "w")) == NULL) { X perror("CANNOT OPEN PANIC FILE, YOU LOSE:"); X _exit(135); X } else { X fprintf(stderr, "DUMPING: %s\n", panicfile); X db_write(f); X fclose(f); X fprintf(stderr, "DUMPING: %s (done)\n", panicfile); X _exit(136); X } X} X Xvoid dump_database() X{ X epoch++; X X fprintf(stderr, "DUMPING: %s.#%d#\n", dumpfile, epoch); X dump_database_internal(); X fprintf(stderr, "DUMPING: %s.#%d# (done)\n", dumpfile, epoch); X} X Xstatic void fork_and_dump() X{ X int child; X X epoch++; X X fprintf(stderr, "CHECKPOINTING: %s.#%d#\n", dumpfile, epoch); X child = fork(); X if(child == 0) { X /* in the child */ X close(0); /* get that file descriptor back */ X dump_database_internal(); X _exit(0); /* !!! */ X } else if(child < 0) { X perror("fork_and_dump: fork()"); X } X X /* in the parent */ X /* reset alarm */ X alarm_triggered = 0; X alarm(DUMP_INTERVAL); X} X Xstatic int reaper() X{ X union wait stat; X X while(wait3(&stat, WNOHANG, 0) > 0); X return 0; X} X Xint init_game(const char *infile, const char *outfile) X{ X FILE *f; X X if((f = fopen(infile, "r")) == NULL) return -1; X X /* ok, read it in */ X fprintf(stderr, "LOADING: %s\n", infile); X if(db_read(f) < 0) return -1; X fprintf(stderr, "LOADING: %s (done)\n", infile); X X /* everything ok */ X fclose(f); X X /* initialize random number generator */ X srandom(getpid()); X X /* set up dumper */ X if(dumpfile) free(dumpfile); X dumpfile = alloc_string(outfile); X signal(SIGALRM, alarm_handler); X signal(SIGHUP, alarm_handler); X signal(SIGCHLD, reaper); X alarm_triggered = 0; X alarm(DUMP_INTERVAL); X X return 0; X} X X/* use this only in process_command */ X#define Matched(string) { if(!string_prefix((string), command)) goto bad; } X Xvoid process_command(dbref player, char *command) X{ X char *arg1; X char *arg2; X char *q; /* utility */ X char *p; /* utility */ X X char *index(char *, char); X X if(command == 0) abort(); X X /* robustify player */ X if(player < 0 || player >= db_top || Typeof(player) != TYPE_PLAYER) { X fprintf(stderr, "process_command: bad player %d\n", player); X return; X } X X#ifdef LOG_COMMANDS X fprintf(stderr, "COMMAND from %s(%d) in %s(%d): %s\n", X getname(player), player, X getname(db[player].location), X db[player].location, X command); X#endif /* LOG_COMMANDS */ X X /* eat leading whitespace */ X while(*command && isspace(*command)) command++; X X /* eat extra white space */ X q = p = command; X while(*p) { X /* scan over word */ X while(*p && !isspace(*p)) *q++ = *p++; X /* smash spaces */ X while(*p && isspace(*++p)); X if(*p) *q++ = ' '; /* add a space to separate next word */ X } X /* terminate */ X *q = '\0'; X X /* block dump to prevent db inconsistencies from showing up */ X alarm_block = 1; X X /* check for single-character commands */ X if(*command == SAY_TOKEN) { X do_say(player, command+1, NULL); X } else if(*command == POSE_TOKEN) { X do_pose(player, command+1, NULL); X } else if(can_move(player, command)) { X /* command is an exact match for an exit */ X do_move(player, command); X } else { X /* parse arguments */ X X /* find arg1 */ X /* move over command word */ X for(arg1 = command; *arg1 && !isspace(*arg1); arg1++); X /* truncate command */ X if(*arg1) *arg1++ = '\0'; X X /* move over spaces */ X while(*arg1 && isspace(*arg1)) arg1++; X X /* find end of arg1, start of arg2 */ X for(arg2 = arg1; *arg2 && *arg2 != '='; arg2++); X X /* truncate arg1 */ X for(p = arg2 - 1; p >= arg1 && isspace(*p); p--) *p = '\0'; X X /* go past '=' if present */ X if(*arg2) *arg2++ = '\0'; X while(*arg2 && isspace(*arg2)) arg2++; X X switch(command[0]) { X case '@': X switch(command[1]) { X case 'c': X case 'C': X /* chown, create */ X switch(command[2]) { X case 'h': X case 'H': X Matched("@chown"); X do_chown(player, arg1, arg2); X break; X case 'r': X case 'R': X Matched("@create"); X do_create(player, arg1, atol(arg2)); X break; X default: X goto bad; X } X break; X case 'd': X case 'D': X /* describe, dig, or dump */ X switch(command[2]) { X case 'e': X case 'E': X Matched("@describe"); X do_describe(player, arg1, arg2); X break; X case 'i': X case 'I': X Matched("@dig"); X do_dig(player, arg1); X break; X case 'u': X case 'U': X Matched("@dump"); X do_dump(player); X break; X default: X goto bad; X } X break; X case 'f': X /* fail, find, or force */ X switch(command[2]) { X case 'a': X case 'A': X Matched("@fail"); X do_fail(player, arg1, arg2); X break; X case 'i': X case 'I': X Matched("@find"); X do_find(player, arg1); X break; X#ifdef DO_FLUSH X case 'l': X case 'L': X if(string_compare(command, "@flush")) goto bad; X do_flush(player); X break; X#endif /* DO_FLUSH */ X case 'o': X case 'O': X Matched("@force"); X do_force(player, arg1, arg2); X break; X default: X goto bad; X } X break; X case 'l': X case 'L': X /* lock or link */ X switch(command[2]) { X case 'i': X case 'I': X Matched("@link"); X do_link(player, arg1, arg2); X break; X case 'o': X case 'O': X Matched("@lock"); X do_lock(player, arg1, arg2); X break; X default: X goto bad; X } X break; X case 'n': X case 'N': X Matched("@name"); X do_name(player, arg1, arg2); X break; X case 'o': X case 'O': X switch(command[2]) { X case 'f': X case 'F': X Matched("@ofail"); X do_ofail(player, arg1, arg2); X break; X case 'p': X case 'P': X Matched("@open"); X do_open(player, arg1, arg2); X break; X case 's': X case 'S': X Matched("@osuccess"); X do_osuccess(player, arg1, arg2); X break; X default: X goto bad; X } X break; X case 'p': X case 'P': X Matched("@password"); X do_password(player, arg1, arg2); X break; X case 's': X case 'S': X /* set, shutdown, success */ X switch(command[2]) { X case 'e': X case 'E': X Matched("@set"); X do_set(player, arg1, arg2); X break; X case 'h': X if(strcmp(command, "@shutdown")) goto bad; X do_shutdown(player); X break; X case 't': X case 'T': X Matched("@stats"); X do_stats(player, arg1); X break; X case 'u': X case 'U': X Matched("@success"); X do_success(player, arg1, arg2); X break; X default: X goto bad; X } X break; X case 't': X case 'T': X switch(command[2]) { X case 'e': X case 'E': X Matched("@teleport"); X do_teleport(player, arg1, arg2); X break; X case 'o': X if(string_compare(command, "@toad")) goto bad; X do_toad(player, arg1); X break; X default: X goto bad; X } X break; X case 'u': X case 'U': X if(string_prefix(command, "@unli")) { X Matched("@unlink"); X do_unlink(player, arg1); X } else if(string_prefix(command, "@unlo")) { X Matched("@unlock"); X do_unlock(player, arg1); X } else { X goto bad; X } X break; X case 'w': X if(strcmp(command, "@wall")) goto bad; X do_wall(player, arg1, arg2); X break; X default: X goto bad; X } X break; X case 'd': X case 'D': X Matched("drop"); X do_drop(player, arg1); X break; X case 'e': X case 'E': X Matched("examine"); X do_examine(player, arg1); X break; X case 'g': X case 'G': X /* get, give, go, or gripe */ X switch(command[1]) { X case 'e': X case 'E': X Matched("get"); X do_get(player, arg1); X break; X case 'i': X case 'I': X Matched("give"); X do_give(player, arg1, atol(arg2)); X break; X case 'o': X case 'O': X Matched("goto"); X do_move(player, arg1); X break; X case 'r': X case 'R': X Matched("gripe"); X do_gripe(player, arg1, arg2); X break; X default: X goto bad; X } X break; X case 'h': X case 'H': X Matched("help"); X do_help(player); X break; X case 'i': X case 'I': X Matched("inventory"); X do_inventory(player); X break; X case 'k': X case 'K': X Matched("kill"); X do_kill(player, arg1, atol(arg2)); X break; X case 'l': X case 'L': X Matched("look"); X do_look_at(player, arg1); X break; X case 'm': X case 'M': X Matched("move"); X do_move(player, arg1); X break; X case 'n': X case 'N': X /* news */ X if(string_compare(command, "news")) goto bad; X do_news(player); X break; X case 'p': X case 'P': X Matched("page"); X do_page(player, arg1); X break; X case 'r': X case 'R': X switch(command[1]) { X case 'e': X case 'E': X Matched("read"); /* undocumented alias for look at */ X do_look_at(player, arg1); X break; X case 'o': X case 'O': X Matched("rob"); X do_rob(player, arg1); X break; X default: X goto bad; X } X break; X case 's': X case 'S': X /* say, "score" */ X switch(command[1]) { X case 'a': X case 'A': X Matched("say"); X do_say(player, arg1, arg2); X break; X case 'c': X case 'C': X Matched("score"); X do_score(player); X break; X default: X goto bad; X } X break; X case 't': X case 'T': X switch(command[1]) { X case 'a': X case 'A': X Matched("take"); X do_get(player, arg1); X break; X case 'h': X case 'H': X Matched("throw"); X do_drop(player, arg1); X break; X default: X goto bad; X } X break; X default: X bad: X notify(player, "Huh? (Type \"help\" for help.)"); X#ifdef LOG_FAILED_COMMANDS X if(!controls(player, db[player].location)) { X fprintf(stderr, "HUH from %s(%d) in %s(%d)[%s]: %s %s\n", X getname(player), player, X getname(db[player].location), X db[player].location, X getname(db[db[player].location].owner), X command, X reconstruct_message(arg1, arg2)); X } X#endif /* LOG_FAILED_COMMANDS */ X break; X } X } X X /* unblock alarms */ X alarm_block = 0; X if(alarm_triggered) { X fork_and_dump(); X } X} X X#undef Matched END_OF_FILE if test 11786 -ne `wc -c <'game.c'`; then echo shar: \"'game.c'\" unpacked with wrong size! fi # end of 'game.c' fi if test -f 'interface.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'interface.c'\" else echo shar: Extracting \"'interface.c'\" \(18586 characters\) sed "s/^X//" >'interface.c' <<'END_OF_FILE' X#include "copyright.h" X X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X X#include "db.h" X#include "interface.h" X#include "config.h" X Xextern int errno; Xint shutdown_flag = 0; X Xstatic const char *connect_fail = "Either that player does not exist, or has a different password.\n"; Xstatic const char *create_fail = "Either there is already a player with that name, or that name is illegal.\n"; Xstatic const char *flushed_message = "\n"; Xstatic const char *shutdown_message = "Going down - Bye\n"; X Xstruct text_block { X int nchars; X struct text_block *nxt; X char *start; X char *buf; X}; X Xstruct text_queue { X struct text_block *head; X struct text_block **tail; X}; X Xstruct descriptor_data { X int descriptor; X int connected; X dbref player; X char *output_prefix; X char *output_suffix; X int output_size; X struct text_queue output; X struct text_queue input; X char *raw_input; X char *raw_input_at; X long last_time; X int quota; X struct descriptor_data *next; X struct descriptor_data **prev; X} *descriptor_list = 0; X Xstatic int sock; Xstatic int ndescriptors = 0; X Xvoid process_commands(void); Xvoid shovechars(int port); Xvoid shutdownsock(struct descriptor_data *d); Xstruct descriptor_data *initializesock(int s); Xvoid make_nonblocking(int s); Xvoid freeqs(struct descriptor_data *d); Xvoid welcome_user(struct descriptor_data *d); Xvoid check_connect(struct descriptor_data *d, const char *msg); Xvoid close_sockets(); Xchar *malloc (int); Xconst char *addrout (long); Xvoid dump_users(struct descriptor_data *d); Xvoid set_signals(); Xstruct descriptor_data *new_connection(int sock); Xvoid parse_connect (const char *msg, char *command, char *user, char *pass); Xvoid set_userstring (char **userstring, const char *command); Xint do_command (struct descriptor_data *d, char *command); Xchar *strsave (const char *s); Xint make_socket(int); Xint queue_string(struct descriptor_data *, const char *); Xint queue_write(struct descriptor_data *, const char *, int); Xint process_output(struct descriptor_data *d); Xint process_input(struct descriptor_data *d); Xint bailout(int, int, struct sigcontext *); X X#define MALLOC(result, type, number) do { \ X if (!((result) = (type *) malloc ((number) * sizeof (type)))) \ X panic("Out of memory"); \ X } while (0) X X#define FREE(x) (free(x)) X Xvoid main(int argc, char **argv) X{ X if (argc < 3) { X fprintf (stderr, "Usage: %s infile dumpfile [port]\n", *argv); X exit (1); X } X if (init_game (argv[1], argv[2]) < 0) { X fprintf (stderr, "Couldn't load %s!\n", argv[1]); X exit (2); X } X set_signals (); X /* go do it */ X shovechars (argc >= 4 ? atoi (argv[3]) : TINYPORT); X close_sockets (); X dump_database (); X exit (0); X} X Xvoid set_signals() X{ X int bailout (), dump_status (); X X /* we don't care about SIGPIPE, we notice it in select() and write() */ X signal (SIGPIPE, SIG_IGN); X X /* standard termination signals */ X signal (SIGINT, bailout); X signal (SIGTERM, bailout); X X /* catch these because we might as well */ X signal (SIGQUIT, bailout); X signal (SIGILL, bailout); X signal (SIGTRAP, bailout); X signal (SIGIOT, bailout); X signal (SIGEMT, bailout); X signal (SIGFPE, bailout); X signal (SIGBUS, bailout); X signal (SIGSEGV, bailout); X signal (SIGSYS, bailout); X signal (SIGTERM, bailout); X signal (SIGXCPU, bailout); X signal (SIGXFSZ, bailout); X signal (SIGVTALRM, bailout); X signal (SIGUSR2, bailout); X X /* status dumper (predates "WHO" command) */ X signal (SIGUSR1, dump_status); X} X Xvoid notify(dbref player, const char *msg) X{ X struct descriptor_data *d; X X for (d = descriptor_list; d; d = d->next) { X if (d->connected && d->player == player) { X queue_string (d, msg); X queue_write (d, "\n", 1); X } X } X} X Xstruct timeval timeval_sub(struct timeval now, struct timeval then) X{ X now.tv_sec -= then.tv_sec; X now.tv_usec -= then.tv_usec; X if (now.tv_usec < 0) { X now.tv_usec += 1000000; X now.tv_sec--; X } X return now; X} X Xint msec_diff(struct timeval now, struct timeval then) X{ X return ((now.tv_sec - then.tv_sec) * 1000 X + (now.tv_usec - then.tv_usec) / 1000); X} X Xstruct timeval msec_add(struct timeval t, int x) X{ X t.tv_sec += x / 1000; X t.tv_usec += (x % 1000) * 1000; X if (t.tv_usec >= 1000000) { X t.tv_sec += t.tv_usec / 1000000; X t.tv_usec = t.tv_usec % 1000000; X } X return t; X} X Xstruct timeval update_quotas(struct timeval last, struct timeval current) X{ X int nslices; X struct descriptor_data *d; X X nslices = msec_diff (current, last) / COMMAND_TIME_MSEC; X X if (nslices > 0) { X for (d = descriptor_list; d; d = d -> next) { X d -> quota += COMMANDS_PER_TIME * nslices; X if (d -> quota > COMMAND_BURST_SIZE) X d -> quota = COMMAND_BURST_SIZE; X } X } X return msec_add (last, nslices * COMMAND_TIME_MSEC); X} X Xvoid shovechars(int port) X{ X fd_set input_set, output_set; X long now; X struct timeval last_slice, current_time; X struct timeval next_slice; X struct timeval timeout, slice_timeout; X int maxd; X struct descriptor_data *d, *dnext; X struct descriptor_data *newd; X int avail_descriptors; X X sock = make_socket (port); X maxd = sock+1; X gettimeofday(&last_slice, (struct timezone *) 0); X X avail_descriptors = getdtablesize() - 4; X X while (shutdown_flag == 0) { X gettimeofday(¤t_time, (struct timezone *) 0); X last_slice = update_quotas (last_slice, current_time); X X process_commands(); X X if (shutdown_flag) X break; X timeout.tv_sec = 1000; X timeout.tv_usec = 0; X next_slice = msec_add (last_slice, COMMAND_TIME_MSEC); X slice_timeout = timeval_sub (next_slice, current_time); X X FD_ZERO (&input_set); X FD_ZERO (&output_set); X if (ndescriptors < avail_descriptors) X FD_SET (sock, &input_set); X for (d = descriptor_list; d; d=d->next) { X if (d->input.head) X timeout = slice_timeout; X else X FD_SET (d->descriptor, &input_set); X if (d->output.head) X FD_SET (d->descriptor, &output_set); X } X X if (select (maxd, &input_set, &output_set, X (fd_set *) 0, &timeout) < 0) { X if (errno != EINTR) { X perror ("select"); X return; X } X } else { X (void) time (&now); X if (FD_ISSET (sock, &input_set)) { X if (!(newd = new_connection (sock))) { X if (errno != EINTR && errno != EMFILE) { X perror ("new_connection"); X return; X } X } else { X if (newd->descriptor >= maxd) X maxd = newd->descriptor + 1; X } X } X for (d = descriptor_list; d; d = dnext) { X dnext = d->next; X if (FD_ISSET (d->descriptor, &input_set)) { X d->last_time = now; X if (!process_input (d)) { X shutdownsock (d); X continue; X } X } X if (FD_ISSET (d->descriptor, &output_set)) { X if (!process_output (d)) { X shutdownsock (d); X } X } X } X } X } X} X Xstruct descriptor_data *new_connection(int sock) X{ X int newsock; X struct sockaddr_in addr; X int addr_len; X X addr_len = sizeof (addr); X newsock = accept (sock, (struct sockaddr *) & addr, &addr_len); X if (newsock < 0) { X return 0; X } else { X fprintf (stderr, "ACCEPT from %s(%d) on descriptor %d\n", addrout (ntohl (addr.sin_addr.s_addr)), ntohs (addr.sin_port), newsock); X return initializesock (newsock); X } X} X Xconst char *addrout(long a) X{ X static char buf[1024]; X X sprintf (buf, "%d.%d.%d.%d", (a >> 24) & 0xff, (a >> 16) & 0xff, X (a >> 8) & 0xff, a & 0xff); X return buf; X} X Xvoid clearstrings(struct descriptor_data *d) X{ X if (d->output_prefix) { X FREE (d->output_prefix); X d->output_prefix = 0; X } X if (d->output_suffix) { X FREE (d->output_suffix); X d->output_suffix = 0; X } X} X Xvoid shutdownsock(struct descriptor_data *d) X{ X if (d->connected) { X fprintf (stderr, "DISCONNECT descriptor %d player %s(%d)\n", X d->descriptor, db[d->player].name, d->player); X } else { X fprintf (stderr, "DISCONNECT descriptor %d never connected\n", X d->descriptor); X } X clearstrings (d); X shutdown (d->descriptor, 2); X close (d->descriptor); X freeqs (d); X *d->prev = d->next; X if (d->next) X d->next->prev = d->prev; X FREE (d); X ndescriptors--; X} X Xstruct descriptor_data *initializesock(int s) X{ X struct descriptor_data *d; X X ndescriptors++; X MALLOC(d, struct descriptor_data, 1); X d->descriptor = s; X d->connected = 0; X make_nonblocking (s); X d->output_prefix = 0; X d->output_suffix = 0; X d->output_size = 0; X d->output.head = 0; X d->output.tail = &d->output.head; X d->input.head = 0; X d->input.tail = &d->input.head; X d->raw_input = 0; X d->raw_input_at = 0; X d->quota = COMMAND_BURST_SIZE; X d->last_time = 0; X if (descriptor_list) X descriptor_list->prev = &d->next; X d->next = descriptor_list; X d->prev = &descriptor_list; X descriptor_list = d; X X welcome_user (d); X return d; X} X Xint make_socket(int port) X{ X int s; X struct sockaddr_in server; X int opt; X X s = socket (AF_INET, SOCK_STREAM, 0); X if (s < 0) { X perror ("creating stream socket"); X exit (3); X } X opt = 1; X if (setsockopt (s, SOL_SOCKET, SO_REUSEADDR, X (char *) &opt, sizeof (opt)) < 0) { X perror ("setsockopt"); X exit (1); X } X server.sin_family = AF_INET; X server.sin_addr.s_addr = INADDR_ANY; X server.sin_port = htons (port); X if (bind (s, (struct sockaddr *) & server, sizeof (server))) { X perror ("binding stream socket"); X close (s); X exit (4); X } X listen (s, 5); X return s; X} X Xstruct text_block *make_text_block(const char *s, int n) X{ X struct text_block *p; X X MALLOC(p, struct text_block, 1); X MALLOC(p->buf, char, n); X bcopy (s, p->buf, n); X p->nchars = n; X p->start = p->buf; X p->nxt = 0; X return p; X} X Xvoid free_text_block (struct text_block *t) X{ X FREE (t->buf); X FREE ((char *) t); X} X Xvoid add_to_queue(struct text_queue *q, const char *b, int n) X{ X struct text_block *p; X X if (n == 0) return; X X p = make_text_block (b, n); X p->nxt = 0; X *q->tail = p; X q->tail = &p->nxt; X} X Xint flush_queue(struct text_queue *q, int n) X{ X struct text_block *p; X int really_flushed = 0; X X n += strlen(flushed_message); X X while (n > 0 && (p = q->head)) { X n -= p->nchars; X really_flushed += p->nchars; X q->head = p->nxt; X free_text_block (p); X } X p = make_text_block(flushed_message, strlen(flushed_message)); X p->nxt = q->head; X q->head = p; X if (!p->nxt) X q->tail = &p->nxt; X really_flushed -= p->nchars; X return really_flushed; X} X Xint queue_write(struct descriptor_data *d, const char *b, int n) X{ X int space; X X space = MAX_OUTPUT - d->output_size - n; X if (space < 0) X d->output_size -= flush_queue(&d->output, -space); X add_to_queue (&d->output, b, n); X d->output_size += n; X return n; X} X Xint queue_string(struct descriptor_data *d, const char *s) X{ X return queue_write (d, s, strlen (s)); X} X Xint process_output(struct descriptor_data *d) X{ X struct text_block **qp, *cur; X int cnt; X X for (qp = &d->output.head; cur = *qp;) { X cnt = write (d->descriptor, cur -> start, cur -> nchars); X if (cnt < 0) { X if (errno == EWOULDBLOCK) X return 1; X return 0; X } X d->output_size -= cnt; X if (cnt == cur -> nchars) { X if (!cur -> nxt) X d->output.tail = qp; X *qp = cur -> nxt; X free_text_block (cur); X continue; /* do not adv ptr */ X } X cur -> nchars -= cnt; X cur -> start += cnt; X break; X } X return 1; X} X Xvoid make_nonblocking(int s) X{ X if (fcntl (s, F_SETFL, FNDELAY) == -1) { X perror ("make_nonblocking: fcntl"); X panic ("FNDELAY fcntl failed"); X } X} X Xvoid freeqs(struct descriptor_data *d) X{ X struct text_block *cur, *next; X X cur = d->output.head; X while (cur) { X next = cur -> nxt; X free_text_block (cur); X cur = next; X } X d->output.head = 0; X d->output.tail = &d->output.head; X X cur = d->input.head; X while (cur) { X next = cur -> nxt; X free_text_block (cur); X cur = next; X } X d->input.head = 0; X d->input.tail = &d->input.head; X X if (d->raw_input) X FREE (d->raw_input); X d->raw_input = 0; X d->raw_input_at = 0; X} X Xvoid welcome_user(struct descriptor_data *d) X{ X queue_string (d, WELCOME_MESSAGE); X} X Xvoid goodbye_user(struct descriptor_data *d) X{ X write (d->descriptor, LEAVE_MESSAGE, strlen (LEAVE_MESSAGE)); X} X Xchar *strsave (const char *s) X{ X char *p; X X MALLOC (p, char, strlen(s) + 1); X X if (p) X strcpy (p, s); X return p; X} X Xvoid save_command (struct descriptor_data *d, const char *command) X{ X add_to_queue (&d->input, command, strlen(command)+1); X} X Xint process_input (struct descriptor_data *d) X{ X char buf[1024]; X int got; X char *p, *pend, *q, *qend; X X got = read (d->descriptor, buf, sizeof buf); X if (got <= 0) X return 0; X if (!d->raw_input) { X MALLOC(d->raw_input,char,MAX_COMMAND_LEN); X d->raw_input_at = d->raw_input; X } X p = d->raw_input_at; X pend = d->raw_input + MAX_COMMAND_LEN - 1; X for (q=buf, qend = buf + got; q < qend; q++) { X if (*q == '\n') { X *p = '\0'; X if (p > d->raw_input) X save_command (d, d->raw_input); X p = d->raw_input; X } else if (p < pend && isascii (*q) && isprint (*q)) { X *p++ = *q; X } X } X if (p > d->raw_input) { X d->raw_input_at = p; X } else { X FREE (d->raw_input); X d->raw_input = 0; X d->raw_input_at = 0; X } X return 1; X} X Xvoid set_userstring (char **userstring, const char *command) X{ X if (*userstring) { X FREE (*userstring); X *userstring = 0; X } X while (*command && isascii (*command) && isspace (*command)) X command++; X if (*command) X *userstring = strsave (command); X} X Xvoid process_commands() X{ X int nprocessed; X struct descriptor_data *d, *dnext; X struct text_block *t; X X do { X nprocessed = 0; X for (d = descriptor_list; d; d = dnext) { X dnext = d->next; X if (d -> quota > 0 && (t = d -> input.head)) { X d -> quota--; X nprocessed++; X if (!do_command (d, t -> start)) { X shutdownsock (d); X } else { X d -> input.head = t -> nxt; X if (!d -> input.head) X d -> input.tail = &d -> input.head; X free_text_block (t); X } X } X } X } while (nprocessed > 0); X} X Xint do_command (struct descriptor_data *d, char *command) X{ X if (!strcmp (command, QUIT_COMMAND)) { X goodbye_user (d); X return 0; X } else if (!strcmp (command, WHO_COMMAND)) { X dump_users (d); X } else if (!strncmp (command, PREFIX_COMMAND, strlen (PREFIX_COMMAND))) { X set_userstring (&d->output_prefix, command+strlen(PREFIX_COMMAND)); X } else if (!strncmp (command, SUFFIX_COMMAND, strlen (SUFFIX_COMMAND))) { X set_userstring (&d->output_suffix, command+strlen(SUFFIX_COMMAND)); X } else { X if (d->connected) { X if (d->output_prefix) { X queue_string (d, d->output_prefix); X queue_write (d, "\n", 1); X } X process_command (d->player, command); X if (d->output_suffix) { X queue_string (d, d->output_suffix); X queue_write (d, "\n", 1); X } X } else { X check_connect (d, command); X } X } X return 1; X} X Xvoid check_connect (struct descriptor_data *d, const char *msg) X{ X char command[MAX_COMMAND_LEN]; X char user[MAX_COMMAND_LEN]; X char password[MAX_COMMAND_LEN]; X dbref player; X X parse_connect (msg, command, user, password); X X if (!strncmp (command, "co", 2)) { X player = connect_player (user, password); X if (player == NOTHING) { X queue_string (d, connect_fail); X fprintf (stderr, "FAILED CONNECT %s on descriptor %d\n", X user, d->descriptor); X } else { X fprintf (stderr, "CONNECTED %s(%d) on descriptor %d\n", X db[player].name, player, d->descriptor); X d->connected = 1; X d->player = player; X do_look_around (player); X } X } else if (!strncmp (command, "cr", 2)) { X player = create_player (user, password); X if (player == NOTHING) { X queue_string (d, create_fail); X fprintf (stderr, "FAILED CREATE %s on descriptor %d\n", X user, d->descriptor); X } else { X fprintf (stderr, "CREATED %s(%d) on descriptor %d\n", X db[player].name, player, d->descriptor); X d->connected = 1; X d->player = player; X do_look_around (player); X } X } else { X welcome_user (d); X } X} X Xvoid parse_connect (const char *msg, char *command, char *user, char *pass) X{ X char *p; X X while (*msg && isascii(*msg) && isspace (*msg)) X msg++; X p = command; X while (*msg && isascii(*msg) && !isspace (*msg)) X *p++ = *msg++; X *p = '\0'; X while (*msg && isascii(*msg) && isspace (*msg)) X msg++; X p = user; X while (*msg && isascii(*msg) && !isspace (*msg)) X *p++ = *msg++; X *p = '\0'; X while (*msg && isascii(*msg) && isspace (*msg)) X msg++; X p = pass; X while (*msg && isascii(*msg) && !isspace (*msg)) X *p++ = *msg++; X *p = '\0'; X} X Xvoid close_sockets() X{ X struct descriptor_data *d, *dnext; X X for (d = descriptor_list; d; d = dnext) { X dnext = d->next; X write (d->descriptor, shutdown_message, strlen (shutdown_message)); X if (shutdown (d->descriptor, 2) < 0) X perror ("shutdown"); X close (d->descriptor); X } X close (sock); X} X Xvoid emergency_shutdown() X{ X close_sockets(); X} X Xint bailout(int sig, int code, struct sigcontext *scp) X{ X char message[1024]; X X sprintf (message, "BAILOUT: caught signal %d code %d", sig, code); X panic(message); X _exit (7); X return 0; X} X Xint dump_status() X{ X struct descriptor_data *d; X long now; X X (void) time (&now); X fprintf (stderr, "STATUS REPORT:\n"); X for (d = descriptor_list; d; d = d->next) { X if (d->connected) { X fprintf (stderr, "PLAYING descriptor %d player %s(%d)", X d->descriptor, db[d->player].name, d->player); X X if (d->last_time) X fprintf (stderr, " idle %d seconds\n", X now - d->last_time); X else X fprintf (stderr, " never used\n"); X } else { X fprintf (stderr, "CONNECTING descriptor %d", d->descriptor); X if (d->last_time) X fprintf (stderr, " idle %d seconds\n", X now - d->last_time); X else X fprintf (stderr, " never used\n"); X } X } X return 0; X} X Xvoid dump_users(struct descriptor_data *e) X{ X struct descriptor_data *d; X long now; X char buf[1024]; X X (void) time (&now); X queue_string (e, "Current Players:\n"); X for (d = descriptor_list; d; d = d->next) { X if (d->connected) { X if (d->last_time) X sprintf (buf, "%s idle %d seconds\n", X db[d->player].name, X now - d->last_time); X else X sprintf (buf, "%s idle forever\n", X db[d->player].name); X queue_string (e, buf); X } X } X} END_OF_FILE if test 18586 -ne `wc -c <'interface.c'`; then echo shar: \"'interface.c'\" unpacked with wrong size! fi # end of 'interface.c' fi if test -f 'minimal.db' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'minimal.db'\" else echo shar: Extracting \"'minimal.db'\" \(379 characters\) sed "s/^X//" >'minimal.db' <<'END_OF_FILE' X#0 XLimbo XYou are in a dense mist that seems to go on forever. If you drop an object here, or set its home to be here, you probably won't be able to find it again. X-1 X1 X-1 X-1 X-1 X X X Xis briefly visible through the mist. X1 X0 X0 X X#1 XWizard XYou see The Wizard. X0 X-1 X0 X-1 X-1 X"Get an honest job!" snarls the Wizard. X Xfoolishly tried to rob the Wizard! X X1 X0 X19 Xpotrzebie X***END OF DUMP*** END_OF_FILE if test 379 -ne `wc -c <'minimal.db'`; then echo shar: \"'minimal.db'\" unpacked with wrong size! fi # end of 'minimal.db' fi if test -f 'move.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'move.c'\" else echo shar: Extracting \"'move.c'\" \(8211 characters\) sed "s/^X//" >'move.c' <<'END_OF_FILE' X#include "copyright.h" X X#include "db.h" X#include "config.h" X#include "interface.h" X#include "match.h" X#include "externs.h" X Xvoid moveto(dbref what, dbref where) X{ X dbref loc; X X /* remove what from old loc */ X if((loc = db[what].location) != NOTHING) { X db[loc].contents = remove_first(db[loc].contents, what); X } X X /* test for special cases */ X switch(where) { X case NOTHING: X db[what].location = NOTHING; X return; /* NOTHING doesn't have contents */ X case HOME: X where = db[what].exits; /* home */ X break; X } X X /* now put what in where */ X PUSH(what, db[where].contents); X X db[what].location = where; X} X Xstatic void send_contents(dbref loc, dbref dest) X{ X dbref first; X dbref rest; X X first = db[loc].contents; X db[loc].contents = NOTHING; X X /* blast locations of everything in list */ X DOLIST(rest, first) { X db[rest].location = NOTHING; X } X X while(first != NOTHING) { X rest = db[first].next; X if(Typeof(first) != TYPE_THING) { X moveto(first, loc); X } else { X moveto(first, (db[first].flags & STICKY) ? HOME : dest); X } X first = rest; X } X X db[loc].contents = reverse(db[loc].contents); X} X Xvoid maybe_dropto(dbref loc, dbref dropto) X{ X dbref thing; X X if(loc == dropto) return; /* bizarre special case */ X X /* check for players */ X DOLIST(thing, db[loc].contents) { X if(Typeof(thing) == TYPE_PLAYER) return; X } X X /* no players, send everything to the dropto */ X send_contents(loc, dropto); X} X Xvoid enter_room(dbref player, dbref loc) X{ X dbref old; X dbref dropto; X char buf[BUFFER_LEN]; X X /* check for room == HOME */ X if(loc == HOME) loc = db[player].exits; /* home */ X X /* get old location */ X old = db[player].location; X X /* check for self-loop */ X /* self-loops don't do move or other player notification */ X /* but you still get autolook and penny check */ X if(loc != old) { X X if(old != NOTHING) { X /* notify others unless DARK */ X if(!Dark(old) && !Dark(player)) { X sprintf(buf, "%s has left.", db[player].name); X notify_except(db[old].contents, player, buf); X } X } X X /* go there */ X moveto(player, loc); X X /* if old location has STICKY dropto, send stuff through it */ X if(old != NOTHING X && (dropto = db[old].location) != NOTHING X && (db[old].flags & STICKY)) { X maybe_dropto(old, dropto); X } X X /* tell other folks in new location if not DARK */ X if(!Dark(loc) && !Dark(player)) { X sprintf(buf, "%s has arrived.", db[player].name); X notify_except(db[loc].contents, player, buf); X } X } X X /* autolook */ X look_room(player, loc); X X /* check for pennies */ X if(!controls(player, loc) X && db[player].pennies <= MAX_PENNIES X && random() % PENNY_RATE == 0) { X notify(player, "You found a penny!"); X db[player].pennies++; X } X} X Xvoid send_home(dbref thing) X{ X switch(Typeof(thing)) { X case TYPE_PLAYER: X /* send his possessions home first! */ X /* that way he sees them when he arrives */ X send_contents(thing, HOME); X enter_room(thing, db[thing].exits); /* home */ X break; X case TYPE_THING: X moveto(thing, db[thing].exits); /* home */ X break; X default: X /* no effect */ X break; X } X} X Xint can_move(dbref player, const char *direction) X{ X if(!string_compare(direction, "home")) return 1; X X /* otherwise match on exits */ X init_match(player, direction, TYPE_EXIT); X match_exit(); X return(last_match_result() != NOTHING); X} X Xvoid do_move(dbref player, const char *direction) X{ X dbref exit; X dbref loc; X char buf[BUFFER_LEN]; X X if(!string_compare(direction, "home")) { X /* send him home */ X /* but steal all his possessions */ X if((loc = db[player].location) != NOTHING) { X /* tell everybody else */ X sprintf(buf, "%s goes home.", db[player].name); X notify_except(db[loc].contents, player, buf); X } X /* give the player the messages */ X notify(player, "There's no place like home..."); X notify(player, "There's no place like home..."); X notify(player, "There's no place like home..."); X notify(player, "You wake up back home, without your possessions."); X send_home(player); X } else { X /* find the exit */ X init_match_check_keys(player, direction, TYPE_EXIT); X match_exit(); X switch(exit = match_result()) { X case NOTHING: X notify(player, "You can't go that way."); X break; X case AMBIGUOUS: X notify(player, "I don't know which way you mean!"); X break; X default: X /* we got one */ X /* check to see if we got through */ X if(can_doit(player, exit, "You can't go that way.")) { X enter_room(player, db[exit].location); X } X break; X } X } X} X Xvoid do_get(dbref player, const char *what) X{ X dbref loc; X dbref thing; X X init_match_check_keys(player, what, TYPE_THING); X match_neighbor(); X match_exit(); X if(Wizard(player)) match_absolute(); /* the wizard has long fingers */ X X if((thing = noisy_match_result()) != NOTHING) { X if(db[thing].location == player) { X notify(player, "You already have that!"); X return; X } X switch(Typeof(thing)) { X case TYPE_THING: X if(can_doit(player, thing, "You can't pick that up.")) { X moveto(thing, player); X notify(player, "Taken."); X } X break; X case TYPE_EXIT: X if(!controls(player, thing)) { X notify(player, "You can't pick that up."); X } else if(db[thing].location != NOTHING) { X notify(player, "You can't pick up a linked exit."); X#ifdef RESTRICTED_BUILDING X } else if(!Builder(player)) { X notify(player, "Only authorized builders may pick up exits."); X#endif /* RESTRICTED_BUILDING */ X } else { X /* take it out of location */ X if((loc = getloc(player)) == NOTHING) return; X if(!member(thing, db[loc].exits)) { X notify(player, X "You can't pick up an exit from another room."); X return; X } X db[loc].exits = remove_first(db[loc].exits, thing); X PUSH(thing, db[player].contents); X db[thing].location = player; X notify(player, "Exit taken."); X } X break; X default: X notify(player, "You can't take that!"); X break; X } X } X} X Xvoid do_drop(dbref player, const char *name) X{ X dbref loc; X dbref thing; X char buf[BUFFER_LEN]; X int reward; X X if((loc = getloc(player)) == NOTHING) return; X X init_match(player, name, TYPE_THING); X match_possession(); X X switch(thing = match_result()) { X case NOTHING: X notify(player, "You don't have that!"); X break; X case AMBIGUOUS: X notify(player, "I don't know which you mean!"); X break; X default: X if(db[thing].location != player) { X /* Shouldn't ever happen. */ X notify(player, "You can't drop that."); X } else if(Typeof(thing) == TYPE_EXIT) { X /* special behavior for exits */ X if(!controls(player, loc)) { X notify(player, "You can't put an exit down here."); X return; X } X /* else we can put it down */ X moveto(thing, NOTHING); /* take it out of the pack */ X PUSH(thing, db[loc].exits); X notify(player, "Exit dropped."); X } else if(db[loc].flags & TEMPLE) { X /* sacrifice time */ X send_home(thing); X sprintf(buf, X "%s is consumed in a burst of flame!", db[thing].name); X notify(player, buf); X sprintf(buf, "%s sacrifices %s.", X db[player].name, db[thing].name); X notify_except(db[loc].contents, player, buf); X X /* check for reward */ X if(!controls(player, thing)) { X reward = db[thing].pennies; X if(reward < 1 || db[player].pennies > MAX_PENNIES) { X reward = 1; X } else if(reward > MAX_OBJECT_ENDOWMENT) { X reward = MAX_OBJECT_ENDOWMENT; X } X X db[player].pennies += reward; X sprintf(buf, X "You have received %d %s for your sacrifice.", X reward, X reward == 1 ? "penny" : "pennies"); X notify(player, buf); X } X } else if(db[thing].flags & STICKY) { X send_home(thing); X notify(player, "Dropped."); X } else if(db[loc].location != NOTHING && !(db[loc].flags & STICKY)) { X /* location has immediate dropto */ X moveto(thing, db[loc].location); X notify(player, "Dropped."); X } else { X moveto(thing, loc); X notify(player, "Dropped."); X sprintf(buf, "%s dropped %s.", db[player].name, db[thing].name); X notify_except(db[loc].contents, player, buf); X } X break; X } X} END_OF_FILE if test 8211 -ne `wc -c <'move.c'`; then echo shar: \"'move.c'\" unpacked with wrong size! fi # end of 'move.c' fi echo shar: End of archive 1 \(of 4\). cp /dev/null ark1isdone MISSING="" for I in 1 2 3 4 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 4 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0